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");