From 8bb1d8a98beb9f8b4e1ef139c619bbdd9c66ba96 Mon Sep 17 00:00:00 2001 From: Tatiana Date: Thu, 12 Oct 2023 16:36:22 +0300 Subject: [PATCH 1/2] Add AST Verifier feature Signed-off-by: Tatiana --- BUILD.gn | 1 + CMakeLists.txt | 1 + compiler/core/ASTVerifier.cpp | 409 ++++++++++++++++++++++++++++++ compiler/core/ASTVerifier.h | 55 ++++ compiler/lowering/phase.cpp | 9 + ir/astNode.h | 10 + test/CMakeLists.txt | 12 + test/public/ast_verifier_test.cpp | 71 ++++++ 8 files changed, 568 insertions(+) create mode 100644 compiler/core/ASTVerifier.cpp create mode 100644 compiler/core/ASTVerifier.h create mode 100644 test/public/ast_verifier_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 99b4a26ef..8663df872 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -138,6 +138,7 @@ libes2panda_sources = [ "compiler/core/JSCompiler.cpp", "compiler/core/JSemitter.cpp", "compiler/core/codeGen.cpp", + "compiler/core/ASTVerifier.cpp", "compiler/core/compileJob.cpp", "compiler/core/compileQueue.cpp", "compiler/core/compilerContext.cpp", diff --git a/CMakeLists.txt b/CMakeLists.txt index e49880048..b34882d04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(ES2PANDA_LIB_SRC compiler/base/literals.cpp compiler/base/lreference.cpp compiler/base/optionalChain.cpp + compiler/core/ASTVerifier.cpp compiler/core/codeGen.cpp compiler/core/compileJob.cpp compiler/core/compileQueue.cpp diff --git a/compiler/core/ASTVerifier.cpp b/compiler/core/ASTVerifier.cpp new file mode 100644 index 000000000..f59438c30 --- /dev/null +++ b/compiler/core/ASTVerifier.cpp @@ -0,0 +1,409 @@ +/** + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ASTVerifier.h" + +#include "es2panda.h" +#include "binder/variableFlags.h" +#include "binder/scope.h" +#include "ir/astNode.h" +#include "ir/base/catchClause.h" +#include "ir/base/classDefinition.h" +#include "ir/base/classStaticBlock.h" +#include "ir/base/methodDefinition.h" +#include "ir/base/scriptFunction.h" +#include "ir/ets/etsFunctionType.h" +#include "ir/ets/etsNewClassInstanceExpression.h" +#include "ir/ets/etsPackageDeclaration.h" +#include "ir/ets/etsParameterExpression.h" +#include "ir/ets/etsTypeReference.h" +#include "ir/ets/etsTypeReferencePart.h" +#include "ir/expressions/callExpression.h" +#include "ir/expressions/functionExpression.h" +#include "ir/expressions/identifier.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/literals/numberLiteral.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/statements/blockStatement.h" +#include "ir/statements/classDeclaration.h" +#include "ir/statements/expressionStatement.h" +#include "ir/statements/throwStatement.h" +#include "ir/statements/tryStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/ts/tsClassImplements.h" +#include "ir/ts/tsTypeParameter.h" +#include "ir/ts/tsTypeParameterDeclaration.h" +#include "ir/ts/tsTypeParameterInstantiation.h" + +namespace panda::es2panda::compiler { + +bool ASTVerifier::IsCorrectProgram(const parser::Program *program) +{ + bool is_correct = true; + error_messages_.clear(); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveParents(statement); + } + is_correct &= HaveParents(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveTypes(statement); + } + is_correct &= HaveTypes(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveVariables(statement); + } + is_correct &= HaveVariables(program->GlobalClass()); + + for (auto *statement : program->Ast()->Statements()) { + is_correct &= HaveScopes(statement); + } + is_correct &= HaveScopes(program->GlobalClass()); + +#ifndef NDEBUG + std::for_each(error_messages_.begin(), error_messages_.end(), [](auto const msg) { LOG(INFO, COMMON) << msg; }); +#endif // NDEBUG + return is_correct; +} + +std::string ToStringHelper(const binder::ScopeType type) +{ + switch (type) { + case binder::ScopeType::CATCH: { + return "CATCH"; + } + case binder::ScopeType::CATCH_PARAM: { + return "CATCH_PARAM"; + } + case binder::ScopeType::CLASS: { + return "CLASS"; + } + case binder::ScopeType::FUNCTION: { + return "FUNCTION"; + } + case binder::ScopeType::FUNCTION_PARAM: { + return "FUNCTION_PARAM"; + } + case binder::ScopeType::GLOBAL: { + return "GLOBAL"; + } + case binder::ScopeType::LOCAL: { + return "LOCAL"; + } + case binder::ScopeType::LOOP: { + return "LOOP"; + } + case binder::ScopeType::LOOP_DECL: { + return "LOOP_DECL"; + } + case binder::ScopeType::MODULE: { + return "MODULE"; + } + case binder::ScopeType::PARAM: { + return "PARAM"; + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +std::string ToStringHelper(const util::StringView &name) +{ + return name == nullptr ? "" : name.Mutf8(); +} + +std::string ToStringHelper(const binder::Scope *scope) +{ + if (scope == nullptr) { + return ""; + } + + switch (scope->Type()) { + case binder::ScopeType::FUNCTION: { + return "FUNC_SCOPE " + ToStringHelper(scope->AsFunctionScope()->Name()); + } + case binder::ScopeType::LOCAL: { + return "LOCAL_SCOPE "; + } + case binder::ScopeType::CATCH: { + return "CATCH_SCOPE "; + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +std::string ToStringHelper(const binder::Variable *var) +{ + if (var == nullptr) { + return ""; + } + + switch (var->Type()) { + case binder::VariableType::LOCAL: { + return "LOCAL_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::MODULE: { + return "MODULE_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::GLOBAL: { + return "GLOBAL_VAR " + ToStringHelper(var->Name()); + } + case binder::VariableType::ENUM: { + return "ENUM_VAR " + ToStringHelper(var->Name()); + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +template +std::string ToStringParamsHelper(const ir::AstNode *parent, const ArenaVector ¶ms) +{ + std::string name; + if (parent != nullptr) { + name = ToStringHelper(parent) + " "; + } + + name += "("; + for (auto const *param : params) { + name += ToStringHelper(param); + } + + return name + ")"; +} + +std::string ToStringHelper(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return ""; + } + + switch (ast->Type()) { + case ir::AstNodeType::IDENTIFIER: { + return "ID " + ToStringHelper(ast->AsIdentifier()->Name()); + } + case ir::AstNodeType::CLASS_DEFINITION: { + return "CLS_DEF " + ToStringHelper(ast->AsClassDefinition()->Ident()); + } + case ir::AstNodeType::CLASS_DECLARATION: { + return "CLS_DECL " + ToStringHelper(ast->AsClassDeclaration()->Definition()); + } + case ir::AstNodeType::BLOCK_STATEMENT: { + return "BLOCK " + ToStringHelper(ast->AsBlockStatement()->Scope()); + } + case ir::AstNodeType::SCRIPT_FUNCTION: { + auto const *sf = ast->AsScriptFunction(); + return "SCRIPT_FUN " + ToStringHelper(sf->Scope()) + "::" + ToStringHelper(sf->Id()); + } + case ir::AstNodeType::FUNCTION_EXPRESSION: { + return "FUN_EXPR " + ToStringHelper(ast->AsFunctionExpression()->Function()); + } + case ir::AstNodeType::METHOD_DEFINITION: { + return "METHOD_DEF " + ToStringHelper(ast->AsMethodDefinition()->Value()); + } + case ir::AstNodeType::ETS_TYPE_REFERENCE_PART: { + return "TYPE_REF_PART " + ToStringHelper(ast->AsETSTypeReferencePart()->Name()); + } + case ir::AstNodeType::ETS_TYPE_REFERENCE: { + return "TYPE_REF " + ToStringHelper(ast->AsETSTypeReference()->Part()); + } + case ir::AstNodeType::VARIABLE_DECLARATOR: { + return "VAR_DECLARATOR " + ToStringHelper(ast->AsVariableDeclarator()->Id()); + } + case ir::AstNodeType::VARIABLE_DECLARATION: { + if (ast->AsVariableDeclaration()->Declarators().empty()) { + return "VAR_DECLARATION "; + } + return "VAR_DECLARATION " + ToStringHelper(ast->AsVariableDeclaration()->Declarators().at(0)); + } + case ir::AstNodeType::CALL_EXPRESSION: { + return "CALL_EXPR " + ToStringHelper(ast->AsCallExpression()->Callee()) + "(...)"; + } + case ir::AstNodeType::EXPRESSION_STATEMENT: { + return "EXPR_STMT " + ToStringHelper(ast->AsExpressionStatement()->GetExpression()); + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + auto const *me = ast->AsMemberExpression(); + return "MEMBER_EXPR " + ToStringHelper(me->Object()) + "." + ToStringHelper(me->Property()); + } + case ir::AstNodeType::CLASS_STATIC_BLOCK: { + return "CLS_STATIC_BLOCK " + ToStringHelper(ast->AsClassStaticBlock()->Function()); + } + case ir::AstNodeType::ETS_PACKAGE_DECLARATION: { + return "PKG_DECL "; + } + case ir::AstNodeType::TS_TYPE_PARAMETER_DECLARATION: { + return "PARAM_DECL " + ToStringParamsHelper( + ast->Parent(), ast->AsTSTypeParameterDeclaration()->Params()); + } + case ir::AstNodeType::TS_TYPE_PARAMETER: { + return "TYPE_PARAM " + ToStringHelper(ast->AsTSTypeParameter()->Name()); + } + case ir::AstNodeType::TS_TYPE_PARAMETER_INSTANTIATION: { + return "PARAM_INSTANTIATION " + + ToStringParamsHelper(ast->Parent(), ast->AsTSTypeParameterInstantiation()->Params()); + } + case ir::AstNodeType::THROW_STATEMENT: { + return "THROW_STMT " + ToStringHelper(ast->AsThrowStatement()->Argument()); + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + return "NEW_CLS_INSTANCE " + ToStringHelper(ast->AsETSNewClassInstanceExpression()->GetTypeRef()); + } + case ir::AstNodeType::STRING_LITERAL: { + return "STR_LITERAL " + ToStringHelper(ast->AsStringLiteral()->Str()); + } + case ir::AstNodeType::TRY_STATEMENT: { + return "TRY_STMT " + ToStringHelper(ast->AsTryStatement()->Block()); + } + case ir::AstNodeType::CATCH_CLAUSE: { + return "CATCH_CLAUSE "; + } + case ir::AstNodeType::NUMBER_LITERAL: { + return "NUMBER_LITERAL " + ToStringHelper(ast->AsNumberLiteral()->Str()); + } + case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: { + return "ETS_PARAM_EXPR " + ToStringHelper(ast->AsETSParameterExpression()->Ident()); + } + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { + return "TS_INTERFACE_DECL " + ToStringHelper(ast->AsTSInterfaceDeclaration()->Id()); + } + case ir::AstNodeType::TS_INTERFACE_BODY: { + return "TS_INTERFACE_BODY "; + } + case ir::AstNodeType::ETS_FUNCTION_TYPE: { + return "ETS_FUNC_TYPE " + + ToStringParamsHelper(ast->Parent(), ast->AsETSFunctionType()->Params()); + } + case ir::AstNodeType::TS_CLASS_IMPLEMENTS: { + return "TS_CLASS_IMPL " + ToStringHelper(ast->AsTSClassImplements()->Expr()); + } + default: { + return "MUST BE UNREACHABLE"; + } + } +} + +bool ASTVerifier::HasParent(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->Parent() == nullptr) { + error_messages_.push_back("NULL_PARENT: " + ToStringHelper(ast)); + return false; + } + + return true; +} + +bool ASTVerifier::HaveParents(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_parent = HasParent(ast); + ast->IterateRecursively([this, &has_parent](ir::AstNode *child) { has_parent &= HasParent(child); }); + return has_parent; +} + +bool ASTVerifier::HasType(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (ast->IsTyped() && static_cast(ast)->TsType() == nullptr) { + error_messages_.push_back("NULL_TS_TYPE: " + ToStringHelper(ast)); + return false; + } + return true; +} + +bool ASTVerifier::HaveTypes(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_type = HasType(ast); + ast->IterateRecursively([this, &has_type](ir::AstNode *child) { has_type &= HasType(child); }); + return has_type; +} + +bool ASTVerifier::HasVariable(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsIdentifier() || ast->AsIdentifier()->Variable() != nullptr) { + return true; + } + + error_messages_.push_back("NULL_VARIABLE: " + ToStringHelper(ast->AsIdentifier())); + return false; +} + +bool ASTVerifier::HaveVariables(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_variable = HasVariable(ast); + ast->IterateRecursively([this, &has_variable](ir::AstNode *child) { has_variable &= HasVariable(child); }); + return has_variable; +} + +bool ASTVerifier::HasScope(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + if (!ast->IsIdentifier()) { + return true; // we will check only Identifier + } + // we will check only local variables of identifiers + if (HasVariable(ast) && ast->AsIdentifier()->Variable()->IsLocalVariable() && + ast->AsIdentifier()->Variable()->AsLocalVariable()->GetScope() == nullptr) { + error_messages_.push_back("NULL_SCOPE_LOCAL_VAR: " + ToStringHelper(ast)); + return false; + } + // TODO(tatiana): Add check that the scope enclose this identifier + return true; +} + +bool ASTVerifier::HaveScopes(const ir::AstNode *ast) +{ + if (ast == nullptr) { + return false; + } + + bool has_scope = HasScope(ast); + ast->IterateRecursively([this, &has_scope](ir::AstNode *child) { has_scope &= HasScope(child); }); + return has_scope; +} + +} // namespace panda::es2panda::compiler diff --git a/compiler/core/ASTVerifier.h b/compiler/core/ASTVerifier.h new file mode 100644 index 000000000..d9bdba118 --- /dev/null +++ b/compiler/core/ASTVerifier.h @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_COMPILER_CORE_ASTVERIFIER_H +#define ES2PANDA_COMPILER_CORE_ASTVERIFIER_H + +#include "parser/program/program.h" + +namespace panda::es2panda::compiler { + +class ASTVerifier { +public: + using ErrorMessages = std::vector; + NO_COPY_SEMANTIC(ASTVerifier); + NO_MOVE_SEMANTIC(ASTVerifier); + + ASTVerifier() = default; + ~ASTVerifier() = default; + + bool IsCorrectProgram(const parser::Program *program); + bool HaveParents(const ir::AstNode *ast); + bool HasParent(const ir::AstNode *ast); + bool HaveTypes(const ir::AstNode *ast); + bool HasType(const ir::AstNode *ast); + bool HaveVariables(const ir::AstNode *ast); + bool HasVariable(const ir::AstNode *ast); + bool HasScope(const ir::AstNode *ast); + bool HaveScopes(const ir::AstNode *ast); + + ErrorMessages GetErrorMessages() + { + return error_messages_; + } + +private: + ErrorMessages error_messages_; +}; + +std::string ToStringHelper(const ir::AstNode *ast); + +} // namespace panda::es2panda::compiler + +#endif // ES2PANDA_COMPILER_CORE_ASTVERIFIER_H diff --git a/compiler/lowering/phase.cpp b/compiler/lowering/phase.cpp index 5877c6e49..0b7c30057 100644 --- a/compiler/lowering/phase.cpp +++ b/compiler/lowering/phase.cpp @@ -15,6 +15,7 @@ #include "phase.h" #include "checker/checker.h" +#include "compiler/core/ASTVerifier.h" #include "compiler/core/compilerContext.h" #include "lexer/token/sourceLocation.h" #include "compiler/lowering/checkerPhase.h" @@ -57,6 +58,10 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG + ASTVerifier ast_before; + if (!ast_before.IsCorrectProgram(program)) { + // TODO(tatiana): Add some error processing + } if (!Precondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Precondition check failed for ", Name()}, lexer::SourcePosition {}); } @@ -72,6 +77,10 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG + ASTVerifier ast_after; + if (!ast_after.IsCorrectProgram(program)) { + // TODO(tatiana): Add some error processing + } if (!Postcondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Postcondition check failed for ", Name()}, lexer::SourcePosition {}); } diff --git a/ir/astNode.h b/ir/astNode.h index b9558663a..5accd1e72 100644 --- a/ir/astNode.h +++ b/ir/astNode.h @@ -227,6 +227,11 @@ public: return false; } + virtual bool IsTyped() const + { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_AS_CASTS(nodeType, className) \ className *As##className() \ @@ -587,6 +592,11 @@ public: ts_type_ = ts_type; } + bool IsTyped() const override + { + return true; + } + protected: explicit Typed(AstNodeType const type) : T(type) {} explicit Typed(AstNodeType const type, ModifierFlags const flags) : T(type, flags) {} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f40aed24b..ff14df7c4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,5 +92,17 @@ if(PANDA_WITH_ETS) add_dependencies(ets_tests es2panda_tests) endif() + panda_add_gtest( + NAME es2panda_astverifier_tests + SOURCES + public/ast_verifier_test.cpp + LIBRARIES + es2panda-lib + INCLUDE_DIRS + ${ES2PANDA_PATH} + SANITIZERS + ${PANDA_SANITIZERS_LIST} + ) + add_subdirectory(tsconfig) endif() diff --git a/test/public/ast_verifier_test.cpp b/test/public/ast_verifier_test.cpp new file mode 100644 index 000000000..dd36a1937 --- /dev/null +++ b/test/public/ast_verifier_test.cpp @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2021-2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "macros.h" + +#include "compiler/core/ASTVerifier.h" +#include "ir/astDump.h" +#include "ir/expressions/literals/stringLiteral.h" + +class ASTVerifierTest : public testing::Test { +public: + ASTVerifierTest() = default; + ~ASTVerifierTest() override = default; + + NO_COPY_SEMANTIC(ASTVerifierTest); + NO_MOVE_SEMANTIC(ASTVerifierTest); + +private: +}; + +TEST_F(ASTVerifierTest, NullParent) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_parent = verifier.HasParent(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_parent, false); + ASSERT_NE(messages.size(), 0); + ASSERT_EQ(messages[0], "NULL_PARENT: STR_LITERAL "); +} + +TEST_F(ASTVerifierTest, NullType) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_type = verifier.HasType(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_type, false); + ASSERT_NE(messages.size(), 0); + ASSERT_EQ(messages[0], "NULL_TS_TYPE: STR_LITERAL "); +} + +TEST_F(ASTVerifierTest, WithoutScope) +{ + panda::es2panda::compiler::ASTVerifier verifier {}; + panda::es2panda::ir::StringLiteral empty_node; + + bool has_scope = verifier.HasScope(&empty_node); + auto messages = verifier.GetErrorMessages(); + + ASSERT_EQ(has_scope, true); + ASSERT_EQ(messages.size(), 0); +} -- Gitee From 89229ac12ad0dde2496ca73a4621ffc9784abe05 Mon Sep 17 00:00:00 2001 From: semenovaleksandr Date: Wed, 25 Oct 2023 10:35:32 +0000 Subject: [PATCH 2/2] Revert 'Pull Request !4 : [ArkTS frontend] Add AST-verifier' --- BUILD.gn | 1 - CMakeLists.txt | 1 - compiler/core/ASTVerifier.cpp | 409 ------------------------------ compiler/core/ASTVerifier.h | 55 ---- compiler/lowering/phase.cpp | 9 - ir/astNode.h | 10 - test/CMakeLists.txt | 12 - test/public/ast_verifier_test.cpp | 71 ------ 8 files changed, 568 deletions(-) delete mode 100644 compiler/core/ASTVerifier.cpp delete mode 100644 compiler/core/ASTVerifier.h delete mode 100644 test/public/ast_verifier_test.cpp diff --git a/BUILD.gn b/BUILD.gn index 8663df872..99b4a26ef 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -138,7 +138,6 @@ libes2panda_sources = [ "compiler/core/JSCompiler.cpp", "compiler/core/JSemitter.cpp", "compiler/core/codeGen.cpp", - "compiler/core/ASTVerifier.cpp", "compiler/core/compileJob.cpp", "compiler/core/compileQueue.cpp", "compiler/core/compilerContext.cpp", diff --git a/CMakeLists.txt b/CMakeLists.txt index b34882d04..e49880048 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,7 +102,6 @@ set(ES2PANDA_LIB_SRC compiler/base/literals.cpp compiler/base/lreference.cpp compiler/base/optionalChain.cpp - compiler/core/ASTVerifier.cpp compiler/core/codeGen.cpp compiler/core/compileJob.cpp compiler/core/compileQueue.cpp diff --git a/compiler/core/ASTVerifier.cpp b/compiler/core/ASTVerifier.cpp deleted file mode 100644 index f59438c30..000000000 --- a/compiler/core/ASTVerifier.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/** - * Copyright (c) 2021-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ASTVerifier.h" - -#include "es2panda.h" -#include "binder/variableFlags.h" -#include "binder/scope.h" -#include "ir/astNode.h" -#include "ir/base/catchClause.h" -#include "ir/base/classDefinition.h" -#include "ir/base/classStaticBlock.h" -#include "ir/base/methodDefinition.h" -#include "ir/base/scriptFunction.h" -#include "ir/ets/etsFunctionType.h" -#include "ir/ets/etsNewClassInstanceExpression.h" -#include "ir/ets/etsPackageDeclaration.h" -#include "ir/ets/etsParameterExpression.h" -#include "ir/ets/etsTypeReference.h" -#include "ir/ets/etsTypeReferencePart.h" -#include "ir/expressions/callExpression.h" -#include "ir/expressions/functionExpression.h" -#include "ir/expressions/identifier.h" -#include "ir/expressions/memberExpression.h" -#include "ir/expressions/literals/numberLiteral.h" -#include "ir/expressions/literals/stringLiteral.h" -#include "ir/statements/blockStatement.h" -#include "ir/statements/classDeclaration.h" -#include "ir/statements/expressionStatement.h" -#include "ir/statements/throwStatement.h" -#include "ir/statements/tryStatement.h" -#include "ir/statements/variableDeclaration.h" -#include "ir/statements/variableDeclarator.h" -#include "ir/ts/tsClassImplements.h" -#include "ir/ts/tsTypeParameter.h" -#include "ir/ts/tsTypeParameterDeclaration.h" -#include "ir/ts/tsTypeParameterInstantiation.h" - -namespace panda::es2panda::compiler { - -bool ASTVerifier::IsCorrectProgram(const parser::Program *program) -{ - bool is_correct = true; - error_messages_.clear(); - - for (auto *statement : program->Ast()->Statements()) { - is_correct &= HaveParents(statement); - } - is_correct &= HaveParents(program->GlobalClass()); - - for (auto *statement : program->Ast()->Statements()) { - is_correct &= HaveTypes(statement); - } - is_correct &= HaveTypes(program->GlobalClass()); - - for (auto *statement : program->Ast()->Statements()) { - is_correct &= HaveVariables(statement); - } - is_correct &= HaveVariables(program->GlobalClass()); - - for (auto *statement : program->Ast()->Statements()) { - is_correct &= HaveScopes(statement); - } - is_correct &= HaveScopes(program->GlobalClass()); - -#ifndef NDEBUG - std::for_each(error_messages_.begin(), error_messages_.end(), [](auto const msg) { LOG(INFO, COMMON) << msg; }); -#endif // NDEBUG - return is_correct; -} - -std::string ToStringHelper(const binder::ScopeType type) -{ - switch (type) { - case binder::ScopeType::CATCH: { - return "CATCH"; - } - case binder::ScopeType::CATCH_PARAM: { - return "CATCH_PARAM"; - } - case binder::ScopeType::CLASS: { - return "CLASS"; - } - case binder::ScopeType::FUNCTION: { - return "FUNCTION"; - } - case binder::ScopeType::FUNCTION_PARAM: { - return "FUNCTION_PARAM"; - } - case binder::ScopeType::GLOBAL: { - return "GLOBAL"; - } - case binder::ScopeType::LOCAL: { - return "LOCAL"; - } - case binder::ScopeType::LOOP: { - return "LOOP"; - } - case binder::ScopeType::LOOP_DECL: { - return "LOOP_DECL"; - } - case binder::ScopeType::MODULE: { - return "MODULE"; - } - case binder::ScopeType::PARAM: { - return "PARAM"; - } - default: { - return "MUST BE UNREACHABLE"; - } - } -} - -std::string ToStringHelper(const util::StringView &name) -{ - return name == nullptr ? "" : name.Mutf8(); -} - -std::string ToStringHelper(const binder::Scope *scope) -{ - if (scope == nullptr) { - return ""; - } - - switch (scope->Type()) { - case binder::ScopeType::FUNCTION: { - return "FUNC_SCOPE " + ToStringHelper(scope->AsFunctionScope()->Name()); - } - case binder::ScopeType::LOCAL: { - return "LOCAL_SCOPE "; - } - case binder::ScopeType::CATCH: { - return "CATCH_SCOPE "; - } - default: { - return "MUST BE UNREACHABLE"; - } - } -} - -std::string ToStringHelper(const binder::Variable *var) -{ - if (var == nullptr) { - return ""; - } - - switch (var->Type()) { - case binder::VariableType::LOCAL: { - return "LOCAL_VAR " + ToStringHelper(var->Name()); - } - case binder::VariableType::MODULE: { - return "MODULE_VAR " + ToStringHelper(var->Name()); - } - case binder::VariableType::GLOBAL: { - return "GLOBAL_VAR " + ToStringHelper(var->Name()); - } - case binder::VariableType::ENUM: { - return "ENUM_VAR " + ToStringHelper(var->Name()); - } - default: { - return "MUST BE UNREACHABLE"; - } - } -} - -template -std::string ToStringParamsHelper(const ir::AstNode *parent, const ArenaVector ¶ms) -{ - std::string name; - if (parent != nullptr) { - name = ToStringHelper(parent) + " "; - } - - name += "("; - for (auto const *param : params) { - name += ToStringHelper(param); - } - - return name + ")"; -} - -std::string ToStringHelper(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return ""; - } - - switch (ast->Type()) { - case ir::AstNodeType::IDENTIFIER: { - return "ID " + ToStringHelper(ast->AsIdentifier()->Name()); - } - case ir::AstNodeType::CLASS_DEFINITION: { - return "CLS_DEF " + ToStringHelper(ast->AsClassDefinition()->Ident()); - } - case ir::AstNodeType::CLASS_DECLARATION: { - return "CLS_DECL " + ToStringHelper(ast->AsClassDeclaration()->Definition()); - } - case ir::AstNodeType::BLOCK_STATEMENT: { - return "BLOCK " + ToStringHelper(ast->AsBlockStatement()->Scope()); - } - case ir::AstNodeType::SCRIPT_FUNCTION: { - auto const *sf = ast->AsScriptFunction(); - return "SCRIPT_FUN " + ToStringHelper(sf->Scope()) + "::" + ToStringHelper(sf->Id()); - } - case ir::AstNodeType::FUNCTION_EXPRESSION: { - return "FUN_EXPR " + ToStringHelper(ast->AsFunctionExpression()->Function()); - } - case ir::AstNodeType::METHOD_DEFINITION: { - return "METHOD_DEF " + ToStringHelper(ast->AsMethodDefinition()->Value()); - } - case ir::AstNodeType::ETS_TYPE_REFERENCE_PART: { - return "TYPE_REF_PART " + ToStringHelper(ast->AsETSTypeReferencePart()->Name()); - } - case ir::AstNodeType::ETS_TYPE_REFERENCE: { - return "TYPE_REF " + ToStringHelper(ast->AsETSTypeReference()->Part()); - } - case ir::AstNodeType::VARIABLE_DECLARATOR: { - return "VAR_DECLARATOR " + ToStringHelper(ast->AsVariableDeclarator()->Id()); - } - case ir::AstNodeType::VARIABLE_DECLARATION: { - if (ast->AsVariableDeclaration()->Declarators().empty()) { - return "VAR_DECLARATION "; - } - return "VAR_DECLARATION " + ToStringHelper(ast->AsVariableDeclaration()->Declarators().at(0)); - } - case ir::AstNodeType::CALL_EXPRESSION: { - return "CALL_EXPR " + ToStringHelper(ast->AsCallExpression()->Callee()) + "(...)"; - } - case ir::AstNodeType::EXPRESSION_STATEMENT: { - return "EXPR_STMT " + ToStringHelper(ast->AsExpressionStatement()->GetExpression()); - } - case ir::AstNodeType::MEMBER_EXPRESSION: { - auto const *me = ast->AsMemberExpression(); - return "MEMBER_EXPR " + ToStringHelper(me->Object()) + "." + ToStringHelper(me->Property()); - } - case ir::AstNodeType::CLASS_STATIC_BLOCK: { - return "CLS_STATIC_BLOCK " + ToStringHelper(ast->AsClassStaticBlock()->Function()); - } - case ir::AstNodeType::ETS_PACKAGE_DECLARATION: { - return "PKG_DECL "; - } - case ir::AstNodeType::TS_TYPE_PARAMETER_DECLARATION: { - return "PARAM_DECL " + ToStringParamsHelper( - ast->Parent(), ast->AsTSTypeParameterDeclaration()->Params()); - } - case ir::AstNodeType::TS_TYPE_PARAMETER: { - return "TYPE_PARAM " + ToStringHelper(ast->AsTSTypeParameter()->Name()); - } - case ir::AstNodeType::TS_TYPE_PARAMETER_INSTANTIATION: { - return "PARAM_INSTANTIATION " + - ToStringParamsHelper(ast->Parent(), ast->AsTSTypeParameterInstantiation()->Params()); - } - case ir::AstNodeType::THROW_STATEMENT: { - return "THROW_STMT " + ToStringHelper(ast->AsThrowStatement()->Argument()); - } - case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { - return "NEW_CLS_INSTANCE " + ToStringHelper(ast->AsETSNewClassInstanceExpression()->GetTypeRef()); - } - case ir::AstNodeType::STRING_LITERAL: { - return "STR_LITERAL " + ToStringHelper(ast->AsStringLiteral()->Str()); - } - case ir::AstNodeType::TRY_STATEMENT: { - return "TRY_STMT " + ToStringHelper(ast->AsTryStatement()->Block()); - } - case ir::AstNodeType::CATCH_CLAUSE: { - return "CATCH_CLAUSE "; - } - case ir::AstNodeType::NUMBER_LITERAL: { - return "NUMBER_LITERAL " + ToStringHelper(ast->AsNumberLiteral()->Str()); - } - case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: { - return "ETS_PARAM_EXPR " + ToStringHelper(ast->AsETSParameterExpression()->Ident()); - } - case ir::AstNodeType::TS_INTERFACE_DECLARATION: { - return "TS_INTERFACE_DECL " + ToStringHelper(ast->AsTSInterfaceDeclaration()->Id()); - } - case ir::AstNodeType::TS_INTERFACE_BODY: { - return "TS_INTERFACE_BODY "; - } - case ir::AstNodeType::ETS_FUNCTION_TYPE: { - return "ETS_FUNC_TYPE " + - ToStringParamsHelper(ast->Parent(), ast->AsETSFunctionType()->Params()); - } - case ir::AstNodeType::TS_CLASS_IMPLEMENTS: { - return "TS_CLASS_IMPL " + ToStringHelper(ast->AsTSClassImplements()->Expr()); - } - default: { - return "MUST BE UNREACHABLE"; - } - } -} - -bool ASTVerifier::HasParent(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - if (ast->Parent() == nullptr) { - error_messages_.push_back("NULL_PARENT: " + ToStringHelper(ast)); - return false; - } - - return true; -} - -bool ASTVerifier::HaveParents(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - bool has_parent = HasParent(ast); - ast->IterateRecursively([this, &has_parent](ir::AstNode *child) { has_parent &= HasParent(child); }); - return has_parent; -} - -bool ASTVerifier::HasType(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - if (ast->IsTyped() && static_cast(ast)->TsType() == nullptr) { - error_messages_.push_back("NULL_TS_TYPE: " + ToStringHelper(ast)); - return false; - } - return true; -} - -bool ASTVerifier::HaveTypes(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - bool has_type = HasType(ast); - ast->IterateRecursively([this, &has_type](ir::AstNode *child) { has_type &= HasType(child); }); - return has_type; -} - -bool ASTVerifier::HasVariable(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - if (!ast->IsIdentifier() || ast->AsIdentifier()->Variable() != nullptr) { - return true; - } - - error_messages_.push_back("NULL_VARIABLE: " + ToStringHelper(ast->AsIdentifier())); - return false; -} - -bool ASTVerifier::HaveVariables(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - bool has_variable = HasVariable(ast); - ast->IterateRecursively([this, &has_variable](ir::AstNode *child) { has_variable &= HasVariable(child); }); - return has_variable; -} - -bool ASTVerifier::HasScope(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - if (!ast->IsIdentifier()) { - return true; // we will check only Identifier - } - // we will check only local variables of identifiers - if (HasVariable(ast) && ast->AsIdentifier()->Variable()->IsLocalVariable() && - ast->AsIdentifier()->Variable()->AsLocalVariable()->GetScope() == nullptr) { - error_messages_.push_back("NULL_SCOPE_LOCAL_VAR: " + ToStringHelper(ast)); - return false; - } - // TODO(tatiana): Add check that the scope enclose this identifier - return true; -} - -bool ASTVerifier::HaveScopes(const ir::AstNode *ast) -{ - if (ast == nullptr) { - return false; - } - - bool has_scope = HasScope(ast); - ast->IterateRecursively([this, &has_scope](ir::AstNode *child) { has_scope &= HasScope(child); }); - return has_scope; -} - -} // namespace panda::es2panda::compiler diff --git a/compiler/core/ASTVerifier.h b/compiler/core/ASTVerifier.h deleted file mode 100644 index d9bdba118..000000000 --- a/compiler/core/ASTVerifier.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ES2PANDA_COMPILER_CORE_ASTVERIFIER_H -#define ES2PANDA_COMPILER_CORE_ASTVERIFIER_H - -#include "parser/program/program.h" - -namespace panda::es2panda::compiler { - -class ASTVerifier { -public: - using ErrorMessages = std::vector; - NO_COPY_SEMANTIC(ASTVerifier); - NO_MOVE_SEMANTIC(ASTVerifier); - - ASTVerifier() = default; - ~ASTVerifier() = default; - - bool IsCorrectProgram(const parser::Program *program); - bool HaveParents(const ir::AstNode *ast); - bool HasParent(const ir::AstNode *ast); - bool HaveTypes(const ir::AstNode *ast); - bool HasType(const ir::AstNode *ast); - bool HaveVariables(const ir::AstNode *ast); - bool HasVariable(const ir::AstNode *ast); - bool HasScope(const ir::AstNode *ast); - bool HaveScopes(const ir::AstNode *ast); - - ErrorMessages GetErrorMessages() - { - return error_messages_; - } - -private: - ErrorMessages error_messages_; -}; - -std::string ToStringHelper(const ir::AstNode *ast); - -} // namespace panda::es2panda::compiler - -#endif // ES2PANDA_COMPILER_CORE_ASTVERIFIER_H diff --git a/compiler/lowering/phase.cpp b/compiler/lowering/phase.cpp index 0b7c30057..5877c6e49 100644 --- a/compiler/lowering/phase.cpp +++ b/compiler/lowering/phase.cpp @@ -15,7 +15,6 @@ #include "phase.h" #include "checker/checker.h" -#include "compiler/core/ASTVerifier.h" #include "compiler/core/compilerContext.h" #include "lexer/token/sourceLocation.h" #include "compiler/lowering/checkerPhase.h" @@ -58,10 +57,6 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG - ASTVerifier ast_before; - if (!ast_before.IsCorrectProgram(program)) { - // TODO(tatiana): Add some error processing - } if (!Precondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Precondition check failed for ", Name()}, lexer::SourcePosition {}); } @@ -77,10 +72,6 @@ bool Phase::Apply(CompilerContext *ctx, parser::Program *program) } #ifndef NDEBUG - ASTVerifier ast_after; - if (!ast_after.IsCorrectProgram(program)) { - // TODO(tatiana): Add some error processing - } if (!Postcondition(ctx, program)) { ctx->Checker()->ThrowTypeError({"Postcondition check failed for ", Name()}, lexer::SourcePosition {}); } diff --git a/ir/astNode.h b/ir/astNode.h index 5accd1e72..b9558663a 100644 --- a/ir/astNode.h +++ b/ir/astNode.h @@ -227,11 +227,6 @@ public: return false; } - virtual bool IsTyped() const - { - return false; - } - // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_AS_CASTS(nodeType, className) \ className *As##className() \ @@ -592,11 +587,6 @@ public: ts_type_ = ts_type; } - bool IsTyped() const override - { - return true; - } - protected: explicit Typed(AstNodeType const type) : T(type) {} explicit Typed(AstNodeType const type, ModifierFlags const flags) : T(type, flags) {} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ff14df7c4..f40aed24b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -92,17 +92,5 @@ if(PANDA_WITH_ETS) add_dependencies(ets_tests es2panda_tests) endif() - panda_add_gtest( - NAME es2panda_astverifier_tests - SOURCES - public/ast_verifier_test.cpp - LIBRARIES - es2panda-lib - INCLUDE_DIRS - ${ES2PANDA_PATH} - SANITIZERS - ${PANDA_SANITIZERS_LIST} - ) - add_subdirectory(tsconfig) endif() diff --git a/test/public/ast_verifier_test.cpp b/test/public/ast_verifier_test.cpp deleted file mode 100644 index dd36a1937..000000000 --- a/test/public/ast_verifier_test.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2021-2023 Huawei Device Co., Ltd. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include "macros.h" - -#include "compiler/core/ASTVerifier.h" -#include "ir/astDump.h" -#include "ir/expressions/literals/stringLiteral.h" - -class ASTVerifierTest : public testing::Test { -public: - ASTVerifierTest() = default; - ~ASTVerifierTest() override = default; - - NO_COPY_SEMANTIC(ASTVerifierTest); - NO_MOVE_SEMANTIC(ASTVerifierTest); - -private: -}; - -TEST_F(ASTVerifierTest, NullParent) -{ - panda::es2panda::compiler::ASTVerifier verifier {}; - panda::es2panda::ir::StringLiteral empty_node; - - bool has_parent = verifier.HasParent(&empty_node); - auto messages = verifier.GetErrorMessages(); - - ASSERT_EQ(has_parent, false); - ASSERT_NE(messages.size(), 0); - ASSERT_EQ(messages[0], "NULL_PARENT: STR_LITERAL "); -} - -TEST_F(ASTVerifierTest, NullType) -{ - panda::es2panda::compiler::ASTVerifier verifier {}; - panda::es2panda::ir::StringLiteral empty_node; - - bool has_type = verifier.HasType(&empty_node); - auto messages = verifier.GetErrorMessages(); - - ASSERT_EQ(has_type, false); - ASSERT_NE(messages.size(), 0); - ASSERT_EQ(messages[0], "NULL_TS_TYPE: STR_LITERAL "); -} - -TEST_F(ASTVerifierTest, WithoutScope) -{ - panda::es2panda::compiler::ASTVerifier verifier {}; - panda::es2panda::ir::StringLiteral empty_node; - - bool has_scope = verifier.HasScope(&empty_node); - auto messages = verifier.GetErrorMessages(); - - ASSERT_EQ(has_scope, true); - ASSERT_EQ(messages.size(), 0); -} -- Gitee