mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10987 from ethereum/customErrorDeclaration
Syntax for defining custom errors.
This commit is contained in:
commit
317eaf643c
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -25,10 +25,10 @@
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
#include <libsolidity/analysis/TypeChecker.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::langutil;
|
||||
@ -433,6 +433,24 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
|
||||
);
|
||||
hashes.insert(hash);
|
||||
}
|
||||
|
||||
map<uint32_t, SourceLocation> 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)
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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<CallableDeclaration const*> const& _baseFunctions,
|
||||
|
@ -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<string> const validEventTags = set<string>{"dev", "notice", "return", "param"};
|
||||
static set<string> const validErrorTags = set<string>{"dev", "notice", "param"};
|
||||
static set<string> const validModifierTags = set<string>{"dev", "notice", "param", "inheritdoc"};
|
||||
static set<string> const validTags = set<string>{"dev", "notice", "return", "param", "inheritdoc"};
|
||||
|
||||
if (dynamic_cast<EventDefinition const*>(&_callable))
|
||||
parseDocStrings(_node, _annotation, validEventTags, "events");
|
||||
else if (dynamic_cast<ErrorDefinition const*>(&_callable))
|
||||
parseDocStrings(_node, _annotation, validErrorTags, "errors");
|
||||
else if (dynamic_cast<ModifierDefinition const*>(&_callable))
|
||||
parseDocStrings(_node, _annotation, validModifierTags, "modifiers");
|
||||
else
|
||||
|
@ -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,
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <memory>
|
||||
@ -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<ModifierContextChecker>(_errorReporter));
|
||||
m_checkers.push_back(make_shared<EventOutsideEmitChecker>(_errorReporter));
|
||||
m_checkers.push_back(make_shared<NoVariablesInInterfaceChecker>(_errorReporter));
|
||||
m_checkers.push_back(make_shared<ReservedErrorSelector>(_errorReporter));
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
#include <range/v3/view/zip.hpp>
|
||||
#include <range/v3/view/drop_exactly.hpp>
|
||||
#include <range/v3/algorithm/count_if.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -686,30 +687,12 @@ void TypeChecker::visitManually(
|
||||
bool TypeChecker::visit(EventDefinition const& _eventDef)
|
||||
{
|
||||
solAssert(_eventDef.visibility() > Visibility::Internal, "");
|
||||
unsigned numIndexed = 0;
|
||||
for (ASTPointer<VariableDeclaration> 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<VariableDeclaration> 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<FunctionType const&>(*_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<EventDefinition const*>(&_callable) ? "event" : "error";
|
||||
for (ASTPointer<VariableDeclaration> 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<ContractDefinition const*> const& _seenContracts
|
||||
|
@ -125,6 +125,7 @@ private:
|
||||
/// case this is a base constructor call.
|
||||
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> 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<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>()
|
||||
|
@ -174,6 +174,15 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
|
||||
});
|
||||
}
|
||||
|
||||
vector<ErrorDefinition const*> ContractDefinition::interfaceErrors() const
|
||||
{
|
||||
set<ErrorDefinition const*, CompareByID> result;
|
||||
// TODO add all referenced errors
|
||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||
result += filteredNodes<ErrorDefinition>(contract->m_subNodes);
|
||||
return convertContainer<vector<ErrorDefinition const*>>(move(result));
|
||||
}
|
||||
|
||||
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
|
||||
{
|
||||
return m_interfaceFunctionList[_includeInheritedFunctions].init([&]{
|
||||
@ -462,6 +471,24 @@ EventDefinitionAnnotation& EventDefinition::annotation() const
|
||||
return initAnnotation<EventDefinitionAnnotation>();
|
||||
}
|
||||
|
||||
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<ErrorDefinitionAnnotation>();
|
||||
}
|
||||
|
||||
SourceUnit const& Scopable::sourceUnit() const
|
||||
{
|
||||
ASTNode const* s = scope();
|
||||
@ -504,10 +531,10 @@ bool Declaration::isStructMember() const
|
||||
return dynamic_cast<StructDefinition const*>(scope());
|
||||
}
|
||||
|
||||
bool Declaration::isEventParameter() const
|
||||
bool Declaration::isEventOrErrorParameter() const
|
||||
{
|
||||
solAssert(scope(), "");
|
||||
return dynamic_cast<EventDefinition const*>(scope());
|
||||
return dynamic_cast<EventDefinition const*>(scope()) || dynamic_cast<ErrorDefinition const*>(scope());
|
||||
}
|
||||
|
||||
DeclarationAnnotation& Declaration::annotation() const
|
||||
@ -653,7 +680,7 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
|
||||
{
|
||||
using Location = VariableDeclaration::Location;
|
||||
|
||||
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
|
||||
if (!hasReferenceOrMappingType() || isStateVariable() || isEventOrErrorParameter())
|
||||
return set<Location>{ Location::Unspecified };
|
||||
else if (isCallableOrCatchParameter())
|
||||
{
|
||||
|
@ -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<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
|
||||
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
|
||||
std::vector<EventDefinition const*> const& interfaceEvents() const;
|
||||
/// @returns all errors defined in this contract or any base contract
|
||||
/// and all errors referenced during execution.
|
||||
std::vector<ErrorDefinition const*> 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<ASTString> const& _name,
|
||||
SourceLocation _nameLocation,
|
||||
ASTPointer<StructuredDocumentation> const& _documentation,
|
||||
ASTPointer<ParameterList> 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
|
||||
|
@ -184,6 +184,11 @@ struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDoc
|
||||
{
|
||||
};
|
||||
|
||||
struct ErrorDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||
{
|
||||
};
|
||||
|
@ -57,6 +57,7 @@ class VariableDeclaration;
|
||||
class ModifierDefinition;
|
||||
class ModifierInvocation;
|
||||
class EventDefinition;
|
||||
class ErrorDefinition;
|
||||
class MagicVariableDeclaration;
|
||||
class TypeName;
|
||||
class ElementaryTypeName;
|
||||
|
@ -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<pair<string, Json::Value>> attributes = {
|
||||
|
@ -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;
|
||||
|
@ -151,6 +151,8 @@ ASTPointer<ASTNode> 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<EventDefinition> ASTJsonImporter::createEventDefinition(Json::Value c
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<ErrorDefinition> ASTJsonImporter::createErrorDefinition(Json::Value const& _node)
|
||||
{
|
||||
return createASTNode<ErrorDefinition>(
|
||||
_node,
|
||||
memberAsASTString(_node, "name"),
|
||||
createNameSourceLocation(_node),
|
||||
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
|
||||
createParameterList(member(_node, "parameters"))
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<ElementaryTypeName> ASTJsonImporter::createElementaryTypeName(Json::Value const& _node)
|
||||
{
|
||||
unsigned short firstNum;
|
||||
|
@ -88,6 +88,7 @@ private:
|
||||
ASTPointer<ModifierDefinition> createModifierDefinition(Json::Value const& _node);
|
||||
ASTPointer<ModifierInvocation> createModifierInvocation(Json::Value const& _node);
|
||||
ASTPointer<EventDefinition> createEventDefinition(Json::Value const& _node);
|
||||
ASTPointer<ErrorDefinition> createErrorDefinition(Json::Value const& _node);
|
||||
ASTPointer<ElementaryTypeName> createElementaryTypeName(Json::Value const& _node);
|
||||
ASTPointer<UserDefinedTypeName> createUserDefinedTypeName(Json::Value const& _node);
|
||||
ASTPointer<FunctionTypeName> createFunctionTypeName(Json::Value const& _node);
|
||||
|
@ -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.
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
|
@ -431,6 +431,11 @@ FunctionType const* TypeProvider::function(EventDefinition const& _def)
|
||||
return createAndGet<FunctionType>(_def);
|
||||
}
|
||||
|
||||
FunctionType const* TypeProvider::function(ErrorDefinition const& _def)
|
||||
{
|
||||
return createAndGet<FunctionType>(_def);
|
||||
}
|
||||
|
||||
FunctionType const* TypeProvider::function(FunctionTypeName const& _typeName)
|
||||
{
|
||||
return createAndGet<FunctionType>(_typeName);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<VariableDeclaration> 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<ContractDefinition const*>(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<ContractDefinition const*>(m_declaration->scope()))
|
||||
inLibrary = contract->isLibrary();
|
||||
|
||||
|
@ -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).
|
||||
|
@ -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<ErrorDefinition const*>(_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<ErrorDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
else if (dynamic_cast<EnumDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
|
@ -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<ErrorDefinition const*>(_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<ErrorDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
else if (dynamic_cast<EnumDefinition const*>(declaration))
|
||||
{
|
||||
// no-op
|
||||
|
@ -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:
|
||||
|
@ -112,8 +112,16 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> 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<ContractDefinition> 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<EventDefinition> Parser::parseEventDefinition()
|
||||
return nodeFactory.createNode<EventDefinition>(name, nameLocation, documentation, parameters, anonymous);
|
||||
}
|
||||
|
||||
ASTPointer<ErrorDefinition> Parser::parseErrorDefinition()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
ASTPointer<StructuredDocumentation> documentation = parseStructuredDocumentation();
|
||||
|
||||
solAssert(*expectIdentifierToken() == "error", "");
|
||||
auto&& [name, nameLocation] = expectIdentifierWithLocation();
|
||||
|
||||
ASTPointer<ParameterList> parameters = parseParameterList({});
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<ErrorDefinition>(name, move(nameLocation), documentation, parameters);
|
||||
}
|
||||
|
||||
ASTPointer<UsingForDirective> Parser::parseUsingDirective()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
|
@ -102,6 +102,7 @@ private:
|
||||
);
|
||||
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
||||
ASTPointer<EventDefinition> parseEventDefinition();
|
||||
ASTPointer<ErrorDefinition> parseErrorDefinition();
|
||||
ASTPointer<UsingForDirective> parseUsingDirective();
|
||||
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
||||
ASTPointer<Identifier> parseIdentifier();
|
||||
|
20
test/libsolidity/semanticTests/error/selector.sol
Normal file
20
test/libsolidity/semanticTests/error/selector.sol
Normal file
@ -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
|
9
test/libsolidity/syntaxTests/errors/abi_decode_error.sol
Normal file
9
test/libsolidity/syntaxTests/errors/abi_decode_error.sol
Normal file
@ -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.
|
8
test/libsolidity/syntaxTests/errors/abi_encode_error.sol
Normal file
8
test/libsolidity/syntaxTests/errors/abi_encode_error.sol
Normal file
@ -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.
|
@ -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.
|
@ -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.
|
3
test/libsolidity/syntaxTests/errors/anonymous.sol
Normal file
3
test/libsolidity/syntaxTests/errors/anonymous.sol
Normal file
@ -0,0 +1,3 @@
|
||||
error E() anonymous;
|
||||
// ----
|
||||
// ParserError 2314: (10-19): Expected ';' but got 'anonymous'
|
@ -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.
|
@ -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.
|
6
test/libsolidity/syntaxTests/errors/basic.sol
Normal file
6
test/libsolidity/syntaxTests/errors/basic.sol
Normal file
@ -0,0 +1,6 @@
|
||||
contract C {
|
||||
error MyError();
|
||||
error MyError2(uint x);
|
||||
error MyError3(uint x, bytes);
|
||||
}
|
||||
// ----
|
@ -0,0 +1,4 @@
|
||||
function Err() pure {}
|
||||
error Err();
|
||||
// ----
|
||||
// DeclarationError 2333: (23-35): Identifier already declared.
|
@ -0,0 +1,4 @@
|
||||
contract A { function Err() public pure {} }
|
||||
contract B is A { error Err(); }
|
||||
// ----
|
||||
// DeclarationError 9097: (63-75): Identifier already declared.
|
@ -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.
|
@ -0,0 +1,2 @@
|
||||
error E(address payable x);
|
||||
// ----
|
@ -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.
|
@ -0,0 +1,4 @@
|
||||
interface C {
|
||||
error E(uint);
|
||||
}
|
||||
// ----
|
4
test/libsolidity/syntaxTests/errors/error_in_library.sol
Normal file
4
test/libsolidity/syntaxTests/errors/error_in_library.sol
Normal file
@ -0,0 +1,4 @@
|
||||
library L {
|
||||
error E(uint);
|
||||
}
|
||||
// ----
|
@ -0,0 +1,3 @@
|
||||
error E(uint[] memory);
|
||||
// ----
|
||||
// ParserError 2314: (15-21): Expected ',' but got 'memory'
|
@ -0,0 +1,3 @@
|
||||
error E(uint[] calldata);
|
||||
// ----
|
||||
// ParserError 2314: (15-23): Expected ',' but got 'calldata'
|
@ -0,0 +1,3 @@
|
||||
error Error(uint);
|
||||
// ----
|
||||
// SyntaxError 1855: (0-18): The built-in errors "Error" and "Panic" cannot be re-defined.
|
6
test/libsolidity/syntaxTests/errors/file_level.sol
Normal file
6
test/libsolidity/syntaxTests/errors/file_level.sol
Normal file
@ -0,0 +1,6 @@
|
||||
error MyError();
|
||||
error MyError2(uint x);
|
||||
contract C {
|
||||
error MyError3(uint x, bytes);
|
||||
}
|
||||
// ----
|
6
test/libsolidity/syntaxTests/errors/hash_collision.sol
Normal file
6
test/libsolidity/syntaxTests/errors/hash_collision.sol
Normal file
@ -0,0 +1,6 @@
|
||||
contract test {
|
||||
error gsf();
|
||||
error tgeo();
|
||||
}
|
||||
// ----
|
||||
// TypeError 4883: (0-52): Error signature hash collision for tgeo()
|
3
test/libsolidity/syntaxTests/errors/indexed_error.sol
Normal file
3
test/libsolidity/syntaxTests/errors/indexed_error.sol
Normal file
@ -0,0 +1,3 @@
|
||||
error E(uint indexed);
|
||||
// ----
|
||||
// ParserError 2314: (13-20): Expected ',' but got 'indexed'
|
9
test/libsolidity/syntaxTests/errors/no_mappings.sol
Normal file
9
test/libsolidity/syntaxTests/errors/no_mappings.sol
Normal file
@ -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.
|
4
test/libsolidity/syntaxTests/errors/no_overloading.sol
Normal file
4
test/libsolidity/syntaxTests/errors/no_overloading.sol
Normal file
@ -0,0 +1,4 @@
|
||||
error Err(uint);
|
||||
error Err(bytes32);
|
||||
// ----
|
||||
// DeclarationError 2333: (17-36): Identifier already declared.
|
@ -0,0 +1,8 @@
|
||||
contract A {
|
||||
error Err(uint);
|
||||
}
|
||||
contract B is A {
|
||||
error Err(bytes32);
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 9097: (58-77): Identifier already declared.
|
@ -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.
|
@ -0,0 +1,3 @@
|
||||
error Panic(bytes2);
|
||||
// ----
|
||||
// SyntaxError 1855: (0-20): The built-in errors "Error" and "Panic" cannot be re-defined.
|
6
test/libsolidity/syntaxTests/errors/selector.sol
Normal file
6
test/libsolidity/syntaxTests/errors/selector.sol
Normal file
@ -0,0 +1,6 @@
|
||||
error E();
|
||||
|
||||
contract C {
|
||||
bytes4 t = E.selector;
|
||||
}
|
||||
// ----
|
@ -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().
|
@ -0,0 +1,6 @@
|
||||
// Test that the parser workaround is not breaking.
|
||||
struct error {uint a;}
|
||||
contract C {
|
||||
error x;
|
||||
}
|
||||
// ----
|
12
test/libsolidity/syntaxTests/errors/using.sol
Normal file
12
test/libsolidity/syntaxTests/errors/using.sol
Normal file
@ -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.
|
12
test/libsolidity/syntaxTests/errors/using_2.sol
Normal file
12
test/libsolidity/syntaxTests/errors/using_2.sol
Normal file
@ -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.
|
6
test/libsolidity/syntaxTests/errors/using_structs.sol
Normal file
6
test/libsolidity/syntaxTests/errors/using_structs.sol
Normal file
@ -0,0 +1,6 @@
|
||||
struct S {uint a;}
|
||||
contract C {
|
||||
error MyError(S);
|
||||
error MyError2(S t);
|
||||
}
|
||||
// ----
|
7
test/libsolidity/syntaxTests/errors/weird1.sol
Normal file
7
test/libsolidity/syntaxTests/errors/weird1.sol
Normal file
@ -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.
|
7
test/libsolidity/syntaxTests/errors/weird3.sol
Normal file
7
test/libsolidity/syntaxTests/errors/weird3.sol
Normal file
@ -0,0 +1,7 @@
|
||||
error E();
|
||||
|
||||
contract C {
|
||||
E x;
|
||||
}
|
||||
// ----
|
||||
// TypeError 5172: (29-30): Name has to refer to a struct, enum or contract.
|
9
test/libsolidity/syntaxTests/errors/weird4.sol
Normal file
9
test/libsolidity/syntaxTests/errors/weird4.sol
Normal file
@ -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.
|
8
test/libsolidity/syntaxTests/errors/zero_signature.sol
Normal file
8
test/libsolidity/syntaxTests/errors/zero_signature.sol
Normal file
@ -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.
|
Loading…
Reference in New Issue
Block a user