From b04b189959cc4571f39269a8252a5a61ae9b14a6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 28 Jan 2021 12:56:22 +0100 Subject: [PATCH] Syntax for custom errors. --- docs/grammar/Solidity.g4 | 18 ++++- docs/grammar/SolidityLexer.g4 | 3 +- libsolidity/analysis/ContractLevelChecker.cpp | 20 +++++- libsolidity/analysis/DeclarationContainer.cpp | 2 +- .../analysis/DeclarationTypeChecker.cpp | 2 +- libsolidity/analysis/DocStringAnalyser.cpp | 7 ++ libsolidity/analysis/DocStringAnalyser.h | 1 + libsolidity/analysis/DocStringTagParser.cpp | 10 +++ libsolidity/analysis/DocStringTagParser.h | 1 + libsolidity/analysis/PostTypeChecker.cpp | 34 ++++++++++ libsolidity/analysis/PostTypeChecker.h | 3 + libsolidity/analysis/TypeChecker.cpp | 67 ++++++++++++------- libsolidity/analysis/TypeChecker.h | 3 + libsolidity/ast/AST.cpp | 33 ++++++++- libsolidity/ast/AST.h | 48 ++++++++++++- libsolidity/ast/ASTAnnotations.h | 5 ++ libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/ASTJsonConverter.cpp | 11 +++ libsolidity/ast/ASTJsonConverter.h | 1 + libsolidity/ast/ASTJsonImporter.cpp | 13 ++++ libsolidity/ast/ASTJsonImporter.h | 1 + libsolidity/ast/ASTUtils.h | 6 ++ libsolidity/ast/ASTVisitor.h | 4 ++ libsolidity/ast/AST_accept.h | 22 ++++++ libsolidity/ast/TypeProvider.cpp | 5 ++ libsolidity/ast/TypeProvider.h | 2 + libsolidity/ast/Types.cpp | 45 ++++++++++--- libsolidity/ast/Types.h | 2 + libsolidity/codegen/ExpressionCompiler.cpp | 13 ++++ .../codegen/ir/IRGeneratorForStatements.cpp | 22 +++++- libsolidity/formal/SMTEncoder.cpp | 1 + libsolidity/parsing/Parser.cpp | 33 ++++++++- libsolidity/parsing/Parser.h | 1 + .../semanticTests/error/selector.sol | 20 ++++++ .../syntaxTests/errors/abi_decode_error.sol | 9 +++ .../syntaxTests/errors/abi_encode_error.sol | 8 +++ .../errors/abi_encode_error_instance.sol | 8 +++ .../syntaxTests/errors/all_ones_signature.sol | 8 +++ .../syntaxTests/errors/anonymous.sol | 3 + .../errors/assert_with_cond_and_error.sol | 6 ++ .../syntaxTests/errors/assert_with_error.sol | 6 ++ test/libsolidity/syntaxTests/errors/basic.sol | 6 ++ .../errors/clash_function_error.sol | 4 ++ .../clash_function_error_inheritance.sol | 4 ++ .../clash_function_error_inheritance_2.sol | 5 ++ .../errors/error_address_payable.sol | 2 + .../errors/error_as_function_param.sol | 4 ++ .../syntaxTests/errors/error_in_interface.sol | 4 ++ .../syntaxTests/errors/error_in_library.sol | 4 ++ .../errors/error_location_memory.sol | 3 + .../errors/error_location_specifier.sol | 3 + .../errors/error_reserved_name.sol | 3 + .../syntaxTests/errors/file_level.sol | 6 ++ .../syntaxTests/errors/hash_collision.sol | 6 ++ .../syntaxTests/errors/indexed_error.sol | 3 + .../syntaxTests/errors/no_mappings.sol | 9 +++ .../syntaxTests/errors/no_overloading.sol | 4 ++ .../errors/no_overloading_inheritance.sol | 8 +++ .../errors/no_structs_in_abiv1.sol | 7 ++ .../errors/panic_reserved_name.sol | 3 + .../syntaxTests/errors/selector.sol | 6 ++ .../errors/selector_on_instance.sol | 7 ++ .../syntaxTests/errors/struct_named_error.sol | 6 ++ test/libsolidity/syntaxTests/errors/using.sol | 12 ++++ .../syntaxTests/errors/using_2.sol | 12 ++++ .../syntaxTests/errors/using_structs.sol | 6 ++ .../libsolidity/syntaxTests/errors/weird1.sol | 7 ++ .../libsolidity/syntaxTests/errors/weird3.sol | 7 ++ .../libsolidity/syntaxTests/errors/weird4.sol | 9 +++ .../syntaxTests/errors/zero_signature.sol | 8 +++ 70 files changed, 629 insertions(+), 47 deletions(-) create mode 100644 test/libsolidity/semanticTests/error/selector.sol create mode 100644 test/libsolidity/syntaxTests/errors/abi_decode_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/abi_encode_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/abi_encode_error_instance.sol create mode 100644 test/libsolidity/syntaxTests/errors/all_ones_signature.sol create mode 100644 test/libsolidity/syntaxTests/errors/anonymous.sol create mode 100644 test/libsolidity/syntaxTests/errors/assert_with_cond_and_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/assert_with_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/basic.sol create mode 100644 test/libsolidity/syntaxTests/errors/clash_function_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/clash_function_error_inheritance.sol create mode 100644 test/libsolidity/syntaxTests/errors/clash_function_error_inheritance_2.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_address_payable.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_as_function_param.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_in_interface.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_in_library.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_location_memory.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_location_specifier.sol create mode 100644 test/libsolidity/syntaxTests/errors/error_reserved_name.sol create mode 100644 test/libsolidity/syntaxTests/errors/file_level.sol create mode 100644 test/libsolidity/syntaxTests/errors/hash_collision.sol create mode 100644 test/libsolidity/syntaxTests/errors/indexed_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/no_mappings.sol create mode 100644 test/libsolidity/syntaxTests/errors/no_overloading.sol create mode 100644 test/libsolidity/syntaxTests/errors/no_overloading_inheritance.sol create mode 100644 test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol create mode 100644 test/libsolidity/syntaxTests/errors/panic_reserved_name.sol create mode 100644 test/libsolidity/syntaxTests/errors/selector.sol create mode 100644 test/libsolidity/syntaxTests/errors/selector_on_instance.sol create mode 100644 test/libsolidity/syntaxTests/errors/struct_named_error.sol create mode 100644 test/libsolidity/syntaxTests/errors/using.sol create mode 100644 test/libsolidity/syntaxTests/errors/using_2.sol create mode 100644 test/libsolidity/syntaxTests/errors/using_structs.sol create mode 100644 test/libsolidity/syntaxTests/errors/weird1.sol create mode 100644 test/libsolidity/syntaxTests/errors/weird3.sol create mode 100644 test/libsolidity/syntaxTests/errors/weird4.sol create mode 100644 test/libsolidity/syntaxTests/errors/zero_signature.sol diff --git a/docs/grammar/Solidity.g4 b/docs/grammar/Solidity.g4 index 3d66df9b6..bd2d1ad5a 100644 --- a/docs/grammar/Solidity.g4 +++ b/docs/grammar/Solidity.g4 @@ -19,6 +19,7 @@ sourceUnit: ( | constantVariableDeclaration | structDefinition | enumDefinition + | errorDefinition )* EOF; //@doc: inline @@ -90,6 +91,7 @@ contractBodyElement: | enumDefinition | stateVariableDeclaration | eventDefinition + | errorDefinition | usingDirective; //@doc:inline namedArgument: name=identifier Colon value=expression; @@ -289,6 +291,18 @@ eventDefinition: Anonymous? Semicolon; +/** + * Parameter of an error. + */ +errorParameter: type=typeName name=identifier?; +/** + * Definition of an error. + */ +errorDefinition: + Error name=identifier + LParen (parameters+=errorParameter (Comma parameters+=errorParameter)*)? RParen + Semicolon; + /** * Using directive to bind library functions to types. * Can occur within contracts and libraries. @@ -365,9 +379,9 @@ tupleExpression: LParen (expression? ( Comma expression?)* ) RParen; inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; /** - * Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements. + * Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers. */ -identifier: Identifier | From; +identifier: Identifier | From | Error; literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral; booleanLiteral: True | False; diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index d8e89b278..560d78e6a 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -29,12 +29,13 @@ Do: 'do'; Else: 'else'; Emit: 'emit'; Enum: 'enum'; +Error: 'error'; // not a real keyword Event: 'event'; External: 'external'; Fallback: 'fallback'; False: 'false'; Fixed: 'fixed' | ('fixed' [1-9][0-9]* 'x' [1-9][0-9]*); -From: 'from'; +From: 'from'; // not a real keyword /** * Bytes types of fixed length. */ diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 3bdf128a9..aece2f8cb 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -25,10 +25,10 @@ #include #include #include +#include #include #include - using namespace std; using namespace solidity; using namespace solidity::langutil; @@ -433,6 +433,24 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra ); hashes.insert(hash); } + + map errorHashes; + for (ErrorDefinition const* error: _contract.interfaceErrors()) + { + if (!error->functionType(true)->interfaceFunctionType()) + // This will result in an error later on, so we can ignore it here. + continue; + uint32_t hash = selectorFromSignature32(error->functionType(true)->externalSignature()); + if (errorHashes.count(hash)) + m_errorReporter.typeError( + 4883_error, + _contract.location(), + SecondarySourceLocation{}.append("This error has the same selector: "s, errorHashes[hash]), + "Error signature hash collision for " + error->functionType(true)->externalSignature() + ); + else + errorHashes[hash] = error->location(); + } } void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 25bd37b9f..499c26ff1 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -124,7 +124,7 @@ bool DeclarationContainer::registerDeclaration( // Do not warn about shadowing for structs and enums because their members are // not accessible without prefixes. Also do not warn about event parameters // because they do not participate in any proper scope. - bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventParameter()); + bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventOrErrorParameter()); if (m_enclosingContainer && !special) m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location()); } diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index d54c22ef3..a6e3b40bf 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -368,7 +368,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) } // Find correct data location. - if (_variable.isEventParameter()) + if (_variable.isEventOrErrorParameter()) { solAssert(varLoc == Location::Unspecified, ""); typeLoc = DataLocation::Memory; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index dba059fd2..018c10f58 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -158,6 +158,13 @@ bool DocStringAnalyser::visit(EventDefinition const& _event) return true; } +bool DocStringAnalyser::visit(ErrorDefinition const& _error) +{ + handleCallable(_error, _error, _error.annotation()); + + return true; +} + void DocStringAnalyser::handleCallable( CallableDeclaration const& _callable, StructurallyDocumented const& _node, diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index e13cab898..1fa076359 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -43,6 +43,7 @@ private: bool visit(VariableDeclaration const& _variable) override; bool visit(ModifierDefinition const& _modifier) override; bool visit(EventDefinition const& _event) override; + bool visit(ErrorDefinition const& _error) override; CallableDeclaration const* resolveInheritDoc( std::set const& _baseFunctions, diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index cb3c0925c..30ecfaf2f 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -92,6 +92,13 @@ bool DocStringTagParser::visit(EventDefinition const& _event) return true; } +bool DocStringTagParser::visit(ErrorDefinition const& _error) +{ + handleCallable(_error, _error, _error.annotation()); + + return true; +} + void DocStringTagParser::checkParameters( CallableDeclaration const& _callable, StructurallyDocumented const& _node, @@ -134,11 +141,14 @@ void DocStringTagParser::handleCallable( ) { static set const validEventTags = set{"dev", "notice", "return", "param"}; + static set const validErrorTags = set{"dev", "notice", "param"}; static set const validModifierTags = set{"dev", "notice", "param", "inheritdoc"}; static set const validTags = set{"dev", "notice", "return", "param", "inheritdoc"}; if (dynamic_cast(&_callable)) parseDocStrings(_node, _annotation, validEventTags, "events"); + else if (dynamic_cast(&_callable)) + parseDocStrings(_node, _annotation, validErrorTags, "errors"); else if (dynamic_cast(&_callable)) parseDocStrings(_node, _annotation, validModifierTags, "modifiers"); else diff --git a/libsolidity/analysis/DocStringTagParser.h b/libsolidity/analysis/DocStringTagParser.h index 41dcca5df..548de1d42 100644 --- a/libsolidity/analysis/DocStringTagParser.h +++ b/libsolidity/analysis/DocStringTagParser.h @@ -44,6 +44,7 @@ private: bool visit(VariableDeclaration const& _variable) override; bool visit(ModifierDefinition const& _modifier) override; bool visit(EventDefinition const& _event) override; + bool visit(ErrorDefinition const& _error) override; void checkParameters( CallableDeclaration const& _callable, diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index cf2a39746..329408832 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,11 @@ void PostTypeChecker::endVisit(VariableDeclaration const& _variable) callEndVisit(_variable); } +void PostTypeChecker::endVisit(ErrorDefinition const& _error) +{ + callEndVisit(_error); +} + bool PostTypeChecker::visit(EmitStatement const& _emit) { return callVisit(_emit); @@ -362,6 +368,33 @@ private: /// Flag indicating whether we are currently inside a StructDefinition. int m_insideStruct = 0; }; + +struct ReservedErrorSelector: public PostTypeChecker::Checker +{ + ReservedErrorSelector(ErrorReporter& _errorReporter): + Checker(_errorReporter) + {} + + void endVisit(ErrorDefinition const& _error) override + { + if (_error.name() == "Error" || _error.name() == "Panic") + m_errorReporter.syntaxError( + 1855_error, + _error.location(), + "The built-in errors \"Error\" and \"Panic\" cannot be re-defined." + ); + else + { + uint32_t selector = selectorFromSignature32(_error.functionType(true)->externalSignature()); + if (selector == 0 || ~selector == 0) + m_errorReporter.syntaxError( + 2855_error, + _error.location(), + "The selector 0x" + toHex(toCompactBigEndian(selector, 4)) + " is reserved. Please rename the error to avoid the collision." + ); + } + } +}; } PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) @@ -371,4 +404,5 @@ PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_err m_checkers.push_back(make_shared(_errorReporter)); m_checkers.push_back(make_shared(_errorReporter)); m_checkers.push_back(make_shared(_errorReporter)); + m_checkers.push_back(make_shared(_errorReporter)); } diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index fd94785e1..f35330fa1 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -40,6 +40,7 @@ namespace solidity::frontend * - whether a modifier is in a function header * - whether an event is used outside of an emit statement * - whether a variable is declared in a interface + * - whether an error uses a reserved signature * * When adding a new checker, make sure a visitor that forwards calls that your * checker uses exists in PostTypeChecker. Add missing ones. @@ -77,6 +78,8 @@ private: bool visit(VariableDeclaration const& _variable) override; void endVisit(VariableDeclaration const& _variable) override; + void endVisit(ErrorDefinition const& _error) override; + bool visit(EmitStatement const& _emit) override; void endVisit(EmitStatement const& _emit) override; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c3309600b..8c99c8396 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -686,30 +687,12 @@ void TypeChecker::visitManually( bool TypeChecker::visit(EventDefinition const& _eventDef) { solAssert(_eventDef.visibility() > Visibility::Internal, ""); - unsigned numIndexed = 0; - for (ASTPointer const& var: _eventDef.parameters()) - { - if (var->isIndexed()) - numIndexed++; - if (type(*var)->containsNestedMapping()) - m_errorReporter.typeError( - 3448_error, - var->location(), - "Type containing a (nested) mapping is not allowed as event parameter type." - ); - if (!type(*var)->interfaceType(false)) - m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); - if ( - !useABICoderV2() && - !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) - ) - m_errorReporter.typeError( - 3061_error, - var->location(), - "This type is only supported in ABI coder v2. " - "Use \"pragma abicoder v2;\" to enable the feature." - ); - } + checkErrorAndEventParameters(_eventDef); + + auto numIndexed = ranges::count_if( + _eventDef.parameters(), + [](ASTPointer const& var) { return var->isIndexed(); } + ); if (_eventDef.isAnonymous() && numIndexed > 4) m_errorReporter.typeError(8598_error, _eventDef.location(), "More than 4 indexed arguments for anonymous event."); else if (!_eventDef.isAnonymous() && numIndexed > 3) @@ -717,6 +700,13 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) return true; } +bool TypeChecker::visit(ErrorDefinition const& _errorDef) +{ + solAssert(_errorDef.visibility() > Visibility::Internal, ""); + checkErrorAndEventParameters(_errorDef); + return true; +} + void TypeChecker::endVisit(FunctionTypeName const& _funType) { FunctionType const& fun = dynamic_cast(*_funType.annotation().type); @@ -2279,7 +2269,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks( _functionType->kind() == FunctionType::Kind::DelegateCall || _functionType->kind() == FunctionType::Kind::External || _functionType->kind() == FunctionType::Kind::Creation || - _functionType->kind() == FunctionType::Kind::Event; + _functionType->kind() == FunctionType::Kind::Event || + _functionType->kind() == FunctionType::Kind::Error; if (callRequiresABIEncoding && !useABICoderV2()) { @@ -3426,6 +3417,32 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) ); } +void TypeChecker::checkErrorAndEventParameters(CallableDeclaration const& _callable) +{ + string kind = dynamic_cast(&_callable) ? "event" : "error"; + for (ASTPointer const& var: _callable.parameters()) + { + if (type(*var)->containsNestedMapping()) + m_errorReporter.typeError( + 3448_error, + var->location(), + "Type containing a (nested) mapping is not allowed as " + kind + " parameter type." + ); + if (!type(*var)->interfaceType(false)) + m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as " + kind + " parameter type."); + if ( + !useABICoderV2() && + !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) + ) + m_errorReporter.typeError( + 3061_error, + var->location(), + "This type is only supported in ABI coder v2. " + "Use \"pragma abicoder v2;\" to enable the feature." + ); + } +} + bool TypeChecker::contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index cc8da6f7f..efbf99ada 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -125,6 +125,7 @@ private: /// case this is a base constructor call. void visitManually(ModifierInvocation const& _modifier, std::vector const& _bases); bool visit(EventDefinition const& _eventDef) override; + bool visit(ErrorDefinition const& _errorDef) override; void endVisit(FunctionTypeName const& _funType) override; bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(IfStatement const& _ifStatement) override; @@ -153,6 +154,8 @@ private: void endVisit(Literal const& _literal) override; void endVisit(UsingForDirective const& _usingForDirective) override; + void checkErrorAndEventParameters(CallableDeclaration const& _callable); + bool contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts = std::set() diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 1967064b2..44acbe2f0 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -174,6 +174,15 @@ vector const& ContractDefinition::interfaceEvents() cons }); } +vector ContractDefinition::interfaceErrors() const +{ + set result; + // TODO add all referenced errors + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) + result += filteredNodes(contract->m_subNodes); + return convertContainer>(move(result)); +} + vector, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const { return m_interfaceFunctionList[_includeInheritedFunctions].init([&]{ @@ -462,6 +471,24 @@ EventDefinitionAnnotation& EventDefinition::annotation() const return initAnnotation(); } +Type const* ErrorDefinition::type() const +{ + return TypeProvider::function(*this); +} + +FunctionTypePointer ErrorDefinition::functionType(bool _internal) const +{ + if (_internal) + return TypeProvider::function(*this); + else + return nullptr; +} + +ErrorDefinitionAnnotation& ErrorDefinition::annotation() const +{ + return initAnnotation(); +} + SourceUnit const& Scopable::sourceUnit() const { ASTNode const* s = scope(); @@ -504,10 +531,10 @@ bool Declaration::isStructMember() const return dynamic_cast(scope()); } -bool Declaration::isEventParameter() const +bool Declaration::isEventOrErrorParameter() const { solAssert(scope(), ""); - return dynamic_cast(scope()); + return dynamic_cast(scope()) || dynamic_cast(scope()); } DeclarationAnnotation& Declaration::annotation() const @@ -653,7 +680,7 @@ set VariableDeclaration::allowedDataLocations() c { using Location = VariableDeclaration::Location; - if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter()) + if (!hasReferenceOrMappingType() || isStateVariable() || isEventOrErrorParameter()) return set{ Location::Unspecified }; else if (isCallableOrCatchParameter()) { diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index d546a029f..1f36875e5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -272,7 +272,7 @@ public: /// @returns true if this is a declaration of a struct member. bool isStructMember() const; /// @returns true if this is a declaration of a parameter of an event. - bool isEventParameter() const; + bool isEventOrErrorParameter() const; /// @returns the type of expressions referencing this declaration. /// This can only be called once types of variable declarations have already been resolved. @@ -513,6 +513,9 @@ public: std::vector definedFunctions() const { return filteredNodes(m_subNodes); } std::vector events() const { return filteredNodes(m_subNodes); } std::vector const& interfaceEvents() const; + /// @returns all errors defined in this contract or any base contract + /// and all errors referenced during execution. + std::vector interfaceErrors() const; bool isInterface() const { return m_contractKind == ContractKind::Interface; } bool isLibrary() const { return m_contractKind == ContractKind::Library; } @@ -740,7 +743,7 @@ private: /** * Base class for all nodes that define function-like objects, i.e. FunctionDefinition, - * EventDefinition and ModifierDefinition. + * EventDefinition, ErrorDefinition and ModifierDefinition. */ class CallableDeclaration: public Declaration, public VariableScope { @@ -1159,6 +1162,47 @@ private: bool m_anonymous = false; }; +/** + * Definition of an error type usable in ``revert(MyError(x))``, ``require(condition, MyError(x))`` + * and ``catch MyError(_x)``. + */ +class ErrorDefinition: public CallableDeclaration, public StructurallyDocumented, public ScopeOpener +{ +public: + ErrorDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer const& _name, + SourceLocation _nameLocation, + ASTPointer const& _documentation, + ASTPointer const& _parameters + ): + CallableDeclaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default, _parameters), + StructurallyDocumented(_documentation) + { + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + Type const* type() const override; + + FunctionTypePointer functionType(bool _internal) const override; + + bool isVisibleInDerivedContracts() const override { return true; } + bool isVisibleViaContractTypeAccess() const override { return true; } + + ErrorDefinitionAnnotation& annotation() const override; + + CallableDeclaration const& resolveVirtual( + ContractDefinition const&, + ContractDefinition const* + ) const override + { + return *this; + } +}; + /** * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global * functions when such an identifier is encountered. Will never have a valid location in the source code diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 6c19f3dc5..c4212e201 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -184,6 +184,11 @@ struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDoc { }; +struct ErrorDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation +{ +}; + + struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation { }; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 80d37d399..d413c9e71 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -57,6 +57,7 @@ class VariableDeclaration; class ModifierDefinition; class ModifierInvocation; class EventDefinition; +class ErrorDefinition; class MagicVariableDeclaration; class TypeName; class ElementaryTypeName; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index d62445081..40f72bde7 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -481,6 +481,17 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) return false; } +bool ASTJsonConverter::visit(ErrorDefinition const& _node) +{ + setJsonNode(_node, "ErrorDefinition", { + make_pair("name", _node.name()), + make_pair("nameLocation", sourceLocationToString(_node.nameLocation())), + make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), + make_pair("parameters", toJson(_node.parameterList())) + }); + return false; +} + bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { std::vector> attributes = { diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index b23978e10..904749004 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -88,6 +88,7 @@ public: bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; bool visit(EventDefinition const& _node) override; + bool visit(ErrorDefinition const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index fef38e5d3..cb58885f7 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -151,6 +151,8 @@ ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js return createModifierInvocation(_json); if (nodeType == "EventDefinition") return createEventDefinition(_json); + if (nodeType == "ErrorDefinition") + return createErrorDefinition(_json); if (nodeType == "ElementaryTypeName") return createElementaryTypeName(_json); if (nodeType == "UserDefinedTypeName") @@ -539,6 +541,17 @@ ASTPointer ASTJsonImporter::createEventDefinition(Json::Value c ); } +ASTPointer ASTJsonImporter::createErrorDefinition(Json::Value const& _node) +{ + return createASTNode( + _node, + memberAsASTString(_node, "name"), + createNameSourceLocation(_node), + _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), + createParameterList(member(_node, "parameters")) + ); +} + ASTPointer ASTJsonImporter::createElementaryTypeName(Json::Value const& _node) { unsigned short firstNum; diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index c4f8c6cc9..3ef6db384 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -88,6 +88,7 @@ private: ASTPointer createModifierDefinition(Json::Value const& _node); ASTPointer createModifierInvocation(Json::Value const& _node); ASTPointer createEventDefinition(Json::Value const& _node); + ASTPointer createErrorDefinition(Json::Value const& _node); ASTPointer createElementaryTypeName(Json::Value const& _node); ASTPointer createUserDefinedTypeName(Json::Value const& _node); ASTPointer createFunctionTypeName(Json::Value const& _node); diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h index 13626a0cf..44caa24dd 100644 --- a/libsolidity/ast/ASTUtils.h +++ b/libsolidity/ast/ASTUtils.h @@ -22,6 +22,12 @@ namespace solidity::frontend { class VariableDeclaration; +class Declaration; +class Expression; + +/// @returns the declaration referenced from the expression which has to be MemberAccess +/// or Identifier. Returns nullptr otherwise. +Declaration const* referencedDeclaration(Expression const& _expression); /// Find the topmost referenced constant variable declaration when the given variable /// declaration value is an identifier. Works only for constant variable declarations. diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 9ad652b06..4acb47273 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -71,6 +71,7 @@ public: virtual bool visit(ModifierDefinition& _node) { return visitNode(_node); } virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); } virtual bool visit(EventDefinition& _node) { return visitNode(_node); } + virtual bool visit(ErrorDefinition& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); } virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); } virtual bool visit(FunctionTypeName& _node) { return visitNode(_node); } @@ -124,6 +125,7 @@ public: virtual void endVisit(ModifierDefinition& _node) { endVisitNode(_node); } virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); } virtual void endVisit(EventDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(ErrorDefinition& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); } virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); } virtual void endVisit(FunctionTypeName& _node) { endVisitNode(_node); } @@ -199,6 +201,7 @@ public: virtual bool visit(ModifierDefinition const& _node) { return visitNode(_node); } virtual bool visit(ModifierInvocation const& _node) { return visitNode(_node); } virtual bool visit(EventDefinition const& _node) { return visitNode(_node); } + virtual bool visit(ErrorDefinition const& _node) { return visitNode(_node); } virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); } virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); } virtual bool visit(FunctionTypeName const& _node) { return visitNode(_node); } @@ -252,6 +255,7 @@ public: virtual void endVisit(ModifierDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); } virtual void endVisit(EventDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(ErrorDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); } virtual void endVisit(FunctionTypeName const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index afd9838a1..678a9ef7e 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -366,6 +366,28 @@ void EventDefinition::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void ErrorDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_documentation) + m_documentation->accept(_visitor); + m_parameters->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ErrorDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_documentation) + m_documentation->accept(_visitor); + m_parameters->accept(_visitor); + } + _visitor.endVisit(*this); +} + void ElementaryTypeName::accept(ASTVisitor& _visitor) { _visitor.visit(*this); diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index c30c6fd75..cc810708e 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -431,6 +431,11 @@ FunctionType const* TypeProvider::function(EventDefinition const& _def) return createAndGet(_def); } +FunctionType const* TypeProvider::function(ErrorDefinition const& _def) +{ + return createAndGet(_def); +} + FunctionType const* TypeProvider::function(FunctionTypeName const& _typeName) { return createAndGet(_typeName); diff --git a/libsolidity/ast/TypeProvider.h b/libsolidity/ast/TypeProvider.h index 007e56788..6cdfdadc6 100644 --- a/libsolidity/ast/TypeProvider.h +++ b/libsolidity/ast/TypeProvider.h @@ -139,6 +139,8 @@ public: /// @returns the function type of an event. static FunctionType const* function(EventDefinition const& _event); + static FunctionType const* function(ErrorDefinition const& _error); + /// @returns the type of a function type name. static FunctionType const* function(FunctionTypeName const& _typeName); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 1accdeb81..b098d6f3f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2736,13 +2736,36 @@ FunctionType::FunctionType(EventDefinition const& _event): } solAssert( - m_parameterNames.size() == m_parameterTypes.size(), - "Parameter names list must match parameter types list!" - ); + m_parameterNames.size() == m_parameterTypes.size(), + "Parameter names list must match parameter types list!" + ); solAssert( - m_returnParameterNames.size() == m_returnParameterTypes.size(), - "Return parameter names list must match return parameter types list!" - ); + m_returnParameterNames.size() == 0 && + m_returnParameterTypes.size() == 0, + "" + ); +} + +FunctionType::FunctionType(ErrorDefinition const& _error): + m_kind(Kind::Error), + m_stateMutability(StateMutability::Pure), + m_declaration(&_error) +{ + for (ASTPointer const& var: _error.parameters()) + { + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); + } + + solAssert( + m_parameterNames.size() == m_parameterTypes.size(), + "Parameter names list must match parameter types list!" + ); + solAssert( + m_returnParameterNames.size() == 0 && + m_returnParameterTypes.size() == 0, + "" + ); } FunctionType::FunctionType(FunctionTypeName const& _typeName): @@ -2870,6 +2893,7 @@ string FunctionType::richIdentifier() const case Kind::RIPEMD160: id += "ripemd160"; break; case Kind::GasLeft: id += "gasleft"; break; case Kind::Event: id += "event"; break; + case Kind::Error: id += "error"; break; case Kind::SetGas: id += "setgas"; break; case Kind::SetValue: id += "setvalue"; break; case Kind::BlockHash: id += "blockhash"; break; @@ -3107,7 +3131,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const // Note that m_declaration might also be a state variable! solAssert(m_declaration, "Declaration needed to determine interface function type."); bool isLibraryFunction = false; - if (kind() != Kind::Event) + if (kind() != Kind::Event && kind() != Kind::Error) if (auto const* contract = dynamic_cast(m_declaration->scope())) isLibraryFunction = contract->isLibrary(); @@ -3230,6 +3254,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const } return {}; } + case Kind::Error: + return {{"selector", TypeProvider::fixedBytes(4)}}; default: return MemberList::MemberMap(); } @@ -3396,15 +3422,16 @@ string FunctionType::externalSignature() const case Kind::External: case Kind::DelegateCall: case Kind::Event: + case Kind::Error: case Kind::Declaration: break; default: solAssert(false, "Invalid function type for requesting external signature."); } - // "inLibrary" is only relevant if this is not an event. + // "inLibrary" is only relevant if this is neither an event nor an error. bool inLibrary = false; - if (kind() != Kind::Event) + if (kind() != Kind::Event && kind() != Kind::Error) if (auto const* contract = dynamic_cast(m_declaration->scope())) inLibrary = contract->isLibrary(); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 469779c8b..7bcd3d44c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1151,6 +1151,7 @@ public: SHA256, ///< CALL to special contract for sha256 RIPEMD160, ///< CALL to special contract for ripemd160 Event, ///< syntactic sugar for LOG* + Error, ///< creating an error instance in revert or require SetGas, ///< modify the default gas value for the function call SetValue, ///< modify the default value transfer for the function call BlockHash, ///< BLOCKHASH @@ -1182,6 +1183,7 @@ public: explicit FunctionType(VariableDeclaration const& _varDecl); /// Creates the function type of an event. explicit FunctionType(EventDefinition const& _event); + explicit FunctionType(ErrorDefinition const& _error); /// Creates the type of a function type name. explicit FunctionType(FunctionTypeName const& _typeName); /// Function type constructor to be used for a plain type (not derived from a declaration). diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7e82da6c4..fb88e0dd2 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -912,6 +912,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << logInstruction(numIndexed); break; } + case FunctionType::Kind::Error: + { + solAssert(false, ""); + } case FunctionType::Kind::BlockHash: { acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true); @@ -1420,6 +1424,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "event not found"); // no-op, because the parent node will do the job break; + case FunctionType::Kind::Error: + if (!dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + solAssert(false, "error not found"); + // no-op, because the parent node will do the job + break; case FunctionType::Kind::DelegateCall: _memberAccess.expression().accept(*this); m_context << funType->externalIdentifier(); @@ -2091,6 +2100,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else if (dynamic_cast(declaration)) { // no-op diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 2578c0fb5..dddf8b80c 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1043,6 +1043,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) m_code << templ.render(); break; } + case FunctionType::Kind::Error: + { + solAssert(false, ""); + } case FunctionType::Kind::Assert: case FunctionType::Kind::Require: { @@ -1710,13 +1714,18 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionSelector")); else if ( functionType.kind() == FunctionType::Kind::Declaration || + functionType.kind() == FunctionType::Kind::Error || // In some situations, internal function types also provide the "selector" member. // See Types.cpp for details. functionType.kind() == FunctionType::Kind::Internal ) { solAssert(functionType.hasDeclaration(), ""); - solAssert(functionType.declaration().isPartOfExternalInterface(), ""); + solAssert( + functionType.kind() == FunctionType::Kind::Error || + functionType.declaration().isPartOfExternalInterface(), + "" + ); define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n"; } else @@ -1981,6 +1990,13 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) ); // the call will do the resolving break; + case FunctionType::Kind::Error: + solAssert( + dynamic_cast(_memberAccess.annotation().referencedDeclaration), + "Error not found" + ); + // the call will do the resolving + break; case FunctionType::Kind::DelegateCall: define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n"; @@ -2310,6 +2326,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else if (dynamic_cast(declaration)) { // no-op diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 9cb9472d1..82498745d 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -699,6 +699,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) arrayPop(_funCall); break; case FunctionType::Kind::Event: + case FunctionType::Kind::Error: // This can be safely ignored. break; case FunctionType::Kind::ObjectCreation: diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6dc0dfa6b..e05134310 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -112,8 +112,16 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) nodes.push_back(parseFunctionDefinition(true)); break; default: + if ( + // Workaround because `error` is not a keyword. + m_scanner->currentToken() == Token::Identifier && + currentLiteral() == "error" && + m_scanner->peekNextToken() == Token::Identifier && + m_scanner->peekNextNextToken() == Token::LParen + ) + nodes.push_back(parseErrorDefinition()); // Constant variable. - if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS) + else if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS) { VarDeclParserOptions options; options.kind = VarDeclKind::FileLevel; @@ -351,6 +359,14 @@ ASTPointer Parser::parseContractDefinition() subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) subNodes.push_back(parseEnumDefinition()); + else if ( + // Workaround because `error` is not a keyword. + currentTokenValue == Token::Identifier && + currentLiteral() == "error" && + m_scanner->peekNextToken() == Token::Identifier && + m_scanner->peekNextNextToken() == Token::LParen + ) + subNodes.push_back(parseErrorDefinition()); else if (variableDeclarationStart()) { VarDeclParserOptions options; @@ -917,6 +933,21 @@ ASTPointer Parser::parseEventDefinition() return nodeFactory.createNode(name, nameLocation, documentation, parameters, anonymous); } +ASTPointer Parser::parseErrorDefinition() +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + ASTPointer documentation = parseStructuredDocumentation(); + + solAssert(*expectIdentifierToken() == "error", ""); + auto&& [name, nameLocation] = expectIdentifierWithLocation(); + + ASTPointer parameters = parseParameterList({}); + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode(name, move(nameLocation), documentation, parameters); +} + ASTPointer Parser::parseUsingDirective() { RecursionGuard recursionGuard(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 77d6cd17c..dd9125e5b 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -102,6 +102,7 @@ private: ); ASTPointer parseModifierDefinition(); ASTPointer parseEventDefinition(); + ASTPointer parseErrorDefinition(); ASTPointer parseUsingDirective(); ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); diff --git a/test/libsolidity/semanticTests/error/selector.sol b/test/libsolidity/semanticTests/error/selector.sol new file mode 100644 index 000000000..fab71f443 --- /dev/null +++ b/test/libsolidity/semanticTests/error/selector.sol @@ -0,0 +1,20 @@ +library L { + error E(); +} +library S { + error E(uint); +} +library T { + error E(); +} +contract C { + function f() public pure returns (bytes4, bytes4) { + assert(L.E.selector == T.E.selector); + assert(L.E.selector != S.E.selector); + return (L.E.selector, S.E.selector); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x92bbf6e800000000000000000000000000000000000000000000000000000000, 0x2ff06700000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/syntaxTests/errors/abi_decode_error.sol b/test/libsolidity/syntaxTests/errors/abi_decode_error.sol new file mode 100644 index 000000000..72b661b6f --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/abi_decode_error.sol @@ -0,0 +1,9 @@ +error E(uint); +contract C { + function f() public pure returns (bytes memory) { + return abi.decode(msg.data, (E)); + } +} +// ---- +// TypeError 1039: (119-120): Argument has to be a type name. +// TypeError 5132: (90-122): Different number of arguments in return statement than in returns declaration. diff --git a/test/libsolidity/syntaxTests/errors/abi_encode_error.sol b/test/libsolidity/syntaxTests/errors/abi_encode_error.sol new file mode 100644 index 000000000..55b4d73df --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/abi_encode_error.sol @@ -0,0 +1,8 @@ +error E(uint); +contract C { + function f() public pure returns (bytes memory) { + return abi.encode(E); + } +} +// ---- +// TypeError 2056: (108-109): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/errors/abi_encode_error_instance.sol b/test/libsolidity/syntaxTests/errors/abi_encode_error_instance.sol new file mode 100644 index 000000000..fbc8b97f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/abi_encode_error_instance.sol @@ -0,0 +1,8 @@ +error E(uint); +contract C { + function f() public pure returns (bytes memory) { + return abi.encode(E(2)); + } +} +// ---- +// TypeError 2056: (108-112): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/errors/all_ones_signature.sol b/test/libsolidity/syntaxTests/errors/all_ones_signature.sol new file mode 100644 index 000000000..4cadf624d --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/all_ones_signature.sol @@ -0,0 +1,8 @@ +error test266151307(); +contract C { + error test266151307(); +} +// ---- +// Warning 2519: (40-62): This declaration shadows an existing declaration. +// SyntaxError 2855: (0-22): The selector 0xffffffff is reserved. Please rename the error to avoid the collision. +// SyntaxError 2855: (40-62): The selector 0xffffffff is reserved. Please rename the error to avoid the collision. diff --git a/test/libsolidity/syntaxTests/errors/anonymous.sol b/test/libsolidity/syntaxTests/errors/anonymous.sol new file mode 100644 index 000000000..5cbf95f2d --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/anonymous.sol @@ -0,0 +1,3 @@ +error E() anonymous; +// ---- +// ParserError 2314: (10-19): Expected ';' but got 'anonymous' diff --git a/test/libsolidity/syntaxTests/errors/assert_with_cond_and_error.sol b/test/libsolidity/syntaxTests/errors/assert_with_cond_and_error.sol new file mode 100644 index 000000000..45ac6bfa1 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/assert_with_cond_and_error.sol @@ -0,0 +1,6 @@ +error E(); +function f(bool x) pure { + assert(x, E()); +} +// ---- +// TypeError 6160: (41-55): Wrong argument count for function call: 2 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/errors/assert_with_error.sol b/test/libsolidity/syntaxTests/errors/assert_with_error.sol new file mode 100644 index 000000000..90f79b40e --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/assert_with_error.sol @@ -0,0 +1,6 @@ +error E(); +function f() pure { + assert(E()); +} +// ---- +// TypeError 9553: (42-45): Invalid type for argument in function call. Invalid implicit conversion from tuple() to bool requested. diff --git a/test/libsolidity/syntaxTests/errors/basic.sol b/test/libsolidity/syntaxTests/errors/basic.sol new file mode 100644 index 000000000..75ac3cf5e --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/basic.sol @@ -0,0 +1,6 @@ +contract C { + error MyError(); + error MyError2(uint x); + error MyError3(uint x, bytes); +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/clash_function_error.sol b/test/libsolidity/syntaxTests/errors/clash_function_error.sol new file mode 100644 index 000000000..0e3a97f7f --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/clash_function_error.sol @@ -0,0 +1,4 @@ +function Err() pure {} +error Err(); +// ---- +// DeclarationError 2333: (23-35): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/errors/clash_function_error_inheritance.sol b/test/libsolidity/syntaxTests/errors/clash_function_error_inheritance.sol new file mode 100644 index 000000000..3b71fb4d6 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/clash_function_error_inheritance.sol @@ -0,0 +1,4 @@ +contract A { function Err() public pure {} } +contract B is A { error Err(); } +// ---- +// DeclarationError 9097: (63-75): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/errors/clash_function_error_inheritance_2.sol b/test/libsolidity/syntaxTests/errors/clash_function_error_inheritance_2.sol new file mode 100644 index 000000000..bfe4e823f --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/clash_function_error_inheritance_2.sol @@ -0,0 +1,5 @@ +contract A { function Err() public pure {} } +contract B { error Err(); } +contract C is A, B {} +// ---- +// DeclarationError 9097: (58-70): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/errors/error_address_payable.sol b/test/libsolidity/syntaxTests/errors/error_address_payable.sol new file mode 100644 index 000000000..3e983e374 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_address_payable.sol @@ -0,0 +1,2 @@ +error E(address payable x); +// ---- diff --git a/test/libsolidity/syntaxTests/errors/error_as_function_param.sol b/test/libsolidity/syntaxTests/errors/error_as_function_param.sol new file mode 100644 index 000000000..3741687f6 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_as_function_param.sol @@ -0,0 +1,4 @@ +error E(uint); +function f(E x) pure returns (uint) {} +// ---- +// TypeError 5172: (26-27): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/errors/error_in_interface.sol b/test/libsolidity/syntaxTests/errors/error_in_interface.sol new file mode 100644 index 000000000..3f0f89b61 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_in_interface.sol @@ -0,0 +1,4 @@ +interface C { + error E(uint); +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/error_in_library.sol b/test/libsolidity/syntaxTests/errors/error_in_library.sol new file mode 100644 index 000000000..1be9718b0 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_in_library.sol @@ -0,0 +1,4 @@ +library L { + error E(uint); +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/error_location_memory.sol b/test/libsolidity/syntaxTests/errors/error_location_memory.sol new file mode 100644 index 000000000..c40d8dfc2 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_location_memory.sol @@ -0,0 +1,3 @@ +error E(uint[] memory); +// ---- +// ParserError 2314: (15-21): Expected ',' but got 'memory' diff --git a/test/libsolidity/syntaxTests/errors/error_location_specifier.sol b/test/libsolidity/syntaxTests/errors/error_location_specifier.sol new file mode 100644 index 000000000..5f5bdc2bb --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_location_specifier.sol @@ -0,0 +1,3 @@ +error E(uint[] calldata); +// ---- +// ParserError 2314: (15-23): Expected ',' but got 'calldata' diff --git a/test/libsolidity/syntaxTests/errors/error_reserved_name.sol b/test/libsolidity/syntaxTests/errors/error_reserved_name.sol new file mode 100644 index 000000000..fc89efc18 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/error_reserved_name.sol @@ -0,0 +1,3 @@ +error Error(uint); +// ---- +// SyntaxError 1855: (0-18): The built-in errors "Error" and "Panic" cannot be re-defined. diff --git a/test/libsolidity/syntaxTests/errors/file_level.sol b/test/libsolidity/syntaxTests/errors/file_level.sol new file mode 100644 index 000000000..07afb90cc --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/file_level.sol @@ -0,0 +1,6 @@ +error MyError(); +error MyError2(uint x); +contract C { + error MyError3(uint x, bytes); +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/hash_collision.sol b/test/libsolidity/syntaxTests/errors/hash_collision.sol new file mode 100644 index 000000000..37b95c822 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/hash_collision.sol @@ -0,0 +1,6 @@ +contract test { + error gsf(); + error tgeo(); +} +// ---- +// TypeError 4883: (0-52): Error signature hash collision for tgeo() diff --git a/test/libsolidity/syntaxTests/errors/indexed_error.sol b/test/libsolidity/syntaxTests/errors/indexed_error.sol new file mode 100644 index 000000000..2efa3d70d --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/indexed_error.sol @@ -0,0 +1,3 @@ +error E(uint indexed); +// ---- +// ParserError 2314: (13-20): Expected ',' but got 'indexed' diff --git a/test/libsolidity/syntaxTests/errors/no_mappings.sol b/test/libsolidity/syntaxTests/errors/no_mappings.sol new file mode 100644 index 000000000..9a8c9e509 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/no_mappings.sol @@ -0,0 +1,9 @@ +error MyError(mapping(uint => uint)); +contract C { + error MyError2(mapping(uint => uint)); +} +// ---- +// TypeError 3448: (14-35): Type containing a (nested) mapping is not allowed as error parameter type. +// TypeError 3417: (14-35): Internal or recursive type is not allowed as error parameter type. +// TypeError 3448: (70-91): Type containing a (nested) mapping is not allowed as error parameter type. +// TypeError 3417: (70-91): Internal or recursive type is not allowed as error parameter type. diff --git a/test/libsolidity/syntaxTests/errors/no_overloading.sol b/test/libsolidity/syntaxTests/errors/no_overloading.sol new file mode 100644 index 000000000..53ba01743 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/no_overloading.sol @@ -0,0 +1,4 @@ +error Err(uint); +error Err(bytes32); +// ---- +// DeclarationError 2333: (17-36): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/errors/no_overloading_inheritance.sol b/test/libsolidity/syntaxTests/errors/no_overloading_inheritance.sol new file mode 100644 index 000000000..fac0da4bc --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/no_overloading_inheritance.sol @@ -0,0 +1,8 @@ +contract A { + error Err(uint); +} +contract B is A { + error Err(bytes32); +} +// ---- +// DeclarationError 9097: (58-77): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol b/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol new file mode 100644 index 000000000..8727a2852 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol @@ -0,0 +1,7 @@ +pragma abicoder v1; +struct S {uint a;} +contract C { + error MyError(S); +} +// ---- +// TypeError 3061: (70-71): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/errors/panic_reserved_name.sol b/test/libsolidity/syntaxTests/errors/panic_reserved_name.sol new file mode 100644 index 000000000..c6f3dd1af --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/panic_reserved_name.sol @@ -0,0 +1,3 @@ +error Panic(bytes2); +// ---- +// SyntaxError 1855: (0-20): The built-in errors "Error" and "Panic" cannot be re-defined. diff --git a/test/libsolidity/syntaxTests/errors/selector.sol b/test/libsolidity/syntaxTests/errors/selector.sol new file mode 100644 index 000000000..6d8826517 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/selector.sol @@ -0,0 +1,6 @@ +error E(); + +contract C { + bytes4 t = E.selector; +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/selector_on_instance.sol b/test/libsolidity/syntaxTests/errors/selector_on_instance.sol new file mode 100644 index 000000000..3eac5984f --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/selector_on_instance.sol @@ -0,0 +1,7 @@ +error E(); + +contract C { + bytes4 t = E().selector; +} +// ---- +// TypeError 9582: (40-52): Member "selector" not found or not visible after argument-dependent lookup in tuple(). diff --git a/test/libsolidity/syntaxTests/errors/struct_named_error.sol b/test/libsolidity/syntaxTests/errors/struct_named_error.sol new file mode 100644 index 000000000..0ee82ead6 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/struct_named_error.sol @@ -0,0 +1,6 @@ +// Test that the parser workaround is not breaking. +struct error {uint a;} +contract C { + error x; +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/using.sol b/test/libsolidity/syntaxTests/errors/using.sol new file mode 100644 index 000000000..388fa52ab --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/using.sol @@ -0,0 +1,12 @@ +error E(uint); +library L { + function f(uint) internal {} +} +contract C { + using L for *; + function f() public pure { + E.f(); + } +} +// ---- +// TypeError 9582: (133-136): Member "f" not found or not visible after argument-dependent lookup in function (uint256) pure. diff --git a/test/libsolidity/syntaxTests/errors/using_2.sol b/test/libsolidity/syntaxTests/errors/using_2.sol new file mode 100644 index 000000000..2f60f3d04 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/using_2.sol @@ -0,0 +1,12 @@ +error E(uint); +library L { + function f(uint) internal {} +} +contract C { + using L for E; + function f() public pure { + E.f(); + } +} +// ---- +// TypeError 5172: (91-92): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/errors/using_structs.sol b/test/libsolidity/syntaxTests/errors/using_structs.sol new file mode 100644 index 000000000..282fce35d --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/using_structs.sol @@ -0,0 +1,6 @@ +struct S {uint a;} +contract C { + error MyError(S); + error MyError2(S t); +} +// ---- diff --git a/test/libsolidity/syntaxTests/errors/weird1.sol b/test/libsolidity/syntaxTests/errors/weird1.sol new file mode 100644 index 000000000..b57f34c22 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/weird1.sol @@ -0,0 +1,7 @@ +error E(); + +contract C { + function() internal pure x = E; +} +// ---- +// TypeError 7407: (58-59): Type function () pure is not implicitly convertible to expected type function () pure. Special functions can not be converted to function types. diff --git a/test/libsolidity/syntaxTests/errors/weird3.sol b/test/libsolidity/syntaxTests/errors/weird3.sol new file mode 100644 index 000000000..c81266969 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/weird3.sol @@ -0,0 +1,7 @@ +error E(); + +contract C { + E x; +} +// ---- +// TypeError 5172: (29-30): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/errors/weird4.sol b/test/libsolidity/syntaxTests/errors/weird4.sol new file mode 100644 index 000000000..be1ef90b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/weird4.sol @@ -0,0 +1,9 @@ +error E(); + +contract C { + function f() public pure { + E x; + } +} +// ---- +// TypeError 5172: (64-65): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/errors/zero_signature.sol b/test/libsolidity/syntaxTests/errors/zero_signature.sol new file mode 100644 index 000000000..06a212d76 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/zero_signature.sol @@ -0,0 +1,8 @@ +error buyAndFree22457070633(uint256); +contract C { + error buyAndFree22457070633(uint256); +} +// ---- +// Warning 2519: (55-92): This declaration shadows an existing declaration. +// SyntaxError 2855: (0-37): The selector 0x00000000 is reserved. Please rename the error to avoid the collision. +// SyntaxError 2855: (55-92): The selector 0x00000000 is reserved. Please rename the error to avoid the collision.