From 89b60ffbd4c2dde26fa5e9f1d750729b5c89373e Mon Sep 17 00:00:00 2001 From: Rhett Aultman Date: Thu, 11 May 2017 06:26:35 -0700 Subject: [PATCH] Refactor error reporting This commit introduces ErrorReporter, a utility class which consolidates all of the error logging functionality into a common set of functions. It also replaces all direct interactions with an ErrorList with calls to an ErrorReporter. This commit resolves issue #2209 --- libjulia/backends/evm/EVMCodeTransform.cpp | 15 +- libjulia/backends/evm/EVMCodeTransform.h | 11 +- libsolidity/analysis/DocStringAnalyser.cpp | 7 +- libsolidity/analysis/DocStringAnalyser.h | 6 +- libsolidity/analysis/NameAndTypeResolver.cpp | 131 ++----- libsolidity/analysis/NameAndTypeResolver.h | 40 +-- libsolidity/analysis/PostTypeChecker.cpp | 19 +- libsolidity/analysis/PostTypeChecker.h | 6 +- libsolidity/analysis/ReferencesResolver.cpp | 24 +- libsolidity/analysis/ReferencesResolver.h | 12 +- libsolidity/analysis/StaticAnalyzer.cpp | 27 +- libsolidity/analysis/StaticAnalyzer.h | 6 +- libsolidity/analysis/SyntaxChecker.cpp | 41 +-- libsolidity/analysis/SyntaxChecker.h | 7 +- libsolidity/analysis/TypeChecker.cpp | 330 ++++++++---------- libsolidity/analysis/TypeChecker.h | 14 +- libsolidity/codegen/ContractCompiler.cpp | 5 +- libsolidity/formal/Why3Translator.cpp | 30 +- libsolidity/formal/Why3Translator.h | 16 +- libsolidity/inlineasm/AsmAnalysis.cpp | 153 ++++---- libsolidity/inlineasm/AsmAnalysis.h | 9 +- libsolidity/inlineasm/AsmCodeGen.cpp | 4 +- libsolidity/inlineasm/AsmCodeGen.h | 8 +- libsolidity/inlineasm/AsmParser.cpp | 6 +- libsolidity/inlineasm/AsmParser.h | 2 +- libsolidity/inlineasm/AsmScopeFiller.cpp | 33 +- libsolidity/inlineasm/AsmScopeFiller.h | 8 +- libsolidity/inlineasm/AsmStack.cpp | 19 +- libsolidity/inlineasm/AsmStack.h | 10 +- libsolidity/interface/AssemblyStack.cpp | 8 +- libsolidity/interface/AssemblyStack.h | 5 +- libsolidity/interface/CompilerStack.cpp | 48 ++- libsolidity/interface/CompilerStack.h | 14 +- libsolidity/interface/ErrorReporter.cpp | 186 ++++++++++ libsolidity/interface/ErrorReporter.h | 106 ++++++ libsolidity/parsing/DocStringParser.cpp | 9 +- libsolidity/parsing/DocStringParser.h | 7 +- libsolidity/parsing/Parser.cpp | 6 +- libsolidity/parsing/Parser.h | 2 +- libsolidity/parsing/ParserBase.cpp | 11 +- libsolidity/parsing/ParserBase.h | 6 +- solc/jsonCompiler.cpp | 7 +- test/libjulia/Parser.cpp | 9 +- test/libsolidity/Assembly.cpp | 14 +- .../SolidityExpressionCompiler.cpp | 10 +- .../SolidityNameAndTypeResolution.cpp | 25 +- test/libsolidity/SolidityParser.cpp | 5 +- 47 files changed, 770 insertions(+), 707 deletions(-) create mode 100644 libsolidity/interface/ErrorReporter.cpp create mode 100644 libsolidity/interface/ErrorReporter.h diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 18c3e63cc..355a9595d 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -32,14 +32,14 @@ using namespace dev::solidity; using namespace dev::solidity::assembly; CodeTransform::CodeTransform( - ErrorList& _errors, + ErrorReporter& _errorReporter, AbstractAssembly& _assembly, Block const& _block, AsmAnalysisInfo& _analysisInfo, ExternalIdentifierAccess const& _identifierAccess, int _initialStackHeight ): - m_errors(_errors), + m_errorReporter(_errorReporter), m_assembly(_assembly), m_info(_analysisInfo), m_scope(*_analysisInfo.scopes.at(&_block)), @@ -91,11 +91,10 @@ int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) { //@TODO move this to analysis phase. - m_errors.push_back(make_shared( - Error::Type::TypeError, - "Variable inaccessible, too deep inside stack (" + boost::lexical_cast(heightDiff) + ")", - _location - )); + m_errorReporter.typeError( + _location, + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast(heightDiff) + ")" + ); return 0; } else @@ -124,7 +123,7 @@ void CodeTransform::assignLabelIdIfUnset(Scope::Label& _label) void CodeTransform::operator()(Block const& _block) { - CodeTransform(m_errors, m_assembly, _block, m_info, m_identifierAccess, m_initialStackHeight); + CodeTransform(m_errorReporter, m_assembly, _block, m_info, m_identifierAccess, m_initialStackHeight); checkStackHeight(&_block); } diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 5408a3aae..64e762465 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -20,8 +20,6 @@ #include -#include - #include #include @@ -31,6 +29,7 @@ namespace dev { namespace solidity { +class ErrorReporter; namespace assembly { struct Literal; @@ -59,18 +58,18 @@ public: /// of its creation. /// @param _identifierAccess used to resolve identifiers external to the inline assembly CodeTransform( - solidity::ErrorList& _errors, + solidity::ErrorReporter& _errorReporter, julia::AbstractAssembly& _assembly, solidity::assembly::Block const& _block, solidity::assembly::AsmAnalysisInfo& _analysisInfo, ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess() - ): CodeTransform(_errors, _assembly, _block, _analysisInfo, _identifierAccess, _assembly.stackHeight()) + ): CodeTransform(_errorReporter, _assembly, _block, _analysisInfo, _identifierAccess, _assembly.stackHeight()) { } private: CodeTransform( - solidity::ErrorList& _errors, + solidity::ErrorReporter& _errorReporter, julia::AbstractAssembly& _assembly, solidity::assembly::Block const& _block, solidity::assembly::AsmAnalysisInfo& _analysisInfo, @@ -107,7 +106,7 @@ private: /// Assigns the label's id to a value taken from eth::Assembly if it has not yet been set. void assignLabelIdIfUnset(solidity::assembly::Scope::Label& _label); - solidity::ErrorList& m_errors; + solidity::ErrorReporter& m_errorReporter; julia::AbstractAssembly& m_assembly; solidity::assembly::AsmAnalysisInfo& m_info; solidity::assembly::Scope& m_scope; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 58261144b..6a8fa08ed 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using namespace std; @@ -110,7 +111,7 @@ void DocStringAnalyser::parseDocStrings( DocStringParser parser; if (_node.documentation() && !_node.documentation()->empty()) { - if (!parser.parse(*_node.documentation(), m_errors)) + if (!parser.parse(*_node.documentation(), m_errorReporter)) m_errorOccured = true; _annotation.docTags = parser.tags(); } @@ -121,8 +122,6 @@ void DocStringAnalyser::parseDocStrings( void DocStringAnalyser::appendError(string const& _description) { - auto err = make_shared(Error::Type::DocstringParsingError); - *err << errinfo_comment(_description); - m_errors.push_back(err); m_errorOccured = true; + m_errorReporter.docstringParsingError(_description); } diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index bfc8befce..e871cb60a 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -30,6 +30,8 @@ namespace dev namespace solidity { +class ErrorReporter; + /** * Parses and analyses the doc strings. * Stores the parsing results in the AST annotations and reports errors. @@ -37,7 +39,7 @@ namespace solidity class DocStringAnalyser: private ASTConstVisitor { public: - DocStringAnalyser(ErrorList& _errors): m_errors(_errors) {} + DocStringAnalyser(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool analyseDocStrings(SourceUnit const& _sourceUnit); private: @@ -64,7 +66,7 @@ private: void appendError(std::string const& _description); bool m_errorOccured = false; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 336dc894d..2742dcf22 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include using namespace std; @@ -36,10 +36,10 @@ namespace solidity NameAndTypeResolver::NameAndTypeResolver( vector const& _globals, map>& _scopes, - ErrorList& _errors + ErrorReporter& _errorReporter ) : m_scopes(_scopes), - m_errors(_errors) + m_errorReporter(_errorReporter) { if (!m_scopes[nullptr]) m_scopes[nullptr].reset(new DeclarationContainer()); @@ -52,11 +52,11 @@ bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode con // The helper registers all declarations in m_scopes as a side-effect of its construction. try { - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors, _currentScope); + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); } catch (FatalError const&) { - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. return false; } @@ -73,7 +73,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapannotation().absolutePath; if (!_sourceUnits.count(path)) { - reportDeclarationError( + m_errorReporter.declarationError( imp->location(), "Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found." ); @@ -88,7 +88,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapsecond->resolveName(alias.first->name(), false); if (declarations.empty()) { - reportDeclarationError( + m_errorReporter.declarationError( imp->location(), "Declaration \"" + alias.first->name() + @@ -106,7 +106,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapname(); if (!target.registerDeclaration(*declaration, name)) { - reportDeclarationError( + m_errorReporter.declarationError( imp->location(), "Identifier \"" + *name + "\" already declared." ); @@ -119,7 +119,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, maplocation(), "Identifier \"" + nameAndDeclaration.first + "\" already declared." ); @@ -137,7 +137,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi } catch (FatalError const&) { - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. return false; } @@ -152,7 +152,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) } catch (FatalError const&) { - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. return false; } @@ -214,7 +214,7 @@ vector NameAndTypeResolver::cleanedDeclarations( for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes()) if (!parameter) - reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context."); + m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context."); if (uniqueFunctions.end() == find_if( uniqueFunctions.begin(), @@ -290,7 +290,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res { if (m_scopes.count(&_node)) m_currentScope = m_scopes[&_node].get(); - return ReferencesResolver(m_errors, *this, _resolveInsideCode).resolve(_node); + return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); } } @@ -328,11 +328,10 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) secondDeclarationLocation = declaration->location(); } - reportDeclarationError( + m_errorReporter.declarationError( secondDeclarationLocation, - "Identifier already declared.", - firstDeclarationLocation, - "The previous declaration is here:" + SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation), + "Identifier already declared." ); } } @@ -347,19 +346,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) UserDefinedTypeName const& baseName = baseSpecifier->name(); auto base = dynamic_cast(baseName.annotation().referencedDeclaration); if (!base) - reportFatalTypeError(baseName.createTypeError("Contract expected.")); + m_errorReporter.fatalTypeError(baseName.location(), "Contract expected."); // "push_front" has the effect that bases mentioned later can overwrite members of bases // mentioned earlier input.back().push_front(base); vector const& basesBases = base->annotation().linearizedBaseContracts; if (basesBases.empty()) - reportFatalTypeError(baseName.createTypeError("Definition of base has to precede definition of derived contract")); + m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract"); input.push_front(list(basesBases.begin(), basesBases.end())); } input.back().push_front(&_contract); vector result = cThreeMerge(input); if (result.empty()) - reportFatalTypeError(_contract.createTypeError("Linearization of inheritance graph impossible")); + m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible"); _contract.annotation().linearizedBaseContracts = result; _contract.annotation().contractDependencies.insert(result.begin() + 1, result.end()); } @@ -415,61 +414,15 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list>& _toMer return result; } -void NameAndTypeResolver::reportDeclarationError( - SourceLocation _sourceLoction, - string const& _description, - SourceLocation _secondarySourceLocation, - string const& _secondaryDescription -) -{ - auto err = make_shared(Error::Type::DeclarationError); // todo remove Error? - *err << - errinfo_sourceLocation(_sourceLoction) << - errinfo_comment(_description) << - errinfo_secondarySourceLocation( - SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation) - ); - - m_errors.push_back(err); -} - -void NameAndTypeResolver::reportDeclarationError(SourceLocation _sourceLocation, string const& _description) -{ - auto err = make_shared(Error::Type::DeclarationError); // todo remove Error? - *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description); - - m_errors.push_back(err); -} - -void NameAndTypeResolver::reportFatalDeclarationError( - SourceLocation _sourceLocation, - string const& _description -) -{ - reportDeclarationError(_sourceLocation, _description); - BOOST_THROW_EXCEPTION(FatalError()); -} - -void NameAndTypeResolver::reportTypeError(Error const& _e) -{ - m_errors.push_back(make_shared(_e)); -} - -void NameAndTypeResolver::reportFatalTypeError(Error const& _e) -{ - reportTypeError(_e); - BOOST_THROW_EXCEPTION(FatalError()); -} - DeclarationRegistrationHelper::DeclarationRegistrationHelper( map>& _scopes, ASTNode& _astRoot, - ErrorList& _errors, + ErrorReporter& _errorReporter, ASTNode const* _currentScope ): m_scopes(_scopes), m_currentScope(_currentScope), - m_errors(_errors) + m_errorReporter(_errorReporter) { _astRoot.accept(*this); solAssert(m_currentScope == _currentScope, "Scopes not correctly closed."); @@ -633,11 +586,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio secondDeclarationLocation = _declaration.location(); } - declarationError( + m_errorReporter.declarationError( secondDeclarationLocation, - "Identifier already declared.", - firstDeclarationLocation, - "The previous declaration is here:" + SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation), + "Identifier already declared." ); } @@ -665,40 +617,5 @@ string DeclarationRegistrationHelper::currentCanonicalName() const return ret; } -void DeclarationRegistrationHelper::declarationError( - SourceLocation _sourceLocation, - string const& _description, - SourceLocation _secondarySourceLocation, - string const& _secondaryDescription -) -{ - auto err = make_shared(Error::Type::DeclarationError); - *err << - errinfo_sourceLocation(_sourceLocation) << - errinfo_comment(_description) << - errinfo_secondarySourceLocation( - SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation) - ); - - m_errors.push_back(err); -} - -void DeclarationRegistrationHelper::declarationError(SourceLocation _sourceLocation, string const& _description) -{ - auto err = make_shared(Error::Type::DeclarationError); - *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description); - - m_errors.push_back(err); -} - -void DeclarationRegistrationHelper::fatalDeclarationError( - SourceLocation _sourceLocation, - string const& _description -) -{ - declarationError(_sourceLocation, _description); - BOOST_THROW_EXCEPTION(FatalError()); -} - } } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 038a887b4..0441867d5 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -35,6 +35,8 @@ namespace dev namespace solidity { +class ErrorReporter; + /** * Resolves name references, typenames and sets the (explicitly given) types for all variable * declarations. @@ -48,7 +50,7 @@ public: NameAndTypeResolver( std::vector const& _globals, std::map>& _scopes, - ErrorList& _errors + ErrorReporter& _errorReporter ); /// Registers all declarations found in the AST node, usually a source unit. /// @returns false in case of error. @@ -103,24 +105,6 @@ private: template static std::vector<_T const*> cThreeMerge(std::list>& _toMerge); - // creates the Declaration error and adds it in the errors list - void reportDeclarationError( - SourceLocation _sourceLoction, - std::string const& _description, - SourceLocation _secondarySourceLocation, - std::string const& _secondaryDescription - ); - // creates the Declaration error and adds it in the errors list - void reportDeclarationError(SourceLocation _sourceLocation, std::string const& _description); - // creates the Declaration error and adds it in the errors list and throws FatalError - void reportFatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description); - - // creates the Declaration error and adds it in the errors list - void reportTypeError(Error const& _e); - // creates the Declaration error and adds it in the errors list and throws FatalError - void reportFatalTypeError(Error const& _e); - - /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration, /// where nullptr denotes the global scope. Note that structs are not scope since they do /// not contain code. @@ -128,7 +112,7 @@ private: std::map>& m_scopes; DeclarationContainer* m_currentScope = nullptr; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; /** @@ -145,7 +129,7 @@ public: DeclarationRegistrationHelper( std::map>& _scopes, ASTNode& _astRoot, - ErrorList& _errors, + ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -175,23 +159,11 @@ private: /// @returns the canonical name of the current scope. std::string currentCanonicalName() const; - // creates the Declaration error and adds it in the errors list - void declarationError( - SourceLocation _sourceLocation, - std::string const& _description, - SourceLocation _secondarySourceLocation, - std::string const& _secondaryDescription - ); - - // creates the Declaration error and adds it in the errors list - void declarationError(SourceLocation _sourceLocation, std::string const& _description); - // creates the Declaration error and adds it in the errors list and throws FatalError - void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description); std::map>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index e8da3ca43..fbc72e527 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -32,17 +33,7 @@ using namespace dev::solidity; bool PostTypeChecker::check(ASTNode const& _astRoot) { _astRoot.accept(*this); - return Error::containsOnlyWarnings(m_errors); -} - -void PostTypeChecker::typeError(SourceLocation const& _location, std::string const& _description) -{ - auto err = make_shared(Error::Type::TypeError); - *err << - errinfo_sourceLocation(_location) << - errinfo_comment(_description); - - m_errors.push_back(err); + return Error::containsOnlyWarnings(m_errorReporter.errors()); } bool PostTypeChecker::visit(ContractDefinition const&) @@ -57,7 +48,11 @@ void PostTypeChecker::endVisit(ContractDefinition const&) solAssert(!m_currentConstVariable, ""); for (auto declaration: m_constVariables) if (auto identifier = findCycle(declaration)) - typeError(declaration->location(), "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + "."); + m_errorReporter.typeError( + declaration->location(), + "The value of the constant " + declaration->name() + + " has a cyclic dependency via " + identifier->name() + "." + ); m_constVariables.clear(); m_constVariableDependencies.clear(); diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index 13751c164..dbdf50e06 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -28,6 +28,8 @@ namespace dev namespace solidity { +class ErrorReporter; + /** * This module performs analyses on the AST that are done after type checking and assignments of types: * - whether there are circular references in constant state variables @@ -37,7 +39,7 @@ class PostTypeChecker: private ASTConstVisitor { public: /// @param _errors the reference to the list of errors and warnings to add them found during type checking. - PostTypeChecker(ErrorList& _errors): m_errors(_errors) {} + PostTypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool check(ASTNode const& _astRoot); @@ -58,7 +60,7 @@ private: std::set const& _seen = std::set{} ); - ErrorList& m_errors; + ErrorReporter& m_errorReporter; VariableDeclaration const* m_currentConstVariable = nullptr; std::vector m_constVariables; ///< Required for determinism. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f5fffb574..b70b0f0e5 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -111,7 +112,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) case VariableDeclaration::Visibility::External: break; default: - typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); + fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); } if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External) @@ -165,7 +166,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // the type and size of external identifiers, which would result in false errors. // The only purpose of this step is to fill the inline assembly annotation with // external references. - ErrorList errorsIgnored; + ErrorList errors; + ErrorReporter errorsIgnored(errors); julia::ExternalIdentifierAccess::Resolver resolver = [&](assembly::Identifier const& _identifier, julia::IdentifierContext) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name); @@ -303,29 +305,19 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description) { - auto err = make_shared(Error::Type::TypeError); - *err << errinfo_sourceLocation(_location) << errinfo_comment(_description); m_errorOccurred = true; - m_errors.push_back(err); + m_errorReporter.typeError(_location, _description); } void ReferencesResolver::fatalTypeError(SourceLocation const& _location, string const& _description) { - typeError(_location, _description); - BOOST_THROW_EXCEPTION(FatalError()); -} - -void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description) -{ - auto err = make_shared(Error::Type::DeclarationError); - *err << errinfo_sourceLocation(_location) << errinfo_comment(_description); m_errorOccurred = true; - m_errors.push_back(err); + m_errorReporter.fatalTypeError(_location, _description); } void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description) { - declarationError(_location, _description); - BOOST_THROW_EXCEPTION(FatalError()); + m_errorOccurred = true; + m_errorReporter.fatalDeclarationError(_location, _description); } diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index dce343d33..bbde19f93 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -33,6 +33,7 @@ namespace dev namespace solidity { +class ErrorReporter; class NameAndTypeResolver; /** @@ -43,11 +44,11 @@ class ReferencesResolver: private ASTConstVisitor { public: ReferencesResolver( - ErrorList& _errors, + ErrorReporter& _errorReporter, NameAndTypeResolver& _resolver, bool _resolveInsideCode = false ): - m_errors(_errors), + m_errorReporter(_errorReporter), m_resolver(_resolver), m_resolveInsideCode(_resolveInsideCode) {} @@ -77,13 +78,10 @@ private: /// Adds a new error to the list of errors and throws to abort type checking. void fatalTypeError(SourceLocation const& _location, std::string const& _description); - /// Adds a new error to the list of errors. - void declarationError(const SourceLocation& _location, std::string const& _description); - /// Adds a new error to the list of errors and throws to abort type checking. - void fatalDeclarationError(const SourceLocation& _location, std::string const& _description); + void fatalDeclarationError(SourceLocation const& _location, std::string const& _description); - ErrorList& m_errors; + ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; /// Stack of return parameters. std::vector m_returnParameters; diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index cfc863e08..375e9bdea 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -22,6 +22,7 @@ #include #include +#include #include using namespace std; @@ -31,7 +32,7 @@ using namespace dev::solidity; bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); - return Error::containsOnlyWarnings(m_errors); + return Error::containsOnlyWarnings(m_errorReporter.errors()); } bool StaticAnalyzer::visit(ContractDefinition const& _contract) @@ -62,7 +63,7 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) m_nonPayablePublic = false; for (auto const& var: m_localVarUseCount) if (var.second == 0) - warning(var.first->location(), "Unused local variable"); + m_errorReporter.warning(var.first->location(), "Unused local variable"); m_localVarUseCount.clear(); } @@ -104,7 +105,11 @@ bool StaticAnalyzer::visit(Return const& _return) bool StaticAnalyzer::visit(ExpressionStatement const& _statement) { if (_statement.expression().annotation().isPure) - warning(_statement.location(), "Statement has no effect."); + m_errorReporter.warning( + _statement.location(), + "Statement has no effect." + ); + return true; } @@ -113,21 +118,14 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (m_nonPayablePublic && !m_library) if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value") - warning(_memberAccess.location(), "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?"); + m_errorReporter.warning( + _memberAccess.location(), + "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?" + ); return true; } -void StaticAnalyzer::warning(SourceLocation const& _location, string const& _description) -{ - auto err = make_shared(Error::Type::Warning); - *err << - errinfo_sourceLocation(_location) << - errinfo_comment(_description); - - m_errors.push_back(err); -} - bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) { if (!m_currentFunction) @@ -145,4 +143,3 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) return true; } - diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 458bab2a4..cd6913b58 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -44,15 +44,13 @@ class StaticAnalyzer: private ASTConstVisitor { public: /// @param _errors the reference to the list of errors and warnings to add them found during static analysis. - explicit StaticAnalyzer(ErrorList& _errors): m_errors(_errors) {} + explicit StaticAnalyzer(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} /// Performs static analysis on the given source unit and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings bool analyze(SourceUnit const& _sourceUnit); private: - /// Adds a new warning to the list of errors. - void warning(SourceLocation const& _location, std::string const& _description); virtual bool visit(ContractDefinition const& _contract) override; virtual void endVisit(ContractDefinition const& _contract) override; @@ -67,7 +65,7 @@ private: virtual bool visit(MemberAccess const& _memberAccess) override; virtual bool visit(InlineAssembly const& _inlineAssembly) override; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; /// Flag that indicates whether the current contract definition is a library. bool m_library = false; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 94e82a878..35d71d854 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include using namespace std; @@ -29,27 +30,7 @@ using namespace dev::solidity; bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) { _astRoot.accept(*this); - return Error::containsOnlyWarnings(m_errors); -} - -void SyntaxChecker::warning(SourceLocation const& _location, string const& _description) -{ - auto err = make_shared(Error::Type::Warning); - *err << - errinfo_sourceLocation(_location) << - errinfo_comment(_description); - - m_errors.push_back(err); -} - -void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string const& _description) -{ - auto err = make_shared(Error::Type::SyntaxError); - *err << - errinfo_sourceLocation(_location) << - errinfo_comment(_description); - - m_errors.push_back(err); + return Error::containsOnlyWarnings(m_errorReporter.errors()); } bool SyntaxChecker::visit(SourceUnit const&) @@ -74,11 +55,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) to_string(recommendedVersion.patch()); string(";\""); - auto err = make_shared(Error::Type::Warning); - *err << - errinfo_sourceLocation(_sourceUnit.location()) << - errinfo_comment(errorString); - m_errors.push_back(err); + m_errorReporter.warning(_sourceUnit.location(), errorString); } } @@ -87,7 +64,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) solAssert(!_pragma.tokens().empty(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity") - syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); + m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); else { vector tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); @@ -96,7 +73,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) auto matchExpression = parser.parse(); SemVerVersion currentVersion{string(VersionString)}; if (!matchExpression.matches(currentVersion)) - syntaxError( + m_errorReporter.syntaxError( _pragma.location(), "Source file requires different compiler version (current compiler is " + string(VersionString) + " - note that nightly builds are considered to be " @@ -116,7 +93,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&) void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) { if (!m_placeholderFound) - syntaxError(_modifier.body().location(), "Modifier body does not contain '_'."); + m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'."); m_placeholderFound = false; } @@ -146,7 +123,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement) { if (m_inLoopDepth <= 0) // we're not in a for/while loop, report syntax error - syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop."); + m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop."); return true; } @@ -154,14 +131,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement) { if (m_inLoopDepth <= 0) // we're not in a for/while loop, report syntax error - syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop."); + m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop."); return true; } bool SyntaxChecker::visit(UnaryOperation const& _operation) { if (_operation.getOperator() == Token::Add) - warning(_operation.location(), "Use of unary + is deprecated."); + m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated."); return true; } diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 8d7dcdd32..b1857aa5a 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -38,14 +38,11 @@ class SyntaxChecker: private ASTConstVisitor { 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) {} + SyntaxChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool checkSyntax(ASTNode const& _astRoot); private: - /// Adds a new error to the list of errors. - void warning(SourceLocation const& _location, std::string const& _description); - void syntaxError(SourceLocation const& _location, std::string const& _description); virtual bool visit(SourceUnit const& _sourceUnit) override; virtual void endVisit(SourceUnit const& _sourceUnit) override; @@ -66,7 +63,7 @@ private: virtual bool visit(PlaceholderStatement const& _placeholderStatement) override; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; /// Flag that indicates whether a function modifier actually contains '_'. bool m_placeholderFound = false; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 528a23dae..0fb0d212f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace std; using namespace dev; @@ -43,10 +44,10 @@ bool TypeChecker::checkTypeRequirements(ASTNode const& _contract) { // We got a fatal error which required to stop further type checking, but we can // continue normally from here. - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. } - return Error::containsOnlyWarnings(m_errors); + return Error::containsOnlyWarnings(m_errorReporter.errors()); } TypePointer const& TypeChecker::type(Expression const& _expression) const @@ -81,11 +82,11 @@ bool TypeChecker::visit(ContractDefinition const& _contract) if (function) { if (!function->returnParameters().empty()) - typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); + m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); if (function->isDeclaredConst()) - typeError(function->location(), "Constructor cannot be defined as constant."); + m_errorReporter.typeError(function->location(), "Constructor cannot be defined as constant."); if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal) - typeError(function->location(), "Constructor must be public or internal."); + m_errorReporter.typeError(function->location(), "Constructor must be public or internal."); } FunctionDefinition const* fallbackFunction = nullptr; @@ -95,21 +96,19 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { if (fallbackFunction) { - auto err = make_shared(Error::Type::DeclarationError); - *err << errinfo_comment("Only one fallback function is allowed."); - m_errors.push_back(err); + m_errorReporter.declarationError(function->location(), "Only one fallback function is allowed."); } else { fallbackFunction = function; if (_contract.isLibrary()) - typeError(fallbackFunction->location(), "Libraries cannot have fallback functions."); + m_errorReporter.typeError(fallbackFunction->location(), "Libraries cannot have fallback functions."); if (fallbackFunction->isDeclaredConst()) - typeError(fallbackFunction->location(), "Fallback function cannot be declared constant."); + m_errorReporter.typeError(fallbackFunction->location(), "Fallback function cannot be declared constant."); if (!fallbackFunction->parameters().empty()) - typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters."); + m_errorReporter.typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters."); if (!fallbackFunction->returnParameters().empty()) - typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values."); + m_errorReporter.typeError(fallbackFunction->returnParameterList()->location(), "Fallback function cannot return values."); } } if (!function->isImplemented()) @@ -127,7 +126,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { FixedHash<4> const& hash = it.first; if (hashes.count(hash)) - typeError( + m_errorReporter.typeError( _contract.location(), string("Function signature hash collision for ") + it.second->externalSignature() ); @@ -156,12 +155,11 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con for (; it != functions[_contract.name()].end(); ++it) ssl.append("Another declaration is here:", (*it)->location()); - auto err = make_shared(Error(Error::Type::DeclarationError)); - *err << - errinfo_sourceLocation(functions[_contract.name()].front()->location()) << - errinfo_comment("More than one constructor defined.") << - errinfo_secondarySourceLocation(ssl); - m_errors.push_back(err); + m_errorReporter.declarationError( + functions[_contract.name()].front()->location(), + ssl, + "More than one constructor defined." + ); } for (auto const& it: functions) { @@ -170,13 +168,14 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con for (size_t j = i + 1; j < overloads.size(); ++j) if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) { - auto err = make_shared(Error(Error::Type::DeclarationError)); - *err << - errinfo_sourceLocation(overloads[j]->location()) << - errinfo_comment("Function with same name and arguments defined twice.") << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "Other declaration is here:", overloads[i]->location())); - m_errors.push_back(err); + m_errorReporter.declarationError( + overloads[j]->location(), + SecondarySourceLocation().append( + "Other declaration is here:", + overloads[i]->location() + ), + "Function with same name and arguments defined twice." + ); } } } @@ -213,7 +212,7 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont else if (it->second) { if (!function->isImplemented()) - typeError(function->location(), "Redeclaring an already implemented function as abstract"); + m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract"); } else if (function->isImplemented()) it->second = true; @@ -285,7 +284,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr continue; // constructors can neither be overridden nor override anything string const& name = function->name(); if (modifiers.count(name)) - typeError(modifiers[name]->location(), "Override changes function to modifier."); + m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier."); FunctionType functionType(*function); // function should not change the return type for (FunctionDefinition const* overriding: functions[name]) @@ -299,7 +298,7 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr overriding->isPayable() != function->isPayable() || overridingType != functionType ) - typeError(overriding->location(), "Override changes extended function signature."); + m_errorReporter.typeError(overriding->location(), "Override changes extended function signature."); } functions[name].push_back(function); } @@ -310,9 +309,9 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr if (!override) override = modifier; else if (ModifierType(*override) != ModifierType(*modifier)) - typeError(override->location(), "Override changes modifier signature."); + m_errorReporter.typeError(override->location(), "Override changes modifier signature."); if (!functions[name].empty()) - typeError(override->location(), "Override changes modifier to function."); + m_errorReporter.typeError(override->location(), "Override changes modifier to function."); } } } @@ -347,7 +346,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co for (size_t i = 0; i < it.second.size(); ++i) for (size_t j = i + 1; j < it.second.size(); ++j) if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) - typeError( + m_errorReporter.typeError( it.second[j].first->location(), "Function overload clash during conversion to external types for arguments." ); @@ -357,11 +356,11 @@ void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract) { solAssert(_contract.isLibrary(), ""); if (!_contract.baseContracts().empty()) - typeError(_contract.location(), "Library is not allowed to inherit."); + m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit."); for (auto const& var: _contract.stateVariables()) if (!var->isConstant()) - typeError(var->location(), "Library cannot have non-constant state variables"); + m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables"); } void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) @@ -370,16 +369,16 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) solAssert(base, "Base contract not available."); if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - typeError(_inheritance.location(), "Interfaces cannot inherit."); + m_errorReporter.typeError(_inheritance.location(), "Interfaces cannot inherit."); if (base->isLibrary()) - typeError(_inheritance.location(), "Libraries cannot be inherited from."); + m_errorReporter.typeError(_inheritance.location(), "Libraries cannot be inherited from."); auto const& arguments = _inheritance.arguments(); TypePointers parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); if (!arguments.empty() && parameterTypes.size() != arguments.size()) { - typeError( + m_errorReporter.typeError( _inheritance.location(), "Wrong argument count for constructor call: " + toString(arguments.size()) + @@ -392,7 +391,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) for (size_t i = 0; i < arguments.size(); ++i) if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - typeError( + m_errorReporter.typeError( arguments[i]->location(), "Invalid type for argument in constructor call. " "Invalid implicit conversion from " + @@ -409,17 +408,17 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) _usingFor.libraryName().annotation().referencedDeclaration ); if (!library || !library->isLibrary()) - typeError(_usingFor.libraryName().location(), "Library name expected."); + m_errorReporter.typeError(_usingFor.libraryName().location(), "Library name expected."); } bool TypeChecker::visit(StructDefinition const& _struct) { if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - typeError(_struct.location(), "Structs cannot be defined in interfaces."); + m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces."); for (ASTPointer const& member: _struct.members()) if (!type(*member)->canBeStored()) - typeError(member->location(), "Type cannot be used in struct."); + m_errorReporter.typeError(member->location(), "Type cannot be used in struct."); // Check recursion, fatal error if detected. using StructPointer = StructDefinition const*; @@ -427,7 +426,7 @@ bool TypeChecker::visit(StructDefinition const& _struct) function check = [&](StructPointer _struct, StructPointersSet const& _parents) { if (_parents.count(_struct)) - fatalTypeError(_struct->location(), "Recursive struct definition."); + m_errorReporter.fatalTypeError(_struct->location(), "Recursive struct definition."); StructPointersSet parents = _parents; parents.insert(_struct); for (ASTPointer const& member: _struct->members()) @@ -452,18 +451,18 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (_function.isPayable()) { if (isLibraryFunction) - typeError(_function.location(), "Library functions cannot be payable."); + m_errorReporter.typeError(_function.location(), "Library functions cannot be payable."); if (!_function.isConstructor() && !_function.name().empty() && !_function.isPartOfExternalInterface()) - typeError(_function.location(), "Internal functions cannot be payable."); + m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); if (_function.isDeclaredConst()) - typeError(_function.location(), "Functions cannot be constant and payable at the same time."); + m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time."); } for (ASTPointer const& var: _function.parameters() + _function.returnParameters()) { if (!type(*var)->canLiveOutsideStorage()) - typeError(var->location(), "Type is required to live outside storage."); + m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) - fatalTypeError(var->location(), "Internal type is not allowed for public or external functions."); + m_errorReporter.fatalTypeError(var->location(), "Internal type is not allowed for public or external functions."); } for (ASTPointer const& modifier: _function.modifiers()) visitManually( @@ -475,11 +474,11 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) { if (_function.isImplemented()) - typeError(_function.location(), "Functions in interfaces cannot have an implementation."); + m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation."); if (_function.visibility() < FunctionDefinition::Visibility::Public) - typeError(_function.location(), "Functions in interfaces cannot be internal or private."); + m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private."); if (_function.isConstructor()) - typeError(_function.location(), "Constructor cannot be defined in interfaces."); + m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); } if (_function.isImplemented()) _function.body().accept(*this); @@ -489,7 +488,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(VariableDeclaration const& _variable) { if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - typeError(_variable.location(), "Variables cannot be declared in interfaces."); + m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); // Variables can be declared without type (with "var"), in which case the first assignment // sets the type. @@ -505,19 +504,19 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (_variable.isConstant()) { if (!_variable.isStateVariable()) - typeError(_variable.location(), "Illegal use of \"constant\" specifier."); + m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier."); if (!_variable.type()->isValueType()) { bool allowed = false; if (auto arrayType = dynamic_cast(_variable.type().get())) allowed = arrayType->isString(); if (!allowed) - typeError(_variable.location(), "Constants of non-value type not yet implemented."); + m_errorReporter.typeError(_variable.location(), "Constants of non-value type not yet implemented."); } if (!_variable.value()) - typeError(_variable.location(), "Uninitialized \"constant\" variable."); + m_errorReporter.typeError(_variable.location(), "Uninitialized \"constant\" variable."); else if (!_variable.value()->annotation().isPure) - warning( + m_errorReporter.warning( _variable.value()->location(), "Initial value for constant variable has to be compile-time constant. " "This will fail to compile with the next breaking version change." @@ -527,20 +526,20 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) { if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) if (!varType->canLiveOutsideStorage()) - typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage."); + m_errorReporter.typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage."); } else if ( _variable.visibility() >= VariableDeclaration::Visibility::Public && !FunctionType(_variable).interfaceFunctionType() ) - typeError(_variable.location(), "Internal type is not allowed for public state variables."); + m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables."); return false; } bool TypeChecker::visit(EnumDefinition const& _enum) { if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - typeError(_enum.location(), "Enumerable cannot be declared in interfaces."); + m_errorReporter.typeError(_enum.location(), "Enumerable cannot be declared in interfaces."); return false; } @@ -572,12 +571,12 @@ void TypeChecker::visitManually( } if (!parameters) { - typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class."); + m_errorReporter.typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class."); return; } if (parameters->size() != arguments.size()) { - typeError( + m_errorReporter.typeError( _modifier.location(), "Wrong argument count for modifier invocation: " + toString(arguments.size()) + @@ -589,7 +588,7 @@ void TypeChecker::visitManually( } for (size_t i = 0; i < _modifier.arguments().size(); ++i) if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i]))) - typeError( + m_errorReporter.typeError( arguments[i]->location(), "Invalid type for argument in modifier invocation. " "Invalid implicit conversion from " + @@ -608,13 +607,13 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) if (var->isIndexed()) numIndexed++; if (_eventDef.isAnonymous() && numIndexed > 4) - typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); + m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); else if (!_eventDef.isAnonymous() && numIndexed > 3) - typeError(_eventDef.location(), "More than 3 indexed arguments for event."); + m_errorReporter.typeError(_eventDef.location(), "More than 3 indexed arguments for event."); if (!type(*var)->canLiveOutsideStorage()) - typeError(var->location(), "Type is required to live outside storage."); + m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) - typeError(var->location(), "Internal type is not allowed as event parameter type."); + m_errorReporter.typeError(var->location(), "Internal type is not allowed as event parameter type."); } return false; } @@ -624,7 +623,7 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType) FunctionType const& fun = dynamic_cast(*_funType.annotation().type); if (fun.kind() == FunctionType::Kind::External) if (!fun.canBeUsedExternally(false)) - typeError(_funType.location(), "External function type uses internal types."); + m_errorReporter.typeError(_funType.location(), "External function type uses internal types."); } bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) @@ -647,39 +646,39 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) { - typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); + m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } else if (_context != julia::IdentifierContext::RValue) { - typeError(_identifier.location, "Storage variables cannot be assigned to."); + m_errorReporter.typeError(_identifier.location, "Storage variables cannot be assigned to."); return size_t(-1); } } else if (var->isConstant()) { - typeError(_identifier.location, "Constant variables not supported by inline assembly."); + m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); return size_t(-1); } else if (!var->isLocalVariable()) { - typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); + m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); return size_t(-1); } else if (var->type()->dataStoredIn(DataLocation::Storage)) { - typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables."); + m_errorReporter.typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables."); return size_t(-1); } else if (var->type()->sizeOnStack() != 1) { - typeError(_identifier.location, "Only types that use one stack slot are supported."); + m_errorReporter.typeError(_identifier.location, "Only types that use one stack slot are supported."); return size_t(-1); } } else if (_context == julia::IdentifierContext::LValue) { - typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); + m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); return size_t(-1); } @@ -696,7 +695,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (!contract->isLibrary()) { - typeError(_identifier.location, "Expected a library."); + m_errorReporter.typeError(_identifier.location, "Expected a library."); return size_t(-1); } } @@ -710,7 +709,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) _inlineAssembly.annotation().analysisInfo = make_shared(); assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, - m_errors, + m_errorReporter, false, identifierAccess ); @@ -754,7 +753,7 @@ void TypeChecker::endVisit(Return const& _return) ParameterList const* params = _return.annotation().functionReturnParameters; if (!params) { - typeError(_return.location(), "Return arguments not allowed."); + m_errorReporter.typeError(_return.location(), "Return arguments not allowed."); return; } TypePointers returnTypes; @@ -763,9 +762,9 @@ void TypeChecker::endVisit(Return const& _return) if (auto tupleType = dynamic_cast(type(*_return.expression()).get())) { if (tupleType->components().size() != params->parameters().size()) - typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); + m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes))) - typeError( + m_errorReporter.typeError( _return.expression()->location(), "Return argument type " + type(*_return.expression())->toString() + @@ -775,12 +774,12 @@ void TypeChecker::endVisit(Return const& _return) ); } else if (params->parameters().size() != 1) - typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); + m_errorReporter.typeError(_return.location(), "Different number of arguments in return statement than in returns declaration."); else { TypePointer const& expected = type(*params->parameters().front()); if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected)) - typeError( + m_errorReporter.typeError( _return.expression()->location(), "Return argument type " + type(*_return.expression())->toString() + @@ -797,20 +796,20 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { // No initial value is only permitted for single variables with specified type. if (_statement.declarations().size() != 1 || !_statement.declarations().front()) - fatalTypeError(_statement.location(), "Assignment necessary for type detection."); + m_errorReporter.fatalTypeError(_statement.location(), "Assignment necessary for type detection."); VariableDeclaration const& varDecl = *_statement.declarations().front(); if (!varDecl.annotation().type) - fatalTypeError(_statement.location(), "Assignment necessary for type detection."); + m_errorReporter.fatalTypeError(_statement.location(), "Assignment necessary for type detection."); if (auto ref = dynamic_cast(type(varDecl).get())) { if (ref->dataStoredIn(DataLocation::Storage)) - warning( + m_errorReporter.warning( varDecl.location(), "Uninitialized storage pointer. Did you mean ' memory " + varDecl.name() + "'?" ); } else if (dynamic_cast(type(varDecl).get())) - typeError( + m_errorReporter.typeError( varDecl.location(), "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." ); @@ -836,7 +835,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (variables.empty()) { if (!valueTypes.empty()) - fatalTypeError( + m_errorReporter.fatalTypeError( _statement.location(), "Too many components (" + toString(valueTypes.size()) + @@ -844,7 +843,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) ); } else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back()) - fatalTypeError( + m_errorReporter.fatalTypeError( _statement.location(), "Wildcard both at beginning and end of variable declaration list is only allowed " "if the number of components is equal." @@ -853,7 +852,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (!variables.empty() && (!variables.back() || !variables.front())) --minNumValues; if (valueTypes.size() < minNumValues) - fatalTypeError( + m_errorReporter.fatalTypeError( _statement.location(), "Not enough components (" + toString(valueTypes.size()) + @@ -861,7 +860,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) toString(minNumValues) + ")." ); if (valueTypes.size() > variables.size() && variables.front() && variables.back()) - fatalTypeError( + m_errorReporter.fatalTypeError( _statement.location(), "Too many components (" + toString(valueTypes.size()) + @@ -892,7 +891,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (!var.annotation().type) { if (valueComponentType->category() == Type::Category::RationalNumber) - fatalTypeError( + m_errorReporter.fatalTypeError( _statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + @@ -902,7 +901,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) solAssert(false, ""); } else if (*var.annotation().type == TupleType()) - typeError( + m_errorReporter.typeError( var.location(), "Cannot declare variable with void (empty tuple) type." ); @@ -918,7 +917,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) dynamic_cast(*valueComponentType).isFractional() && valueComponentType->mobileType() ) - typeError( + m_errorReporter.typeError( _statement.location(), "Type " + valueComponentType->toString() + @@ -929,7 +928,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) " or use an explicit conversion." ); else - typeError( + m_errorReporter.typeError( _statement.location(), "Type " + valueComponentType->toString() + @@ -947,7 +946,7 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) { if (type(_statement.expression())->category() == Type::Category::RationalNumber) if (!dynamic_cast(*type(_statement.expression())).mobileType()) - typeError(_statement.expression().location(), "Invalid rational number."); + m_errorReporter.typeError(_statement.expression().location(), "Invalid rational number."); if (auto call = dynamic_cast(&_statement.expression())) { @@ -959,9 +958,9 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) kind == FunctionType::Kind::BareCallCode || kind == FunctionType::Kind::BareDelegateCall ) - warning(_statement.location(), "Return value of low-level calls not used."); + m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used."); else if (kind == FunctionType::Kind::Send) - warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead."); + m_errorReporter.warning(_statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead."); } } } @@ -976,14 +975,14 @@ bool TypeChecker::visit(Conditional const& _conditional) TypePointer trueType = type(_conditional.trueExpression())->mobileType(); TypePointer falseType = type(_conditional.falseExpression())->mobileType(); if (!trueType) - fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type."); + m_errorReporter.fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type."); if (!falseType) - fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type."); + m_errorReporter.fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type."); TypePointer commonType = Type::commonType(trueType, falseType); if (!commonType) { - typeError( + m_errorReporter.typeError( _conditional.location(), "True expression's type " + trueType->toString() + @@ -1003,7 +1002,7 @@ bool TypeChecker::visit(Conditional const& _conditional) _conditional.falseExpression().annotation().isPure; if (_conditional.annotation().lValueRequested) - typeError( + m_errorReporter.typeError( _conditional.location(), "Conditional expression as left value is not supported yet." ); @@ -1019,7 +1018,7 @@ bool TypeChecker::visit(Assignment const& _assignment) if (TupleType const* tupleType = dynamic_cast(t.get())) { if (_assignment.assignmentOperator() != Token::Assign) - typeError( + m_errorReporter.typeError( _assignment.location(), "Compound assignment is not allowed for tuple types." ); @@ -1029,7 +1028,7 @@ bool TypeChecker::visit(Assignment const& _assignment) } else if (t->category() == Type::Category::Mapping) { - typeError(_assignment.location(), "Mappings cannot be assigned to."); + m_errorReporter.typeError(_assignment.location(), "Mappings cannot be assigned to."); _assignment.rightHandSide().accept(*this); } else if (_assignment.assignmentOperator() == Token::Assign) @@ -1043,7 +1042,7 @@ bool TypeChecker::visit(Assignment const& _assignment) type(_assignment.rightHandSide()) ); if (!resultType || *resultType != *t) - typeError( + m_errorReporter.typeError( _assignment.location(), "Operator " + string(Token::toString(_assignment.assignmentOperator())) + @@ -1064,7 +1063,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (_tuple.annotation().lValueRequested) { if (_tuple.isInlineArray()) - fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue."); + m_errorReporter.fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue."); for (auto const& component: components) if (component) { @@ -1088,7 +1087,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { // Outside of an lvalue-context, the only situation where a component can be empty is (x,). if (!components[i] && !(i == 1 && components.size() == 2)) - fatalTypeError(_tuple.location(), "Tuple component cannot be empty."); + m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty."); else if (components[i]) { components[i]->accept(*this); @@ -1098,7 +1097,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) { if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) - fatalTypeError(components[i]->location(), "Invalid mobile type."); + m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type."); if (i == 0) inlineArrayType = types[i]->mobileType(); @@ -1115,7 +1114,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) { if (!inlineArrayType) - fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements."); + m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements."); _tuple.annotation().type = make_shared(DataLocation::Memory, inlineArrayType, types.size()); } else @@ -1147,7 +1146,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation) TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op); if (!t) { - typeError( + m_errorReporter.typeError( _operation.location(), "Unary operator " + string(Token::toString(op)) + @@ -1168,7 +1167,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); if (!commonType) { - typeError( + m_errorReporter.typeError( _operation.location(), "Operator " + string(Token::toString(_operation.getOperator())) + @@ -1201,7 +1200,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) commonType->category() == Type::Category::FixedPoint && dynamic_cast(*commonType).numBits() != 256 )) - warning( + m_errorReporter.warning( _operation.location(), "Result of exponentiation has type " + commonType->toString() + " and thus " "might overflow. Silence this warning by converting the literal to the " @@ -1254,9 +1253,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) TypeType const& t = dynamic_cast(*expressionType); TypePointer resultType = t.actualType(); if (arguments.size() != 1) - typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion."); + m_errorReporter.typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion."); else if (!isPositionalCall) - typeError(_functionCall.location(), "Type conversion cannot allow named arguments."); + m_errorReporter.typeError(_functionCall.location(), "Type conversion cannot allow named arguments."); else { TypePointer const& argType = type(*arguments.front()); @@ -1265,7 +1264,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) // (data location cannot yet be specified for type conversions) resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType); if (!argType->isExplicitlyConvertibleTo(*resultType)) - typeError(_functionCall.location(), "Explicit type conversion not allowed."); + m_errorReporter.typeError(_functionCall.location(), "Explicit type conversion not allowed."); } _functionCall.annotation().type = resultType; _functionCall.annotation().isPure = isPure; @@ -1298,7 +1297,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (!functionType) { - typeError(_functionCall.location(), "Type is not callable"); + m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); _functionCall.annotation().type = make_shared(); return false; } @@ -1323,7 +1322,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (auto const& member: membersRemovedForStructConstructor) msg += " " + member; } - typeError(_functionCall.location(), msg); + m_errorReporter.typeError(_functionCall.location(), msg); } else if (isPositionalCall) { @@ -1335,10 +1334,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) { if (auto t = dynamic_cast(argType.get())) if (!t->mobileType()) - typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); + m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - typeError( + m_errorReporter.typeError( arguments[i]->location(), "Invalid type for argument in function call. " "Invalid implicit conversion from " + @@ -1354,14 +1353,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) // call by named arguments auto const& parameterNames = functionType->parameterNames(); if (functionType->takesArbitraryParameters()) - typeError( + m_errorReporter.typeError( _functionCall.location(), "Named arguments cannnot be used for functions that take arbitrary parameters." ); else if (parameterNames.size() > argumentNames.size()) - typeError(_functionCall.location(), "Some argument names are missing."); + m_errorReporter.typeError(_functionCall.location(), "Some argument names are missing."); else if (parameterNames.size() < argumentNames.size()) - typeError(_functionCall.location(), "Too many arguments."); + m_errorReporter.typeError(_functionCall.location(), "Too many arguments."); else { // check duplicate names @@ -1371,7 +1370,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (*argumentNames[i] == *argumentNames[j]) { duplication = true; - typeError(arguments[i]->location(), "Duplicate named argument."); + m_errorReporter.typeError(arguments[i]->location(), "Duplicate named argument."); } // check actual types @@ -1385,7 +1384,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) found = true; // check type convertible if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j])) - typeError( + m_errorReporter.typeError( arguments[i]->location(), "Invalid type for argument in function call. " "Invalid implicit conversion from " + @@ -1398,7 +1397,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } if (!found) - typeError( + m_errorReporter.typeError( _functionCall.location(), "Named argument does not match function declaration." ); @@ -1419,11 +1418,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) auto contract = dynamic_cast(&dereference(*contractName)); if (!contract) - fatalTypeError(_newExpression.location(), "Identifier is not a contract."); + m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (!contract->annotation().isFullyImplemented) - typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); + m_errorReporter.typeError(_newExpression.location(), "Trying to create an instance of an abstract contract."); if (!contract->constructorIsPublic()) - typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); + m_errorReporter.typeError(_newExpression.location(), "Contract with internal constructor cannot be created directly."); solAssert(!!m_scope, ""); m_scope->annotation().contractDependencies.insert(contract); @@ -1432,7 +1431,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) "Linearized base contracts not yet available." ); if (contractDependenciesAreCyclic(*m_scope)) - typeError( + m_errorReporter.typeError( _newExpression.location(), "Circular reference for contract creation (cannot create instance of derived or same contract)." ); @@ -1442,12 +1441,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) else if (type->category() == Type::Category::Array) { if (!type->canLiveOutsideStorage()) - fatalTypeError( + m_errorReporter.fatalTypeError( _newExpression.typeName().location(), "Type cannot live outside storage." ); if (!type->isDynamicallySized()) - typeError( + m_errorReporter.typeError( _newExpression.typeName().location(), "Length has to be placed in parentheses after the array type for new expression." ); @@ -1462,7 +1461,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) _newExpression.annotation().isPure = true; } else - fatalTypeError(_newExpression.location(), "Contract or array type expected."); + m_errorReporter.fatalTypeError(_newExpression.location(), "Contract or array type expected."); } bool TypeChecker::visit(MemberAccess const& _memberAccess) @@ -1493,13 +1492,13 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) exprType ); if (!storageType->members(m_scope).membersByName(memberName).empty()) - fatalTypeError( + m_errorReporter.fatalTypeError( _memberAccess.location(), "Member \"" + memberName + "\" is not available in " + exprType->toString() + " outside of storage." ); - fatalTypeError( + m_errorReporter.fatalTypeError( _memberAccess.location(), "Member \"" + memberName + "\" not found or not visible " "after argument-dependent lookup in " + exprType->toString() + @@ -1507,7 +1506,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ); } else if (possibleMembers.size() > 1) - fatalTypeError( + m_errorReporter.fatalTypeError( _memberAccess.location(), "Member \"" + memberName + "\" not unique " "after argument-dependent lookup in " + exprType->toString() + @@ -1520,7 +1519,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (auto funType = dynamic_cast(annotation.type.get())) if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType())) - typeError( + m_errorReporter.typeError( _memberAccess.location(), "Function \"" + memberName + "\" cannot be called on an object of type " + exprType->toString() + " (expected " + funType->selfType()->toString() + ")" @@ -1568,10 +1567,10 @@ bool TypeChecker::visit(IndexAccess const& _access) { ArrayType const& actualType = dynamic_cast(*baseType); if (!index) - typeError(_access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else if (actualType.isString()) { - typeError(_access.location(), "Index access for string is not possible."); + m_errorReporter.typeError(_access.location(), "Index access for string is not possible."); index->accept(*this); } else @@ -1581,7 +1580,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { if (!numberType->isFractional()) // error is reported above if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) - typeError(_access.location(), "Out of bounds array access."); + m_errorReporter.typeError(_access.location(), "Out of bounds array access."); } } resultType = actualType.baseType(); @@ -1592,7 +1591,7 @@ bool TypeChecker::visit(IndexAccess const& _access) { MappingType const& actualType = dynamic_cast(*baseType); if (!index) - typeError(_access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else expectType(*index, *actualType.keyType()); resultType = actualType.valueType(); @@ -1614,7 +1613,7 @@ bool TypeChecker::visit(IndexAccess const& _access) length->literalValue(nullptr) )); else - fatalTypeError(index->location(), "Integer constant expected."); + m_errorReporter.fatalTypeError(index->location(), "Integer constant expected."); } break; } @@ -1622,20 +1621,20 @@ bool TypeChecker::visit(IndexAccess const& _access) { FixedBytesType const& bytesType = dynamic_cast(*baseType); if (!index) - typeError(_access.location(), "Index expression cannot be omitted."); + m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else { expectType(*index, IntegerType(256)); if (auto integerType = dynamic_cast(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) - typeError(_access.location(), "Out of bounds array access."); + m_errorReporter.typeError(_access.location(), "Out of bounds array access."); } resultType = make_shared(1); isLValue = false; // @todo this heavily depends on how it is embedded break; } default: - fatalTypeError( + m_errorReporter.fatalTypeError( _access.baseExpression().location(), "Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")" ); @@ -1665,14 +1664,14 @@ bool TypeChecker::visit(Identifier const& _identifier) candidates.push_back(declaration); } if (candidates.empty()) - fatalTypeError(_identifier.location(), "No matching declaration found after variable lookup."); + m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after variable lookup."); else if (candidates.size() == 1) annotation.referencedDeclaration = candidates.front(); else - fatalTypeError(_identifier.location(), "No unique declaration found after variable lookup."); + m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after variable lookup."); } else if (annotation.overloadedDeclarations.empty()) - fatalTypeError(_identifier.location(), "No candidates for overload resolution found."); + m_errorReporter.fatalTypeError(_identifier.location(), "No candidates for overload resolution found."); else if (annotation.overloadedDeclarations.size() == 1) annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin(); else @@ -1688,11 +1687,11 @@ bool TypeChecker::visit(Identifier const& _identifier) candidates.push_back(declaration); } if (candidates.empty()) - fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup."); + m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup."); else if (candidates.size() == 1) annotation.referencedDeclaration = candidates.front(); else - fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup."); + m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup."); } } solAssert( @@ -1702,7 +1701,7 @@ bool TypeChecker::visit(Identifier const& _identifier) annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.type = annotation.referencedDeclaration->type(); if (!annotation.type) - fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined."); + m_errorReporter.fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined."); if (auto variableDeclaration = dynamic_cast(annotation.referencedDeclaration)) annotation.isPure = annotation.isConstant = variableDeclaration->isConstant(); else if (dynamic_cast(annotation.referencedDeclaration)) @@ -1727,7 +1726,7 @@ void TypeChecker::endVisit(Literal const& _literal) return; } else - warning( + m_errorReporter.warning( _literal.location(), "This looks like an address but has an invalid checksum. " "If this is not used as an address, please prepend '00'." @@ -1736,7 +1735,7 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().type = Type::forLiteral(_literal); _literal.annotation().isPure = true; if (!_literal.annotation().type) - fatalTypeError(_literal.location(), "Invalid literal value."); + m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); } bool TypeChecker::contractDependenciesAreCyclic( @@ -1777,7 +1776,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte dynamic_pointer_cast(type(_expression))->isFractional() && type(_expression)->mobileType() ) - typeError( + m_errorReporter.typeError( _expression.location(), "Type " + type(_expression)->toString() + @@ -1788,7 +1787,7 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte " or use an explicit conversion." ); else - typeError( + m_errorReporter.typeError( _expression.location(), "Type " + type(_expression)->toString() + @@ -1805,33 +1804,8 @@ void TypeChecker::requireLValue(Expression const& _expression) _expression.accept(*this); if (_expression.annotation().isConstant) - typeError(_expression.location(), "Cannot assign to a constant variable."); + m_errorReporter.typeError(_expression.location(), "Cannot assign to a constant variable."); else if (!_expression.annotation().isLValue) - typeError(_expression.location(), "Expression has to be an lvalue."); + m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue."); } -void TypeChecker::typeError(SourceLocation const& _location, string const& _description) -{ - auto err = make_shared(Error::Type::TypeError); - *err << - errinfo_sourceLocation(_location) << - errinfo_comment(_description); - - m_errors.push_back(err); -} - -void TypeChecker::warning(SourceLocation const& _location, string const& _description) -{ - auto err = make_shared(Error::Type::Warning); - *err << - errinfo_sourceLocation(_location) << - errinfo_comment(_description); - - m_errors.push_back(err); -} - -void TypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description) -{ - typeError(_location, _description); - BOOST_THROW_EXCEPTION(FatalError()); -} diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 88559f44e..2fa66f97c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -22,7 +22,6 @@ #pragma once -#include #include #include #include @@ -33,6 +32,7 @@ namespace dev namespace solidity { +class ErrorReporter; /** * The module that performs type analysis on the AST, checks the applicability of operations on @@ -43,7 +43,7 @@ class TypeChecker: private ASTConstVisitor { public: /// @param _errors the reference to the list of errors and warnings to add them found during type checking. - TypeChecker(ErrorList& _errors): m_errors(_errors) {} + TypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} /// 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 @@ -56,14 +56,6 @@ public: TypePointer const& type(VariableDeclaration const& _variable) const; private: - /// Adds a new error to the list of errors. - void typeError(SourceLocation const& _location, std::string const& _description); - - /// Adds a new warning to the list of errors. - void warning(SourceLocation const& _location, std::string const& _description); - - /// Adds a new error to the list of errors and throws to abort type checking. - void fatalTypeError(SourceLocation const& _location, std::string const& _description); virtual bool visit(ContractDefinition const& _contract) override; /// Checks that two functions defined in this contract with the same name have different @@ -127,7 +119,7 @@ private: ContractDefinition const* m_scope = nullptr; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index e79bb6dc8..1fc063338 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -520,7 +520,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { ErrorList errors; - assembly::CodeGenerator codeGen(errors); + ErrorReporter errorReporter(errors); + assembly::CodeGenerator codeGen(errorReporter); unsigned startStackHeight = m_context.stackHeight(); julia::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&](assembly::Identifier const& _identifier, julia::IdentifierContext) @@ -648,7 +649,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) m_context.nonConstAssembly(), identifierAccess ); - solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested."); + solAssert(Error::containsOnlyWarnings(errorReporter.errors()), "Code generation for inline assembly with errors requested."); m_context.setStackOffset(startStackHeight); return false; } diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index 77d3c2177..fecab2de3 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -32,7 +32,7 @@ bool Why3Translator::process(SourceUnit const& _source) try { if (m_lines.size() != 1 || !m_lines.back().contents.empty()) - fatalError(_source, "Multiple source units not yet supported"); + fatalError(_source, string("Multiple source units not yet supported")); appendPreface(); _source.accept(*this); } @@ -55,22 +55,6 @@ string Why3Translator::translation() const return result; } -void Why3Translator::error(ASTNode const& _node, string const& _description) -{ - auto err = make_shared(Error::Type::Why3TranslatorError); - *err << - errinfo_sourceLocation(_node.location()) << - errinfo_comment(_description); - m_errors.push_back(err); - m_errorOccured = true; -} - -void Why3Translator::fatalError(ASTNode const& _node, string const& _description) -{ - error(_node, _description); - BOOST_THROW_EXCEPTION(FatalError()); -} - string Why3Translator::toFormalType(Type const& _type) const { if (_type.category() == Type::Category::Bool) @@ -900,3 +884,15 @@ module Address end )", 0}); } + +void Why3Translator::error(ASTNode const& _source, std::string const& _description) +{ + m_errorOccured = true; + m_errorReporter.why3TranslatorError(_source, _description); +} +void Why3Translator::fatalError(ASTNode const& _source, std::string const& _description) +{ + m_errorOccured = true; + m_errorReporter.fatalWhy3TranslatorError(_source, _description); +} + diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h index 03f3bf9cf..b48317be9 100644 --- a/libsolidity/formal/Why3Translator.h +++ b/libsolidity/formal/Why3Translator.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include namespace dev @@ -43,7 +43,7 @@ class SourceUnit; class Why3Translator: private ASTConstVisitor { public: - Why3Translator(ErrorList& _errors): m_lines(std::vector{{std::string(), 0}}), m_errors(_errors) {} + Why3Translator(ErrorReporter& _errorReporter): m_lines(std::vector{{std::string(), 0}}), m_errorReporter(_errorReporter) {} /// Appends formalisation of the given source unit to the output. /// @returns false on error. @@ -52,11 +52,6 @@ public: std::string translation() const; private: - /// Creates an error and adds it to errors list. - void error(ASTNode const& _node, std::string const& _description); - /// Reports a fatal error and throws. - void fatalError(ASTNode const& _node, std::string const& _description); - /// Appends imports and constants use throughout the formal code. void appendPreface(); @@ -65,6 +60,9 @@ private: using errinfo_noFormalTypeFrom = boost::error_info; struct NoFormalType: virtual Exception {}; + void error(ASTNode const& _source, std::string const& _description); + void fatalError(ASTNode const& _source, std::string const& _description); + void indent() { newLine(); m_lines.back().indentation++; } void unindent(); void addLine(std::string const& _line); @@ -98,7 +96,7 @@ private: virtual bool visitNode(ASTNode const& _node) override { - error(_node, "Code not supported for formal verification."); + m_errorReporter.why3TranslatorError(_node, "Code not supported for formal verification."); return false; } @@ -142,7 +140,7 @@ private: unsigned indentation; }; std::vector m_lines; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 9f512f87f..22e431272 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -46,7 +46,7 @@ set const builtinTypes{"bool", "u8", "s8", "u32", "s32", "u64", "s64", " bool AsmAnalyzer::analyze(Block const& _block) { - if (!(ScopeFiller(m_info, m_errors))(_block)) + if (!(ScopeFiller(m_info, m_errorReporter))(_block)) return false; return (*this)(_block); @@ -74,11 +74,10 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ++m_stackHeight; if (_literal.kind == assembly::LiteralKind::String && _literal.value.size() > 32) { - m_errors.push_back(make_shared( - Error::Type::TypeError, - "String literal too long (" + boost::lexical_cast(_literal.value.size()) + " > 32)", - _literal.location - )); + m_errorReporter.typeError( + _literal.location, + "String literal too long (" + boost::lexical_cast(_literal.value.size()) + " > 32)" + ); return false; } m_info.stackHeightInfo[&_literal] = m_stackHeight; @@ -87,18 +86,17 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) { - size_t numErrorsBefore = m_errors.size(); + size_t numErrorsBefore = m_errorReporter.errors().size(); bool success = true; if (m_currentScope->lookup(_identifier.name, Scope::Visitor( [&](Scope::Variable const& _var) { if (!_var.active) { - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Variable " + _identifier.name + " used before it was declared.", - _identifier.location - )); + m_errorReporter.declarationError( + _identifier.location, + "Variable " + _identifier.name + " used before it was declared." + ); success = false; } ++m_stackHeight; @@ -109,11 +107,10 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) }, [&](Scope::Function const&) { - m_errors.push_back(make_shared( - Error::Type::TypeError, - "Function " + _identifier.name + " used without being called.", - _identifier.location - )); + m_errorReporter.typeError( + _identifier.location, + "Function " + _identifier.name + " used without being called." + ); success = false; } ))) @@ -127,12 +124,8 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) if (stackSize == size_t(-1)) { // Only add an error message if the callback did not do it. - if (numErrorsBefore == m_errors.size()) - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Identifier not found.", - _identifier.location - )); + if (numErrorsBefore == m_errorReporter.errors().size()) + m_errorReporter.declarationError(_identifier.location, "Identifier not found."); success = false; } m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize; @@ -187,11 +180,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) bool success = boost::apply_visitor(*this, *_varDecl.value); if ((m_stackHeight - stackHeight) != expectedItems) { - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Variable count mismatch.", - _varDecl.location - )); + m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch."); return false; } @@ -233,20 +222,18 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( [&](Scope::Variable const&) { - m_errors.push_back(make_shared( - Error::Type::TypeError, - "Attempt to call variable instead of function.", - _funCall.functionName.location - )); + m_errorReporter.typeError( + _funCall.functionName.location, + "Attempt to call variable instead of function." + ); success = false; }, [&](Scope::Label const&) { - m_errors.push_back(make_shared( - Error::Type::TypeError, - "Attempt to call label instead of function.", - _funCall.functionName.location - )); + m_errorReporter.typeError( + _funCall.functionName.location, + "Attempt to call label instead of function." + ); success = false; }, [&](Scope::Function const& _fun) @@ -257,26 +244,18 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) } ))) { - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Function not found.", - _funCall.functionName.location - )); + m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); success = false; } if (success) { if (_funCall.arguments.size() != arguments) { - m_errors.push_back(make_shared( - Error::Type::TypeError, - "Expected " + - boost::lexical_cast(arguments) + - " arguments but got " + - boost::lexical_cast(_funCall.arguments.size()) + - ".", - _funCall.functionName.location - )); + m_errorReporter.typeError( + _funCall.functionName.location, + "Expected " + boost::lexical_cast(arguments) + " arguments but got " + + boost::lexical_cast(_funCall.arguments.size()) + "." + ); success = false; } } @@ -317,11 +296,10 @@ bool AsmAnalyzer::operator()(Switch const& _switch) auto val = make_tuple(_case.value->kind, _case.value->value); if (!cases.insert(val).second) { - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Duplicate case defined", - _case.location - )); + m_errorReporter.declarationError( + _case.location, + "Duplicate case defined" + ); success = false; } } @@ -354,16 +332,15 @@ bool AsmAnalyzer::operator()(Block const& _block) int const stackDiff = m_stackHeight - initialStackHeight; if (stackDiff != 0) { - m_errors.push_back(make_shared( - Error::Type::DeclarationError, + m_errorReporter.declarationError( + _block.location, "Unbalanced stack at the end of a block: " + ( stackDiff > 0 ? to_string(stackDiff) + string(" surplus item(s).") : to_string(-stackDiff) + string(" missing item(s).") - ), - _block.location - )); + ) + ); success = false; } @@ -375,27 +352,22 @@ bool AsmAnalyzer::operator()(Block const& _block) bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize) { bool success = true; - size_t numErrorsBefore = m_errors.size(); + size_t numErrorsBefore = m_errorReporter.errors().size(); size_t variableSize(-1); if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { // Check that it is a variable if (var->type() != typeid(Scope::Variable)) { - m_errors.push_back(make_shared( - Error::Type::TypeError, - "Assignment requires variable.", - _variable.location - )); + m_errorReporter.typeError(_variable.location, "Assignment requires variable."); success = false; } else if (!boost::get(*var).active) { - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Variable " + _variable.name + " used before it was declared.", - _variable.location - )); + m_errorReporter.declarationError( + _variable.location, + "Variable " + _variable.name + " used before it was declared." + ); success = false; } variableSize = 1; @@ -405,12 +377,8 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t if (variableSize == size_t(-1)) { // Only add message if the callback did not. - if (numErrorsBefore == m_errors.size()) - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Variable not found or variable not lvalue.", - _variable.location - )); + if (numErrorsBefore == m_errorReporter.errors().size()) + m_errorReporter.declarationError(_variable.location, "Variable not found or variable not lvalue."); success = false; } if (_valueSize == size_t(-1)) @@ -420,15 +388,14 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t if (_valueSize != variableSize && variableSize != size_t(-1)) { - m_errors.push_back(make_shared( - Error::Type::TypeError, + m_errorReporter.typeError( + _variable.location, "Variable size (" + to_string(variableSize) + ") and value size (" + to_string(_valueSize) + - ") do not match.", - _variable.location - )); + ") do not match." + ); success = false; } return success; @@ -439,15 +406,14 @@ bool AsmAnalyzer::expectDeposit(int const _deposit, int const _oldHeight, Source int stackDiff = m_stackHeight - _oldHeight; if (stackDiff != _deposit) { - m_errors.push_back(make_shared( - Error::Type::TypeError, + m_errorReporter.typeError( + _location, "Expected instruction(s) to deposit " + boost::lexical_cast(_deposit) + " item(s) to the stack, but did deposit " + boost::lexical_cast(stackDiff) + - " item(s).", - _location - )); + " item(s)." + ); return false; } else @@ -468,9 +434,8 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc return; if (!builtinTypes.count(type)) - m_errors.push_back(make_shared( - Error::Type::TypeError, - "\"" + type + "\" is not a valid type (user defined types are not yet supported).", - _location - )); + m_errorReporter.typeError( + _location, + "\"" + type + "\" is not a valid type (user defined types are not yet supported)." + ); } diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 14b18c4a5..7e4d78df2 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -22,8 +22,6 @@ #include -#include - #include #include @@ -33,6 +31,7 @@ namespace dev { namespace solidity { +class ErrorReporter; namespace assembly { @@ -63,10 +62,10 @@ class AsmAnalyzer: public boost::static_visitor public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, - ErrorList& _errors, + ErrorReporter& _errorReporter, bool _julia = false, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errors(_errors), m_julia(_julia) {} + ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_julia(_julia) {} bool analyze(assembly::Block const& _block); @@ -95,7 +94,7 @@ private: julia::ExternalIdentifierAccess::Resolver const& m_resolver; Scope* m_currentScope = nullptr; AsmAnalysisInfo& m_info; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; bool m_julia = false; }; diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index af3547fd4..11494c2db 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -107,7 +107,7 @@ eth::Assembly assembly::CodeGenerator::assemble( { eth::Assembly assembly; EthAssemblyAdapter assemblyAdapter(assembly); - julia::CodeTransform(m_errors, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess); + julia::CodeTransform(m_errorReporter, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess); return assembly; } @@ -119,5 +119,5 @@ void assembly::CodeGenerator::assemble( ) { EthAssemblyAdapter assemblyAdapter(_assembly); - julia::CodeTransform(m_errors, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess); + julia::CodeTransform(m_errorReporter, assemblyAdapter, _parsedData, _analysisInfo, _identifierAccess); } diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index 1b43d2f61..f075fa933 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include @@ -35,6 +34,7 @@ class Assembly; } namespace solidity { +class ErrorReporter; namespace assembly { struct Block; @@ -42,8 +42,8 @@ struct Block; class CodeGenerator { public: - CodeGenerator(ErrorList& _errors): - m_errors(_errors) {} + CodeGenerator(ErrorReporter& _errorReporter): + m_errorReporter(_errorReporter) {} /// Performs code generation and @returns the result. eth::Assembly assemble( Block const& _parsedData, @@ -59,7 +59,7 @@ public: ); private: - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 80409c63c..0f836406b 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -21,10 +21,10 @@ */ #include +#include +#include #include #include -#include -#include using namespace std; using namespace dev; @@ -40,7 +40,7 @@ shared_ptr Parser::parse(std::shared_ptr const& _scann } catch (FatalError const&) { - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. } return nullptr; diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 138af337b..e32b1d258 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -37,7 +37,7 @@ namespace assembly class Parser: public ParserBase { public: - explicit Parser(ErrorList& _errors, bool _julia = false): ParserBase(_errors), m_julia(_julia) {} + explicit Parser(ErrorReporter& _errorReporter, bool _julia = false): ParserBase(_errorReporter), m_julia(_julia) {} /// Parses an inline assembly block starting with `{` and ending with `}`. /// @returns an empty shared pointer on error. diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 3fade842a..4d26dcf82 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -37,8 +37,8 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorList& _errors): - m_info(_info), m_errors(_errors) +ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): + m_info(_info), m_errorReporter(_errorReporter) { m_currentScope = &scope(nullptr); } @@ -48,11 +48,10 @@ bool ScopeFiller::operator()(Label const& _item) if (!m_currentScope->registerLabel(_item.name)) { //@TODO secondary location - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Label name " + _item.name + " already taken in this scope.", - _item.location - )); + m_errorReporter.declarationError( + _item.location, + "Label name " + _item.name + " already taken in this scope." + ); return false; } return true; @@ -78,11 +77,10 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { //@TODO secondary location - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Function name " + _funDef.name + " already taken in this scope.", - _funDef.location - )); + m_errorReporter.declarationError( + _funDef.location, + "Function name " + _funDef.name + " already taken in this scope." + ); success = false; } @@ -132,11 +130,10 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& if (!_scope.registerVariable(_name.name, _name.type)) { //@TODO secondary location - m_errors.push_back(make_shared( - Error::Type::DeclarationError, - "Variable name " + _name.name + " already taken in this scope.", - _location - )); + m_errorReporter.declarationError( + _location, + "Variable name " + _name.name + " already taken in this scope." + ); return false; } return true; diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index 42e801413..1166d50f6 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -20,8 +20,6 @@ #pragma once -#include - #include #include @@ -29,8 +27,10 @@ namespace dev { +struct SourceLocation; namespace solidity { +class ErrorReporter; namespace assembly { @@ -58,7 +58,7 @@ struct AsmAnalysisInfo; class ScopeFiller: public boost::static_visitor { public: - ScopeFiller(AsmAnalysisInfo& _info, ErrorList& _errors); + ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter); bool operator()(assembly::Instruction const&) { return true; } bool operator()(assembly::Literal const&) { return true; } @@ -84,7 +84,7 @@ private: Scope* m_currentScope = nullptr; AsmAnalysisInfo& m_info; - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp index fe443c08e..73b1604df 100644 --- a/libsolidity/inlineasm/AsmStack.cpp +++ b/libsolidity/inlineasm/AsmStack.cpp @@ -46,14 +46,14 @@ bool InlineAssemblyStack::parse( ) { m_parserResult = make_shared(); - Parser parser(m_errors); + Parser parser(m_errorReporter); auto result = parser.parse(_scanner); if (!result) return false; *m_parserResult = std::move(*result); AsmAnalysisInfo analysisInfo; - return (AsmAnalyzer(analysisInfo, m_errors, false, _resolver)).analyze(*m_parserResult); + return (AsmAnalyzer(analysisInfo, m_errorReporter, false, _resolver)).analyze(*m_parserResult); } string InlineAssemblyStack::toString() @@ -64,9 +64,9 @@ string InlineAssemblyStack::toString() eth::Assembly InlineAssemblyStack::assemble() { AsmAnalysisInfo analysisInfo; - AsmAnalyzer analyzer(analysisInfo, m_errors); + AsmAnalyzer analyzer(analysisInfo, m_errorReporter); solAssert(analyzer.analyze(*m_parserResult), ""); - CodeGenerator codeGen(m_errors); + CodeGenerator codeGen(m_errorReporter); return codeGen.assemble(*m_parserResult, analysisInfo); } @@ -77,19 +77,20 @@ bool InlineAssemblyStack::parseAndAssemble( ) { ErrorList errors; + ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_input), "--CODEGEN--"); - auto parserResult = Parser(errors).parse(scanner); - if (!errors.empty()) + auto parserResult = Parser(errorReporter).parse(scanner); + if (!errorReporter.errors().empty()) return false; solAssert(parserResult, ""); AsmAnalysisInfo analysisInfo; - AsmAnalyzer analyzer(analysisInfo, errors, false, _identifierAccess.resolve); + AsmAnalyzer analyzer(analysisInfo, errorReporter, false, _identifierAccess.resolve); solAssert(analyzer.analyze(*parserResult), ""); - CodeGenerator(errors).assemble(*parserResult, analysisInfo, _assembly, _identifierAccess); + CodeGenerator(errorReporter).assemble(*parserResult, analysisInfo, _assembly, _identifierAccess); // At this point, the assembly might be messed up, but we should throw an // internal compiler error anyway. - return errors.empty(); + return errorReporter.errors().empty(); } diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h index 23072a881..7452804d7 100644 --- a/libsolidity/inlineasm/AsmStack.h +++ b/libsolidity/inlineasm/AsmStack.h @@ -22,9 +22,8 @@ #pragma once -#include - #include +#include #include #include @@ -46,6 +45,8 @@ struct Identifier; class InlineAssemblyStack { public: + InlineAssemblyStack(): + m_errorReporter(m_errorList) {} /// Parse the given inline assembly chunk starting with `{` and ending with the corresponding `}`. /// @return false or error. bool parse( @@ -65,11 +66,12 @@ public: julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess() ); - ErrorList const& errors() const { return m_errors; } + ErrorList const& errors() const { return m_errorReporter.errors(); } private: std::shared_ptr m_parserResult; - ErrorList m_errors; + ErrorList m_errorList; + ErrorReporter m_errorReporter; }; } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index c4bd63c41..b027ac3c7 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -45,13 +45,13 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string { m_analysisSuccessful = false; m_scanner = make_shared(CharStream(_source), _sourceName); - m_parserResult = assembly::Parser(m_errors, m_language == Language::JULIA).parse(m_scanner); - if (!m_errors.empty()) + m_parserResult = assembly::Parser(m_errorReporter, m_language == Language::JULIA).parse(m_scanner); + if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); m_analysisInfo = make_shared(); - assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errors); + assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter); m_analysisSuccessful = analyzer.analyze(*m_parserResult); return m_analysisSuccessful; } @@ -66,7 +66,7 @@ eth::LinkerObject AssemblyStack::assemble(Machine _machine) { case Machine::EVM: { - auto assembly = assembly::CodeGenerator(m_errors).assemble(*m_parserResult, *m_analysisInfo); + auto assembly = assembly::CodeGenerator(m_errorReporter).assemble(*m_parserResult, *m_analysisInfo); return assembly.assemble(); } case Machine::EVM15: diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 40662ac3b..62a5134a7 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -21,8 +21,8 @@ #pragma once -#include #include +#include #include #include @@ -50,7 +50,7 @@ public: enum class Machine { EVM, EVM15, eWasm }; explicit AssemblyStack(Language _language = Language::Assembly): - m_language(_language) + m_language(_language), m_errorReporter(m_errors) {} /// @returns the scanner used during parsing @@ -79,6 +79,7 @@ private: std::shared_ptr m_parserResult; std::shared_ptr m_analysisInfo; ErrorList m_errors; + ErrorReporter m_errorReporter; }; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 328df91f3..442204023 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -57,9 +57,6 @@ using namespace std; using namespace dev; using namespace dev::solidity; -CompilerStack::CompilerStack(ReadFile::Callback const& _readFile): - m_readFile(_readFile) {} - void CompilerStack::setRemappings(vector const& _remappings) { vector remappings; @@ -96,7 +93,7 @@ void CompilerStack::reset(bool _keepSources) m_scopes.clear(); m_sourceOrder.clear(); m_contracts.clear(); - m_errors.clear(); + m_errorReporter.clear(); m_stackState = Empty; } @@ -121,15 +118,11 @@ bool CompilerStack::parse() //reset if(m_stackState != SourcesSet) return false; - m_errors.clear(); + m_errorReporter.clear(); ASTNode::resetID(); if (SemVerVersion{string(VersionString)}.isPrerelease()) - { - auto err = make_shared(Error::Type::Warning); - *err << errinfo_comment("This is a pre-release compiler version, please do not use it in production."); - m_errors.push_back(err); - } + m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production."); vector sourcesToParse; for (auto const& s: m_sources) @@ -139,9 +132,9 @@ bool CompilerStack::parse() string const& path = sourcesToParse[i]; Source& source = m_sources[path]; source.scanner->reset(); - source.ast = Parser(m_errors).parse(source.scanner); + source.ast = Parser(m_errorReporter).parse(source.scanner); if (!source.ast) - solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error."); + solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); else { source.ast->annotation().path = path; @@ -154,7 +147,7 @@ bool CompilerStack::parse() } } } - if (Error::containsOnlyWarnings(m_errors)) + if (Error::containsOnlyWarnings(m_errorReporter.errors())) { m_stackState = ParsingSuccessful; return true; @@ -170,18 +163,18 @@ bool CompilerStack::analyze() resolveImports(); bool noErrors = true; - SyntaxChecker syntaxChecker(m_errors); + SyntaxChecker syntaxChecker(m_errorReporter); for (Source const* source: m_sourceOrder) if (!syntaxChecker.checkSyntax(*source->ast)) noErrors = false; - DocStringAnalyser docStringAnalyser(m_errors); + DocStringAnalyser docStringAnalyser(m_errorReporter); for (Source const* source: m_sourceOrder) if (!docStringAnalyser.analyseDocStrings(*source->ast)) noErrors = false; m_globalContext = make_shared(); - NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errors); + NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter); for (Source const* source: m_sourceOrder) if (!resolver.registerDeclarations(*source->ast)) return false; @@ -217,7 +210,7 @@ bool CompilerStack::analyze() { m_globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*m_globalContext->currentThis()); - TypeChecker typeChecker(m_errors); + TypeChecker typeChecker(m_errorReporter); if (typeChecker.checkTypeRequirements(*contract)) { contract->setDevDocumentation(Natspec::devDocumentation(*contract)); @@ -237,7 +230,7 @@ bool CompilerStack::analyze() if (noErrors) { - PostTypeChecker postTypeChecker(m_errors); + PostTypeChecker postTypeChecker(m_errorReporter); for (Source const* source: m_sourceOrder) if (!postTypeChecker.check(*source->ast)) noErrors = false; @@ -245,7 +238,7 @@ bool CompilerStack::analyze() if (noErrors) { - StaticAnalyzer staticAnalyzer(m_errors); + StaticAnalyzer staticAnalyzer(m_errorReporter); for (Source const* source: m_sourceOrder) if (!staticAnalyzer.analyze(*source->ast)) noErrors = false; @@ -323,11 +316,11 @@ void CompilerStack::link() } } -bool CompilerStack::prepareFormalAnalysis(ErrorList* _errors) +bool CompilerStack::prepareFormalAnalysis(ErrorReporter* _errorReporter) { - if (!_errors) - _errors = &m_errors; - Why3Translator translator(*_errors); + if (!_errorReporter) + _errorReporter = &m_errorReporter; + Why3Translator translator(*_errorReporter); for (Source const* source: m_sourceOrder) if (!translator.process(*source->ast)) return false; @@ -582,11 +575,10 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string newSources[importPath] = result.contentsOrErrorMessage; else { - auto err = make_shared(Error::Type::ParserError); - *err << - errinfo_sourceLocation(import->location()) << - errinfo_comment("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage); - m_errors.push_back(std::move(err)); + m_errorReporter.parserError( + import->location(), + string("Source \"" + importPath + "\" not found: " + result.contentsOrErrorMessage) + ); continue; } } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 84d15d705..76d36c7be 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include namespace dev @@ -80,7 +80,10 @@ public: /// Creates a new compiler stack. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. - explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback()); + explicit CompilerStack(ReadFile::Callback const& _readFile = ReadFile::Callback()): + m_readFile(_readFile), + m_errorList(), + m_errorReporter(m_errorList) {} /// Sets path remappings in the format "context:prefix=target" void setRemappings(std::vector const& _remappings); @@ -130,7 +133,7 @@ public: /// Tries to translate all source files into a language suitable for formal analysis. /// @param _errors list to store errors - defaults to the internal error list. /// @returns false on error. - bool prepareFormalAnalysis(ErrorList* _errors = nullptr); + bool prepareFormalAnalysis(ErrorReporter* _errorReporter = nullptr); std::string const& formalTranslation() const { return m_formalTranslation; } /// @returns the assembled object for a contract. @@ -207,7 +210,7 @@ public: std::tuple positionFromSourceLocation(SourceLocation const& _sourceLocation) const; /// @returns the list of errors that occured during parsing and type checking. - ErrorList const& errors() const { return m_errors; } + ErrorList const& errors() { return m_errorReporter.errors(); } private: /** @@ -289,7 +292,8 @@ private: std::vector m_sourceOrder; std::map m_contracts; std::string m_formalTranslation; - ErrorList m_errors; + ErrorList m_errorList; + ErrorReporter m_errorReporter; bool m_metadataLiteralSources = false; State m_stackState = Empty; }; diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp new file mode 100644 index 000000000..2cbb952c2 --- /dev/null +++ b/libsolidity/interface/ErrorReporter.cpp @@ -0,0 +1,186 @@ +/* + 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 . +*/ +/** + * @author Rhett + * @date 2017 + * Error helper class. + */ + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter) +{ + if (&_errorReporter == this) + return *this; + m_errorList = _errorReporter.m_errorList; + return *this; +} + + +void ErrorReporter::warning(string const& _description) +{ + error(Error::Type::Warning, SourceLocation(), _description); +} + +void ErrorReporter::warning(SourceLocation const& _location, string const& _description) +{ + error(Error::Type::Warning, _location, _description); +} + +void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description) +{ + auto err = make_shared(_type); + *err << + errinfo_sourceLocation(_location) << + errinfo_comment(_description); + + m_errorList.push_back(err); +} + +void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) +{ + auto err = make_shared(_type); + *err << + errinfo_sourceLocation(_location) << + errinfo_secondarySourceLocation(_secondaryLocation) << + errinfo_comment(_description); + + m_errorList.push_back(err); +} + + +void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description) +{ + error(_type, _location, _description); + BOOST_THROW_EXCEPTION(FatalError()); +} + +ErrorList const& ErrorReporter::errors() const +{ + return m_errorList; +} + +void ErrorReporter::clear() +{ + m_errorList.clear(); +} + +void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const&_secondaryLocation, string const& _description) +{ + error( + Error::Type::DeclarationError, + _location, + _secondaryLocation, + _description + ); +} + +void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::DeclarationError, + _location, + _description + ); +} + +void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description) +{ + fatalError( + Error::Type::DeclarationError, + _location, + _description); +} + +void ErrorReporter::parserError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::ParserError, + _location, + _description + ); +} + +void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description) +{ + fatalError( + Error::Type::ParserError, + _location, + _description + ); +} + +void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::SyntaxError, + _location, + _description + ); +} + +void ErrorReporter::typeError(SourceLocation const& _location, string const& _description) +{ + error( + Error::Type::TypeError, + _location, + _description + ); +} + + +void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description) +{ + fatalError(Error::Type::TypeError, + _location, + _description + ); +} + +void ErrorReporter::docstringParsingError(string const& _description) +{ + error( + Error::Type::DocstringParsingError, + SourceLocation(), + _description + ); +} + +void ErrorReporter::why3TranslatorError(ASTNode const& _location, std::string const& _description) +{ + error( + Error::Type::Why3TranslatorError, + _location.location(), + _description + ); +} + +void ErrorReporter::fatalWhy3TranslatorError(ASTNode const& _location, std::string const& _description) +{ + fatalError( + Error::Type::Why3TranslatorError, + _location.location(), + _description + ); +} + diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h new file mode 100644 index 000000000..83324446d --- /dev/null +++ b/libsolidity/interface/ErrorReporter.h @@ -0,0 +1,106 @@ +/* + 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 . +*/ +/** + * @author Rhett + * @date 2017 + * Error reporting helper class. + */ + +#pragma once + +#include +#include + +namespace dev +{ +namespace solidity +{ + +class ASTNode; + +class ErrorReporter +{ +public: + + ErrorReporter(ErrorList& _errors): + m_errorList(_errors) { } + + ErrorReporter& operator=(ErrorReporter const& _errorReporter); + + void warning(std::string const& _description = std::string()); + + void warning( + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string() + ); + + void error( + Error::Type _type, + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string() + ); + + void declarationError( + SourceLocation const& _location, + SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(), + std::string const& _description = std::string() + ); + + void declarationError( + SourceLocation const& _location, + std::string const& _description = std::string() + ); + + void fatalDeclarationError(SourceLocation const& _location, std::string const& _description); + + void parserError(SourceLocation const& _location, std::string const& _description); + + void fatalParserError(SourceLocation const& _location, std::string const& _description); + + void syntaxError(SourceLocation const& _location, std::string const& _description); + + void typeError(SourceLocation const& _location, std::string const& _description); + + void fatalTypeError(SourceLocation const& _location, std::string const& _description); + + void docstringParsingError(std::string const& _location); + + void why3TranslatorError(ASTNode const& _location, std::string const& _description); + + void fatalWhy3TranslatorError(ASTNode const& _location, std::string const& _description); + + ErrorList const& errors() const; + + void clear(); + +private: + void error(Error::Type _type, + SourceLocation const& _location, + SecondarySourceLocation const& _secondaryLocation, + std::string const& _description = std::string()); + + void fatalError(Error::Type _type, + SourceLocation const& _location = SourceLocation(), + std::string const& _description = std::string()); + + ErrorList& m_errorList; +}; + + +} +} + diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 8e9121266..cd6c1d8af 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -51,9 +52,9 @@ string::const_iterator skipWhitespace( } -bool DocStringParser::parse(string const& _docString, ErrorList& _errors) +bool DocStringParser::parse(string const& _docString, ErrorReporter& _errorReporter) { - m_errors = &_errors; + m_errorReporter = &_errorReporter; m_errorsOccurred = false; m_lastTag = nullptr; @@ -172,8 +173,6 @@ void DocStringParser::newTag(string const& _tagName) void DocStringParser::appendError(string const& _description) { - auto err = make_shared(Error::Type::DocstringParsingError); - *err << errinfo_comment(_description); - m_errors->push_back(err); m_errorsOccurred = true; + m_errorReporter->docstringParsingError(_description); } diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h index c7f81c554..5f2819cc5 100644 --- a/libsolidity/parsing/DocStringParser.h +++ b/libsolidity/parsing/DocStringParser.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include namespace dev @@ -31,12 +30,14 @@ namespace dev namespace solidity { +class ErrorReporter; + class DocStringParser { public: /// Parse the given @a _docString and stores the parsed components internally. /// @returns false on error and appends the error to @a _errors. - bool parse(std::string const& _docString, ErrorList& _errors); + bool parse(std::string const& _docString, ErrorReporter& _errorReporter); std::multimap const& tags() const { return m_docTags; } @@ -62,7 +63,7 @@ private: /// Mapping tag name -> content. std::multimap m_docTags; DocTag* m_lastTag = nullptr; - ErrorList* m_errors = nullptr; + ErrorReporter* m_errorReporter = nullptr; bool m_errorsOccurred = false; }; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 5b318b2bd..ec27f89be 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include using namespace std; @@ -94,7 +94,7 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) } catch (FatalError const&) { - if (m_errors.empty()) + if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. return nullptr; } @@ -865,7 +865,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con m_scanner->next(); } - assembly::Parser asmParser(m_errors); + assembly::Parser asmParser(m_errorReporter); shared_ptr block = asmParser.parse(m_scanner); nodeFactory.markEndPosition(); return nodeFactory.createNode(_docString, block); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5ec3dbeda..19631c585 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -35,7 +35,7 @@ class Scanner; class Parser: public ParserBase { public: - Parser(ErrorList& _errors): ParserBase(_errors) {} + Parser(ErrorReporter& _errorReporter): ParserBase(_errorReporter) {} ASTPointer parse(std::shared_ptr const& _scanner); diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index ac103bda0..9987b82c8 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -22,6 +22,7 @@ #include #include +#include using namespace std; using namespace dev; @@ -82,16 +83,10 @@ void ParserBase::expectToken(Token::Value _value) void ParserBase::parserError(string const& _description) { - auto err = make_shared(Error::Type::ParserError); - *err << - errinfo_sourceLocation(SourceLocation(position(), position(), sourceName())) << - errinfo_comment(_description); - - m_errors.push_back(err); + m_errorReporter.parserError(SourceLocation(position(), position(), sourceName()), _description); } void ParserBase::fatalParserError(string const& _description) { - parserError(_description); - BOOST_THROW_EXCEPTION(FatalError()); + m_errorReporter.fatalParserError(SourceLocation(position(), position(), sourceName()), _description); } diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index 18b39a5e8..ae56ceada 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -23,7 +23,6 @@ #pragma once #include -#include #include #include @@ -32,12 +31,13 @@ namespace dev namespace solidity { +class ErrorReporter; class Scanner; class ParserBase { public: - ParserBase(ErrorList& errors): m_errors(errors) {} + ParserBase(ErrorReporter& errorReporter): m_errorReporter(errorReporter) {} std::shared_ptr const& sourceName() const; @@ -67,7 +67,7 @@ protected: std::shared_ptr m_scanner; /// The reference to the list of errors and warning to add errors/warnings during parsing - ErrorList& m_errors; + ErrorReporter& m_errorReporter; }; } diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 7f99324eb..1505a43d6 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -218,12 +218,13 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback { // Do not taint the internal error list ErrorList formalErrors; - if (compiler.prepareFormalAnalysis(&formalErrors)) + ErrorReporter errorReporter(formalErrors); + if (compiler.prepareFormalAnalysis(&errorReporter)) output["formal"]["why3"] = compiler.formalTranslation(); - if (!formalErrors.empty()) + if (!errorReporter.errors().empty()) { Json::Value errors(Json::arrayValue); - for (auto const& error: formalErrors) + for (auto const& error: errorReporter.errors()) errors.append(SourceReferenceFormatter::formatExceptionInformation( *error, (error->type() == Error::Type::Warning) ? "Warning" : "Error", diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index b82c446ab..afeb95f8e 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -45,16 +45,16 @@ namespace test namespace { -bool parse(string const& _source, ErrorList& errors) +bool parse(string const& _source, ErrorReporter& errorReporter) { try { auto scanner = make_shared(CharStream(_source)); - auto parserResult = assembly::Parser(errors, true).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); if (parserResult) { assembly::AsmAnalysisInfo analysisInfo; - return (assembly::AsmAnalyzer(analysisInfo, errors, true)).analyze(*parserResult); + return (assembly::AsmAnalyzer(analysisInfo, errorReporter, true)).analyze(*parserResult); } } catch (FatalError const&) @@ -67,7 +67,8 @@ bool parse(string const& _source, ErrorList& errors) boost::optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) { ErrorList errors; - if (!parse(_source, errors)) + ErrorReporter errorReporter(errors); + if (!parse(_source, errorReporter)) { BOOST_REQUIRE_EQUAL(errors.size(), 1); return *errors.front(); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index c4ec0d202..e52f4d50e 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -31,6 +31,7 @@ #include #include #include +#include using namespace std; using namespace dev::eth; @@ -48,28 +49,29 @@ namespace eth::AssemblyItems compileContract(const string& _sourceCode) { ErrorList errors; - Parser parser(errors); + ErrorReporter errorReporter(errors); + Parser parser(errorReporter); ASTPointer sourceUnit; BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(CharStream(_sourceCode)))); BOOST_CHECK(!!sourceUnit); map> scopes; - NameAndTypeResolver resolver({}, scopes, errors); - solAssert(Error::containsOnlyWarnings(errors), ""); + NameAndTypeResolver resolver({}, scopes, errorReporter); + solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - if (!Error::containsOnlyWarnings(errors)) + if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); } for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker checker(errors); + TypeChecker checker(errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); - if (!Error::containsOnlyWarnings(errors)) + if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); } for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 3116aea8c..58efa0a2e 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "../TestHelper.h" using namespace std; @@ -98,7 +99,8 @@ bytes compileFirstExpression( try { ErrorList errors; - sourceUnit = Parser(errors).parse(make_shared(CharStream(_sourceCode))); + ErrorReporter errorReporter(errors); + sourceUnit = Parser(errorReporter).parse(make_shared(CharStream(_sourceCode))); if (!sourceUnit) return bytes(); } @@ -114,8 +116,9 @@ bytes compileFirstExpression( declarations.push_back(variable.get()); ErrorList errors; + ErrorReporter errorReporter(errors); map> scopes; - NameAndTypeResolver resolver(declarations, scopes, errors); + NameAndTypeResolver resolver(declarations, scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); vector inheritanceHierarchy; @@ -128,7 +131,8 @@ bytes compileFirstExpression( for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { - TypeChecker typeChecker(errors); + ErrorReporter errorReporter(errors); + TypeChecker typeChecker(errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 97c4303fd..0553c6911 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -56,7 +56,8 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, // Silence compiler version warning string source = _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source; ErrorList errors; - Parser parser(errors); + ErrorReporter errorReporter(errors); + Parser parser(errorReporter); ASTPointer sourceUnit; // catch exceptions for a transition period try @@ -65,14 +66,14 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, if(!sourceUnit) BOOST_FAIL("Parsing failed in type checker test."); - SyntaxChecker syntaxChecker(errors); + SyntaxChecker syntaxChecker(errorReporter); if (!syntaxChecker.checkSyntax(*sourceUnit)) - return make_pair(sourceUnit, errors.at(0)); + return make_pair(sourceUnit, errorReporter.errors().at(0)); std::shared_ptr globalContext = make_shared(); map> scopes; - NameAndTypeResolver resolver(globalContext->declarations(), scopes, errors); - solAssert(Error::containsOnlyWarnings(errors), ""); + NameAndTypeResolver resolver(globalContext->declarations(), scopes, errorReporter); + solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); bool success = true; @@ -92,19 +93,19 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*globalContext->currentThis()); - TypeChecker typeChecker(errors); + TypeChecker typeChecker(errorReporter); bool success = typeChecker.checkTypeRequirements(*contract); - BOOST_CHECK(success || !errors.empty()); + BOOST_CHECK(success || !errorReporter.errors().empty()); } if (success) - if (!PostTypeChecker(errors).check(*sourceUnit)) + if (!PostTypeChecker(errorReporter).check(*sourceUnit)) success = false; if (success) - if (!StaticAnalyzer(errors).analyze(*sourceUnit)) + if (!StaticAnalyzer(errorReporter).analyze(*sourceUnit)) success = false; - if (errors.size() > 1 && !_allowMultipleErrors) + if (errorReporter.errors().size() > 1 && !_allowMultipleErrors) BOOST_FAIL("Multiple errors found"); - for (auto const& currentError: errors) + for (auto const& currentError: errorReporter.errors()) { if ( (_reportWarnings && currentError->type() == Error::Type::Warning) || diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 6e33aba52..31dfada9f 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "../TestHelper.h" #include "ErrorCheck.h" @@ -41,7 +41,8 @@ namespace { ASTPointer parseText(std::string const& _source, ErrorList& _errors) { - ASTPointer sourceUnit = Parser(_errors).parse(std::make_shared(CharStream(_source))); + ErrorReporter errorReporter(_errors); + ASTPointer sourceUnit = Parser(errorReporter).parse(std::make_shared(CharStream(_source))); if (!sourceUnit) return ASTPointer(); for (ASTPointer const& node: sourceUnit->nodes())