Skip to content

Commit 4438a59

Browse files
committed
Syntax for custom errors.
1 parent 4080748 commit 4438a59

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+624
-47
lines changed

docs/grammar/Solidity.g4

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ sourceUnit: (
1919
| constantVariableDeclaration
2020
| structDefinition
2121
| enumDefinition
22+
| errorDefinition
2223
)* EOF;
2324

2425
//@doc: inline
@@ -90,6 +91,7 @@ contractBodyElement:
9091
| enumDefinition
9192
| stateVariableDeclaration
9293
| eventDefinition
94+
| errorDefinition
9395
| usingDirective;
9496
//@doc:inline
9597
namedArgument: name=identifier Colon value=expression;
@@ -289,6 +291,18 @@ eventDefinition:
289291
Anonymous?
290292
Semicolon;
291293

294+
/**
295+
* Parameter of an error.
296+
*/
297+
errorParameter: type=typeName name=identifier?;
298+
/**
299+
* Definition of an error.
300+
*/
301+
errorDefinition:
302+
Error name=identifier
303+
LParen (parameters+=errorParameter (Comma parameters+=errorParameter)*)? RParen
304+
Semicolon;
305+
292306
/**
293307
* Using directive to bind library functions to types.
294308
* Can occur within contracts and libraries.
@@ -365,9 +379,9 @@ tupleExpression: LParen (expression? ( Comma expression?)* ) RParen;
365379
inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
366380

367381
/**
368-
* Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements.
382+
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
369383
*/
370-
identifier: Identifier | From;
384+
identifier: Identifier | From | Error;
371385

372386
literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;
373387
booleanLiteral: True | False;

docs/grammar/SolidityLexer.g4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ Do: 'do';
2929
Else: 'else';
3030
Emit: 'emit';
3131
Enum: 'enum';
32+
Error: 'error'; // not a real keyword
3233
Event: 'event';
3334
External: 'external';
3435
Fallback: 'fallback';
3536
False: 'false';
3637
Fixed: 'fixed' | ('fixed' [1-9][0-9]* 'x' [1-9][0-9]*);
37-
From: 'from';
38+
From: 'from'; // not a real keyword
3839
/**
3940
* Bytes types of fixed length.
4041
*/

libsolidity/analysis/ContractLevelChecker.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
#include <libsolidity/ast/AST.h>
2626
#include <libsolidity/ast/TypeProvider.h>
2727
#include <libsolidity/analysis/TypeChecker.h>
28+
#include <libsolutil/FunctionSelector.h>
2829
#include <liblangutil/ErrorReporter.h>
2930
#include <boost/range/adaptor/reversed.hpp>
3031

31-
3232
using namespace std;
3333
using namespace solidity;
3434
using namespace solidity::langutil;
@@ -433,6 +433,24 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
433433
);
434434
hashes.insert(hash);
435435
}
436+
437+
map<uint32_t, SourceLocation> errorHashes;
438+
for (ErrorDefinition const* error: _contract.interfaceErrors())
439+
{
440+
if (!error->functionType(true)->interfaceFunctionType())
441+
// This will result in an error later on, so we can ignore it here.
442+
continue;
443+
uint32_t hash = selectorFromSignature32(error->functionType(true)->externalSignature());
444+
if (errorHashes.count(hash))
445+
m_errorReporter.typeError(
446+
4883_error,
447+
_contract.location(),
448+
SecondarySourceLocation{}.append("This error has the same selector: "s, errorHashes[hash]),
449+
"Error signature hash collision for " + error->functionType(true)->externalSignature()
450+
);
451+
else
452+
errorHashes[hash] = error->location();
453+
}
436454
}
437455

438456
void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract)

libsolidity/analysis/DeclarationContainer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ bool DeclarationContainer::registerDeclaration(
124124
// Do not warn about shadowing for structs and enums because their members are
125125
// not accessible without prefixes. Also do not warn about event parameters
126126
// because they do not participate in any proper scope.
127-
bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventParameter());
127+
bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventOrErrorParameter());
128128
if (m_enclosingContainer && !special)
129129
m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location());
130130
}

libsolidity/analysis/DeclarationTypeChecker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
368368
}
369369

370370
// Find correct data location.
371-
if (_variable.isEventParameter())
371+
if (_variable.isEventOrErrorParameter())
372372
{
373373
solAssert(varLoc == Location::Unspecified, "");
374374
typeLoc = DataLocation::Memory;

libsolidity/analysis/DocStringAnalyser.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
158158
return true;
159159
}
160160

