Skip to content

Commit

Permalink
Revert statement.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Mar 3, 2021
1 parent 4b8159f commit 046f830
Show file tree
Hide file tree
Showing 13 changed files with 164 additions and 26 deletions.
67 changes: 48 additions & 19 deletions libsolidity/analysis/PostTypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ void PostTypeChecker::endVisit(EmitStatement const& _emit)
callEndVisit(_emit);
}

bool PostTypeChecker::visit(RevertStatement const& _revert)
{
return callVisit(_revert);
}

void PostTypeChecker::endVisit(RevertStatement const& _revert)
{
callEndVisit(_revert);
}

bool PostTypeChecker::visit(FunctionCall const& _functionCall)
{
return callVisit(_functionCall);
Expand Down Expand Up @@ -281,42 +291,59 @@ struct ModifierContextChecker: public PostTypeChecker::Checker
bool m_insideModifierInvocation = false;
};

struct EventOutsideEmitChecker: public PostTypeChecker::Checker
struct EventOutsideEmitErrorOutsideRevertChecker: public PostTypeChecker::Checker
{
EventOutsideEmitChecker(ErrorReporter& _errorReporter):
EventOutsideEmitErrorOutsideRevertChecker(ErrorReporter& _errorReporter):
Checker(_errorReporter) {}

bool visit(EmitStatement const&) override
bool visit(EmitStatement const& _emitStatement) override
{
m_insideEmitStatement = true;
m_currentStatement = &_emitStatement;
return true;
}

void endVisit(EmitStatement const&) override
{
m_insideEmitStatement = true;
m_currentStatement = nullptr;
}

bool visit(FunctionCall const& _functionCall) override
bool visit(RevertStatement const& _revertStatement) override
{
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
return true;
m_currentStatement = &_revertStatement;
return true;
}

if (FunctionTypePointer const functionType = dynamic_cast<FunctionTypePointer const>(_functionCall.expression().annotation().type))
// Check for event outside of emit statement
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
m_errorReporter.typeError(
3132_error,
_functionCall.location(),
"Event invocations have to be prefixed by \"emit\"."
);
void endVisit(RevertStatement const&) override
{
m_currentStatement = nullptr;
}

bool visit(FunctionCall const& _functionCall) override
{
if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
if (FunctionTypePointer const functionType = dynamic_cast<FunctionTypePointer const>(_functionCall.expression().annotation().type))
{
// Check for event outside of emit statement
if (!dynamic_cast<EmitStatement const*>(m_currentStatement) && functionType->kind() == FunctionType::Kind::Event)
m_errorReporter.typeError(
3132_error,
_functionCall.location(),
"Event invocations have to be prefixed by \"emit\"."
);
else if (!dynamic_cast<RevertStatement const*>(m_currentStatement) && functionType->kind() == FunctionType::Kind::Error)
m_errorReporter.typeError(
7757_error,
_functionCall.location(),
"Errors can only be used with revert statements: \"revert MyError();\"."
);
}
m_currentStatement = nullptr;

return true;
}

private:
/// Flag indicating whether we are currently inside an EmitStatement.
bool m_insideEmitStatement = false;
Statement const* m_currentStatement = nullptr;
};

struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker
Expand Down Expand Up @@ -395,14 +422,16 @@ struct ReservedErrorSelector: public PostTypeChecker::Checker
}
}
};

}


PostTypeChecker::PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter)
{
m_checkers.push_back(make_shared<ConstStateVarCircularReferenceChecker>(_errorReporter));
m_checkers.push_back(make_shared<OverrideSpecifierChecker>(_errorReporter));
m_checkers.push_back(make_shared<ModifierContextChecker>(_errorReporter));
m_checkers.push_back(make_shared<EventOutsideEmitChecker>(_errorReporter));
m_checkers.push_back(make_shared<EventOutsideEmitErrorOutsideRevertChecker>(_errorReporter));
m_checkers.push_back(make_shared<NoVariablesInInterfaceChecker>(_errorReporter));
m_checkers.push_back(make_shared<ReservedErrorSelector>(_errorReporter));
}
3 changes: 3 additions & 0 deletions libsolidity/analysis/PostTypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class PostTypeChecker: private ASTConstVisitor
bool visit(EmitStatement const& _emit) override;
void endVisit(EmitStatement const& _emit) override;

bool visit(RevertStatement const& _revert) override;
void endVisit(RevertStatement const& _revert) override;

bool visit(FunctionCall const& _functionCall) override;

bool visit(Identifier const& _identifier) override;
Expand Down
22 changes: 22 additions & 0 deletions libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,28 @@ class Throw: public Statement
void accept(ASTConstVisitor& _visitor) const override;
};

