• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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/types/ets/etsTupleType.h"
18 #include "checker/ETSchecker.h"
19 
20 namespace ark::es2panda::checker {
ValidatePropertyAccess(varbinder::Variable * var,ETSObjectType * obj,const lexer::SourcePosition & pos)21 void ETSChecker::ValidatePropertyAccess(varbinder::Variable *var, ETSObjectType *obj, const lexer::SourcePosition &pos)
22 {
23     if ((Context().Status() & CheckerStatus::IGNORE_VISIBILITY) != 0U) {
24         return;
25     }
26     if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
27         return;
28     }
29 
30     if (var->HasFlag(varbinder::VariableFlags::PRIVATE) || var->HasFlag(varbinder::VariableFlags::PROTECTED)) {
31         if ((Context().ContainingClass() == obj ||
32              Context().ContainingClass()->GetOriginalBaseType() == obj->GetOriginalBaseType()) &&
33             obj->IsPropertyInherited(var)) {
34             return;
35         }
36 
37         if (var->HasFlag(varbinder::VariableFlags::PROTECTED) && Context().ContainingClass()->IsDescendantOf(obj) &&
38             obj->IsPropertyInherited(var)) {
39             return;
40         }
41 
42         auto *currentOutermost = Context().ContainingClass()->OutermostClass();
43         auto *objOutermost = obj->OutermostClass();
44 
45         if (currentOutermost != nullptr && objOutermost != nullptr && currentOutermost == objOutermost &&
46             obj->IsPropertyInherited(var)) {
47             return;
48         }
49 
50         std::ignore = TypeError(var, diagnostic::PROP_INVISIBLE, {var->Name()}, pos);
51     }
52 }
53 
ValidateCallExpressionIdentifier(ir::Identifier * const ident,Type * const type)54 void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, Type *const type)
55 {
56     if (ident->Variable()->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) &&
57         ident->Parent()->AsCallExpression()->Callee() != ident) {
58         std::ignore =
59             TypeError(ident->Variable(), diagnostic::CLASS_OR_IFACE_AS_OBJ, {ident->ToString()}, ident->Start());
60     }
61 
62     if (ident->Parent()->AsCallExpression()->Callee() != ident) {
63         return;
64     }
65 
66     ES2PANDA_ASSERT(ident->Variable() != nullptr);
67     if (ident->Variable()->Declaration()->Node() != nullptr &&
68         ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) {
69         std::ignore = TypeError(ident->Variable(), diagnostic::NAMESPACE_CALL, {ident->ToString()}, ident->Start());
70     }
71     if (type->IsETSFunctionType() || type->IsETSDynamicType()) {
72         return;
73     }
74     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
75     if (TryTransformingToStaticInvoke(ident, type)) {
76         return;
77     }
78 
79     std::ignore = TypeError(ident->Variable(), diagnostic::EXPR_NOT_CALLABLE, {}, ident->Start());
80 }
81 
ValidateNewClassInstanceIdentifier(ir::Identifier * const ident)82 void ETSChecker::ValidateNewClassInstanceIdentifier(ir::Identifier *const ident)
83 {
84     auto const resolved = ident->Variable();
85     ES2PANDA_ASSERT(resolved != nullptr);
86 
87     if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident &&
88         !resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) && !resolved->TsType()->IsTypeError()) {
89         LogError(diagnostic::REF_INVALID, {ident->Name()}, ident->Start());
90     }
91 }
92 
ValidateMemberIdentifier(ir::Identifier * const ident)93 void ETSChecker::ValidateMemberIdentifier(ir::Identifier *const ident)
94 {
95     auto const resolved = ident->Variable();
96     // Handles Enum[enumVar] MemberExpression
97     if (resolved->Declaration()->Node()->IsClassDefinition() &&
98         resolved->Declaration()->Node()->AsClassDefinition()->TsType()->IsETSEnumType() &&
99         ident->Parent()->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) {
100         return;
101     }
102     if (ident->Parent()->AsMemberExpression()->IsComputed()) {
103         if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
104             WrongContextErrorClassifyByType(ident);
105         }
106         return;
107     }
108 }
109 
ValidateAssignmentIdentifier(ir::Identifier * const ident,Type * const type)110 void ETSChecker::ValidateAssignmentIdentifier(ir::Identifier *const ident, Type *const type)
111 {
112     auto const resolved = ident->Variable();
113     const auto *const assignmentExpr = ident->Parent()->AsAssignmentExpression();
114     if (assignmentExpr->Left() == ident && (resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
115         WrongContextErrorClassifyByType(ident);
116         return;
117     }
118 
119     if (assignmentExpr->Right() == ident && (resolved != nullptr) &&
120         (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) {
121         WrongContextErrorClassifyByType(ident);
122         return;
123     }
124 }
125 
ValidateBinaryExpressionIdentifier(ir::Identifier * const ident,Type * const type)126 bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type)
127 {
128     auto const resolved = ident->Variable();
129     const auto *const binaryExpr = ident->Parent()->AsBinaryExpression();
130     bool isFinished = false;
131 
132     if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Left() == ident) {
133         if (!IsReferenceType(type)) {
134             std::ignore =
135                 TypeError(ident->Variable(), diagnostic::INSTANCEOF_NONOBJECT, {ident->Name()}, ident->Start());
136         }
137         isFinished = true;
138     }
139 
140     if (binaryExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING ||
141         binaryExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR ||
142         binaryExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
143         if ((resolved != nullptr) && (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) {
144             WrongContextErrorClassifyByType(ident);
145         }
146         isFinished = true;
147     }
148     return isFinished;
149 }
150 
ValidateResolvedIdentifier(ir::Identifier * const ident)151 void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident)
152 {
153     varbinder::Variable *const resolved = ident->Variable();
154     if (resolved->Declaration()->IsAnnotationDecl() && !ident->IsAnnotationUsage()) {
155         LogError(diagnostic::ANNOT_WITHOUT_AT, {}, ident->Start());
156     }
157 
158     auto *smartType = Context().GetSmartCast(resolved);
159     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
160     auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved));
161 
162     switch (ident->Parent()->Type()) {
163         case ir::AstNodeType::CALL_EXPRESSION:
164             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
165             ValidateCallExpressionIdentifier(ident, resolvedType);
166             break;
167         case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION:
168             ValidateNewClassInstanceIdentifier(ident);
169             break;
170         case ir::AstNodeType::MEMBER_EXPRESSION:
171             ValidateMemberIdentifier(ident);
172             break;
173         case ir::AstNodeType::BINARY_EXPRESSION:
174             if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) {
175                 return;
176             }
177             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) {
178                 WrongContextErrorClassifyByType(ident);
179             }
180             break;
181         case ir::AstNodeType::UPDATE_EXPRESSION:
182         case ir::AstNodeType::UNARY_EXPRESSION:
183             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ()) {
184                 WrongContextErrorClassifyByType(ident);
185             }
186             break;
187         case ir::AstNodeType::ASSIGNMENT_EXPRESSION:
188             ValidateAssignmentIdentifier(ident, resolvedType);
189             break;
190         default:
191             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) {
192                 WrongContextErrorClassifyByType(ident);
193             }
194     }
195 }
196 
ValidateAnnotationPropertyType(checker::Type * type)197 bool ETSChecker::ValidateAnnotationPropertyType(checker::Type *type)
198 {
199     if (type == nullptr || type->IsTypeError()) {
200         ES2PANDA_ASSERT(IsAnyError());
201         return false;
202     }
203 
204     if (type->IsETSArrayType() || type->IsETSResizableArrayType()) {
205         return ValidateAnnotationPropertyType(MaybeUnboxType(GetElementTypeOfArray(type)));
206     }
207 
208     return type->HasTypeFlag(TypeFlag::ETS_NUMERIC | TypeFlag::ETS_ENUM | TypeFlag::ETS_BOOLEAN) ||
209            type->IsETSStringType();
210 }
211 
ValidateUnaryOperatorOperand(varbinder::Variable * variable)212 void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable)
213 {
214     if (IsVariableGetterSetter(variable)) {
215         return;
216     }
217 
218     if (variable->Declaration()->IsConstDecl() || variable->Declaration()->IsReadonlyDecl()) {
219         std::string_view fieldType = variable->Declaration()->IsConstDecl() ? "constant" : "readonly";
220         if ((HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) &&
221              !variable->HasFlag(varbinder::VariableFlags::EXPLICIT_INIT_REQUIRED)) ||
222             (variable->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK) &&
223              variable->HasFlag(varbinder::VariableFlags::INITIALIZED))) {
224             std::ignore = TypeError(variable, diagnostic::FIELD_REASSIGNMENT, {fieldType, variable->Name()},
225                                     variable->Declaration()->Node()->Start());
226             return;
227         }
228         if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) {
229             std::ignore = TypeError(variable, diagnostic::FIELD_ASSIGN_TYPE_MISMATCH, {fieldType, variable->Name()},
230                                     variable->Declaration()->Node()->Start());
231         }
232 
233         if (variable->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK)) {
234             variable->AddFlag(varbinder::VariableFlags::INITIALIZED);
235         }
236     }
237 }
238 
ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration * const typeAliasNode,const ir::TSTypeParameterInstantiation * const exactTypeParams)239 void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode,
240                                                        const ir::TSTypeParameterInstantiation *const exactTypeParams)
241 {
242     static std::string_view const TRANSFORMATION_NAME = __func__;
243 
244     RecursionPreserver<ir::TSTypeAliasDeclaration> recursionPreserver(elementStack_, typeAliasNode);
245 
246     if (*recursionPreserver) {
247         // NOTE(SM): We in recursion for type alias. So isn't make sense check the same type alias twice;
248         return;
249     }
250 
251     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
252     auto *const clonedNode = typeAliasNode->TypeAnnotation()->Clone(Allocator(), typeAliasNode);
253 
254     // Basic check, we really don't want to change the original type nodes, more precise checking should be made
255     ES2PANDA_ASSERT(clonedNode != typeAliasNode->TypeAnnotation());
256 
257     // Currently only reference types are checked. This should be extended for other types in a follow up patch, but for
258     // complete usability, if the type isn't a simple reference type, then doN't check type alias declaration at all.
259     bool checkTypealias = true;
260 
261     // Only transforming a temporary cloned node, so no modification is made in the AST
262     clonedNode->TransformChildrenRecursively(
263         // CC-OFFNXT(G.FMT.14-CPP) project code style
264         [this, &checkTypealias, &exactTypeParams, typeAliasNode](ir::AstNode *const node) -> ir::AstNode * {
265             if (!node->IsETSTypeReference()) {
266                 return node;
267             }
268 
269             const auto *const nodeIdent = node->AsETSTypeReference()->Part()->GetIdent();
270 
271             size_t typeParamIdx = 0;
272             for (const auto *const typeParam : typeAliasNode->TypeParams()->Params()) {
273                 if (typeParam->Name()->AsIdentifier()->Variable() == nodeIdent->Variable()) {
274                     break;
275                 }
276                 typeParamIdx++;
277             }
278 
279             if (typeParamIdx == typeAliasNode->TypeParams()->Params().size()) {
280                 return node;
281             }
282 
283             ir::TypeNode *typeParamType = nullptr;
284 
285             if (exactTypeParams != nullptr && exactTypeParams->Params().size() > typeParamIdx) {
286                 typeParamType = exactTypeParams->Params().at(typeParamIdx);
287             } else {
288                 typeParamType = typeAliasNode->TypeParams()->Params().at(typeParamIdx)->DefaultType();
289             }
290 
291             if (!typeParamType->IsETSTypeReference()) {
292                 checkTypealias = false;
293                 return node;
294             }
295 
296             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
297             return typeParamType->Clone(Allocator(), nullptr);
298         },
299         TRANSFORMATION_NAME);
300 
301     if (checkTypealias) {
302         clonedNode->Check(this);
303     }
304 }
305 
IsArrayExprSizeValidForTuple(const ir::ArrayExpression * const arrayExpr,const ETSTupleType * const tuple)306 bool ETSChecker::IsArrayExprSizeValidForTuple(const ir::ArrayExpression *const arrayExpr,
307                                               const ETSTupleType *const tuple)
308 {
309     std::size_t size = 0;
310 
311     for (ir::Expression *const element : arrayExpr->Elements()) {
312         if (element->IsSpreadElement()) {
313             const Type *const argType = element->AsSpreadElement()->Argument()->Check(this);
314             if (argType->IsETSTupleType()) {
315                 size += argType->AsETSTupleType()->GetTupleSize();
316                 continue;
317             }
318             LogError(diagnostic::INVALID_SPREAD_IN_TUPLE, {argType}, element->Start());
319         }
320         ++size;
321     }
322 
323     if (size != tuple->GetTupleSize()) {
324         LogError(diagnostic::TUPLE_WRONG_NUMBER_OF_ELEMS, {size, tuple->GetTupleSize()}, arrayExpr->Start());
325         return false;
326     }
327 
328     return true;
329 }
330 }  // namespace ark::es2panda::checker
331