diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 5e823d8ee..8feb665e0 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -30,6 +30,8 @@ set(sources analysis/PostTypeChecker.h analysis/ReferencesResolver.cpp analysis/ReferencesResolver.h + analysis/Scoper.cpp + analysis/Scoper.h analysis/StaticAnalyzer.cpp analysis/StaticAnalyzer.h analysis/SyntaxChecker.cpp diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 957046810..bff5e931f 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -607,7 +607,7 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&) bool DeclarationRegistrationHelper::visit(TryCatchClause& _tryCatchClause) { - _tryCatchClause.annotation().scope = m_currentScope; + solAssert(_tryCatchClause.annotation().scope == m_currentScope, ""); enterNewSubScope(_tryCatchClause); return true; } @@ -643,7 +643,7 @@ void DeclarationRegistrationHelper::endVisit(FunctionTypeName&) bool DeclarationRegistrationHelper::visit(Block& _block) { - _block.annotation().scope = m_currentScope; + solAssert(_block.annotation().scope == m_currentScope, ""); enterNewSubScope(_block); return true; } @@ -655,7 +655,7 @@ void DeclarationRegistrationHelper::endVisit(Block&) bool DeclarationRegistrationHelper::visit(ForStatement& _for) { - _for.annotation().scope = m_currentScope; + solAssert(_for.annotation().scope == m_currentScope, ""); enterNewSubScope(_for); return true; } @@ -729,8 +729,8 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); - _declaration.annotation().scope = m_currentScope; - _declaration.annotation().contract = m_currentContract; + solAssert(_declaration.annotation().scope == m_currentScope, ""); + solAssert(_declaration.annotation().contract == m_currentContract, ""); if (_opensScope) enterNewSubScope(_declaration); } diff --git a/libsolidity/analysis/Scoper.cpp b/libsolidity/analysis/Scoper.cpp new file mode 100644 index 000000000..98716cf0f --- /dev/null +++ b/libsolidity/analysis/Scoper.cpp @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; + +void Scoper::assignScopes(ASTNode const& _astRoot) +{ + Scoper scoper; + _astRoot.accept(scoper); +} + +bool Scoper::visit(ContractDefinition const& _contract) +{ + solAssert(m_contract == nullptr, ""); + m_contract = &_contract; + return ASTConstVisitor::visit(_contract); +} + +void Scoper::endVisit(ContractDefinition const& _contract) +{ + solAssert(m_contract == &_contract, ""); + m_contract = nullptr; + ASTConstVisitor::endVisit(_contract); +} + +bool Scoper::visitNode(ASTNode const& _node) +{ + if (auto const* scopable = dynamic_cast(&_node)) + { + scopable->annotation().scope = m_scopes.empty() ? nullptr : m_scopes.back(); + scopable->annotation().contract = m_contract; + } + if (dynamic_cast(&_node)) + m_scopes.push_back(&_node); + return true; +} + +void Scoper::endVisitNode(ASTNode const& _node) +{ + if (dynamic_cast(&_node)) + m_scopes.pop_back(); +} diff --git a/libsolidity/analysis/Scoper.h b/libsolidity/analysis/Scoper.h new file mode 100644 index 000000000..c55e58040 --- /dev/null +++ b/libsolidity/analysis/Scoper.h @@ -0,0 +1,45 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +namespace solidity::frontend +{ + +/** + * AST visitor that assigns syntactic scopes. + */ +class Scoper: private ASTConstVisitor +{ +public: + static void assignScopes(ASTNode const& _astRoot); + +private: + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; + bool visitNode(ASTNode const& _node) override; + void endVisitNode(ASTNode const& _node) override; + + ContractDefinition const* m_contract = nullptr; + std::vector m_scopes; +}; + +} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index cfa438ce5..2f328759c 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -152,10 +152,19 @@ std::vector ASTNode::filteredNodes(std::vector> co return ret; } +/** + * Abstract marker class that specifies that this AST node opens a scope. + */ +class ScopeOpener +{ +public: + virtual ~ScopeOpener() = default; +}; + /** * Source unit containing import directives and contract definitions. */ -class SourceUnit: public ASTNode +class SourceUnit: public ASTNode, public ScopeOpener { public: SourceUnit( @@ -455,7 +464,7 @@ protected: * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public StructurallyDocumented +class ContractDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener { public: ContractDefinition( @@ -593,7 +602,7 @@ private: ASTPointer m_typeName; }; -class StructDefinition: public Declaration +class StructDefinition: public Declaration, public ScopeOpener { public: StructDefinition( @@ -620,7 +629,7 @@ private: std::vector> m_members; }; -class EnumDefinition: public Declaration +class EnumDefinition: public Declaration, public ScopeOpener { public: EnumDefinition( @@ -765,7 +774,7 @@ protected: std::vector> m_overrides; }; -class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional +class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener { public: FunctionDefinition( @@ -989,7 +998,7 @@ private: /** * Definition of a function modifier. */ -class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional +class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener { public: ModifierDefinition( @@ -1061,7 +1070,7 @@ private: /** * Definition of a (loggable) event. */ -class EventDefinition: public CallableDeclaration, public StructurallyDocumented +class EventDefinition: public CallableDeclaration, public StructurallyDocumented, public ScopeOpener { public: EventDefinition( @@ -1199,7 +1208,7 @@ private: /** * A literal function type. Its source form is "function (paramType1, paramType2) internal / external returns (retType1, retType2)" */ -class FunctionTypeName: public TypeName +class FunctionTypeName: public TypeName, public ScopeOpener { public: FunctionTypeName( @@ -1334,7 +1343,7 @@ private: /** * Brace-enclosed block containing zero or more statements. */ -class Block: public Statement, public Scopable +class Block: public Statement, public Scopable, public ScopeOpener { public: Block( @@ -1411,7 +1420,7 @@ private: * unsuccessful cases. * Names are only allowed for the unsuccessful cases. */ -class TryCatchClause: public ASTNode, public Scopable +class TryCatchClause: public ASTNode, public Scopable, public ScopeOpener { public: TryCatchClause( @@ -1526,7 +1535,7 @@ private: /** * For loop statement */ -class ForStatement: public BreakableStatement, public Scopable +class ForStatement: public BreakableStatement, public Scopable, public ScopeOpener { public: ForStatement( diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 2aac79837..768bac502 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -108,10 +108,10 @@ struct ScopableAnnotation virtual ~ScopableAnnotation() = default; /// The scope this declaration resides in. Can be nullptr if it is the global scope. - /// Available only after name and type resolution step. + /// Filled by the Scoper. ASTNode const* scope = nullptr; /// Pointer to the contract this declaration resides in. Can be nullptr if the current scope - /// is not part of a contract. Available only after name and type resolution step. + /// is not part of a contract. Filled by the Scoper. ContractDefinition const* contract = nullptr; }; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 3f082d975..80d37d399 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -38,6 +38,7 @@ namespace solidity::frontend { class ASTNode; +class ScopeOpener; class SourceUnit; class PragmaDirective; class ImportDirective; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 42acb75ec..da42cbfa4 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -297,6 +298,10 @@ bool CompilerStack::analyze() BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed.")); resolveImports(); + for (Source const* source: m_sourceOrder) + if (source->ast) + Scoper::assignScopes(*source->ast); + bool noErrors = true; try diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 12e088fac..a10604aee 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(_sourceCode))); BOOST_CHECK(!!sourceUnit); + Scoper::assignScopes(*sourceUnit); GlobalContext globalContext; NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 91a41a65b..8c0ce780d 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,7 @@ bytes compileFirstExpression( ErrorList errors; ErrorReporter errorReporter(errors); GlobalContext globalContext; + Scoper::assignScopes(*sourceUnit); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");