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         LogTypeError({"Property ", var->Name(), " is not visible here."}, pos);
83         var->SetTsType(GlobalTypeError());
84     }
85 }
86 
ValidateCallExpressionIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved, Type *const type)87 void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
88                                                   Type *const type)
89 {
90     if (resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) &&
91         ident->Parent()->AsCallExpression()->Callee() != ident) {
92         LogTypeError({"Class or interface '", ident->Name(), "' cannot be used as object"}, ident->Start());
93         ident->Variable()->SetTsType(GlobalTypeError());
94     }
95 
96     if (ident->Parent()->AsCallExpression()->Callee() != ident) {
97         return;
98     }
99     if (ident->Variable() != nullptr &&  // It should always be true!
100         ident->Variable()->Declaration()->Node() != nullptr &&
101         ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) {
102         LogTypeError({"Namespace style identifier ", ident->Name(), " is not callable."}, ident->Start());
103         ident->Variable()->SetTsType(GlobalTypeError());
104     }
105     if (type->IsETSFunctionType() || type->IsETSDynamicType() ||
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()) {
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     LogTypeError({"This expression is not callable."}, ident->Start());
124     ident->Variable()->SetTsType(GlobalTypeError());
125 }
126 
ValidateNewClassInstanceIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)127 void ETSChecker::ValidateNewClassInstanceIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)
128 {
129     if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident && (resolved != nullptr) &&
130         !resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)) {
131         LogUnResolvedError(ident);
132         return;
133     }
134 }
135 
ValidateMemberIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved, Type *const type)136 void ETSChecker::ValidateMemberIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
137                                           Type *const type)
138 {
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, resolved);
146         }
147 
148         return;
149     }
150 
151     if (!IsReferenceType(type) && !type->IsETSEnumType() && !type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
152         LogUnResolvedError(ident);
153         return;
154     }
155 }
156 
ValidatePropertyOrDeclaratorIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)157 void ETSChecker::ValidatePropertyOrDeclaratorIdentifier(ir::Identifier *const ident,
158                                                         varbinder::Variable *const resolved)
159 {
160     const auto [target_ident, typeAnnotation] = GetTargetIdentifierAndType(ident);
161 
162     if ((resolved != nullptr) && resolved->TsType()->IsETSFunctionType()) {
163         CheckEtsFunctionType(ident, target_ident);
164         return;
165     }
166 
167     if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
168         LogUnResolvedError(ident);
169         return;
170     }
171 }
172 
ValidateAssignmentIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved, Type *const type)173 void ETSChecker::ValidateAssignmentIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved,
174                                               Type *const type)
175 {
176     const auto *const assignmentExpr = ident->Parent()->AsAssignmentExpression();
177     if (assignmentExpr->Left() == ident && (resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) {
178         WrongContextErrorClassifyByType(ident, resolved);
179         return;
180     }
181 
182     if (assignmentExpr->Right() == ident && (resolved != nullptr) &&
183         (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) {
184         WrongContextErrorClassifyByType(ident, resolved);
185         return;
186     }
187 }
188 
ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type)189 bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type)
190 {
191     const auto *const binaryExpr = ident->Parent()->AsBinaryExpression();
192     bool isFinished = false;
193     if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Right() == ident) {
194         if (!IsReferenceType(type)) {
195             LogTypeError({R"(Using the "instance of" operator with non-object type ")", ident->Name(), "\""},
196                          ident->Start());
197             ident->Variable()->SetTsType(GlobalTypeError());
198         }
199         isFinished = true;
200     }
201     return isFinished;
202 }
203 
ValidateResolvedIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)204 void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved)
205 {
206     auto *smartType = Context().GetSmartCast(resolved);
207     auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved));
208 
209     switch (ident->Parent()->Type()) {
210         case ir::AstNodeType::CALL_EXPRESSION: {
211             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
212             ValidateCallExpressionIdentifier(ident, resolved, resolvedType);
213             break;
214         }
215         case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
216             ValidateNewClassInstanceIdentifier(ident, resolved);
217             break;
218         }
219         case ir::AstNodeType::MEMBER_EXPRESSION: {
220             ValidateMemberIdentifier(ident, resolved, resolvedType);
221             break;
222         }
223         case ir::AstNodeType::BINARY_EXPRESSION: {
224             if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) {
225                 return;
226             }
227 
228             [[fallthrough]];
229         }
230         case ir::AstNodeType::UPDATE_EXPRESSION:
231         case ir::AstNodeType::UNARY_EXPRESSION: {
232             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ()) {
233                 WrongContextErrorClassifyByType(ident, resolved);
234             }
235             break;
236         }
237         case ir::AstNodeType::CLASS_PROPERTY:
238         case ir::AstNodeType::VARIABLE_DECLARATOR: {
239             ValidatePropertyOrDeclaratorIdentifier(ident, resolved);
240             break;
241         }
242         case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
243             ValidateAssignmentIdentifier(ident, resolved, resolvedType);
244             break;
245         }
246         default: {
247             if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) {
248                 WrongContextErrorClassifyByType(ident, resolved);
249             }
250             break;
251         }
252     }
253 }
254 
ValidateUnaryOperatorOperand(varbinder::Variable *variable)255 void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable)
256 {
257     if (variable == nullptr || IsVariableGetterSetter(variable) || variable->Declaration() == nullptr) {
258         return;
259     }
260 
261     if (variable->Declaration()->IsConstDecl() || variable->Declaration()->IsReadonlyDecl()) {
262         std::string_view fieldType = variable->Declaration()->IsConstDecl() ? "constant" : "readonly";
263         if (HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) &&
264             !variable->HasFlag(varbinder::VariableFlags::EXPLICIT_INIT_REQUIRED)) {
265             LogTypeError({"Cannot reassign ", fieldType, " ", variable->Name()},
266                          variable->Declaration()->Node()->Start());
267             variable->SetTsType(GlobalTypeError());
268             return;
269         }
270         if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) {
271             LogTypeError({"Cannot assign to a ", fieldType, " variable ", variable->Name()},
272                          variable->Declaration()->Node()->Start());
273             variable->SetTsType(GlobalTypeError());
274         }
275     }
276 }
277 
ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode, const ir::TSTypeParameterInstantiation *const exactTypeParams)278 void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode,
279                                                        const ir::TSTypeParameterInstantiation *const exactTypeParams)
280 {
281     static std::string_view const TRANSFORMATION_NAME = __func__;
282 
283     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
284     auto *const clonedNode = typeAliasNode->TypeAnnotation()->Clone(Allocator(), typeAliasNode);
285 
286     // Basic check, we really don't want to change the original type nodes, more precise checking should be made
287     ASSERT(clonedNode != typeAliasNode->TypeAnnotation());
288 
289     // Currently only reference types are checked. This should be extended for other types in a follow up patch, but for
290     // complete usability, if the type isn't a simple reference type, then doN't check type alias declaration at all.
291     bool checkTypealias = true;
292 
293     // Only transforming a temporary cloned node, so no modification is made in the AST
294     clonedNode->TransformChildrenRecursively(
295         [this, &checkTypealias, &exactTypeParams, typeAliasNode](ir::AstNode *const node) -> ir::AstNode * {
296             if (!node->IsETSTypeReference()) {
297                 return node;
298             }
299 
300             const auto *const nodeIdent = node->AsETSTypeReference()->Part()->Name()->AsIdentifier();
301 
302             size_t typeParamIdx = 0;
303             for (const auto *const typeParam : typeAliasNode->TypeParams()->Params()) {
304                 if (typeParam->Name()->AsIdentifier()->Variable() == nodeIdent->Variable()) {
305                     break;
306                 }
307                 typeParamIdx++;
308             }
309 
310             if (typeParamIdx == typeAliasNode->TypeParams()->Params().size()) {
311                 return node;
312             }
313 
314             auto *const typeParamType = exactTypeParams->Params().at(typeParamIdx);
315 
316             if (!typeParamType->IsETSTypeReference()) {
317                 checkTypealias = false;
318                 return node;
319             }
320 
321             return typeParamType->Clone(Allocator(), nullptr);
322         },
323         TRANSFORMATION_NAME);
324 
325     if (checkTypealias) {
326         clonedNode->Check(this);
327     }
328 }
329 
ValidateTupleMinElementSize(ir::ArrayExpression *const arrayExpr, ETSTupleType *tuple)330 bool ETSChecker::ValidateTupleMinElementSize(ir::ArrayExpression *const arrayExpr, ETSTupleType *tuple)
331 {
332     if (arrayExpr->Elements().size() < static_cast<size_t>(tuple->GetMinTupleSize())) {
333         LogTypeError({"Few elements in array initializer for tuple with size of ",
334                       static_cast<size_t>(tuple->GetMinTupleSize())},
335                      arrayExpr->Start());
336         return false;
337     }
338     return true;
339 }
340 }  // namespace ark::es2panda::checker
341