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