• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "variableHasScope.h"
19 #include "ir/base/scriptFunction.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 VariableHasScope::operator()(CheckContext &ctx, const ir::AstNode *ast)
26 {
27     if (!ast->IsIdentifier()) {
28         // Check invariant of Identifier only
29         return {CheckDecision::CORRECT, CheckAction::CONTINUE};
30     }
31 
32     // we will check invariant for only local variables of identifiers
33     if (const auto maybeVar = GetLocalScopeVariable(allocator_, ctx, ast); maybeVar.has_value()) {
34         const auto var = *maybeVar;
35         const auto scope = var->GetScope();
36         if (scope == nullptr) {
37             ctx.AddCheckMessage("NULL_SCOPE_LOCAL_VAR", *ast, ast->Start());
38             return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
39         }
40 
41         auto result = std::make_tuple(CheckDecision::CORRECT, CheckAction::CONTINUE);
42         if (!ScopeEncloseVariable(ctx, var)) {
43             result = {CheckDecision::INCORRECT, CheckAction::CONTINUE};
44         }
45 
46         return result;
47     }
48 
49     return {CheckDecision::CORRECT, CheckAction::CONTINUE};
50 }
51 
GetLocalScopeVariable(ArenaAllocator & allocator,CheckContext & ctx,const ir::AstNode * ast)52 std::optional<varbinder::LocalVariable *> VariableHasScope::GetLocalScopeVariable(ArenaAllocator &allocator,
53                                                                                   CheckContext &ctx,
54                                                                                   const ir::AstNode *ast)
55 {
56     if (!ast->IsIdentifier()) {
57         return std::nullopt;
58     }
59 
60     auto invariantHasVariable = IdentifierHasVariable {allocator};
61     const auto variable = ast->AsIdentifier()->Variable();
62     const auto [decision, action] = invariantHasVariable(ctx, ast);
63 
64     if (variable == nullptr) {
65         // NOTE(kkonkuznetsov): variable should not be null
66         // but currently some identifiers do not have variables,
67         // see exceptions in IdentifierHasVariable check
68         return std::nullopt;
69     }
70 
71     if (decision == CheckDecision::CORRECT && variable->IsLocalVariable()) {
72         const auto localVar = variable->AsLocalVariable();
73         if (localVar->HasFlag(varbinder::VariableFlags::LOCAL)) {
74             return localVar;
75         }
76     }
77 
78     return std::nullopt;
79 }
80 
ScopeEncloseVariable(CheckContext & ctx,const varbinder::LocalVariable * var)81 bool VariableHasScope::ScopeEncloseVariable(CheckContext &ctx, const varbinder::LocalVariable *var)
82 {
83     ASSERT(var);
84 
85     const auto scope = var->GetScope();
86     if (scope == nullptr || var->Declaration() == nullptr) {
87         return true;
88     }
89 
90     const auto node = var->Declaration()->Node();
91     if (node == nullptr) {
92         return true;
93     }
94 
95     const auto varStart = node->Start();
96     bool isOk = true;
97     if (scope->Bindings().count(var->Name()) == 0) {
98         ctx.AddCheckMessage("SCOPE_DO_NOT_ENCLOSE_LOCAL_VAR", *node, varStart);
99         isOk = false;
100     }
101 
102     const auto scopeNode = scope->Node();
103     const auto varNode = node;
104     bool skip = CheckAstExceptions(varNode);
105 
106     if (!IsContainedIn(varNode, scopeNode) || scopeNode == nullptr) {
107         if (!skip) {
108             ctx.AddCheckMessage("SCOPE_NODE_DONT_DOMINATE_VAR_NODE", *node, varStart);
109             isOk = false;
110         }
111     }
112 
113     const auto &decls = scope->Decls();
114     const auto declDominate = std::count(decls.begin(), decls.end(), var->Declaration());
115     if (declDominate == 0) {
116         if (!skip) {
117             ctx.AddCheckMessage("SCOPE_DECL_DONT_DOMINATE_VAR_DECL", *node, varStart);
118             isOk = false;
119         }
120     }
121 
122     return isOk;
123 }
124 
CheckAstExceptions(const ir::AstNode * ast)125 bool VariableHasScope::CheckAstExceptions(const ir::AstNode *ast)
126 {
127     // Labels are attached to loop scopes,
128     // however label identifier is outside of loop.
129     // Example:
130     //
131     // ```
132     // loop: for (let i = 0; i < 10; i++) {
133     // }
134     // ```
135     return ast->IsLabelledStatement();
136 }
137 
138 }  // namespace ark::es2panda::compiler::ast_verifier
139