• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         std::ignore = TypeError(var, FormatMsg({"Property ", var->Name(), " is not visible here."}), pos);
83     }
84 }
85 
ValidateCallExpressionIdentifier(ir::Identifier * const ident,Type * const type)86 void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, Type *const type)
87 {
88     if (ident->Variable()->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) &&
89         ident->Parent()->AsCallExpression()->Callee() != ident) {
90         std::ignore =
91             TypeError(ident->Variable(),
92                       FormatMsg({"Class or interface '", ident->Name(), "' cannot be used as object"}), ident->Start());
93     }
94 
95     if (ident->Parent()->AsCallExpression()->Callee() != ident) {
96         return;
97     }
98     if (ident->Variable() != nullptr &&  // It should always be true!
99         ident->Variable()->Declaration()->Node() != nullptr &&
100         ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) {
101         std::ignore =
102             TypeError(ident->Variable(), FormatMsg({"Namespace style identifier ", ident->Name(), " is not callable."}),
103                       ident->Start());
104     }
105     if (type->IsETSFunctionType() || type->IsETSDynamicType() ||  // NOTE(vpukhov): #19822
106         (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
107         return;
108     }
109     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
110     if (TryTransformingToStaticInvoke(ident, type)) {
111         return;
112     }
113 
114     if (type->IsETSUnionType()) {  // NOTE(vpukhov): #19822
115         for (auto it : type->AsETSUnionType()->ConstituentTypes()) {
116             if (it->IsETSFunctionType() || it->IsETSDynamicType() ||
117                 (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
118                 return;
119             }
120         }
121     }
122 
123     std::ignore = TypeError(ident->Variable(), FormatMsg({"This expression is not callable."}), ident->Start());
124 }
125 
ValidateNewClassInstanceIdentifier(ir::Identifier * const ident)126 void ETSChecker::ValidateNewClassInstanceIdentifier(ir::Identifier *const ident)
127 {
128     auto const resolved = ident->Variable();
129     if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident && (resolved != nullptr) &&
130         !resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)) {
131         LogUnresolvedReferenceError(ident);
132         return;
133     }
134 }
135 
ValidateMemberIdentifier(ir::Identifier * const ident)136 void ETSChecker::ValidateMemberIdentifier(ir::Identifier *const ident)
137 {
138     auto const resolved = ident->Variable();
139     if (resolved->Declaration()->Node()->IsTSEnumDeclaration() &&
140         ident->Parent()->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) {
141         return;
142     }
143     if (ident->Parent()->AsMemberExpression()->IsComputed()) {
144         if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
145             WrongContextErrorClassifyByType(ident);
146         }
147         return;
148     }
149 }
150 
ValidatePropertyOrDeclaratorIdentifier(ir::Identifier * const ident)151 void ETSChecker::ValidatePropertyOrDeclaratorIdentifier(ir::Identifier *const ident)
152 {
153     const auto [target_ident, typeAnnotation] = GetTargetIdentifierAndType(ident);
154     auto const resolved = ident->Variable();
155     if ((resolved != nullptr) && resolved->TsType() != nullptr && resolved->TsType()->IsETSFunctionType()) {
156         CheckEtsFunctionType(ident, target_ident);
157         return;
158     }
159 
160     if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
161         LogUnresolvedReferenceError(ident);
162         return;
163     }
164 }
165 
ValidateAssignmentIdentifier(ir::Identifier * const ident,Type * const type)166 void ETSChecker::ValidateAssignmentIdentifier(ir::Identifier *const ident, Type *const type)
167 {
168     auto const resolved = ident->Variable();
169     const auto *const assignmentExpr = ident->Parent()->AsAssignmentExpression();
170     if (assignmentExpr->Left() == ident && (resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
171         WrongContextErrorClassifyByType(ident);
172         return;
173     }
174 
175     if (assignmentExpr->Right() == ident && (resolved != nullptr) &&
176         (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) {
177         WrongContextErrorClassifyByType(ident);
178         return;
179     }
180 }
181 
ValidateBinaryExpressionIdentifier(ir::Identifier * const ident,Type * const type)182 bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type)
183 {
184     const auto *const binaryExpr = ident->Parent()->AsBinaryExpression();
185     bool isFinished = false;
186     if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Right() == ident) {
187         if (!IsReferenceType(type)) {
188             std::ignore = TypeError(
189                 ident->Variable(),
190                 FormatMsg({R"(Using the "instance of" operator with non-object type ")", ident->Name(), "\""}),
191                 ident->Start());
192         }
193         isFinished = true;
194     }
195     return isFinished;
196 }
197 
ValidateOverloadedFunctionIdentifier(ETSChecker * checker,ir::Identifier * const ident)198 static void ValidateOverloadedFunctionIdentifier(ETSChecker *checker, ir::Identifier *const ident)
199 {
200     auto const callable =
201         ident->Parent()->IsMemberExpression() && ident->Parent()->AsMemberExpression()->Property() == ident
202             ? ident->Parent()
203             : ident;
204     if (callable->Parent()->IsCallExpression() && callable->Parent()->AsCallExpression()->Callee() == callable) {
205         return;
206     }
207     checker->LogTypeError({"Overloaded function identifier \"", ident->Name(), "\" can not be used as value"},
208                           ident->Start());
209 }
210 
ValidateResolvedIdentifier(ir::Identifier * const ident)211 void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident)
212 {
213     varbinder::Variable *const resolved = ident->Variable();
214     if (resolved->Declaration()->IsAnnotationDecl() && !ident->IsAnnotationUsage()) {
215         LogTypeError("Annotation missing '@' symbol before annotation name.", ident->Start());
216     }
217 
218     auto *smartType = Context().GetSmartCast(resolved);
219     auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved));
220 
221     if (resolvedType->IsETSFunctionType() && !resolvedType->IsETSArrowType()) {
222         ValidateOverloadedFunctionIdentifier(this, ident);
223         return;
224     }
225 
226     switch (ident->Parent()->Type()) {
227         case ir::AstNodeType::CALL_EXPRESSION:
228             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
229             ValidateCallExpressionIdentifier(ident, resolvedType);
230             break;
231         case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION:
232             ValidateNewClassInstanceIdentifier(ident);
233             break;
234         case ir::AstNodeType::MEMBER_EXPRESSION:
235             ValidateMemberIdentifier(ident);
236             break;
237         case ir::AstNodeType::BINARY_EXPRESSION:
238             if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) {
239                 return;
240             }
241             [[fallthrough]];
242         case ir::AstNodeType::UPDATE_EXPRESSION:
243         case ir::AstNodeType::UNARY_EXPRESSION:
244             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ()) {
245                 WrongContextErrorClassifyByType(ident);
246             }
247             break;
248         case ir::AstNodeType::CLASS_PROPERTY:
249         case ir::AstNodeType::VARIABLE_DECLARATOR:
250             ValidatePropertyOrDeclaratorIdentifier(ident);
251             break;
252         case ir::AstNodeType::ASSIGNMENT_EXPRESSION:
253             ValidateAssignmentIdentifier(ident, resolvedType);
254             break;
255         default:
256             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) {
257                 WrongContextErrorClassifyByType(ident);
258             }
259     }
260 }
261 
ValidateAnnotationPropertyType(checker::Type * type)262 bool ETSChecker::ValidateAnnotationPropertyType(checker::Type *type)
263 {
264     if (type->IsETSArrayType()) {
265         return ValidateAnnotationPropertyType(type->AsETSArrayType()->ElementType());
266     }
267 
268     return type->HasTypeFlag(TypeFlag::ETS_NUMERIC | TypeFlag::ETS_ENUM | TypeFlag::ETS_BOOLEAN) ||
269            type->IsETSStringType();
270 }
271 
ValidateUnaryOperatorOperand(varbinder::Variable * variable)272 void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable)
273 {
274     if (variable == nullptr || IsVariableGetterSetter(variable) || variable->Declaration() == nullptr) {
275         return;
276     }
277 
278     if (variable->Declaration()->IsConstDecl() || variable->Declaration()->IsReadonlyDecl()) {
279         std::string_view fieldType = variable->Declaration()->IsConstDecl() ? "constant" : "readonly";
280         if (HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) &&
281             !variable->HasFlag(varbinder::VariableFlags::EXPLICIT_INIT_REQUIRED)) {
282             std::ignore = TypeError(variable, FormatMsg({"Cannot reassign ", fieldType, " ", variable->Name()}),
283                                     variable->Declaration()->Node()->Start());
284             return;
285         }
286         if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) {
287             std::ignore =
288                 TypeError(variable, FormatMsg({"Cannot assign to a ", fieldType, " variable ", variable->Name()}),
289                           variable->Declaration()->Node()->Start());
290         }
291     }
292 }
293 
ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration * const typeAliasNode,const ir::TSTypeParameterInstantiation * const exactTypeParams)294 void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode,
295                                                        const ir::TSTypeParameterInstantiation *const exactTypeParams)
296 {
297     static std::string_view const TRANSFORMATION_NAME = __func__;
298 
299     RecursionPreserver<ir::TSTypeAliasDeclaration> recursionPreserver(elementStack_, typeAliasNode);
300 
301     if (*recursionPreserver) {
302         // NOTE(SM): We in recursion for type alias. So isn't make sense check the same type alias twice;
303         return;
304     }
305 
306     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
307     auto *const clonedNode = typeAliasNode->TypeAnnotation()->Clone(Allocator(), typeAliasNode);
308 
309     // Basic check, we really don't want to change the original type nodes, more precise checking should be made
310     ASSERT(clonedNode != typeAliasNode->TypeAnnotation());
311 
312     // Currently only reference types are checked. This should be extended for other types in a follow up patch, but for
313     // complete usability, if the type isn't a simple reference type, then doN't check type alias declaration at all.
314     bool checkTypealias = true;
315 
316     // Only transforming a temporary cloned node, so no modification is made in the AST
317     clonedNode->TransformChildrenRecursively(
318         // CC-OFFNXT(G.FMT.14-CPP) project code style
319         [this, &checkTypealias, &exactTypeParams, typeAliasNode](ir::AstNode *const node) -> ir::AstNode * {
320             if (!node->IsETSTypeReference()) {
321                 return node;
322             }
323 
324             const auto *const nodeIdent = node->AsETSTypeReference()->Part()->Name()->AsIdentifier();
325 
326             size_t typeParamIdx = 0;
327             for (const auto *const typeParam : typeAliasNode->TypeParams()->Params()) {
328                 if (typeParam->Name()->AsIdentifier()->Variable() == nodeIdent->Variable()) {
329                     break;
330                 }
331                 typeParamIdx++;
332             }
333 
334             if (typeParamIdx == typeAliasNode->TypeParams()->Params().size()) {
335                 return node;
336             }
337 
338             auto *const typeParamType = exactTypeParams->Params().at(typeParamIdx);
339 
340             if (!typeParamType->IsETSTypeReference()) {
341                 checkTypealias = false;
342                 return node;
343             }
344 
345             return typeParamType->Clone(Allocator(), nullptr);
346         },
347         TRANSFORMATION_NAME);
348 
349     if (checkTypealias) {
350         clonedNode->Check(this);
351     }
352 }
353 
ValidateTupleMinElementSize(ir::ArrayExpression * const arrayExpr,ETSTupleType * tuple)354 bool ETSChecker::ValidateTupleMinElementSize(ir::ArrayExpression *const arrayExpr, ETSTupleType *tuple)
355 {
356     size_t size = 0;
357     for (ir::Expression *element : arrayExpr->Elements()) {
358         if (element->IsSpreadElement()) {
359             Type *argType = element->AsSpreadElement()->Argument()->Check(this);
360             if (argType->IsETSTupleType()) {
361                 size += argType->AsETSTupleType()->GetTupleTypesList().size();
362                 continue;
363             }
364             LogTypeError({"'", argType, "' cannot be spread in tuple."}, element->Start());
365         }
366         ++size;
367     }
368 
369     if (size < static_cast<size_t>(tuple->GetMinTupleSize())) {
370         LogTypeError({"Few elements in array initializer for tuple with size of ",
371                       static_cast<size_t>(tuple->GetMinTupleSize())},
372                      arrayExpr->Start());
373         return false;
374     }
375 
376     return true;
377 }
378 }  // namespace ark::es2panda::checker
379