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 24namespace ark::es2panda::compiler::ast_verifier { 25 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 66bool 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 87bool 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