Syntax for custom errors.

This commit is contained in:
chriseth
2021-02-01 18:26:31 +01:00
parent c7d1e1911e
commit 4d1fd84150
62 changed files with 651 additions and 52 deletions
+21 -3
View File
@@ -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
View File
@@ -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
+5
View File
@@ -178,6 +178,11 @@ struct EventDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDoc
{
};
struct ErrorDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};
struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
{
};
+1
View File
@@ -57,6 +57,7 @@ class VariableDeclaration;
class ModifierDefinition;
class ModifierInvocation;
class EventDefinition;
class ErrorDefinition;
class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
+11
View File
@@ -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 = {
+1
View File
@@ -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;
+12
View File
@@ -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;
+1
View File
@@ -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);
+10
View File
@@ -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;
}
}
+6
View File
@@ -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);
}
+4
View File
@@ -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); }
+22
View File
@@ -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);
+5
View File
@@ -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);
+2
View File
@@ -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);
+33 -9
View File
@@ -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();
+2
View File
@@ -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).