1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "varbinder/variableFlags.h"
17 #include "checker/checker.h"
18 #include "checker/checkerContext.h"
19 #include "checker/types/ets/etsObjectType.h"
20 #include "checker/types/ets/etsTupleType.h"
21 #include "ir/astNode.h"
22 #include "lexer/token/tokenType.h"
23 #include "ir/base/catchClause.h"
24 #include "ir/expression.h"
25 #include "ir/typeNode.h"
26 #include "ir/base/scriptFunction.h"
27 #include "ir/base/classProperty.h"
28 #include "ir/base/methodDefinition.h"
29 #include "ir/statements/variableDeclarator.h"
30 #include "ir/statements/switchCaseStatement.h"
31 #include "ir/expressions/identifier.h"
32 #include "ir/expressions/arrayExpression.h"
33 #include "ir/expressions/callExpression.h"
34 #include "ir/expressions/memberExpression.h"
35 #include "ir/expressions/binaryExpression.h"
36 #include "ir/expressions/assignmentExpression.h"
37 #include "ir/statements/labelledStatement.h"
38 #include "ir/ets/etsFunctionType.h"
39 #include "ir/ets/etsNewClassInstanceExpression.h"
40 #include "ir/ts/tsTypeAliasDeclaration.h"
41 #include "ir/ts/tsEnumMember.h"
42 #include "ir/ts/tsTypeParameter.h"
43 #include "ir/ets/etsTypeReference.h"
44 #include "ir/ets/etsTypeReferencePart.h"
45 #include "varbinder/variable.h"
46 #include "varbinder/scope.h"
47 #include "varbinder/declaration.h"
48 #include "checker/ETSchecker.h"
49 #include "checker/ets/typeRelationContext.h"
50 #include "util/helpers.h"
51
52 namespace ark::es2panda::checker {
ValidatePropertyAccess(varbinder::Variable * var,ETSObjectType * obj,const lexer::SourcePosition & pos)53 void ETSChecker::ValidatePropertyAccess(varbinder::Variable *var, ETSObjectType *obj, const lexer::SourcePosition &pos)
54 {
55 if ((Context().Status() & CheckerStatus::IGNORE_VISIBILITY) != 0U) {
56 return;
57 }
58 if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
59 return;
60 }
61
62 if (var->HasFlag(varbinder::VariableFlags::PRIVATE) || var->HasFlag(varbinder::VariableFlags::PROTECTED)) {
63 if ((Context().ContainingClass() == obj ||
64 Context().ContainingClass()->GetOriginalBaseType() == obj->GetOriginalBaseType()) &&
65 obj->IsPropertyInherited(var)) {
66 return;
67 }
68
69 if (var->HasFlag(varbinder::VariableFlags::PROTECTED) && Context().ContainingClass()->IsDescendantOf(obj) &&
70 obj->IsPropertyInherited(var)) {
71 return;
72 }
73
74 auto *currentOutermost = Context().ContainingClass()->OutermostClass();
75 auto *objOutermost = obj->OutermostClass();
76
77 if (currentOutermost != nullptr && objOutermost != nullptr && currentOutermost == objOutermost &&
78 obj->IsPropertyInherited(var)) {
79 return;
80 }
81
82 ThrowTypeError({"Property ", var->Name(), " is not visible here."}, pos);
83 }
84 }
85
ValidateCallExpressionIdentifier(ir::Identifier * const ident,varbinder::Variable * const resolved,Type * const type)86 void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
87 Type *const type)
88 {
89 if (resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) &&
90 ident->Parent()->AsCallExpression()->Callee() != ident) {
91 ThrowTypeError({"Class or interface '", ident->Name(), "' cannot be used as object"}, ident->Start());
92 }
93
94 if (ident->Parent()->AsCallExpression()->Callee() != ident) {
95 return;
96 }
97 if (ident->Variable() != nullptr && // It should always be true!
98 ident->Variable()->Declaration()->Node() != nullptr &&
99 ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) {
100 ThrowTypeError({"Namespace style identifier ", ident->Name(), " is not callable."}, ident->Start());
101 }
102 if (type->IsETSFunctionType() || type->IsETSDynamicType() ||
103 (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
104 return;
105 }
106 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
107 if (TryTransformingToStaticInvoke(ident, type)) {
108 return;
109 }
110
111 if (type->IsETSUnionType()) {
112 for (auto it : type->AsETSUnionType()->ConstituentTypes()) {
113 if (it->IsETSFunctionType() || it->IsETSDynamicType() ||
114 (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
115 return;
116 }
117 }
118 }
119
120 ThrowTypeError({"This expression is not callable."}, ident->Start());
121 }
122
ValidateNewClassInstanceIdentifier(ir::Identifier * const ident,varbinder::Variable * const resolved)123 void ETSChecker::ValidateNewClassInstanceIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)
124 {
125 if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident && (resolved != nullptr) &&
126 !resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)) {
127 ThrowError(ident);
128 }
129 }
130
ValidateMemberIdentifier(ir::Identifier * const ident,varbinder::Variable * const resolved,Type * const type)131 void ETSChecker::ValidateMemberIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
132 Type *const type)
133 {
134 if (resolved->Declaration()->Node()->IsTSEnumDeclaration() &&
135 ident->Parent()->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) {
136 return;
137 }
138 if (ident->Parent()->AsMemberExpression()->IsComputed()) {
139 if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
140 WrongContextErrorClassifyByType(ident, resolved);
141 }
142
143 return;
144 }
145
146 if (!IsReferenceType(type) && !type->IsETSEnumType() && !type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
147 ThrowError(ident);
148 }
149 }
150
ValidatePropertyOrDeclaratorIdentifier(ir::Identifier * const ident,varbinder::Variable * const resolved)151 void ETSChecker::ValidatePropertyOrDeclaratorIdentifier(ir::Identifier *const ident,
152 varbinder::Variable *const resolved)
153 {
154 const auto [target_ident, typeAnnotation] = GetTargetIdentifierAndType(ident);
155
156 if ((resolved != nullptr) && resolved->TsType()->IsETSFunctionType()) {
157 CheckEtsFunctionType(ident, target_ident, typeAnnotation);
158 return;
159 }
160
161 if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
162 ThrowError(ident);
163 }
164 }
165
ValidateAssignmentIdentifier(ir::Identifier * const ident,varbinder::Variable * const resolved,Type * const type)166 void ETSChecker::ValidateAssignmentIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
167 Type *const type)
168 {
169 const auto *const assignmentExpr = ident->Parent()->AsAssignmentExpression();
170 if (assignmentExpr->Left() == ident && (resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
171 WrongContextErrorClassifyByType(ident, resolved);
172 }
173
174 if (assignmentExpr->Right() == ident && (resolved != nullptr) &&
175 (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) {
176 WrongContextErrorClassifyByType(ident, resolved);
177 }
178 }
179
ValidateBinaryExpressionIdentifier(ir::Identifier * const ident,Type * const type)180 bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type)
181 {
182 const auto *const binaryExpr = ident->Parent()->AsBinaryExpression();
183 bool isFinished = false;
184 if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Right() == ident) {
185 if (!IsReferenceType(type)) {
186 ThrowTypeError({R"(Using the "instance of" operator with non-object type ")", ident->Name(), "\""},
187 ident->Start());
188 }
189 isFinished = true;
190 }
191 return isFinished;
192 }
193
ValidateResolvedIdentifier(ir::Identifier * const ident,varbinder::Variable * const resolved)194 void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)
195 {
196 if (resolved == nullptr) {
197 ExtraCheckForResolvedError(ident);
198 return;
199 }
200
201 auto *smartType = Context().GetSmartCast(resolved);
202 auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved));
203
204 switch (ident->Parent()->Type()) {
205 case ir::AstNodeType::CALL_EXPRESSION: {
206 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
207 ValidateCallExpressionIdentifier(ident, resolved, resolvedType);
208 break;
209 }
210 case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
211 ValidateNewClassInstanceIdentifier(ident, resolved);
212 break;
213 }
214 case ir::AstNodeType::MEMBER_EXPRESSION: {
215 ValidateMemberIdentifier(ident, resolved, resolvedType);
216 break;
217 }
218 case ir::AstNodeType::BINARY_EXPRESSION: {
219 if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) {
220 return;
221 }
222
223 [[fallthrough]];
224 }
225 case ir::AstNodeType::UPDATE_EXPRESSION:
226 case ir::AstNodeType::UNARY_EXPRESSION: {
227 if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ()) {
228 WrongContextErrorClassifyByType(ident, resolved);
229 }
230 break;
231 }
232 case ir::AstNodeType::CLASS_PROPERTY:
233 case ir::AstNodeType::VARIABLE_DECLARATOR: {
234 ValidatePropertyOrDeclaratorIdentifier(ident, resolved);
235 break;
236 }
237 case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
238 ValidateAssignmentIdentifier(ident, resolved, resolvedType);
239 break;
240 }
241 default: {
242 if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) {
243 WrongContextErrorClassifyByType(ident, resolved);
244 }
245 break;
246 }
247 }
248 }
249
ValidateUnaryOperatorOperand(varbinder::Variable * variable)250 void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable)
251 {
252 if (IsVariableGetterSetter(variable) || variable->Declaration() == nullptr) {
253 return;
254 }
255
256 if (variable->Declaration()->IsConstDecl() || variable->Declaration()->IsReadonlyDecl()) {
257 std::string_view fieldType = variable->Declaration()->IsConstDecl() ? "constant" : "readonly";
258 if (HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) &&
259 !variable->HasFlag(varbinder::VariableFlags::EXPLICIT_INIT_REQUIRED)) {
260 ThrowTypeError({"Cannot reassign ", fieldType, " ", variable->Name()},
261 variable->Declaration()->Node()->Start());
262 }
263 if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) {
264 ThrowTypeError({"Cannot assign to a ", fieldType, " variable ", variable->Name()},
265 variable->Declaration()->Node()->Start());
266 }
267 }
268 }
269
ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration * const typeAliasNode,const ir::TSTypeParameterInstantiation * const exactTypeParams)270 void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode,
271 const ir::TSTypeParameterInstantiation *const exactTypeParams)
272 {
273 static std::string_view const TRANSFORMATION_NAME = __func__;
274
275 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
276 auto *const clonedNode = typeAliasNode->TypeAnnotation()->Clone(Allocator(), typeAliasNode);
277
278 // Basic check, we really don't want to change the original type nodes, more precise checking should be made
279 ASSERT(clonedNode != typeAliasNode->TypeAnnotation());
280
281 // Currently only reference types are checked. This should be extended for other types in a follow up patch, but for
282 // complete usability, if the type isn't a simple reference type, then doN't check type alias declaration at all.
283 bool checkTypealias = true;
284
285 // Only transforming a temporary cloned node, so no modification is made in the AST
286 clonedNode->TransformChildrenRecursively(
287 [this, &checkTypealias, &exactTypeParams, typeAliasNode](ir::AstNode *const node) -> ir::AstNode * {
288 if (!node->IsETSTypeReference()) {
289 return node;
290 }
291
292 const auto *const nodeIdent = node->AsETSTypeReference()->Part()->Name()->AsIdentifier();
293
294 size_t typeParamIdx = 0;
295 for (const auto *const typeParam : typeAliasNode->TypeParams()->Params()) {
296 if (typeParam->Name()->AsIdentifier()->Variable() == nodeIdent->Variable()) {
297 break;
298 }
299 typeParamIdx++;
300 }
301
302 if (typeParamIdx == typeAliasNode->TypeParams()->Params().size()) {
303 return node;
304 }
305
306 auto *const typeParamType = exactTypeParams->Params().at(typeParamIdx);
307
308 if (!typeParamType->IsETSTypeReference()) {
309 checkTypealias = false;
310 return node;
311 }
312
313 return typeParamType->Clone(Allocator(), nullptr);
314 },
315 TRANSFORMATION_NAME);
316
317 if (checkTypealias) {
318 clonedNode->Check(this);
319 }
320 }
321
ValidateTupleMinElementSize(ir::ArrayExpression * const arrayExpr,ETSTupleType * tuple)322 void ETSChecker::ValidateTupleMinElementSize(ir::ArrayExpression *const arrayExpr, ETSTupleType *tuple)
323 {
324 if (arrayExpr->Elements().size() < static_cast<size_t>(tuple->GetMinTupleSize())) {
325 ThrowTypeError({"Few elements in array initializer for tuple with size of ",
326 static_cast<size_t>(tuple->GetMinTupleSize())},
327 arrayExpr->Start());
328 }
329 }
330 } // namespace ark::es2panda::checker
331