mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Syntax for custom errors.
This commit is contained in:
+21
-3
@@ -450,6 +450,24 @@ EventDefinitionAnnotation& EventDefinition::annotation() const
|
||||
return initAnnotation<EventDefinitionAnnotation>();
|
||||
}
|
||||
|
||||
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<ErrorDefinitionAnnotation>();
|
||||
}
|
||||
|
||||
SourceUnit const& Scopable::sourceUnit() const
|
||||
{
|
||||
ASTNode const* s = scope();
|
||||
@@ -492,10 +510,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
|
||||
@@ -641,7 +659,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())
|
||||
{
|
||||
|
||||
+42
-2
@@ -266,7 +266,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.
|
||||
@@ -729,7 +729,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
|
||||
{
|
||||
@@ -1143,6 +1143,46 @@ 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,
|
||||
ASTPointer<StructuredDocumentation> const& _documentation,
|
||||
ASTPointer<ParameterList> const& _parameters
|
||||
):
|
||||
CallableDeclaration(_id, _location, _name, 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 false; /* TODO */ }
|
||||
|
||||
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
|
||||
|
||||
@@ -178,6 +178,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;
|
||||
|
||||
@@ -459,6 +459,17 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(ErrorDefinition const& _node)
|
||||
{
|
||||
m_inEvent = true;
|
||||
setJsonNode(_node, "ErrorDefinition", {
|
||||
make_pair("name", _node.name()),
|
||||
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;
|
||||
|
||||
@@ -144,6 +144,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")
|
||||
@@ -521,6 +523,16 @@ ASTPointer<EventDefinition> ASTJsonImporter::createEventDefinition(Json::Value c
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<ErrorDefinition> ASTJsonImporter::createErrorDefinition(Json::Value const& _node)
|
||||
{
|
||||
return createASTNode<ErrorDefinition>(
|
||||
_node,
|
||||
memberAsASTString(_node, "name"),
|
||||
_node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")),
|
||||
createParameterList(member(_node, "parameters"))
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<ElementaryTypeName> ASTJsonImporter::createElementaryTypeName(Json::Value const& _node)
|
||||
{
|
||||
unsigned short firstNum;
|
||||
|
||||
@@ -87,6 +87,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);
|
||||
|
||||
@@ -67,4 +67,14 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons
|
||||
return rootDecl;
|
||||
}
|
||||
|
||||
Declaration const* referencedDeclaration(Expression const& _expression)
|
||||
{
|
||||
if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(&_expression))
|
||||
return memberAccess->annotation().referencedDeclaration;
|
||||
else if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||
return identifier->annotation().referencedDeclaration;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace solidity::frontend
|
||||
{
|
||||
|
||||
class VariableDeclaration;
|
||||
class Declaration;
|
||||
class Expression;
|
||||
|
||||
/// Find the topmost referenced constant variable declaration when the given variable
|
||||
/// declaration value is an identifier. Works only for constant variable declarations.
|
||||
@@ -31,4 +33,8 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons
|
||||
/// Returns true if the constant variable declaration is recursive.
|
||||
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl);
|
||||
|
||||
/// @returns the declaration referenced from the expression which has to be MemberAccess
|
||||
/// or Identifier. Returns nullptr otherwise.
|
||||
Declaration const* referencedDeclaration(Expression const& _expression);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -2724,13 +2724,35 @@ 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<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!"
|
||||
);
|
||||
// TODO actually they have to be empty.
|
||||
solAssert(
|
||||
m_returnParameterNames.size() == m_returnParameterTypes.size(),
|
||||
"Return parameter names list must match return parameter types list!"
|
||||
);
|
||||
}
|
||||
|
||||
FunctionType::FunctionType(FunctionTypeName const& _typeName):
|
||||
@@ -2858,6 +2880,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;
|
||||
@@ -3095,7 +3118,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();
|
||||
|
||||
@@ -3384,15 +3407,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();
|
||||
|
||||
|
||||
@@ -1145,6 +1145,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
|
||||
@@ -1176,6 +1177,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).
|
||||
|
||||
Reference in New Issue
Block a user