161+
bool DocStringAnalyser::visit(ErrorDefinition const& _error)
162+
{
163+
handleCallable(_error, _error, _error.annotation());
164+
165+
return true;
166+
}
167+
161168
void DocStringAnalyser::handleCallable(
162169
CallableDeclaration const& _callable,
163170
StructurallyDocumented const& _node,

libsolidity/analysis/DocStringAnalyser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class DocStringAnalyser: private ASTConstVisitor
4343
bool visit(VariableDeclaration const& _variable) override;
4444
bool visit(ModifierDefinition const& _modifier) override;
4545
bool visit(EventDefinition const& _event) override;
46+
bool visit(ErrorDefinition const& _error) override;
4647

4748
CallableDeclaration const* resolveInheritDoc(
4849
std::set<CallableDeclaration const*> const& _baseFunctions,

libsolidity/analysis/DocStringTagParser.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ bool DocStringTagParser::visit(EventDefinition const& _event)
9292
return true;
9393
}
9494

95+
bool DocStringTagParser::visit(ErrorDefinition const& _error)
96+
{
97+
handleCallable(_error, _error, _error.annotation());
98+
99+
return true;
100+
}
101+
95102
void DocStringTagParser::checkParameters(
96103
CallableDeclaration const& _callable,
97104
StructurallyDocumented const& _node,
@@ -134,11 +141,14 @@ void DocStringTagParser::handleCallable(
134141
)
135142
{
136143
static set<string> const validEventTags = set<string>{"dev", "notice", "return", "param"};
144+
static set<string> const validErrorTags = set<string>{"dev", "notice", "param"};
137145
static set<string> const validModifierTags = set<string>{"dev", "notice", "param", "inheritdoc"};
138146
static set<string> const validTags = set<string>{"dev", "notice", "return", "param", "inheritdoc"};
139147

140148
if (dynamic_cast<EventDefinition const*>(&_callable))
141149
parseDocStrings(_node, _annotation, validEventTags, "events");
150+
else if (dynamic_cast<ErrorDefinition const*>(&_callable))
151+
parseDocStrings(_node, _annotation, validErrorTags, "errors");
142152
else if (dynamic_cast<ModifierDefinition const*>(&_callable))
143153
parseDocStrings(_node, _annotation, validModifierTags, "modifiers");
144154
else

libsolidity/analysis/DocStringTagParser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class DocStringTagParser: private ASTConstVisitor
4444
bool visit(VariableDeclaration const& _variable) override;
4545
bool visit(ModifierDefinition const& _modifier) override;
4646
bool visit(EventDefinition const& _event) override;
47+
bool visit(ErrorDefinition const& _error) override;
4748

4849
void checkParameters(
4950
CallableDeclaration const& _callable,

libsolidity/analysis/PostTypeChecker.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <liblangutil/ErrorReporter.h>
2424
#include <liblangutil/SemVerHandler.h>
2525
#include <libsolutil/Algorithms.h>
26+
#include <libsolutil/FunctionSelector.h>
2627

2728
#include <boost/range/adaptor/map.hpp>
2829
#include <memory>
@@ -70,6 +71,11 @@ void PostTypeChecker::endVisit(VariableDeclaration const& _variable)
7071
callEndVisit(_variable);
7172
}
7273

74+
void PostTypeChecker::endVisit(ErrorDefinition const& _error)
75+
{
76+
callEndVisit(_error);
77+
}
78+
7379
bool PostTypeChecker::visit(EmitStatement const& _emit)
7480
{
7581
return callVisit(_emit);
@@ -362,6 +368,33 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker
362368
/// Flag indicating whether we are currently inside a StructDefinition.
363369
int m_insideStruct = 0;
364370
};
371+
372+
struct ReservedErrorSelector: public PostTypeChecker::Checker
373+
{
374+
ReservedErrorSelector(ErrorReporter& _errorReporter):
375+
Checker(_errorReporter)
376+
{}
377+
378+
void endVisit(ErrorDefinition const& _error) override
379+
{
380+
if (_error.name() == "Error" || _error.name() == "Panic")
381+
m_errorReporter.syntaxError(
382+
1855_error,
383+
_error.location(),
384+
"The built-in errors \"Error\" and \"Panic\" cannot be re-defined."
385+
);
386+
else
387+
{
388+
uint32_t selector = selectorFromSignature32(_error.functionType(true)->externalSignature());
389+
if (selector == 0 || ~selector == 0)
390+
m_errorReporter.syntaxError(
391+
2855_error,
392+
_error.location(),
393+
"The selector 0x" + toHex(toCompactBigEndian(selector, 4)) + " is reserved. Please rename the error to avoid the collision."
394+
);
395+
}
396+
}
397+
};
365398
}
366399

367400
PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter)
@@ -371,4 +404,5 @@ PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_err
371404
m_checkers.push_back(make_shared<ModifierContextChecker>(_errorReporter));
372405
m_checkers.push_back(make_shared<EventOutsideEmitChecker>(_errorReporter));
373406
m_checkers.push_back(make_shared<NoVariablesInInterfaceChecker>(_errorReporter));
407+
m_checkers.push_back(make_shared<ReservedErrorSelector>(_errorReporter));
374408
}

0 commit comments

Comments
 (0)