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:
parent
510bbaf672
commit
b04b189959
@ -19,6 +19,7 @@ sourceUnit: (
|
|||||||
| constantVariableDeclaration
|
| constantVariableDeclaration
|
||||||
| structDefinition
|
| structDefinition
|
||||||
| enumDefinition
|
| enumDefinition
|
||||||
|
| errorDefinition
|
||||||
)* EOF;
|
)* EOF;
|
||||||
|
|
||||||
//@doc: inline
|
//@doc: inline
|
||||||
@ -90,6 +91,7 @@ contractBodyElement:
|
|||||||
| enumDefinition
|
| enumDefinition
|
||||||
| stateVariableDeclaration
|
| stateVariableDeclaration
|
||||||
| eventDefinition
|
| eventDefinition
|
||||||
|
| errorDefinition
|
||||||
| usingDirective;
|
| usingDirective;
|
||||||
//@doc:inline
|
//@doc:inline
|
||||||
namedArgument: name=identifier Colon value=expression;
|
namedArgument: name=identifier Colon value=expression;
|
||||||
@ -289,6 +291,18 @@ eventDefinition:
|
|||||||
Anonymous?
|
Anonymous?
|
||||||
Semicolon;
|
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.
|
* Using directive to bind library functions to types.
|
||||||
* Can occur within contracts and libraries.
|
* Can occur within contracts and libraries.
|
||||||
@ -365,9 +379,9 @@ tupleExpression: LParen (expression? ( Comma expression?)* ) RParen;
|
|||||||
inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
|
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;
|
literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;
|
||||||
booleanLiteral: True | False;
|
booleanLiteral: True | False;
|
||||||
|
@ -29,12 +29,13 @@ Do: 'do';
|
|||||||
Else: 'else';
|
Else: 'else';
|
||||||
Emit: 'emit';
|
Emit: 'emit';
|
||||||
Enum: 'enum';
|
Enum: 'enum';
|
||||||
|
Error: 'error'; // not a real keyword
|
||||||
Event: 'event';
|
Event: 'event';
|
||||||
External: 'external';
|
External: 'external';
|
||||||
Fallback: 'fallback';
|
Fallback: 'fallback';
|
||||||
False: 'false';
|
False: 'false';
|
||||||
Fixed: 'fixed' | ('fixed' [1-9][0-9]* 'x' [1-9][0-9]*);
|
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.
|
* Bytes types of fixed length.
|
||||||
*/
|
*/
|
||||||
|
@ -25,10 +25,10 @@
|
|||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/TypeProvider.h>
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
|
#include <libsolutil/FunctionSelector.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
@ -433,6 +433,24 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
|
|||||||
);
|
);
|
||||||
hashes.insert(hash);
|
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)
|
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
|
// Do not warn about shadowing for structs and enums because their members are
|
||||||
// not accessible without prefixes. Also do not warn about event parameters
|
// not accessible without prefixes. Also do not warn about event parameters
|
||||||
// because they do not participate in any proper scope.
|
// 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)
|
if (m_enclosingContainer && !special)
|
||||||
m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location());
|
m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location());
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find correct data location.
|
// Find correct data location.
|
||||||
if (_variable.isEventParameter())
|
if (_variable.isEventOrErrorParameter())
|
||||||
{
|
{
|
||||||
solAssert(varLoc == Location::Unspecified, "");
|
solAssert(varLoc == Location::Unspecified, "");
|
||||||
typeLoc = DataLocation::Memory;
|
typeLoc = DataLocation::Memory;
|
||||||
|
@ -158,6 +158,13 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DocStringAnalyser::visit(ErrorDefinition const& _error)
|
||||||
|
{
|
||||||
|
handleCallable(_error, _error, _error.annotation());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DocStringAnalyser::handleCallable(
|
void DocStringAnalyser::handleCallable(
|
||||||
CallableDeclaration const& _callable,
|
CallableDeclaration const& _callable,
|
||||||
StructurallyDocumented const& _node,
|
StructurallyDocumented const& _node,
|
||||||
|
@ -43,6 +43,7 @@ private:
|
|||||||
bool visit(VariableDeclaration const& _variable) override;
|
bool visit(VariableDeclaration const& _variable) override;
|
||||||
bool visit(ModifierDefinition const& _modifier) override;
|
bool visit(ModifierDefinition const& _modifier) override;
|
||||||
bool visit(EventDefinition const& _event) override;
|
bool visit(EventDefinition const& _event) override;
|
||||||
|
bool visit(ErrorDefinition const& _error) override;
|
||||||
|
|
||||||
CallableDeclaration const* resolveInheritDoc(
|
CallableDeclaration const* resolveInheritDoc(
|
||||||
std::set<CallableDeclaration const*> const& _baseFunctions,
|
std::set<CallableDeclaration const*> const& _baseFunctions,
|
||||||
|
@ -92,6 +92,13 @@ bool DocStringTagParser::visit(EventDefinition const& _event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DocStringTagParser::visit(ErrorDefinition const& _error)
|
||||||
|
{
|
||||||
|
handleCallable(_error, _error, _error.annotation());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void DocStringTagParser::checkParameters(
|
void DocStringTagParser::checkParameters(
|
||||||
CallableDeclaration const& _callable,
|
CallableDeclaration const& _callable,
|
||||||
StructurallyDocumented const& _node,
|
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 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 validModifierTags = set<string>{"dev", "notice", "param", "inheritdoc"};
|
||||||
static set<string> const validTags = set<string>{"dev", "notice", "return", "param", "inheritdoc"};
|
static set<string> const validTags = set<string>{"dev", "notice", "return", "param", "inheritdoc"};
|
||||||
|
|
||||||
if (dynamic_cast<EventDefinition const*>(&_callable))
|
if (dynamic_cast<EventDefinition const*>(&_callable))
|
||||||
parseDocStrings(_node, _annotation, validEventTags, "events");
|
parseDocStrings(_node, _annotation, validEventTags, "events");
|
||||||
|
else if (dynamic_cast<ErrorDefinition const*>(&_callable))
|
||||||
|
parseDocStrings(_node, _annotation, validErrorTags, "errors");
|
||||||
else if (dynamic_cast<ModifierDefinition const*>(&_callable))
|
else if (dynamic_cast<ModifierDefinition const*>(&_callable))
|
||||||
parseDocStrings(_node, _annotation, validModifierTags, "modifiers");
|
parseDocStrings(_node, _annotation, validModifierTags, "modifiers");
|
||||||
else
|
else
|
||||||
|
@ -44,6 +44,7 @@ private:
|
|||||||
bool visit(VariableDeclaration const& _variable) override;
|
bool visit(VariableDeclaration const& _variable) override;
|
||||||
bool visit(ModifierDefinition const& _modifier) override;
|
bool visit(ModifierDefinition const& _modifier) override;
|
||||||
bool visit(EventDefinition const& _event) override;
|
bool visit(EventDefinition const& _event) override;
|
||||||
|
bool visit(ErrorDefinition const& _error) override;
|
||||||
|
|
||||||
void checkParameters(
|
void checkParameters(
|
||||||
CallableDeclaration const& _callable,
|
CallableDeclaration const& _callable,
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/SemVerHandler.h>
|
#include <liblangutil/SemVerHandler.h>
|
||||||
#include <libsolutil/Algorithms.h>
|
#include <libsolutil/Algorithms.h>
|
||||||
|
#include <libsolutil/FunctionSelector.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/map.hpp>
|
#include <boost/range/adaptor/map.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -70,6 +71,11 @@ void PostTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
callEndVisit(_variable);
|
callEndVisit(_variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PostTypeChecker::endVisit(ErrorDefinition const& _error)
|
||||||
|
{
|
||||||
|
callEndVisit(_error);
|
||||||
|
}
|
||||||
|
|
||||||
bool PostTypeChecker::visit(EmitStatement const& _emit)
|
bool PostTypeChecker::visit(EmitStatement const& _emit)
|
||||||
{
|
{
|
||||||
return callVisit(_emit);
|
return callVisit(_emit);
|
||||||
@ -362,6 +368,33 @@ private:
|
|||||||
/// Flag indicating whether we are currently inside a StructDefinition.
|
/// Flag indicating whether we are currently inside a StructDefinition.
|
||||||
int m_insideStruct = 0;
|
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)
|
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<ModifierContextChecker>(_errorReporter));
|
||||||
m_checkers.push_back(make_shared<EventOutsideEmitChecker>(_errorReporter));
|
m_checkers.push_back(make_shared<EventOutsideEmitChecker>(_errorReporter));
|
||||||
m_checkers.push_back(make_shared<NoVariablesInInterfaceChecker>(_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 a modifier is in a function header
|
||||||
* - whether an event is used outside of an emit statement
|
* - whether an event is used outside of an emit statement
|
||||||
* - whether a variable is declared in a interface
|
* - 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
|
* When adding a new checker, make sure a visitor that forwards calls that your
|
||||||
* checker uses exists in PostTypeChecker. Add missing ones.
|
* checker uses exists in PostTypeChecker. Add missing ones.
|
||||||
@ -77,6 +78,8 @@ private:
|
|||||||
bool visit(VariableDeclaration const& _variable) override;
|
bool visit(VariableDeclaration const& _variable) override;
|
||||||
void endVisit(VariableDeclaration const& _variable) override;
|
void endVisit(VariableDeclaration const& _variable) override;
|
||||||
|
|
||||||
|
void endVisit(ErrorDefinition const& _error) override;
|
||||||
|
|
||||||
bool visit(EmitStatement const& _emit) override;
|
bool visit(EmitStatement const& _emit) override;
|
||||||
void endVisit(EmitStatement const& _emit) override;
|
void endVisit(EmitStatement const& _emit) override;
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include <range/v3/view/zip.hpp>
|
#include <range/v3/view/zip.hpp>
|
||||||
#include <range/v3/view/drop_exactly.hpp>
|
#include <range/v3/view/drop_exactly.hpp>
|
||||||
|
#include <range/v3/algorithm/count_if.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -686,30 +687,12 @@ void TypeChecker::visitManually(
|
|||||||
bool TypeChecker::visit(EventDefinition const& _eventDef)
|
bool TypeChecker::visit(EventDefinition const& _eventDef)
|
||||||
{
|
{
|
||||||
solAssert(_eventDef.visibility() > Visibility::Internal, "");
|
solAssert(_eventDef.visibility() > Visibility::Internal, "");
|
||||||
unsigned numIndexed = 0;
|
checkErrorAndEventParameters(_eventDef);
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
|
|
||||||
{
|
auto numIndexed = ranges::count_if(
|
||||||
if (var->isIndexed())
|
_eventDef.parameters(),
|
||||||
numIndexed++;
|
[](ASTPointer<VariableDeclaration> const& var) { return var->isIndexed(); }
|
||||||
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."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (_eventDef.isAnonymous() && numIndexed > 4)
|
if (_eventDef.isAnonymous() && numIndexed > 4)
|
||||||
m_errorReporter.typeError(8598_error, _eventDef.location(), "More than 4 indexed arguments for anonymous event.");
|
m_errorReporter.typeError(8598_error, _eventDef.location(), "More than 4 indexed arguments for anonymous event.");
|
||||||
else if (!_eventDef.isAnonymous() && numIndexed > 3)
|
else if (!_eventDef.isAnonymous() && numIndexed > 3)
|
||||||
@ -717,6 +700,13 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TypeChecker::visit(ErrorDefinition const& _errorDef)
|
||||||
|
{
|
||||||
|
solAssert(_errorDef.visibility() > Visibility::Internal, "");
|
||||||
|
checkErrorAndEventParameters(_errorDef);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void TypeChecker::endVisit(FunctionTypeName const& _funType)
|
void TypeChecker::endVisit(FunctionTypeName const& _funType)
|
||||||
{
|
{
|
||||||
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
|
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::DelegateCall ||
|
||||||
_functionType->kind() == FunctionType::Kind::External ||
|
_functionType->kind() == FunctionType::Kind::External ||
|
||||||
_functionType->kind() == FunctionType::Kind::Creation ||
|
_functionType->kind() == FunctionType::Kind::Creation ||
|
||||||
_functionType->kind() == FunctionType::Kind::Event;
|
_functionType->kind() == FunctionType::Kind::Event ||
|
||||||
|
_functionType->kind() == FunctionType::Kind::Error;
|
||||||
|
|
||||||
if (callRequiresABIEncoding && !useABICoderV2())
|
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(
|
bool TypeChecker::contractDependenciesAreCyclic(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::set<ContractDefinition const*> const& _seenContracts
|
std::set<ContractDefinition const*> const& _seenContracts
|
||||||
|
@ -125,6 +125,7 @@ private:
|
|||||||
/// case this is a base constructor call.
|
/// case this is a base constructor call.
|
||||||
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
|
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
|
||||||
bool visit(EventDefinition const& _eventDef) override;
|
bool visit(EventDefinition const& _eventDef) override;
|
||||||
|
bool visit(ErrorDefinition const& _errorDef) override;
|
||||||
void endVisit(FunctionTypeName const& _funType) override;
|
void endVisit(FunctionTypeName const& _funType) override;
|
||||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
bool visit(IfStatement const& _ifStatement) override;
|
bool visit(IfStatement const& _ifStatement) override;
|
||||||
@ -153,6 +154,8 @@ private:
|
|||||||
void endVisit(Literal const& _literal) override;
|
void endVisit(Literal const& _literal) override;
|
||||||
void endVisit(UsingForDirective const& _usingForDirective) override;
|
void endVisit(UsingForDirective const& _usingForDirective) override;
|
||||||
|
|
||||||
|
void checkErrorAndEventParameters(CallableDeclaration const& _callable);
|
||||||
|
|
||||||
bool contractDependenciesAreCyclic(
|
bool contractDependenciesAreCyclic(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>()
|
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
|
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
|
||||||
{
|
{
|
||||||
return m_interfaceFunctionList[_includeInheritedFunctions].init([&]{
|
return m_interfaceFunctionList[_includeInheritedFunctions].init([&]{
|
||||||
@ -462,6 +471,24 @@ EventDefinitionAnnotation& EventDefinition::annotation() const
|
|||||||
return initAnnotation<EventDefinitionAnnotation>();
|
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
|
SourceUnit const& Scopable::sourceUnit() const
|
||||||
{
|
{
|
||||||
ASTNode const* s = scope();
|
ASTNode const* s = scope();
|
||||||
@ -504,10 +531,10 @@ bool Declaration::isStructMember() const
|
|||||||
return dynamic_cast<StructDefinition const*>(scope());
|
return dynamic_cast<StructDefinition const*>(scope());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Declaration::isEventParameter() const
|
bool Declaration::isEventOrErrorParameter() const
|
||||||
{
|
{
|
||||||
solAssert(scope(), "");
|
solAssert(scope(), "");
|
||||||
return dynamic_cast<EventDefinition const*>(scope());
|
return dynamic_cast<EventDefinition const*>(scope()) || dynamic_cast<ErrorDefinition const*>(scope());
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclarationAnnotation& Declaration::annotation() const
|
DeclarationAnnotation& Declaration::annotation() const
|
||||||
@ -653,7 +680,7 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
|
|||||||
{
|
{
|
||||||
using Location = VariableDeclaration::Location;
|
using Location = VariableDeclaration::Location;
|
||||||
|
|
||||||
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
|
if (!hasReferenceOrMappingType() || isStateVariable() || isEventOrErrorParameter())
|
||||||
return set<Location>{ Location::Unspecified };
|
return set<Location>{ Location::Unspecified };
|
||||||
else if (isCallableOrCatchParameter())
|
else if (isCallableOrCatchParameter())
|
||||||
{
|
{
|
||||||
|
@ -272,7 +272,7 @@ public:
|
|||||||
/// @returns true if this is a declaration of a struct member.
|
/// @returns true if this is a declaration of a struct member.
|
||||||
bool isStructMember() const;
|
bool isStructMember() const;
|
||||||
/// @returns true if this is a declaration of a parameter of an event.
|
/// @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.
|
/// @returns the type of expressions referencing this declaration.
|
||||||
/// This can only be called once types of variable declarations have already been resolved.
|
/// 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<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
|
||||||
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
|
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
|
||||||
std::vector<EventDefinition const*> const& interfaceEvents() const;
|
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 isInterface() const { return m_contractKind == ContractKind::Interface; }
|
||||||
bool isLibrary() const { return m_contractKind == ContractKind::Library; }
|
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,
|
* 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
|
class CallableDeclaration: public Declaration, public VariableScope
|
||||||
{
|
{
|
||||||
@ -1159,6 +1162,47 @@ private:
|
|||||||
bool m_anonymous = false;
|
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
|
* 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
|
* 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
|
struct ModifierDefinitionAnnotation: CallableDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
@ -57,6 +57,7 @@ class VariableDeclaration;
|
|||||||
class ModifierDefinition;
|
class ModifierDefinition;
|
||||||
class ModifierInvocation;
|
class ModifierInvocation;
|
||||||
class EventDefinition;
|
class EventDefinition;
|
||||||
|
class ErrorDefinition;
|
||||||
class MagicVariableDeclaration;
|
class MagicVariableDeclaration;
|
||||||
class TypeName;
|
class TypeName;
|
||||||
class ElementaryTypeName;
|
class ElementaryTypeName;
|
||||||
|
@ -481,6 +481,17 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
|
|||||||
return false;
|
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)
|
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
|
||||||
{
|
{
|
||||||
std::vector<pair<string, Json::Value>> attributes = {
|
std::vector<pair<string, Json::Value>> attributes = {
|
||||||
|
@ -88,6 +88,7 @@ public:
|
|||||||
bool visit(ModifierDefinition const& _node) override;
|
bool visit(ModifierDefinition const& _node) override;
|
||||||
bool visit(ModifierInvocation const& _node) override;
|
bool visit(ModifierInvocation const& _node) override;
|
||||||
bool visit(EventDefinition const& _node) override;
|
bool visit(EventDefinition const& _node) override;
|
||||||
|
bool visit(ErrorDefinition const& _node) override;
|
||||||
bool visit(ElementaryTypeName const& _node) override;
|
bool visit(ElementaryTypeName const& _node) override;
|
||||||
bool visit(UserDefinedTypeName const& _node) override;
|
bool visit(UserDefinedTypeName const& _node) override;
|
||||||
bool visit(FunctionTypeName const& _node) override;
|
bool visit(FunctionTypeName const& _node) override;
|
||||||
|
@ -151,6 +151,8 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
|
|||||||
return createModifierInvocation(_json);
|
return createModifierInvocation(_json);
|
||||||
if (nodeType == "EventDefinition")
|
if (nodeType == "EventDefinition")
|
||||||
return createEventDefinition(_json);
|
return createEventDefinition(_json);
|
||||||
|
if (nodeType == "ErrorDefinition")
|
||||||
|
return createErrorDefinition(_json);
|
||||||
if (nodeType == "ElementaryTypeName")
|
if (nodeType == "ElementaryTypeName")
|
||||||
return createElementaryTypeName(_json);
|
return createElementaryTypeName(_json);
|
||||||
if (nodeType == "UserDefinedTypeName")
|
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)
|
ASTPointer<ElementaryTypeName> ASTJsonImporter::createElementaryTypeName(Json::Value const& _node)
|
||||||
{
|
{
|
||||||
unsigned short firstNum;
|
unsigned short firstNum;
|
||||||
|
@ -88,6 +88,7 @@ private:
|
|||||||
ASTPointer<ModifierDefinition> createModifierDefinition(Json::Value const& _node);
|
ASTPointer<ModifierDefinition> createModifierDefinition(Json::Value const& _node);
|
||||||
ASTPointer<ModifierInvocation> createModifierInvocation(Json::Value const& _node);
|
ASTPointer<ModifierInvocation> createModifierInvocation(Json::Value const& _node);
|
||||||
ASTPointer<EventDefinition> createEventDefinition(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<ElementaryTypeName> createElementaryTypeName(Json::Value const& _node);
|
||||||
ASTPointer<UserDefinedTypeName> createUserDefinedTypeName(Json::Value const& _node);
|
ASTPointer<UserDefinedTypeName> createUserDefinedTypeName(Json::Value const& _node);
|
||||||
ASTPointer<FunctionTypeName> createFunctionTypeName(Json::Value const& _node);
|
ASTPointer<FunctionTypeName> createFunctionTypeName(Json::Value const& _node);
|
||||||
|
@ -22,6 +22,12 @@ namespace solidity::frontend
|
|||||||
{
|
{
|
||||||
|
|
||||||
class VariableDeclaration;
|
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
|
/// Find the topmost referenced constant variable declaration when the given variable
|
||||||
/// declaration value is an identifier. Works only for constant variable declarations.
|
/// 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(ModifierDefinition& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); }
|
virtual bool visit(ModifierInvocation& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(EventDefinition& _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(ElementaryTypeName& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); }
|
virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(FunctionTypeName& _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(ModifierDefinition& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); }
|
virtual void endVisit(ModifierInvocation& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(EventDefinition& _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(ElementaryTypeName& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); }
|
virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(FunctionTypeName& _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(ModifierDefinition const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ModifierInvocation 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(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(ElementaryTypeName const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); }
|
virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(FunctionTypeName 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(ModifierDefinition const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); }
|
virtual void endVisit(ModifierInvocation const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(EventDefinition 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(ElementaryTypeName const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); }
|
virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(FunctionTypeName 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);
|
_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)
|
void ElementaryTypeName::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
_visitor.visit(*this);
|
_visitor.visit(*this);
|
||||||
|
@ -431,6 +431,11 @@ FunctionType const* TypeProvider::function(EventDefinition const& _def)
|
|||||||
return createAndGet<FunctionType>(_def);
|
return createAndGet<FunctionType>(_def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionType const* TypeProvider::function(ErrorDefinition const& _def)
|
||||||
|
{
|
||||||
|
return createAndGet<FunctionType>(_def);
|
||||||
|
}
|
||||||
|
|
||||||
FunctionType const* TypeProvider::function(FunctionTypeName const& _typeName)
|
FunctionType const* TypeProvider::function(FunctionTypeName const& _typeName)
|
||||||
{
|
{
|
||||||
return createAndGet<FunctionType>(_typeName);
|
return createAndGet<FunctionType>(_typeName);
|
||||||
|
@ -139,6 +139,8 @@ public:
|
|||||||
/// @returns the function type of an event.
|
/// @returns the function type of an event.
|
||||||
static FunctionType const* function(EventDefinition const& _event);
|
static FunctionType const* function(EventDefinition const& _event);
|
||||||
|
|
||||||
|
static FunctionType const* function(ErrorDefinition const& _error);
|
||||||
|
|
||||||
/// @returns the type of a function type name.
|
/// @returns the type of a function type name.
|
||||||
static FunctionType const* function(FunctionTypeName const& _typeName);
|
static FunctionType const* function(FunctionTypeName const& _typeName);
|
||||||
|
|
||||||
|
@ -2736,13 +2736,36 @@ FunctionType::FunctionType(EventDefinition const& _event):
|
|||||||
}
|
}
|
||||||
|
|
||||||
solAssert(
|
solAssert(
|
||||||
m_parameterNames.size() == m_parameterTypes.size(),
|
m_parameterNames.size() == m_parameterTypes.size(),
|
||||||
"Parameter names list must match parameter types list!"
|
"Parameter names list must match parameter types list!"
|
||||||
);
|
);
|
||||||
solAssert(
|
solAssert(
|
||||||
m_returnParameterNames.size() == m_returnParameterTypes.size(),
|
m_returnParameterNames.size() == 0 &&
|
||||||
"Return parameter names list must match return parameter types list!"
|
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):
|
FunctionType::FunctionType(FunctionTypeName const& _typeName):
|
||||||
@ -2870,6 +2893,7 @@ string FunctionType::richIdentifier() const
|
|||||||
case Kind::RIPEMD160: id += "ripemd160"; break;
|
case Kind::RIPEMD160: id += "ripemd160"; break;
|
||||||
case Kind::GasLeft: id += "gasleft"; break;
|
case Kind::GasLeft: id += "gasleft"; break;
|
||||||
case Kind::Event: id += "event"; break;
|
case Kind::Event: id += "event"; break;
|
||||||
|
case Kind::Error: id += "error"; break;
|
||||||
case Kind::SetGas: id += "setgas"; break;
|
case Kind::SetGas: id += "setgas"; break;
|
||||||
case Kind::SetValue: id += "setvalue"; break;
|
case Kind::SetValue: id += "setvalue"; break;
|
||||||
case Kind::BlockHash: id += "blockhash"; 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!
|
// Note that m_declaration might also be a state variable!
|
||||||
solAssert(m_declaration, "Declaration needed to determine interface function type.");
|
solAssert(m_declaration, "Declaration needed to determine interface function type.");
|
||||||
bool isLibraryFunction = false;
|
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()))
|
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()))
|
||||||
isLibraryFunction = contract->isLibrary();
|
isLibraryFunction = contract->isLibrary();
|
||||||
|
|
||||||
@ -3230,6 +3254,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
|
|||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
case Kind::Error:
|
||||||
|
return {{"selector", TypeProvider::fixedBytes(4)}};
|
||||||
default:
|
default:
|
||||||
return MemberList::MemberMap();
|
return MemberList::MemberMap();
|
||||||
}
|
}
|
||||||
@ -3396,15 +3422,16 @@ string FunctionType::externalSignature() const
|
|||||||
case Kind::External:
|
case Kind::External:
|
||||||
case Kind::DelegateCall:
|
case Kind::DelegateCall:
|
||||||
case Kind::Event:
|
case Kind::Event:
|
||||||
|
case Kind::Error:
|
||||||
case Kind::Declaration:
|
case Kind::Declaration:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
solAssert(false, "Invalid function type for requesting external signature.");
|
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;
|
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()))
|
if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_declaration->scope()))
|
||||||
inLibrary = contract->isLibrary();
|
inLibrary = contract->isLibrary();
|
||||||
|
|
||||||
|
@ -1151,6 +1151,7 @@ public:
|
|||||||
SHA256, ///< CALL to special contract for sha256
|
SHA256, ///< CALL to special contract for sha256
|
||||||
RIPEMD160, ///< CALL to special contract for ripemd160
|
RIPEMD160, ///< CALL to special contract for ripemd160
|
||||||
Event, ///< syntactic sugar for LOG*
|
Event, ///< syntactic sugar for LOG*
|
||||||
|
Error, ///< creating an error instance in revert or require
|
||||||
SetGas, ///< modify the default gas value for the function call
|
SetGas, ///< modify the default gas value for the function call
|
||||||
SetValue, ///< modify the default value transfer for the function call
|
SetValue, ///< modify the default value transfer for the function call
|
||||||
BlockHash, ///< BLOCKHASH
|
BlockHash, ///< BLOCKHASH
|
||||||
@ -1182,6 +1183,7 @@ public:
|
|||||||
explicit FunctionType(VariableDeclaration const& _varDecl);
|
explicit FunctionType(VariableDeclaration const& _varDecl);
|
||||||
/// Creates the function type of an event.
|
/// Creates the function type of an event.
|
||||||
explicit FunctionType(EventDefinition const& _event);
|
explicit FunctionType(EventDefinition const& _event);
|
||||||
|
explicit FunctionType(ErrorDefinition const& _error);
|
||||||
/// Creates the type of a function type name.
|
/// Creates the type of a function type name.
|
||||||
explicit FunctionType(FunctionTypeName const& _typeName);
|
explicit FunctionType(FunctionTypeName const& _typeName);
|
||||||
/// Function type constructor to be used for a plain type (not derived from a declaration).
|
/// 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);
|
m_context << logInstruction(numIndexed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::Error:
|
||||||
|
{
|
||||||
|
solAssert(false, "");
|
||||||
|
}
|
||||||
case FunctionType::Kind::BlockHash:
|
case FunctionType::Kind::BlockHash:
|
||||||
{
|
{
|
||||||
acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true);
|
acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true);
|
||||||
@ -1420,6 +1424,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
solAssert(false, "event not found");
|
solAssert(false, "event not found");
|
||||||
// no-op, because the parent node will do the job
|
// no-op, because the parent node will do the job
|
||||||
break;
|
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:
|
case FunctionType::Kind::DelegateCall:
|
||||||
_memberAccess.expression().accept(*this);
|
_memberAccess.expression().accept(*this);
|
||||||
m_context << funType->externalIdentifier();
|
m_context << funType->externalIdentifier();
|
||||||
@ -2091,6 +2100,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
else if (dynamic_cast<ErrorDefinition const*>(declaration))
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
else if (dynamic_cast<EnumDefinition const*>(declaration))
|
else if (dynamic_cast<EnumDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -1043,6 +1043,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
m_code << templ.render();
|
m_code << templ.render();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::Error:
|
||||||
|
{
|
||||||
|
solAssert(false, "");
|
||||||
|
}
|
||||||
case FunctionType::Kind::Assert:
|
case FunctionType::Kind::Assert:
|
||||||
case FunctionType::Kind::Require:
|
case FunctionType::Kind::Require:
|
||||||
{
|
{
|
||||||
@ -1710,13 +1714,18 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionSelector"));
|
define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionSelector"));
|
||||||
else if (
|
else if (
|
||||||
functionType.kind() == FunctionType::Kind::Declaration ||
|
functionType.kind() == FunctionType::Kind::Declaration ||
|
||||||
|
functionType.kind() == FunctionType::Kind::Error ||
|
||||||
// In some situations, internal function types also provide the "selector" member.
|
// In some situations, internal function types also provide the "selector" member.
|
||||||
// See Types.cpp for details.
|
// See Types.cpp for details.
|
||||||
functionType.kind() == FunctionType::Kind::Internal
|
functionType.kind() == FunctionType::Kind::Internal
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(functionType.hasDeclaration(), "");
|
solAssert(functionType.hasDeclaration(), "");
|
||||||
solAssert(functionType.declaration().isPartOfExternalInterface(), "");
|
solAssert(
|
||||||
|
functionType.kind() == FunctionType::Kind::Error ||
|
||||||
|
functionType.declaration().isPartOfExternalInterface(),
|
||||||
|
""
|
||||||
|
);
|
||||||
define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n";
|
define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1981,6 +1990,13 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
);
|
);
|
||||||
// the call will do the resolving
|
// the call will do the resolving
|
||||||
break;
|
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:
|
case FunctionType::Kind::DelegateCall:
|
||||||
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
|
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
|
||||||
define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n";
|
define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n";
|
||||||
@ -2310,6 +2326,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
else if (dynamic_cast<ErrorDefinition const*>(declaration))
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
else if (dynamic_cast<EnumDefinition const*>(declaration))
|
else if (dynamic_cast<EnumDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -699,6 +699,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
|||||||
arrayPop(_funCall);
|
arrayPop(_funCall);
|
||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Event:
|
case FunctionType::Kind::Event:
|
||||||
|
case FunctionType::Kind::Error:
|
||||||
// This can be safely ignored.
|
// This can be safely ignored.
|
||||||
break;
|
break;
|
||||||
case FunctionType::Kind::ObjectCreation:
|
case FunctionType::Kind::ObjectCreation:
|
||||||
|
@ -112,8 +112,16 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
nodes.push_back(parseFunctionDefinition(true));
|
nodes.push_back(parseFunctionDefinition(true));
|
||||||
break;
|
break;
|
||||||
default:
|
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.
|
// Constant variable.
|
||||||
if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS)
|
else if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS)
|
||||||
{
|
{
|
||||||
VarDeclParserOptions options;
|
VarDeclParserOptions options;
|
||||||
options.kind = VarDeclKind::FileLevel;
|
options.kind = VarDeclKind::FileLevel;
|
||||||
@ -351,6 +359,14 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
|||||||
subNodes.push_back(parseStructDefinition());
|
subNodes.push_back(parseStructDefinition());
|
||||||
else if (currentTokenValue == Token::Enum)
|
else if (currentTokenValue == Token::Enum)
|
||||||
subNodes.push_back(parseEnumDefinition());
|
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())
|
else if (variableDeclarationStart())
|
||||||
{
|
{
|
||||||
VarDeclParserOptions options;
|
VarDeclParserOptions options;
|
||||||
@ -917,6 +933,21 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
|
|||||||
return nodeFactory.createNode<EventDefinition>(name, nameLocation, documentation, parameters, anonymous);
|
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()
|
ASTPointer<UsingForDirective> Parser::parseUsingDirective()
|
||||||
{
|
{
|
||||||
RecursionGuard recursionGuard(*this);
|
RecursionGuard recursionGuard(*this);
|
||||||
|
@ -102,6 +102,7 @@ private:
|
|||||||
);
|
);
|
||||||
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
||||||
ASTPointer<EventDefinition> parseEventDefinition();
|
ASTPointer<EventDefinition> parseEventDefinition();
|
||||||
|
ASTPointer<ErrorDefinition> parseErrorDefinition();
|
||||||
ASTPointer<UsingForDirective> parseUsingDirective();
|
ASTPointer<UsingForDirective> parseUsingDirective();
|
||||||
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
||||||
ASTPointer<Identifier> parseIdentifier();
|
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