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 23namespace ark::es2panda::compiler::ast_verifier { 24 25CheckResult 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 52std::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 81bool 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 125bool 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