/**
* The revert statement is used to revert state changes and return error data.
*/
class RevertStatement: public Statement
{
public:
explicit RevertStatement(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<FunctionCall> _functionCall
):
Statement(_id, _location, _docString), m_errorCall(std::move(_functionCall)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;

FunctionCall const& errorCall() const { return *m_errorCall; }

private:
ASTPointer<FunctionCall> m_errorCall;
};

/**
* The emit statement is used to emit events: emit EventName(arg1, ..., argn)
*/
Expand Down
4 changes: 4 additions & 0 deletions libsolidity/ast/ASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class ASTVisitor
virtual bool visit(Return& _node) { return visitNode(_node); }
virtual bool visit(Throw& _node) { return visitNode(_node); }
virtual bool visit(EmitStatement& _node) { return visitNode(_node); }
virtual bool visit(RevertStatement& _node) { return visitNode(_node); }
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
virtual bool visit(Conditional& _node) { return visitNode(_node); }
Expand Down Expand Up @@ -144,6 +145,7 @@ class ASTVisitor
virtual void endVisit(Return& _node) { endVisitNode(_node); }
virtual void endVisit(Throw& _node) { endVisitNode(_node); }
virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); }
virtual void endVisit(RevertStatement& _node) { endVisitNode(_node); }
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
virtual void endVisit(Conditional& _node) { endVisitNode(_node); }
Expand Down Expand Up @@ -220,6 +222,7 @@ class ASTConstVisitor
virtual bool visit(Return const& _node) { return visitNode(_node); }
virtual bool visit(Throw const& _node) { return visitNode(_node); }
virtual bool visit(EmitStatement const& _node) { return visitNode(_node); }
virtual bool visit(RevertStatement const& _node) { return visitNode(_node); }
virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
virtual bool visit(Conditional const& _node) { return visitNode(_node); }
Expand Down Expand Up @@ -274,6 +277,7 @@ class ASTConstVisitor
virtual void endVisit(Return const& _node) { endVisitNode(_node); }
virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); }
virtual void endVisit(RevertStatement const& _node) { endVisitNode(_node); }
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
virtual void endVisit(Conditional const& _node) { endVisitNode(_node); }
Expand Down
14 changes: 14 additions & 0 deletions libsolidity/ast/AST_accept.h
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}

void RevertStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
m_errorCall->accept(_visitor);
_visitor.endVisit(*this);
}

void RevertStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
m_errorCall->accept(_visitor);
_visitor.endVisit(*this);
}

void EmitStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
Expand Down
47 changes: 40 additions & 7 deletions libsolidity/parsing/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1250,11 +1250,13 @@ ASTPointer<Statement> Parser::parseStatement(bool _allowUnchecked)
statement = parseEmitStatement(docString);
break;
case Token::Identifier:
if (m_insideModifier && m_scanner->currentLiteral() == "_")
{
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
m_scanner->next();
}
if (m_scanner->currentLiteral() == "revert" && m_scanner->peekNextToken() == Token::Identifier)
statement = parseRevertStatement(docString);
else if (m_insideModifier && m_scanner->currentLiteral() == "_")
{
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
m_scanner->next();
}
else
statement = parseSimpleStatement(docString);
break;
Expand Down Expand Up @@ -1474,8 +1476,39 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
nodeFactory.markEndPosition();
expectToken(Token::RParen);
auto eventCall = eventCallNodeFactory.createNode<FunctionCall>(eventName, arguments, names);
auto statement = nodeFactory.createNode<EmitStatement>(_docString, eventCall);
return statement;
return nodeFactory.createNode<EmitStatement>(_docString, eventCall);
}

ASTPointer<RevertStatement> Parser::parseRevertStatement(ASTPointer<ASTString> const& _docString)
{
ASTNodeFactory nodeFactory(*this);
solAssert(*expectIdentifierToken() == "revert", "");

ASTNodeFactory errorCallNodeFactory(*this);

if (m_scanner->currentToken() != Token::Identifier)
fatalParserError(1965_error, "Expected error name or path.");

IndexAccessedPath iap;
while (true)
{
iap.path.push_back(parseIdentifier());
if (m_scanner->currentToken() != Token::Period)
break;
m_scanner->next();
}

auto errorName = expressionFromIndexAccessStructure(iap);
expectToken(Token::LParen);

vector<ASTPointer<Expression>> arguments;
vector<ASTPointer<ASTString>> names;
std::tie(arguments, names) = parseFunctionCallArguments();
errorCallNodeFactory.markEndPosition();
nodeFactory.markEndPosition();
expectToken(Token::RParen);
auto errorCall = errorCallNodeFactory.createNode<FunctionCall>(errorName, arguments, names);
return nodeFactory.createNode<RevertStatement>(_docString, errorCall);
}

ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
Expand Down
1 change: 1 addition & 0 deletions libsolidity/parsing/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class Parser: public langutil::ParserBase
ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString);
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
ASTPointer<EmitStatement> parseEmitStatement(ASTPointer<ASTString> const& docString);
ASTPointer<RevertStatement> parseRevertStatement(ASTPointer<ASTString> const& docString);
/// A "simple statement" can be a variable declaration statement or an expression statement.
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
revert X();
// ----
// ParserError 2314: (8-9): Expected ';' but got '('
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error E();
function f() pure {
E();
}
// ----
// TypeError 7757: (35-38): Errors can only be used with revert statements: "revert MyError();".
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error E();
function f() pure {
revert E();
}
5 changes: 5 additions & 0 deletions test/libsolidity/syntaxTests/revertStatement/non_error.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function f() public pure {
revert 1;
}
// ----
// ParserError 2314: (38-39): Expected ';' but got 'Number'
6 changes: 6 additions & 0 deletions test/libsolidity/syntaxTests/revertStatement/regular.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
error E();
contract C {
function f() public pure {
revert E();
}
}
8 changes: 8 additions & 0 deletions test/libsolidity/syntaxTests/revertStatement/scoped.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
contract A {
error E();
}
contract C {
function f() public pure {
revert A.E();
}
}

0 comments on commit 046f830

Please sign in to comment.