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 "variableHasEnclosingScope.h"
17 #include "variableHasScope.h"
18 #include "helpers.h"
19 #include "ir/expressions/functionExpression.h"
20 #include "ir/base/scriptFunction.h"
21 #include "ir/expressions/identifier.h"
22 #include "ir/typeNode.h"
23
24 namespace ark::es2panda::compiler::ast_verifier {
25
operator ()(CheckContext &ctx, const ir::AstNode *ast)26 [[nodiscard]] CheckResult VariableHasEnclosingScope::operator()(CheckContext &ctx, const ir::AstNode *ast)
27 {
28 const auto maybeVar = VariableHasScope::GetLocalScopeVariable(allocator_, ctx, ast);
29 if (!maybeVar) {
30 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
31 }
32 const auto var = *maybeVar;
33 const auto scope = var->GetScope();
34 if (scope == nullptr) {
35 // already checked
36 return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
37 }
38 const auto encloseScope = scope->EnclosingVariableScope();
39 if (encloseScope == nullptr) {
40 ctx.AddCheckMessage("NO_ENCLOSING_VAR_SCOPE", *ast, ast->Start());
41 return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
42 }
43 const auto node = scope->Node();
44 auto result = std::make_tuple(CheckDecision::CORRECT, CheckAction::CONTINUE);
45 if (!IsContainedIn(ast, node)) {
46 if (CheckCatchClause(ast, node)) {
47 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
48 }
49
50 if (CheckAstExceptions(ast)) {
51 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
52 }
53
54 result = {CheckDecision::INCORRECT, CheckAction::CONTINUE};
55 ctx.AddCheckMessage("VARIABLE_NOT_ENCLOSE_SCOPE", *ast, ast->Start());
56 }
57
58 if (!IsContainedIn<varbinder::Scope>(scope, encloseScope)) {
59 result = {CheckDecision::INCORRECT, CheckAction::CONTINUE};
60 ctx.AddCheckMessage("VARIABLE_NOT_ENCLOSE_SCOPE", *ast, ast->Start());
61 }
62
63 return result;
64 }
65
CheckCatchClause(const ir::AstNode *ast, const ir::AstNode *node) const66 bool VariableHasEnclosingScope::CheckCatchClause(const ir::AstNode *ast, const ir::AstNode *node) const
67 {
68 if (node == nullptr) {
69 return false;
70 }
71
72 // Check that ast node is contained within node parent for Catch Clause:
73 // Catch Clause {
74 // Catch Body {
75 // AST that we need to check
76 // }
77 // Param (Scope Node) {
78 // }
79 // }
80 if (node->Parent() != nullptr && node->Parent()->IsCatchClause()) {
81 return IsContainedIn(ast, node->Parent());
82 }
83
84 return false;
85 }
86
CheckAstExceptions(const ir::AstNode *ast) const87 bool VariableHasEnclosingScope::CheckAstExceptions(const ir::AstNode *ast) const
88 {
89 // NOTE(kkonkuznetsov): skip parameter expression inside arrow function expression
90 auto parent = ast->Parent();
91 while (parent != nullptr) {
92 if (parent->IsETSParameterExpression()) {
93 return true;
94 }
95
96 parent = parent->Parent();
97 }
98
99 // Labels are attached to loop scopes,
100 // however label identifier is outside of loop.
101 // Example:
102 //
103 // loop: for (let i = 0; i < 10; i++) {
104 // }
105 return (ast->Parent()->IsLabelledStatement());
106 }
107
108 } // namespace ark::es2panda::compiler::ast_verifier
109