1 /*
2 * Copyright (c) 2024-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 "identifierHasVariable.h"
17 #include "ir/base/scriptFunction.h"
18 #include "ir/expressions/memberExpression.h"
19 #include "ir/ts/tsEnumDeclaration.h"
20
21 namespace ark::es2panda::compiler::ast_verifier {
22
23 class IdentifierHasVariable::ExceptionsMatcher {
24 public:
ExceptionsMatcher(const IdentifierHasVariable * inv,const ir::Identifier * ast)25 ExceptionsMatcher(const IdentifierHasVariable *inv, const ir::Identifier *ast) : inv_(inv), ast_(ast) {}
Match()26 bool Match()
27 {
28 auto res = IsLengthProp() || IsEmptyName() || IsInObjectExpr() || IsInPackageDecl() || IsBuiltinType() ||
29 IsUnionMemberAccess();
30 return res;
31 }
32
33 private:
IsLengthProp()34 bool IsLengthProp()
35 {
36 return ast_->Parent() != nullptr && ast_->Parent()->IsMemberExpression() && ast_->Name().Is("length");
37 }
38
IsEmptyName()39 bool IsEmptyName()
40 {
41 // NOTE(kkonkuznetsov): some identifiers have empty names
42 return ast_->Name().Empty();
43 }
44
IsInObjectExpr()45 bool IsInObjectExpr()
46 {
47 // NOTE(kkonkuznetsov): object expressions
48 const auto *parent = ast_->Parent();
49 while (parent != nullptr) {
50 if (parent->IsObjectExpression()) {
51 return true;
52 }
53
54 parent = parent->Parent();
55 }
56 return false;
57 }
58
IsInPackageDecl()59 bool IsInPackageDecl()
60 {
61 // NOTE(kkonkuznetsov): skip package declarations
62 const auto *parent = ast_->Parent();
63 while (parent != nullptr) {
64 if (parent->IsETSPackageDeclaration()) {
65 return true;
66 }
67 parent = parent->Parent();
68 }
69 return false;
70 }
71
IsBuiltinType()72 bool IsBuiltinType()
73 {
74 auto name = ast_->Name();
75 // NOTE(mmartin): find a better solution to handle utility type resolution
76 return name.Is(Signatures::PARTIAL_TYPE_NAME) || name.Is(Signatures::REQUIRED_TYPE_NAME) ||
77 name.Is(Signatures::READONLY_TYPE_NAME) || name.Is(Signatures::FIXED_ARRAY_TYPE_NAME) ||
78 name.Is(compiler::Signatures::ANY_TYPE_NAME);
79 }
80
IsUnionMemberAccess()81 bool IsUnionMemberAccess()
82 {
83 return ast_->Parent() != nullptr && ast_->Parent()->IsMemberExpression() &&
84 ast_->Parent()->AsMemberExpression()->Object()->TsType()->IsETSUnionType() &&
85 !inv_->UnionLoweringOccurred();
86 }
87
88 private:
89 const IdentifierHasVariable *inv_ {};
90 const ir::Identifier *ast_ {};
91 };
92
operator ()(const ir::AstNode * ast)93 CheckResult IdentifierHasVariable::operator()(const ir::AstNode *ast)
94 {
95 if (ast->IsScriptFunction() && ast->AsScriptFunction()->IsExternal()) {
96 // Identifiers in external functions' definitions are not resolved, so skip it
97 ES2PANDA_ASSERT(!ast->AsScriptFunction()->IsExternalOverload());
98 return {CheckDecision::CORRECT, CheckAction::SKIP_SUBTREE};
99 }
100 if (!ast->IsIdentifier()) {
101 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
102 }
103
104 if (ast->AsIdentifier()->Variable() != nullptr) {
105 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
106 }
107
108 const auto *id = ast->AsIdentifier();
109 if (ExceptionsMatcher {this, id}.Match()) {
110 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
111 }
112
113 AddCheckMessage("NULL_VARIABLE", *id);
114 return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
115 }
116
117 } // namespace ark::es2panda::compiler::ast_verifier
118