mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #11037 from ethereum/revertStatement
Revert statement.
This commit is contained in:
commit
4fd61927af
@ -381,7 +381,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
|
||||
/**
|
||||
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
|
||||
*/
|
||||
identifier: Identifier | From | Error;
|
||||
identifier: Identifier | From | Error | Revert;
|
||||
|
||||
literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;
|
||||
booleanLiteral: True | False;
|
||||
@ -422,6 +422,7 @@ statement:
|
||||
| tryStatement
|
||||
| returnStatement
|
||||
| emitStatement
|
||||
| revertStatement
|
||||
| assemblyStatement
|
||||
;
|
||||
|
||||
@ -459,6 +460,10 @@ returnStatement: Return expression? Semicolon;
|
||||
* An emit statement. The contained expression needs to refer to an event.
|
||||
*/
|
||||
emitStatement: Emit expression callArgumentList Semicolon;
|
||||
/**
|
||||
* A revert statement. The contained expression needs to refer to an error.
|
||||
*/
|
||||
revertStatement: Revert expression callArgumentList Semicolon;
|
||||
/**
|
||||
* An inline assembly block.
|
||||
* The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and
|
||||
|
@ -30,6 +30,7 @@ Else: 'else';
|
||||
Emit: 'emit';
|
||||
Enum: 'enum';
|
||||
Error: 'error'; // not a real keyword
|
||||
Revert: 'revert'; // not a real keyword
|
||||
Event: 'event';
|
||||
External: 'external';
|
||||
Fallback: 'fallback';
|
||||
|
@ -30,6 +30,8 @@ set(sources
|
||||
analysis/OverrideChecker.h
|
||||
analysis/PostTypeChecker.cpp
|
||||
analysis/PostTypeChecker.h
|
||||
analysis/PostTypeContractLevelChecker.cpp
|
||||
analysis/PostTypeContractLevelChecker.h
|
||||
analysis/ReferencesResolver.cpp
|
||||
analysis/ReferencesResolver.h
|
||||
analysis/Scoper.cpp
|
||||
|
@ -433,24 +433,6 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
|
||||
);
|
||||
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)
|
||||
|
@ -236,6 +236,16 @@ bool ControlFlowBuilder::visit(Throw const& _throw)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControlFlowBuilder::visit(RevertStatement const& _revert)
|
||||
{
|
||||
solAssert(!!m_currentNode, "");
|
||||
solAssert(!!m_revertNode, "");
|
||||
visitNode(_revert);
|
||||
connect(m_currentNode, m_revertNode);
|
||||
m_currentNode = newLabel();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ControlFlowBuilder::visit(PlaceholderStatement const&)
|
||||
{
|
||||
solAssert(!!m_currentNode, "");
|
||||
|
@ -56,6 +56,7 @@ private:
|
||||
bool visit(Break const&) override;
|
||||
bool visit(Continue const&) override;
|
||||
bool visit(Throw const&) override;
|
||||
bool visit(RevertStatement const&) override;
|
||||
bool visit(PlaceholderStatement const&) override;
|
||||
bool visit(FunctionCall const& _functionCall) override;
|
||||
bool visit(ModifierInvocation const& _modifierInvocation) override;
|
||||
|
@ -125,6 +125,8 @@ bool FunctionCallGraphBuilder::visit(FunctionCall const& _functionCall)
|
||||
// change at runtime). All we can do is to add an edge to the dispatch which in turn has
|
||||
// edges to all functions could possibly be called.
|
||||
add(m_currentNode, CallGraph::SpecialNode::InternalDispatch);
|
||||
else if (functionType->kind() == FunctionType::Kind::Error)
|
||||
m_graph.usedErrors.insert(&dynamic_cast<ErrorDefinition const&>(functionType->declaration()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -65,8 +65,8 @@ public:
|
||||
|
||||
private:
|
||||
FunctionCallGraphBuilder(ContractDefinition const& _contract):
|
||||
m_contract(_contract),
|
||||
m_graph{{}, {}, {}} {}
|
||||
m_contract(_contract)
|
||||
{}
|
||||
|
||||
bool visit(FunctionCall const& _functionCall) override;
|
||||
bool visit(EmitStatement const& _emitStatement) override;
|
||||
|
@ -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);
|
||||
@ -281,42 +291,59 @@ private:
|
||||
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(RevertStatement const& _revertStatement) override
|
||||
{
|
||||
m_currentStatement = &_revertStatement;
|
||||
return true;
|
||||
}
|
||||
|
||||
void endVisit(RevertStatement const&) override
|
||||
{
|
||||
m_currentStatement = nullptr;
|
||||
}
|
||||
|
||||
bool visit(FunctionCall const& _functionCall) override
|
||||
{
|
||||
if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
|
||||
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\"."
|
||||
);
|
||||
if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
|
||||
if (auto const* functionType = dynamic_cast<FunctionType 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
|
||||
@ -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));
|
||||
}
|
||||
|
@ -83,6 +83,9 @@ private:
|
||||
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;
|
||||
|
72
libsolidity/analysis/PostTypeContractLevelChecker.cpp
Normal file
72
libsolidity/analysis/PostTypeContractLevelChecker.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
/**
|
||||
* Component that verifies overloads, abstract contracts, function clashes and others
|
||||
* checks at contract or function level.
|
||||
*/
|
||||
|
||||
#include <libsolidity/analysis/PostTypeContractLevelChecker.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
bool PostTypeContractLevelChecker::check(SourceUnit const& _sourceUnit)
|
||||
{
|
||||
bool noErrors = true;
|
||||
for (auto* contract: ASTNode::filteredNodes<ContractDefinition>(_sourceUnit.nodes()))
|
||||
if (!check(*contract))
|
||||
noErrors = false;
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
bool PostTypeContractLevelChecker::check(ContractDefinition const& _contract)
|
||||
{
|
||||
solAssert(
|
||||
_contract.annotation().creationCallGraph.set() &&
|
||||
_contract.annotation().deployedCallGraph.set(),
|
||||
""
|
||||
);
|
||||
|
||||
map<uint32_t, map<string, SourceLocation>> errorHashes;
|
||||
for (ErrorDefinition const* error: _contract.interfaceErrors())
|
||||
{
|
||||
string signature = error->functionType(true)->externalSignature();
|
||||
uint32_t hash = selectorFromSignature32(signature);
|
||||
// Fail if there is a different signature for the same hash.
|
||||
if (!errorHashes[hash].empty() && !errorHashes[hash].count(signature))
|
||||
{
|
||||
SourceLocation& otherLocation = errorHashes[hash].begin()->second;
|
||||
m_errorReporter.typeError(
|
||||
4883_error,
|
||||
error->nameLocation(),
|
||||
SecondarySourceLocation{}.append("This error has a different signature but the same hash: ", otherLocation),
|
||||
"Error signature hash collision for " + error->functionType(true)->externalSignature()
|
||||
);
|
||||
}
|
||||
else
|
||||
errorHashes[hash][signature] = error->location();
|
||||
}
|
||||
|
||||
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
||||
}
|
56
libsolidity/analysis/PostTypeContractLevelChecker.h
Normal file
56
libsolidity/analysis/PostTypeContractLevelChecker.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
/**
|
||||
* Component that verifies properties at contract level, after the type checker has run.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
}
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
/**
|
||||
* Component that verifies properties at contract level, after the type checker has run.
|
||||
*/
|
||||
class PostTypeContractLevelChecker
|
||||
{
|
||||
public:
|
||||
explicit PostTypeContractLevelChecker(langutil::ErrorReporter& _errorReporter):
|
||||
m_errorReporter(_errorReporter)
|
||||
{}
|
||||
|
||||
/// Performs checks on the given source ast.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
bool check(SourceUnit const& _sourceUnit);
|
||||
|
||||
private:
|
||||
/// Performs checks on the given contract.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
bool check(ContractDefinition const& _contract);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
};
|
||||
|
||||
}
|
@ -1167,6 +1167,18 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
|
||||
m_errorReporter.typeError(9292_error, _emit.eventCall().expression().location(), "Expression has to be an event invocation.");
|
||||
}
|
||||
|
||||
void TypeChecker::endVisit(RevertStatement const& _revert)
|
||||
{
|
||||
FunctionCall const& errorCall = _revert.errorCall();
|
||||
if (
|
||||
*errorCall.annotation().kind != FunctionCallKind::FunctionCall ||
|
||||
type(errorCall.expression())->category() != Type::Category::Function ||
|
||||
dynamic_cast<FunctionType const&>(*type(errorCall.expression())).kind() != FunctionType::Kind::Error
|
||||
)
|
||||
m_errorReporter.typeError(1885_error, errorCall.expression().location(), "Expression has to be an error.");
|
||||
}
|
||||
|
||||
|
||||
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
{
|
||||
if (!_statement.initialValue())
|
||||
|
@ -134,6 +134,7 @@ private:
|
||||
bool visit(ForStatement const& _forStatement) override;
|
||||
void endVisit(Return const& _return) override;
|
||||
void endVisit(EmitStatement const& _emit) override;
|
||||
void endVisit(RevertStatement const& _revert) override;
|
||||
bool visit(VariableDeclarationStatement const& _variable) override;
|
||||
void endVisit(ExpressionStatement const& _statement) override;
|
||||
bool visit(Conditional const& _conditional) override;
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
|
||||
#include <libsolidity/ast/CallGraph.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/ast/AST_accept.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
@ -174,12 +175,19 @@ vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() cons
|
||||
});
|
||||
}
|
||||
|
||||
vector<ErrorDefinition const*> ContractDefinition::interfaceErrors() const
|
||||
vector<ErrorDefinition const*> ContractDefinition::interfaceErrors(bool _requireCallGraph) const
|
||||
{
|
||||
set<ErrorDefinition const*, CompareByID> result;
|
||||
// TODO add all referenced errors
|
||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||
result += filteredNodes<ErrorDefinition>(contract->m_subNodes);
|
||||
solAssert(annotation().creationCallGraph.set() == annotation().deployedCallGraph.set(), "");
|
||||
if (_requireCallGraph)
|
||||
solAssert(annotation().creationCallGraph.set(), "");
|
||||
if (annotation().creationCallGraph.set())
|
||||
{
|
||||
result += (*annotation().creationCallGraph)->usedErrors;
|
||||
result += (*annotation().deployedCallGraph)->usedErrors;
|
||||
}
|
||||
return convertContainer<vector<ErrorDefinition const*>>(move(result));
|
||||
}
|
||||
|
||||
|
@ -515,7 +515,8 @@ public:
|
||||
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;
|
||||
/// @param _requireCallGraph if false, do not fail if the call graph has not been computed yet.
|
||||
std::vector<ErrorDefinition const*> interfaceErrors(bool _requireCallGraph = true) const;
|
||||
bool isInterface() const { return m_contractKind == ContractKind::Interface; }
|
||||
bool isLibrary() const { return m_contractKind == ContractKind::Library; }
|
||||
|
||||
@ -1726,6 +1727,31 @@ public:
|
||||
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))
|
||||
{
|
||||
solAssert(m_errorCall, "");
|
||||
}
|
||||
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)
|
||||
*/
|
||||
|
@ -675,6 +675,14 @@ bool ASTJsonConverter::visit(EmitStatement const& _node)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(RevertStatement const& _node)
|
||||
{
|
||||
setJsonNode(_node, "RevertStatement", {
|
||||
make_pair("errorCall", toJson(_node.errorCall()))
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
|
||||
{
|
||||
Json::Value varDecs(Json::arrayValue);
|
||||
|
@ -107,6 +107,7 @@ public:
|
||||
bool visit(Return const& _node) override;
|
||||
bool visit(Throw const& _node) override;
|
||||
bool visit(EmitStatement const& _node) override;
|
||||
bool visit(RevertStatement const& _node) override;
|
||||
bool visit(VariableDeclarationStatement const& _node) override;
|
||||
bool visit(ExpressionStatement const& _node) override;
|
||||
bool visit(Conditional const& _node) override;
|
||||
|
@ -191,6 +191,8 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
|
||||
return createReturn(_json);
|
||||
if (nodeType == "EmitStatement")
|
||||
return createEmitStatement(_json);
|
||||
if (nodeType == "RevertStatement")
|
||||
return createRevertStatement(_json);
|
||||
if (nodeType == "Throw")
|
||||
return createThrow(_json);
|
||||
if (nodeType == "VariableDeclarationStatement")
|
||||
@ -747,6 +749,15 @@ ASTPointer<EmitStatement> ASTJsonImporter::createEmitStatement(Json::Value const
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<RevertStatement> ASTJsonImporter::createRevertStatement(Json::Value const& _node)
|
||||
{
|
||||
return createASTNode<RevertStatement>(
|
||||
_node,
|
||||
nullOrASTString(_node, "documentation"),
|
||||
createFunctionCall(member(_node, "errorCall"))
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<VariableDeclarationStatement> ASTJsonImporter::createVariableDeclarationStatement(Json::Value const& _node)
|
||||
{
|
||||
std::vector<ASTPointer<VariableDeclaration>> variables;
|
||||
|
@ -107,6 +107,7 @@ private:
|
||||
ASTPointer<Return> createReturn(Json::Value const& _node);
|
||||
ASTPointer<Throw> createThrow(Json::Value const& _node);
|
||||
ASTPointer<EmitStatement> createEmitStatement(Json::Value const& _node);
|
||||
ASTPointer<RevertStatement> createRevertStatement(Json::Value const& _node);
|
||||
ASTPointer<VariableDeclarationStatement> createVariableDeclarationStatement(Json::Value const& _node);
|
||||
ASTPointer<ExpressionStatement> createExpressionStatement(Json::Value const& _node);
|
||||
ASTPointer<Conditional> createConditional(Json::Value const& _node);
|
||||
|
@ -25,10 +25,6 @@ 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
|
||||
/// declaration value is an identifier. Works only for constant variable declarations.
|
||||
/// Returns nullptr if an identifier in the chain is not referencing a constant variable declaration.
|
||||
|
@ -90,6 +90,7 @@ public:
|
||||
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); }
|
||||
@ -144,6 +145,7 @@ public:
|
||||
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); }
|
||||
@ -220,6 +222,7 @@ public:
|
||||
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); }
|
||||
@ -274,6 +277,7 @@ public:
|
||||
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); }
|
||||
|
@ -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))
|
||||
|
@ -67,6 +67,9 @@ struct CallGraph
|
||||
|
||||
/// Events that may get emitted by functions present in the graph.
|
||||
std::set<EventDefinition const*, ASTNode::CompareByID> emittedEvents;
|
||||
|
||||
/// Errors that are used by functions present in the graph.
|
||||
std::set<ErrorDefinition const*, ASTNode::CompareByID> usedErrors;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -103,6 +103,22 @@ void CompilerUtils::revertWithStringData(Type const& _argumentType)
|
||||
m_context << Instruction::REVERT;
|
||||
}
|
||||
|
||||
void CompilerUtils::revertWithError(
|
||||
string const& _signature,
|
||||
vector<Type const*> const& _parameterTypes,
|
||||
vector<Type const*> const& _argumentTypes
|
||||
)
|
||||
{
|
||||
fetchFreeMemoryPointer();
|
||||
m_context << util::selectorFromSignature(_signature);
|
||||
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||
m_context << u256(4) << Instruction::ADD;
|
||||
// Stack: <arguments...> <mem pos of encoding start>
|
||||
abiEncode(_argumentTypes, _parameterTypes);
|
||||
toSizeAfterFreeMemoryPointer();
|
||||
m_context << Instruction::REVERT;
|
||||
}
|
||||
|
||||
void CompilerUtils::returnDataToArray()
|
||||
{
|
||||
if (m_context.evmVersion().supportsReturndata())
|
||||
|
@ -69,6 +69,12 @@ public:
|
||||
/// Stack post:
|
||||
void revertWithStringData(Type const& _argumentType);
|
||||
|
||||
void revertWithError(
|
||||
std::string const& _errorName,
|
||||
std::vector<Type const*> const& _parameterTypes,
|
||||
std::vector<Type const*> const& _argumentTypes
|
||||
);
|
||||
|
||||
/// Allocates a new array and copies the return data to it.
|
||||
/// If the EVM does not support return data, creates an empty array.
|
||||
void returnDataToArray();
|
||||
|
@ -1281,6 +1281,15 @@ bool ContractCompiler::visit(EmitStatement const& _emit)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContractCompiler::visit(RevertStatement const& _revert)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _revert);
|
||||
StackHeightChecker checker(m_context);
|
||||
compileExpression(_revert.errorCall());
|
||||
checker.check();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
|
||||
|
@ -117,6 +117,7 @@ private:
|
||||
bool visit(Return const& _return) override;
|
||||
bool visit(Throw const& _throw) override;
|
||||
bool visit(EmitStatement const& _emit) override;
|
||||
bool visit(RevertStatement const& _revert) override;
|
||||
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
||||
bool visit(ExpressionStatement const& _expressionStatement) override;
|
||||
bool visit(PlaceholderStatement const&) override;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <libsolidity/codegen/LValue.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTUtils.h>
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libevmasm/GasMeter.h>
|
||||
@ -914,7 +915,20 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::Error:
|
||||
{
|
||||
solAssert(false, "");
|
||||
_functionCall.expression().accept(*this);
|
||||
vector<Type const*> argumentTypes;
|
||||
for (ASTPointer<Expression const> const& arg: _functionCall.sortedArguments())
|
||||
{
|
||||
arg->accept(*this);
|
||||
argumentTypes.push_back(arg->annotation().type);
|
||||
}
|
||||
solAssert(dynamic_cast<ErrorDefinition const*>(&function.declaration()), "");
|
||||
utils().revertWithError(
|
||||
function.externalSignature(),
|
||||
function.parameterTypes(),
|
||||
argumentTypes
|
||||
);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::BlockHash:
|
||||
{
|
||||
@ -1868,6 +1882,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
solAssert(
|
||||
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||
dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||
category == Type::Category::TypeType ||
|
||||
category == Type::Category::Module,
|
||||
""
|
||||
|
@ -1045,7 +1045,14 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::Error:
|
||||
{
|
||||
solAssert(false, "");
|
||||
ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_functionCall.expression()));
|
||||
solAssert(error, "");
|
||||
revertWithError(
|
||||
error->functionType(true)->externalSignature(),
|
||||
error->functionType(true)->parameterTypes(),
|
||||
_functionCall.sortedArguments()
|
||||
);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Assert:
|
||||
case FunctionType::Kind::Require:
|
||||
@ -1199,41 +1206,19 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
case FunctionType::Kind::Revert:
|
||||
{
|
||||
solAssert(arguments.size() == parameterTypes.size(), "");
|
||||
if (arguments.empty())
|
||||
solAssert(arguments.size() <= 1, "");
|
||||
solAssert(
|
||||
arguments.empty() ||
|
||||
arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),
|
||||
"");
|
||||
if (m_context.revertStrings() == RevertStrings::Strip || arguments.empty())
|
||||
m_code << "revert(0, 0)\n";
|
||||
else
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
|
||||
if (m_context.revertStrings() == RevertStrings::Strip)
|
||||
m_code << "revert(0, 0)\n";
|
||||
else
|
||||
{
|
||||
solAssert(type(*arguments.front()).isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),"");
|
||||
|
||||
Whiskers templ(R"({
|
||||
let <pos> := <allocateUnbounded>()
|
||||
mstore(<pos>, <hash>)
|
||||
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
|
||||
revert(<pos>, sub(<end>, <pos>))
|
||||
})");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("hash", util::selectorFromSignature("Error(string)").str());
|
||||
templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
|
||||
templ(
|
||||
"argumentVars",
|
||||
joinHumanReadablePrefixed(IRVariable{*arguments.front()}.stackSlots())
|
||||
);
|
||||
templ("encode", m_context.abiFunctions().tupleEncoder(
|
||||
{&type(*arguments.front())},
|
||||
{TypeProvider::stringMemory()}
|
||||
));
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
}
|
||||
|
||||
revertWithError(
|
||||
"Error(string)",
|
||||
{TypeProvider::stringMemory()},
|
||||
{arguments.front()}
|
||||
);
|
||||
break;
|
||||
}
|
||||
// Array creation using new
|
||||
@ -1995,7 +1980,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration),
|
||||
"Error not found"
|
||||
);
|
||||
// the call will do the resolving
|
||||
// The function call will resolve the selector.
|
||||
break;
|
||||
case FunctionType::Kind::DelegateCall:
|
||||
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
|
||||
@ -2043,6 +2028,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
solAssert(
|
||||
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||
dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||
category == Type::Category::TypeType ||
|
||||
category == Type::Category::Module,
|
||||
""
|
||||
@ -3140,6 +3126,38 @@ void IRGeneratorForStatements::rethrow()
|
||||
m_code << "revert(0, 0) // rethrow\n"s;
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::revertWithError(
|
||||
string const& _signature,
|
||||
vector<Type const*> const& _parameterTypes,
|
||||
vector<ASTPointer<Expression const>> const& _errorArguments
|
||||
)
|
||||
{
|
||||
Whiskers templ(R"({
|
||||
let <pos> := <allocateUnbounded>()
|
||||
mstore(<pos>, <hash>)
|
||||
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
|
||||
revert(<pos>, sub(<end>, <pos>))
|
||||
})");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("hash", util::selectorFromSignature(_signature).str());
|
||||
templ("allocateUnbounded", m_utils.allocateUnboundedFunction());
|
||||
|
||||
vector<string> errorArgumentVars;
|
||||
vector<Type const*> errorArgumentTypes;
|
||||
for (ASTPointer<Expression const> const& arg: _errorArguments)
|
||||
{
|
||||
errorArgumentVars += IRVariable(*arg).stackSlots();
|
||||
solAssert(arg->annotation().type, "");
|
||||
errorArgumentTypes.push_back(arg->annotation().type);
|
||||
}
|
||||
templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
|
||||
templ("encode", m_context.abiFunctions().tupleEncoder(errorArgumentTypes, _parameterTypes));
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
|
||||
|
||||
bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
|
||||
{
|
||||
_clause.block().accept(*this);
|
||||
|
@ -106,6 +106,15 @@ private:
|
||||
/// Generates code to rethrow an exception.
|
||||
void rethrow();
|
||||
|
||||
/// Generates code to revert with an error. The error arguments are assumed to
|
||||
/// be already evaluated and available in local IRVariables, but not yet
|
||||
/// converted.
|
||||
void revertWithError(
|
||||
std::string const& _signature,
|
||||
std::vector<Type const*> const& _parameterTypes,
|
||||
std::vector<ASTPointer<Expression const>> const& _errorArguments
|
||||
);
|
||||
|
||||
void handleVariableReference(
|
||||
VariableDeclaration const& _variable,
|
||||
Expression const& _referencingExpression
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <libsolidity/analysis/GlobalContext.h>
|
||||
#include <libsolidity/analysis/NameAndTypeResolver.h>
|
||||
#include <libsolidity/analysis/PostTypeChecker.h>
|
||||
#include <libsolidity/analysis/PostTypeContractLevelChecker.h>
|
||||
#include <libsolidity/analysis/StaticAnalyzer.h>
|
||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||
#include <libsolidity/analysis/Scoper.h>
|
||||
@ -433,6 +434,11 @@ bool CompilerStack::analyze()
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
if (noErrors)
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast && !PostTypeContractLevelChecker{m_errorReporter}.check(*source->ast))
|
||||
noErrors = false;
|
||||
|
||||
// Check that immutable variables are never read in c'tors and assigned
|
||||
// exactly once
|
||||
if (noErrors)
|
||||
|
@ -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;
|
||||
@ -1474,8 +1476,38 @@ 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);
|
||||
|
||||
solAssert(m_scanner->currentToken() == Token::Identifier, "");
|
||||
|
||||
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)
|
||||
|
@ -127,6 +127,7 @@ private:
|
||||
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(
|
||||
|
@ -117,9 +117,12 @@ done < <(
|
||||
"^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (1684|2837|3716|3997|5333|6275|6281|6933|7319)|^==== Source:" \
|
||||
"${ROOT_DIR}/test/libsolidity/syntaxTests" \
|
||||
"${ROOT_DIR}/test/libsolidity/semanticTests" |
|
||||
grep -v -E 'comments/.*_direction_override.*.sol' |
|
||||
grep -v -E 'literals/.*_direction_override.*.sol'
|
||||
# Skipping the unicode tests as I couldn't adapt the lexical grammar to recursively counting RLO/LRO/PDF's.
|
||||
grep -v -E 'comments/.*_direction_override.*.sol' |
|
||||
grep -v -E 'literals/.*_direction_override.*.sol' |
|
||||
# Skipping a test with "revert E;" because ANTLR cannot distinguish it from
|
||||
# a variable declaration.
|
||||
grep -v -E 'revertStatement/non_called.sol'
|
||||
)
|
||||
|
||||
YUL_FILES=()
|
||||
|
@ -0,0 +1,24 @@
|
||||
error E(uint a);
|
||||
library L {
|
||||
error E(uint a, uint b);
|
||||
}
|
||||
interface I {
|
||||
error E(uint a, uint b, uint c);
|
||||
}
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert E(1);
|
||||
}
|
||||
function g() public pure {
|
||||
revert L.E(1, 2);
|
||||
}
|
||||
function h() public pure {
|
||||
revert I.E(1, 2, 3);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
// g() -> FAILURE, hex"85208890", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"0000000000000000000000000000000000000000000000000000000000000002"
|
||||
// h() -> FAILURE, hex"7924ea7c", hex"0000000000000000000000000000000000000000000000000000000000000001", hex"0000000000000000000000000000000000000000000000000000000000000002", hex"0000000000000000000000000000000000000000000000000000000000000003"
|
10
test/libsolidity/semanticTests/errors/named_error_args.sol
Normal file
10
test/libsolidity/semanticTests/errors/named_error_args.sol
Normal file
@ -0,0 +1,10 @@
|
||||
error E(uint a, uint b);
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert E({b: 7, a: 2});
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"85208890", hex"0000000000000000000000000000000000000000000000000000000000000002", hex"0000000000000000000000000000000000000000000000000000000000000007"
|
18
test/libsolidity/semanticTests/errors/panic_via_import.sol
Normal file
18
test/libsolidity/semanticTests/errors/panic_via_import.sol
Normal file
@ -0,0 +1,18 @@
|
||||
==== Source: s1.sol ====
|
||||
error E(uint);
|
||||
==== Source: s2.sol ====
|
||||
import { E as Panic } from "s1.sol";
|
||||
contract C {
|
||||
error E(uint);
|
||||
function a() public pure {
|
||||
revert Panic(1);
|
||||
}
|
||||
function b() public pure {
|
||||
revert E(1);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// a() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
// b() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
|
12
test/libsolidity/semanticTests/errors/revert_conversion.sol
Normal file
12
test/libsolidity/semanticTests/errors/revert_conversion.sol
Normal file
@ -0,0 +1,12 @@
|
||||
error E(string a, uint[] b);
|
||||
contract C {
|
||||
uint[] x;
|
||||
function f() public {
|
||||
x.push(7);
|
||||
revert E("abc", x);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"59e4d4df", 0x40, 0x80, 3, "abc", 1, 7
|
10
test/libsolidity/semanticTests/errors/simple.sol
Normal file
10
test/libsolidity/semanticTests/errors/simple.sol
Normal file
@ -0,0 +1,10 @@
|
||||
error E(uint a, uint b);
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert E(2, 7);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"85208890", 2, 7
|
20
test/libsolidity/semanticTests/errors/using_structs.sol
Normal file
20
test/libsolidity/semanticTests/errors/using_structs.sol
Normal file
@ -0,0 +1,20 @@
|
||||
pragma abicoder v2;
|
||||
struct S { uint a; string b; }
|
||||
error E(uint a, S s, uint b);
|
||||
contract C {
|
||||
S s;
|
||||
function f(bool c) public {
|
||||
if (c) {
|
||||
s.a = 9;
|
||||
s.b = "abc";
|
||||
revert E(2, s, 7);
|
||||
} else {
|
||||
revert E({b: 7, a: 2, s: S({b: "abc", a: 9})});
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(bool): true -> FAILURE, hex"e96e07f0", hex"0000000000000000000000000000000000000000000000000000000000000002", hex"0000000000000000000000000000000000000000000000000000000000000060", hex"0000000000000000000000000000000000000000000000000000000000000007", hex"0000000000000000000000000000000000000000000000000000000000000009", hex"0000000000000000000000000000000000000000000000000000000000000040", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"6162630000000000000000000000000000000000000000000000000000000000"
|
||||
// f(bool): false -> FAILURE, hex"e96e07f0", hex"0000000000000000000000000000000000000000000000000000000000000002", hex"0000000000000000000000000000000000000000000000000000000000000060", hex"0000000000000000000000000000000000000000000000000000000000000007", hex"0000000000000000000000000000000000000000000000000000000000000009", hex"0000000000000000000000000000000000000000000000000000000000000040", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"6162630000000000000000000000000000000000000000000000000000000000"
|
18
test/libsolidity/semanticTests/errors/via_contract_type.sol
Normal file
18
test/libsolidity/semanticTests/errors/via_contract_type.sol
Normal file
@ -0,0 +1,18 @@
|
||||
contract A {
|
||||
error E(uint);
|
||||
}
|
||||
contract X {
|
||||
error E(string);
|
||||
}
|
||||
contract B is A {
|
||||
function f() public pure { revert E(1); }
|
||||
function g() public pure { revert A.E(1); }
|
||||
function h() public pure { revert X.E("abc"); }
|
||||
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
// g() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
// h() -> FAILURE, hex"3e9992c9", hex"0000000000000000000000000000000000000000000000000000000000000020", hex"0000000000000000000000000000000000000000000000000000000000000003", hex"6162630000000000000000000000000000000000000000000000000000000000"
|
25
test/libsolidity/semanticTests/errors/via_import.sol
Normal file
25
test/libsolidity/semanticTests/errors/via_import.sol
Normal file
@ -0,0 +1,25 @@
|
||||
==== Source: s1.sol ====
|
||||
error E(uint);
|
||||
==== Source: s2.sol ====
|
||||
import "s1.sol" as S;
|
||||
==== Source: s3.sol ====
|
||||
import "s1.sol" as S;
|
||||
import "s2.sol" as T;
|
||||
import "s1.sol";
|
||||
contract C {
|
||||
function x() public pure {
|
||||
revert E(1);
|
||||
}
|
||||
function y() public pure {
|
||||
revert S.E(2);
|
||||
}
|
||||
function z() public pure {
|
||||
revert T.S.E(3);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// x() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
// y() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000002"
|
||||
// z() -> FAILURE, hex"002ff067", hex"0000000000000000000000000000000000000000000000000000000000000003"
|
10
test/libsolidity/semanticTests/errors/weird_name.sol
Normal file
10
test/libsolidity/semanticTests/errors/weird_name.sol
Normal file
@ -0,0 +1,10 @@
|
||||
error error(uint a);
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert error(2);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"b48fb6cf", hex"0000000000000000000000000000000000000000000000000000000000000002"
|
17
test/libsolidity/semanticTests/reverts/error_struct.sol
Normal file
17
test/libsolidity/semanticTests/reverts/error_struct.sol
Normal file
@ -0,0 +1,17 @@
|
||||
struct error { uint error; }
|
||||
contract C {
|
||||
error test();
|
||||
error _struct;
|
||||
function f() public {
|
||||
revert test();
|
||||
}
|
||||
function g(uint x) public returns (uint) {
|
||||
_struct.error = x;
|
||||
return _struct.error;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f() -> FAILURE, hex"f8a8fd6d"
|
||||
// g(uint256): 7 -> 7
|
21
test/libsolidity/syntaxTests/errors/duplicate.sol
Normal file
21
test/libsolidity/syntaxTests/errors/duplicate.sol
Normal file
@ -0,0 +1,21 @@
|
||||
==== Source: A ====
|
||||
|
||||
error E();
|
||||
|
||||
==== Source: B ====
|
||||
|
||||
error E();
|
||||
|
||||
==== Source: C ====
|
||||
|
||||
import "A" as A;
|
||||
import "B" as B;
|
||||
|
||||
contract Test {
|
||||
function f() public pure {
|
||||
revert A.E();
|
||||
}
|
||||
function g() public pure {
|
||||
revert B.E();
|
||||
}
|
||||
}
|
@ -3,4 +3,4 @@ contract test {
|
||||
error tgeo();
|
||||
}
|
||||
// ----
|
||||
// TypeError 4883: (0-52): Error signature hash collision for tgeo()
|
||||
// TypeError 4883: (43-47): Error signature hash collision for tgeo()
|
||||
|
@ -0,0 +1,14 @@
|
||||
library L {
|
||||
error gsf();
|
||||
}
|
||||
contract test {
|
||||
error tgeo();
|
||||
function f(bool a) public {
|
||||
if (a)
|
||||
revert L.gsf();
|
||||
else
|
||||
revert tgeo();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4883: (57-61): Error signature hash collision for tgeo()
|
8
test/libsolidity/syntaxTests/errors/require_custom.sol
Normal file
8
test/libsolidity/syntaxTests/errors/require_custom.sol
Normal file
@ -0,0 +1,8 @@
|
||||
error E(uint a, uint b);
|
||||
contract C {
|
||||
function f(bool c) public pure {
|
||||
require(c, E(2, 7));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9322: (83-90): No matching declaration found after argument-dependent lookup.
|
@ -0,0 +1,8 @@
|
||||
error E(uint a, uint b);
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert(E(2, 7));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9322: (77-83): No matching declaration found after argument-dependent lookup.
|
10
test/libsolidity/syntaxTests/errors/unreachable.sol
Normal file
10
test/libsolidity/syntaxTests/errors/unreachable.sol
Normal file
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
error E();
|
||||
uint x = 2;
|
||||
function f() public {
|
||||
revert E();
|
||||
x = 4;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 5740: (98-103): Unreachable code.
|
@ -0,0 +1,3 @@
|
||||
revert X();
|
||||
// ----
|
||||
// ParserError 2314: (8-9): Expected ';' but got '('
|
@ -0,0 +1,6 @@
|
||||
error E();
|
||||
function f() pure {
|
||||
E();
|
||||
}
|
||||
// ----
|
||||
// TypeError 7757: (35-38): Errors can only be used with revert statements: "revert MyError();".
|
@ -0,0 +1,4 @@
|
||||
error E();
|
||||
function f() pure {
|
||||
revert E();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
error E();
|
||||
function f() public pure {
|
||||
revert E;
|
||||
}
|
||||
// ----
|
||||
// ParserError 2314: (50-51): Expected '(' but got ';'
|
@ -0,0 +1,5 @@
|
||||
function f() public pure {
|
||||
revert 1;
|
||||
}
|
||||
// ----
|
||||
// ParserError 2314: (38-39): Expected ';' but got 'Number'
|
6
test/libsolidity/syntaxTests/revertStatement/regular.sol
Normal file
6
test/libsolidity/syntaxTests/revertStatement/regular.sol
Normal file
@ -0,0 +1,6 @@
|
||||
error E();
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert E();
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
event E();
|
||||
function f() public pure {
|
||||
revert E();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 1885: (74-75): Expression has to be an error.
|
@ -0,0 +1,8 @@
|
||||
error revert();
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert revert();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2319: (0-15): This declaration shadows a builtin symbol.
|
8
test/libsolidity/syntaxTests/revertStatement/scoped.sol
Normal file
8
test/libsolidity/syntaxTests/revertStatement/scoped.sol
Normal file
@ -0,0 +1,8 @@
|
||||
contract A {
|
||||
error E();
|
||||
}
|
||||
contract C {
|
||||
function f() public pure {
|
||||
revert A.E();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
error f(uint, uint);
|
||||
contract C {
|
||||
function f(uint) public {
|
||||
revert f(10);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (38-91): This declaration shadows an existing declaration.
|
||||
// TypeError 1885: (79-80): Expression has to be an error.
|
@ -0,0 +1,8 @@
|
||||
struct S { uint x; }
|
||||
contract C {
|
||||
function f() public {
|
||||
revert S(10);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 1885: (75-76): Expression has to be an error.
|
Loading…
Reference in New Issue
Block a user