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
20 namespace ark::es2panda::compiler::ast_verifier {
21
operator ()(CheckContext & ctx,const ir::AstNode * ast)22 [[nodiscard]] CheckResult VariableHasEnclosingScope::operator()(CheckContext &ctx, const ir::AstNode *ast)
23 {
24 const auto maybeVar = VariableHasScope::GetLocalScopeVariable(allocator_, ctx, ast);
25 if (!maybeVar) {
26 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
27 }
28 const auto var = *maybeVar;
29 const auto scope = var->GetScope();
30 if (scope == nullptr) {
31 // already checked
32 return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
33 }
34 const auto encloseScope = scope->EnclosingVariableScope();
35 if (encloseScope == nullptr) {
36 ctx.AddCheckMessage("NO_ENCLOSING_VAR_SCOPE", *ast, ast->Start());
37 return {CheckDecision::INCORRECT, CheckAction::CONTINUE};
38 }
39 const auto node = scope->Node();
40 auto result = std::make_tuple(CheckDecision::CORRECT, CheckAction::CONTINUE);
41 if (!IsContainedIn(ast, node)) {
42 if (CheckCatchClause(ast, node)) {
43 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
44 }
45
46 if (CheckAstExceptions(ast)) {
47 return {CheckDecision::CORRECT, CheckAction::CONTINUE};
48 }
49
50 result = {CheckDecision::INCORRECT, CheckAction::CONTINUE};
51 ctx.AddCheckMessage("VARIABLE_NOT_ENCLOSE_SCOPE", *ast, ast->Start());
52 }
53
54 if (!IsContainedIn<varbinder::Scope>(scope, encloseScope)) {
55 result = {CheckDecision::INCORRECT, CheckAction::CONTINUE};
56 ctx.AddCheckMessage("VARIABLE_NOT_ENCLOSE_SCOPE", *ast, ast->Start());
57 }
58
59 return result;
60 }
61
CheckCatchClause(const ir::AstNode * ast,const ir::AstNode * node) const62 bool VariableHasEnclosingScope::CheckCatchClause(const ir::AstNode *ast, const ir::AstNode *node) const
63 {
64 if (node == nullptr) {
65 return false;
66 }
67
68 // Check that ast node is contained within node parent for Catch Clause:
69 // Catch Clause {
70 // Catch Body {
71 // AST that we need to check
72 // }
73 // Param (Scope Node) {
74 // }
75 // }
76 if (node->Parent() != nullptr && node->Parent()->IsCatchClause()) {
77 return IsContainedIn(ast, node->Parent());
78 }
79
80 return false;
81 }
82
CheckAstExceptions(const ir::AstNode * ast) const83 bool VariableHasEnclosingScope::CheckAstExceptions(const ir::AstNode *ast) const
84 {
85 // Labels are attached to loop scopes,
86 // however label identifier is outside of loop.
87 // Example:
88 //
89 // loop: for (let i = 0; i < 10; i++) {
90 // }
91 return (ast->Parent()->IsLabelledStatement());
92 }
93
94 } // namespace ark::es2panda::compiler::ast_verifier
95