From 19ad0b370e3c6618b9dfcd47f501b013b7d6f589 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 | 16 ++++- 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/SyntaxChecker.cpp | 1 + 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.cpp | 26 ++++--- 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 | 41 +++++++++--- libsolidity/ast/Types.h | 2 + libsolidity/codegen/ExpressionCompiler.cpp | 13 ++++ .../codegen/ir/IRGeneratorForStatements.cpp | 8 +++ libsolidity/formal/SMTEncoder.cpp | 1 + libsolidity/parsing/Parser.cpp | 33 ++++++++- libsolidity/parsing/Parser.h | 1 + .../syntaxTests/errors/abi_decode_error.sol | 9 +++ .../syntaxTests/errors/abi_encode_error.sol | 8 +++ .../errors/abi_encode_error_instance.sol | 8 +++ .../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 ++ .../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/struct_named_error.sol | 6 ++ .../syntaxTests/errors/using_structs.sol | 6 ++ .../libsolidity/syntaxTests/errors/weird1.sol | 7 ++ .../libsolidity/syntaxTests/errors/weird2.sol | 7 ++ .../libsolidity/syntaxTests/errors/weird3.sol | 7 ++ .../libsolidity/syntaxTests/errors/weird4.sol | 9 +++ .../syntaxTests/errors/zero_signature.sol | 8 +++ 65 files changed, 562 insertions(+), 55 deletions(-) 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/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/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/struct_named_error.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/weird2.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 3d66df9b6470..611556db2357 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. @@ -367,7 +381,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; /** * Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements. */ -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 d8e89b27800d..560d78e6abb8 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 784c03d078c7..3dba9c2a77f9 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()) + // Will create an error later on. + 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: ", errorHashes[hash]), + string("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 25bd37b9f14b..499c26ff1851 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 939c3d202e15..da96709aa3ef 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 9de1d89798b8..09e95c49097a 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -157,6 +157,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 e13cab8985a4..1fa076359f3c 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -43,6 +43,7 @@ class DocStringAnalyser: private ASTConstVisitor 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 02c22e9b83d3..7f61c0f03708 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -85,6 +85,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, @@ -127,11 +134,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 41dcca5dfcd1..548de1d42c84 100644 --- a/libsolidity/analysis/DocStringTagParser.h +++ b/libsolidity/analysis/DocStringTagParser.h @@ -44,6 +44,7 @@ class DocStringTagParser: private ASTConstVisitor 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 cf2a397460c5..cc3079e37b36 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 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker /// 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 " + to_string(selector) + " 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 fd94785e1521..f35330fa1fae 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 @@ class PostTypeChecker: private ASTConstVisitor 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/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c857914499ec..ff8fdd88e29c 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -29,6 +29,7 @@ #include #include +#include #include diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 830f3d458926..505d77865e75 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -681,30 +682,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) @@ -712,6 +695,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); @@ -2248,7 +2238,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()) { @@ -3392,6 +3383,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 d75bb8a57cbf..d16b307ac3f8 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -119,6 +119,7 @@ class TypeChecker: private ASTConstVisitor /// 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; @@ -147,6 +148,8 @@ class TypeChecker: private ASTConstVisitor 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 61f038abdd64..e65536d5adf5 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -162,6 +162,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([&]{ @@ -450,6 +459,24 @@ EventDefinitionAnnotation& EventDefinition::annotation() const return initAnnotation(); } +TypePointer 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(); @@ -492,10 +519,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 @@ -641,7 +668,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 ab50410498d5..b7c8069ba808 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -268,7 +268,7 @@ class Declaration: public ASTNode, public Scopable /// @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. @@ -509,6 +509,9 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub 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; } @@ -736,7 +739,7 @@ class ParameterList: public ASTNode /** * 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 { @@ -1155,6 +1158,47 @@ class EventDefinition: public CallableDeclaration, public StructurallyDocumented 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; + + TypePointer 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 e6ef18c3a377..bfa610c8e2e1 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -178,6 +178,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 80d37d3994d5..d413c9e71939 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 ce9ec4eee462..152083b58f13 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -473,6 +473,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 699113ea8ca9..cdd674da64ac 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -88,6 +88,7 @@ class ASTJsonConverter: public ASTConstVisitor 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 6bf5910f242d..6e7ac1f24d97 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") @@ -536,6 +538,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 c4f8c6cc93bb..3ef6db384931 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -88,6 +88,7 @@ class ASTJsonImporter 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.cpp b/libsolidity/ast/ASTUtils.cpp index 2f713824911d..90e565285e40 100644 --- a/libsolidity/ast/ASTUtils.cpp +++ b/libsolidity/ast/ASTUtils.cpp @@ -24,24 +24,29 @@ namespace solidity::frontend { +Declaration const* referencedDeclaration(Expression const& _expression) +{ + if (auto const* memberAccess = dynamic_cast(&_expression)) + return memberAccess->annotation().referencedDeclaration; + else if (auto const* identifier = dynamic_cast(&_expression)) + return identifier->annotation().referencedDeclaration; + else + return nullptr; +} + bool isConstantVariableRecursive(VariableDeclaration const& _varDecl) { solAssert(_varDecl.isConstant(), "Constant variable expected"); - auto referencedDeclaration = [&](Expression const* _e) -> VariableDeclaration const* - { - if (auto identifier = dynamic_cast(_e)) - return dynamic_cast(identifier->annotation().referencedDeclaration); - else if (auto memberAccess = dynamic_cast(_e)) - return dynamic_cast(memberAccess->annotation().referencedDeclaration); - return nullptr; - }; - auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector& _cycleDetector, size_t _depth) { solAssert(_depth < 256, "Recursion depth limit reached"); - if (auto referencedVarDecl = referencedDeclaration(_variable.value().get())) + if (!_variable.value()) + return; + if (auto referencedVarDecl = dynamic_cast( + referencedDeclaration(*_variable.value()) + )) if (referencedVarDecl->isConstant()) if (_cycleDetector.run(*referencedVarDecl)) return; @@ -67,4 +72,5 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons return rootDecl; } + } diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h index 13626a0cf78c..44caa24ddf35 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 9ad652b06365..4acb472736a7 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -71,6 +71,7 @@ class ASTVisitor 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 @@ class ASTVisitor 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 @@ class ASTConstVisitor 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 @@ class ASTConstVisitor 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 afd9838a19f5..678a9ef7e5c9 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 cf81574a7676..08900e6a18b4 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 911eb7b3153e..510b16324950 100644 --- a/libsolidity/ast/TypeProvider.h +++ b/libsolidity/ast/TypeProvider.h @@ -139,6 +139,8 @@ class TypeProvider /// @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 182b2b72b9c9..a5470127e296 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2729,13 +2729,34 @@ 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() == m_returnParameterTypes.size(), + "Return parameter names list must match return parameter types list!" + ); +} + +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.empty() && m_returnParameterTypes.empty(), + "Return parameter names list must match return parameter types list!" + ); } FunctionType::FunctionType(FunctionTypeName const& _typeName): @@ -2863,6 +2884,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; @@ -3100,7 +3122,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(); @@ -3389,15 +3411,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 0414ea783872..0e302526658a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1149,6 +1149,7 @@ class FunctionType: public Type 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 @@ -1180,6 +1181,7 @@ class FunctionType: public Type 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 4a3b7c7b4a6c..108f1da5cfb5 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -908,6 +908,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); @@ -1381,6 +1385,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(); @@ -2052,6 +2061,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 d616dfde8e1b..eabef2f277eb 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1041,6 +1041,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: { @@ -2275,6 +2279,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 e0c4c2cc1dc9..d1f587bdb6b8 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -725,6 +725,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 623060f0b336..93e9910f58b5 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 77152d38f925..109c2b6146a4 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -102,6 +102,7 @@ class Parser: public langutil::ParserBase ); ASTPointer parseModifierDefinition(); ASTPointer parseEventDefinition(); + ASTPointer parseErrorDefinition(); ASTPointer parseUsingDirective(); ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); 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 000000000000..72b661b6f8f6 --- /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 000000000000..55b4d73df8e4 --- /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 000000000000..fbc8b97f1726 --- /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/assert_with_cond_and_error.sol b/test/libsolidity/syntaxTests/errors/assert_with_cond_and_error.sol new file mode 100644 index 000000000000..45ac6bfa10dd --- /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 000000000000..90f79b40eeb7 --- /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 000000000000..75ac3cf5e799 --- /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 000000000000..0e3a97f7f0fb --- /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 000000000000..3b71fb4d67ee --- /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/error_address_payable.sol b/test/libsolidity/syntaxTests/errors/error_address_payable.sol new file mode 100644 index 000000000000..3e983e3741e4 --- /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 000000000000..3741687f67d2 --- /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 000000000000..3f0f89b61afe --- /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 000000000000..1be9718b01e9 --- /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 000000000000..c40d8dfc2bf9 --- /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 000000000000..5f5bdc2bbca2 --- /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 000000000000..fc89efc182f8 --- /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 000000000000..07afb90ccc7c --- /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 000000000000..37b95c822e40 --- /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 000000000000..2efa3d70d338 --- /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 000000000000..9a8c9e509d82 --- /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 000000000000..53ba017438f9 --- /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 000000000000..fac0da4bc036 --- /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 000000000000..8727a2852913 --- /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 000000000000..c6f3dd1afde7 --- /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/struct_named_error.sol b/test/libsolidity/syntaxTests/errors/struct_named_error.sol new file mode 100644 index 000000000000..0ee82ead66ae --- /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_structs.sol b/test/libsolidity/syntaxTests/errors/using_structs.sol new file mode 100644 index 000000000000..282fce35d3cc --- /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 000000000000..b57f34c222aa --- /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/weird2.sol b/test/libsolidity/syntaxTests/errors/weird2.sol new file mode 100644 index 000000000000..2e93df819387 --- /dev/null +++ b/test/libsolidity/syntaxTests/errors/weird2.sol @@ -0,0 +1,7 @@ +error E(); + +contract C { + bytes4 t = E.selector; +} +// ---- +// TypeError 9582: (40-50): Member "selector" not found or not visible after argument-dependent lookup in function () pure. diff --git a/test/libsolidity/syntaxTests/errors/weird3.sol b/test/libsolidity/syntaxTests/errors/weird3.sol new file mode 100644 index 000000000000..c81266969c26 --- /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 000000000000..be1ef90b6dc2 --- /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 000000000000..696fa7b39934 --- /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 0 is reserved. Please rename the error to avoid the collision. +// SyntaxError 2855: (55-92): The selector 0 is reserved. Please rename the error to avoid the collision.