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