diff options
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 133 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 35 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 27 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.h | 9 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 4 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.h | 2 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 8 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 2 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 3 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.h | 3 | ||||
-rw-r--r-- | test/libsolidity/Assembly.cpp | 3 | ||||
-rw-r--r-- | test/libsolidity/SolidityExpressionCompiler.cpp | 3 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 5 |
13 files changed, 150 insertions, 87 deletions
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index b0a82715..01384260 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -34,8 +34,10 @@ namespace solidity NameAndTypeResolver::NameAndTypeResolver( vector<Declaration const*> const& _globals, + map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes, ErrorList& _errors ) : + m_scopes(_scopes), m_errors(_errors) { if (!m_scopes[nullptr]) @@ -44,18 +46,12 @@ NameAndTypeResolver::NameAndTypeResolver( m_scopes[nullptr]->registerDeclaration(*declaration); } -bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) +bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope) { - if (!m_scopes[&_sourceUnit]) - // By importing, it is possible that the container already exists. - m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get())); - m_currentScope = m_scopes[&_sourceUnit].get(); - // The helper registers all declarations in m_scopes as a side-effect of its construction. try { - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors); - _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations(); + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors, _currentScope); } catch (FatalError const&) { @@ -132,68 +128,64 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So return !error; } -bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) +bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode) { + bool success = true; try { - m_currentScope = m_scopes[_contract.scope()].get(); - solAssert(!!m_currentScope, ""); + if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node)) + { + m_currentScope = m_scopes[contract->scope()].get(); + solAssert(!!m_currentScope, ""); - ReferencesResolver resolver(m_errors, *this, nullptr); - bool success = true; - for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts()) - if (!resolver.resolve(*baseContract)) - success = false; + for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts()) + if (!resolveNamesAndTypes(*baseContract, true)) + success = false; - m_currentScope = m_scopes[&_contract].get(); + m_currentScope = m_scopes[contract].get(); - if (success) - { - linearizeBaseContracts(_contract); - vector<ContractDefinition const*> properBases( - ++_contract.annotation().linearizedBaseContracts.begin(), - _contract.annotation().linearizedBaseContracts.end() - ); - - for (ContractDefinition const* base: properBases) - importInheritedScope(*base); - } + if (success) + { + linearizeBaseContracts(*contract); + vector<ContractDefinition const*> properBases( + ++contract->annotation().linearizedBaseContracts.begin(), + contract->annotation().linearizedBaseContracts.end() + ); - // these can contain code, only resolve parameters for now - for (ASTPointer<ASTNode> const& node: _contract.subNodes()) - { - m_currentScope = m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract].get(); - if (!resolver.resolve(*node)) - success = false; - } + for (ContractDefinition const* base: properBases) + importInheritedScope(*base); + } - if (!success) - return false; + // these can contain code, only resolve parameters for now + for (ASTPointer<ASTNode> const& node: contract->subNodes()) + { + m_currentScope = m_scopes[contract].get(); + if (!resolveNamesAndTypes(*node, false)) + success = false; + } - m_currentScope = m_scopes[&_contract].get(); + if (!success) + return false; - // now resolve references inside the code - for (ModifierDefinition const* modifier: _contract.functionModifiers()) - { - m_currentScope = m_scopes[modifier].get(); - ReferencesResolver resolver(m_errors, *this, nullptr, true); - if (!resolver.resolve(*modifier)) - success = false; - } + if (!_resolveInsideCode) + return success; - for (FunctionDefinition const* function: _contract.definedFunctions()) + m_currentScope = m_scopes[contract].get(); + + // now resolve references inside the code + for (ASTPointer<ASTNode> const& node: contract->subNodes()) + { + m_currentScope = m_scopes[contract].get(); + if (!resolveNamesAndTypes(*node, true)) + success = false; + } + } + else { - m_currentScope = m_scopes[function].get(); - if (!ReferencesResolver( - m_errors, - *this, - function->returnParameterList().get(), - true - ).resolve(*function)) - success = false; + if (m_scopes.count(&_node)) + m_currentScope = m_scopes[&_node].get(); + return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node); } - if (!success) - return false; } catch (FatalError const&) { @@ -201,7 +193,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) throw; // Something is weird here, rather throw again. return false; } - return true; + return success; } bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) @@ -459,14 +451,30 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e) DeclarationRegistrationHelper::DeclarationRegistrationHelper( map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, - ErrorList& _errors + ErrorList& _errors, + ASTNode const* _currentScope ): m_scopes(_scopes), - m_currentScope(&_astRoot), + m_currentScope(_currentScope), m_errors(_errors) { - solAssert(!!m_scopes.at(m_currentScope), ""); _astRoot.accept(*this); + solAssert(m_currentScope == _currentScope, "Scopes not correctly closed."); +} + +bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit) +{ + if (!m_scopes[&_sourceUnit]) + // By importing, it is possible that the container already exists. + m_scopes[&_sourceUnit].reset(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); + m_currentScope = &_sourceUnit; + return true; +} + +void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit) +{ + _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations(); + closeCurrentScope(); } bool DeclarationRegistrationHelper::visit(ImportDirective& _import) @@ -587,12 +595,13 @@ void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declara void DeclarationRegistrationHelper::closeCurrentScope() { - solAssert(m_currentScope, "Closed non-existing scope."); + solAssert(m_currentScope && m_scopes.count(m_currentScope), "Closed non-existing scope."); m_currentScope = m_scopes[m_currentScope]->enclosingNode(); } void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) { + solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope."); if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract())) { SourceLocation firstDeclarationLocation; diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 68c3ffa1..828b566f 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -42,15 +42,27 @@ namespace solidity class NameAndTypeResolver: private boost::noncopyable { public: - NameAndTypeResolver(std::vector<Declaration const*> const& _globals, ErrorList& _errors); - /// Registers all declarations found in the source unit. + /// Creates the resolver with the given declarations added to the global scope. + /// @param _scopes mapping of scopes to be used (usually default constructed), these + /// are filled during the lifetime of this object. + NameAndTypeResolver( + std::vector<Declaration const*> const& _globals, + std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, + ErrorList& _errors + ); + /// Registers all declarations found in the AST node, usually a source unit. /// @returns false in case of error. - bool registerDeclarations(SourceUnit& _sourceUnit); + /// @param _currentScope should be nullptr but can be used to inject new declarations into + /// existing scopes, used by the snippets feature. + bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr); /// Applies the effect of import directives. bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits); - /// Resolves all names and types referenced from the given contract. + /// Resolves all names and types referenced from the given AST Node. + /// This is usually only called at the contract level, but with a bit of care, it can also + /// be called at deeper levels. + /// @param _resolveInsideCode if false, does not descend into nodes that contain code. /// @returns false in case of error. - bool resolveNamesAndTypes(ContractDefinition& _contract); + bool resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode = true); /// Updates the given global declaration (used for "this"). Not to be used with declarations /// that create their own scope. /// @returns false in case of error. @@ -77,8 +89,6 @@ public: ); private: - void reset(); - /// Imports all members declared directly in the given contract (i.e. does not import inherited members) /// into the current scope if they are not present already. void importInheritedScope(ContractDefinition const& _base); @@ -112,7 +122,7 @@ private: /// where nullptr denotes the global scope. Note that structs are not scope since they do /// not contain code. /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope. - std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; + std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; DeclarationContainer* m_currentScope = nullptr; ErrorList& m_errors; @@ -125,13 +135,20 @@ private: class DeclarationRegistrationHelper: private ASTVisitor { public: + /// Registers declarations in their scopes and creates new scopes as a side-effect + /// of construction. + /// @param _currentScope should be nullptr if we start at SourceUnit, but can be different + /// to inject new declarations into an existing scope, used by snippets. DeclarationRegistrationHelper( std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, - ErrorList& _errors + ErrorList& _errors, + ASTNode const* _currentScope = nullptr ); private: + bool visit(SourceUnit& _sourceUnit) override; + void endVisit(SourceUnit& _sourceUnit) override; bool visit(ImportDirective& _declaration) override; bool visit(ContractDefinition& _contract) override; void endVisit(ContractDefinition& _contract) override; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index d589f4a0..c06181d8 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -65,6 +65,30 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) return true; } +bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition) +{ + m_returnParameters.push_back(_functionDefinition.returnParameterList().get()); + return true; +} + +void ReferencesResolver::endVisit(FunctionDefinition const&) +{ + solAssert(!m_returnParameters.empty(), ""); + m_returnParameters.pop_back(); +} + +bool ReferencesResolver::visit(ModifierDefinition const&) +{ + m_returnParameters.push_back(nullptr); + return true; +} + +void ReferencesResolver::endVisit(ModifierDefinition const&) +{ + solAssert(!m_returnParameters.empty(), ""); + m_returnParameters.pop_back(); +} + void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) { Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); @@ -161,7 +185,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) bool ReferencesResolver::visit(Return const& _return) { - _return.annotation().functionReturnParameters = m_returnParameters; + solAssert(!m_returnParameters.empty(), ""); + _return.annotation().functionReturnParameters = m_returnParameters.back(); return true; } diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index caa3a78f..23ac6b07 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -45,12 +45,10 @@ public: ReferencesResolver( ErrorList& _errors, NameAndTypeResolver& _resolver, - ParameterList const* _returnParameters, bool _resolveInsideCode = false ): m_errors(_errors), m_resolver(_resolver), - m_returnParameters(_returnParameters), m_resolveInsideCode(_resolveInsideCode) {} @@ -61,6 +59,10 @@ private: virtual bool visit(Block const&) override { return m_resolveInsideCode; } virtual bool visit(Identifier const& _identifier) override; virtual bool visit(ElementaryTypeName const& _typeName) override; + virtual bool visit(FunctionDefinition const& _functionDefinition) override; + virtual void endVisit(FunctionDefinition const& _functionDefinition) override; + virtual bool visit(ModifierDefinition const& _modifierDefinition) override; + virtual void endVisit(ModifierDefinition const& _modifierDefinition) override; virtual void endVisit(UserDefinedTypeName const& _typeName) override; virtual void endVisit(FunctionTypeName const& _typeName) override; virtual void endVisit(Mapping const& _typeName) override; @@ -83,7 +85,8 @@ private: ErrorList& m_errors; NameAndTypeResolver& m_resolver; - ParameterList const* m_returnParameters; + /// Stack of return parameters. + std::vector<ParameterList const*> m_returnParameters; bool const m_resolveInsideCode; bool m_errorOccurred = false; }; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 0a4943fe..89014133 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -26,9 +26,9 @@ using namespace dev; using namespace dev::solidity; -bool SyntaxChecker::checkSyntax(SourceUnit const& _sourceUnit) +bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) { - _sourceUnit.accept(*this); + _astRoot.accept(*this); return Error::containsOnlyWarnings(m_errors); } diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index c24bae09..308e128b 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -39,7 +39,7 @@ public: /// @param _errors the reference to the list of errors and warnings to add them found during type checking. SyntaxChecker(ErrorList& _errors): m_errors(_errors) {} - bool checkSyntax(SourceUnit const& _sourceUnit); + bool checkSyntax(ASTNode const& _astRoot); private: /// Adds a new error to the list of errors. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 533c787b..28cb9acc 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -32,11 +32,11 @@ using namespace dev; using namespace dev::solidity; -bool TypeChecker::checkTypeRequirements(ContractDefinition const& _contract) +bool TypeChecker::checkTypeRequirements(ASTNode const& _contract) { try { - visit(_contract); + _contract.accept(*this); } catch (FatalError const&) { @@ -427,7 +427,9 @@ bool TypeChecker::visit(StructDefinition const& _struct) bool TypeChecker::visit(FunctionDefinition const& _function) { - bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary(); + bool isLibraryFunction = + dynamic_cast<ContractDefinition const*>(_function.scope()) && + dynamic_cast<ContractDefinition const*>(_function.scope())->isLibrary(); if (_function.isPayable()) { if (isLibraryFunction) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 143b15b2..46d8230a 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -47,7 +47,7 @@ public: /// Performs type checking on the given contract and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings - bool checkTypeRequirements(ContractDefinition const& _contract); + bool checkTypeRequirements(ASTNode const& _contract); /// @returns the type of an expression and asserts that it is present. TypePointer const& type(Expression const& _expression) const; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 3335c40e..9d8d872f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -88,6 +88,7 @@ void CompilerStack::reset(bool _keepSources) m_optimize = false; m_optimizeRuns = 200; m_globalContext.reset(); + m_scopes.clear(); m_sourceOrder.clear(); m_contracts.clear(); m_errors.clear(); @@ -165,7 +166,7 @@ bool CompilerStack::parse() noErrors = false; m_globalContext = make_shared<GlobalContext>(); - NameAndTypeResolver resolver(m_globalContext->declarations(), m_errors); + NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errors); for (Source const* source: m_sourceOrder) if (!resolver.registerDeclarations(*source->ast)) return false; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9ee70215..eddfea68 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -52,6 +52,7 @@ namespace solidity // forward declarations class Scanner; +class ASTNode; class ContractDefinition; class FunctionDefinition; class SourceUnit; @@ -59,6 +60,7 @@ class Compiler; class GlobalContext; class InterfaceHandler; class Error; +class DeclarationContainer; enum class DocumentationType: uint8_t { @@ -271,6 +273,7 @@ private: bool m_parseSuccessful; std::map<std::string const, Source> m_sources; std::shared_ptr<GlobalContext> m_globalContext; + std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; std::vector<Source const*> m_sourceOrder; std::map<std::string const, Contract> m_contracts; std::string m_formalTranslation; diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 497bfc77..c4ec0d20 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -53,7 +53,8 @@ eth::AssemblyItems compileContract(const string& _sourceCode) BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_CHECK(!!sourceUnit); - NameAndTypeResolver resolver({}, errors); + map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; + NameAndTypeResolver resolver({}, scopes, errors); solAssert(Error::containsOnlyWarnings(errors), ""); resolver.registerDeclarations(*sourceUnit); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index a769776e..3116aea8 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -114,7 +114,8 @@ bytes compileFirstExpression( declarations.push_back(variable.get()); ErrorList errors; - NameAndTypeResolver resolver(declarations, errors); + map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; + NameAndTypeResolver resolver(declarations, scopes, errors); resolver.registerDeclarations(*sourceUnit); vector<ContractDefinition const*> inheritanceHierarchy; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 0151d244..1a4f3cdc 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -66,7 +66,8 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, return make_pair(sourceUnit, errors.at(0)); std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>(); - NameAndTypeResolver resolver(globalContext->declarations(), errors); + map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; + NameAndTypeResolver resolver(globalContext->declarations(), scopes, errors); solAssert(Error::containsOnlyWarnings(errors), ""); resolver.registerDeclarations(*sourceUnit); @@ -1080,7 +1081,7 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value) modifier mod(uint a) { _; return 7; } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR(text, TypeError, "Return arguments not allowed."); } BOOST_AUTO_TEST_CASE(state_variable_accessors) |