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