1 /*
2  * Copyright (c) 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 "helpers.h"
17 #include "identifierHasVariable.h"
18 #include "ir/base/scriptFunction.h"
19 #include "ir/expressions/memberExpression.h"
20 #include "ir/ts/tsEnumDeclaration.h"
21 #include "ir/typeNode.h"
22 
23 namespace ark::es2panda::compiler::ast_verifier {
24 
operator ()(CheckContext &ctx, const ir::AstNode *ast)25 CheckResult IdentifierHasVariable::operator()(CheckContext &ctx, const ir::AstNode *ast)
26 {
27     if (!ast->IsIdentifier()) {
28         return {CheckDecision::CORRECT, CheckAction::CONTINUE};
29     }
30 
31     if (ast->AsIdentifier()->Variable() != nullptr) {
32         return {CheckDecision::CORRECT, CheckAction::CONTINUE};
33     }
34 
35     const auto *id = ast->AsIdentifier();
36     if (CheckAstExceptions(id)) {
37         return {CheckDecision::CORRECT, CheckAction::CONTINUE};
38     }
39 
40     // Another function with exceptions to reduce function size
41     if (CheckMoreAstExceptions(id)) {
42         return {CheckDecision::CORRECT, CheckAction::CONTINUE};
43     }
44 
45     ctx.AddCheckMessage("NULL_VARIABLE", *id, id->Start());
46     return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
47 }
48 
CheckMoreAstExceptions(const ir::Identifier *ast) const49 bool IdentifierHasVariable::CheckMoreAstExceptions(const ir::Identifier *ast) const
50 {
51     // NOTE(kkonkuznetsov): skip async functions
52     auto parent = ast->Parent();
53     while (parent != nullptr) {
54         if (parent->IsScriptFunction()) {
55             auto script = parent->AsScriptFunction();
56             if (script->IsAsyncFunc()) {
57                 return true;
58             }
59 
60             break;
61         }
62 
63         parent = parent->Parent();
64     }
65 
66     // NOTE(kkonkuznetsov): skip reexport declarations
67     if (ast->Parent() != nullptr && ast->Parent()->Parent() != nullptr) {
68         parent = ast->Parent()->Parent();
69         if (parent->IsETSReExportDeclaration()) {
70             return true;
71         }
72     }
73 
74     // NOTE(kkonkuznetsov): object expressions
75     parent = ast->Parent();
76     while (parent != nullptr) {
77         if (parent->IsObjectExpression()) {
78             return true;
79         }
80 
81         parent = parent->Parent();
82     }
83 
84     // NOTE(kkonkuznetsov): some identifiers have empty names
85     if (ast->Name().Empty()) {
86         return true;
87     }
88 
89     // NOTE(mmartin): find a better solution to handle utility type resolution
90     if (ast->Name().Is(Signatures::PARTIAL_TYPE_NAME) || ast->Name().Is(Signatures::REQUIRED_TYPE_NAME) ||
91         ast->Name().Is(Signatures::READONLY_TYPE_NAME)) {
92         return true;
93     }
94 
95     return false;
96 }
97 
CheckAstExceptions(const ir::Identifier *ast) const98 bool IdentifierHasVariable::CheckAstExceptions(const ir::Identifier *ast) const
99 {
100     // NOTE(kkonkuznetsov): skip enums
101     if (ast->Parent()->IsMemberExpression() &&
102         (ast->Parent()->AsMemberExpression()->Object()->TsType() == nullptr ||
103          ast->Parent()->AsMemberExpression()->Object()->TsType()->IsETSEnumType())) {
104         return true;
105     }
106 
107     // NOTE(kkonkuznetsov): skip length property
108     if (ast->Parent()->IsMemberExpression() && ast->Name().Is("length")) {
109         return true;
110     }
111 
112     // NOTE(kkonkuznetsov): skip anonymous class id
113     if (ast->Parent()->Parent() != nullptr && ast->Parent()->Parent()->IsETSNewClassInstanceExpression()) {
114         return true;
115     }
116 
117     // NOTE(kkonkuznetsov): skip package declarations
118     auto parent = ast->Parent();
119     while (parent != nullptr) {
120         if (parent->IsETSPackageDeclaration()) {
121             return true;
122         }
123 
124         parent = parent->Parent();
125     }
126 
127     return false;
128 }
129 
130 }  // namespace ark::es2panda::compiler::ast_verifier
131