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