mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Type Checker for try/catch.
This commit is contained in:
parent
b5bc52f2a7
commit
8e736a9f49
@ -396,7 +396,7 @@ bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
|
|||||||
_variableDeclaration.value().get()
|
_variableDeclaration.value().get()
|
||||||
);
|
);
|
||||||
// Function arguments are considered to be immediately assigned as well (they are "externally assigned").
|
// Function arguments are considered to be immediately assigned as well (they are "externally assigned").
|
||||||
else if (_variableDeclaration.isCallableParameter() && !_variableDeclaration.isReturnParameter())
|
else if (_variableDeclaration.isCallableOrCatchParameter() && !_variableDeclaration.isReturnParameter())
|
||||||
m_currentNode->variableOccurrences.emplace_back(
|
m_currentNode->variableOccurrences.emplace_back(
|
||||||
_variableDeclaration,
|
_variableDeclaration,
|
||||||
VariableOccurrence::Kind::Assignment,
|
VariableOccurrence::Kind::Assignment,
|
||||||
|
@ -624,6 +624,18 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
|
|||||||
closeCurrentScope();
|
closeCurrentScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeclarationRegistrationHelper::visit(TryCatchClause& _tryCatchClause)
|
||||||
|
{
|
||||||
|
_tryCatchClause.setScope(m_currentScope);
|
||||||
|
enterNewSubScope(_tryCatchClause);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeclarationRegistrationHelper::endVisit(TryCatchClause&)
|
||||||
|
{
|
||||||
|
closeCurrentScope();
|
||||||
|
}
|
||||||
|
|
||||||
bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
|
bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
|
||||||
{
|
{
|
||||||
registerDeclaration(_modifier, true);
|
registerDeclaration(_modifier, true);
|
||||||
|
@ -177,6 +177,8 @@ private:
|
|||||||
bool visit(EnumValue& _value) override;
|
bool visit(EnumValue& _value) override;
|
||||||
bool visit(FunctionDefinition& _function) override;
|
bool visit(FunctionDefinition& _function) override;
|
||||||
void endVisit(FunctionDefinition& _function) override;
|
void endVisit(FunctionDefinition& _function) override;
|
||||||
|
bool visit(TryCatchClause& _tryCatchClause) override;
|
||||||
|
void endVisit(TryCatchClause& _tryCatchClause) override;
|
||||||
bool visit(ModifierDefinition& _modifier) override;
|
bool visit(ModifierDefinition& _modifier) override;
|
||||||
void endVisit(ModifierDefinition& _modifier) override;
|
void endVisit(ModifierDefinition& _modifier) override;
|
||||||
bool visit(FunctionTypeName& _funTypeName) override;
|
bool visit(FunctionTypeName& _funTypeName) override;
|
||||||
|
@ -388,7 +388,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
|
|||||||
", ",
|
", ",
|
||||||
" or "
|
" or "
|
||||||
);
|
);
|
||||||
if (_variable.isCallableParameter())
|
if (_variable.isCallableOrCatchParameter())
|
||||||
errorString +=
|
errorString +=
|
||||||
" for " +
|
" for " +
|
||||||
string(_variable.isReturnParameter() ? "return " : "") +
|
string(_variable.isReturnParameter() ? "return " : "") +
|
||||||
|
@ -118,10 +118,12 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
|||||||
for (auto const& var: m_localVarUseCount)
|
for (auto const& var: m_localVarUseCount)
|
||||||
if (var.second == 0)
|
if (var.second == 0)
|
||||||
{
|
{
|
||||||
if (var.first.second->isCallableParameter())
|
if (var.first.second->isCallableOrCatchParameter())
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
var.first.second->location(),
|
var.first.second->location(),
|
||||||
"Unused function parameter. Remove or comment out the variable name to silence this warning."
|
"Unused " +
|
||||||
|
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
|
||||||
|
" parameter. Remove or comment out the variable name to silence this warning."
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
||||||
|
@ -433,7 +433,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
// * or inside of a struct definition.
|
// * or inside of a struct definition.
|
||||||
if (
|
if (
|
||||||
m_scope->isInterface()
|
m_scope->isInterface()
|
||||||
&& !_variable.isCallableParameter()
|
&& !_variable.isCallableOrCatchParameter()
|
||||||
&& !m_insideStruct
|
&& !m_insideStruct
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
|
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
|
||||||
@ -743,6 +743,133 @@ bool TypeChecker::visit(IfStatement const& _ifStatement)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
||||||
|
{
|
||||||
|
FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
|
||||||
|
if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_tryStatement.externalCall().location(),
|
||||||
|
"Try can only be used with external function calls and contract creation calls."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionType const& functionType = dynamic_cast<FunctionType const&>(*externalCall->expression().annotation().type);
|
||||||
|
if (
|
||||||
|
functionType.kind() != FunctionType::Kind::External &&
|
||||||
|
functionType.kind() != FunctionType::Kind::Creation &&
|
||||||
|
functionType.kind() != FunctionType::Kind::DelegateCall
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
_tryStatement.externalCall().location(),
|
||||||
|
"Try can only be used with external function calls and contract creation calls."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
externalCall->annotation().tryCall = true;
|
||||||
|
|
||||||
|
solAssert(_tryStatement.clauses().size() >= 2, "");
|
||||||
|
solAssert(_tryStatement.clauses().front(), "");
|
||||||
|
|
||||||
|
TryCatchClause const& successClause = *_tryStatement.clauses().front();
|
||||||
|
if (successClause.parameters())
|
||||||
|
{
|
||||||
|
TypePointers returnTypes =
|
||||||
|
m_evmVersion.supportsReturndata() ?
|
||||||
|
functionType.returnParameterTypes() :
|
||||||
|
functionType.returnParameterTypesWithoutDynamicTypes();
|
||||||
|
std::vector<ASTPointer<VariableDeclaration>> const& parameters =
|
||||||
|
successClause.parameters()->parameters();
|
||||||
|
if (returnTypes.size() != parameters.size())
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
successClause.location(),
|
||||||
|
"Function returns " +
|
||||||
|
to_string(functionType.returnParameterTypes().size()) +
|
||||||
|
" values, but returns clause has " +
|
||||||
|
to_string(parameters.size()) +
|
||||||
|
" variables."
|
||||||
|
);
|
||||||
|
size_t len = min(returnTypes.size(), parameters.size());
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
solAssert(returnTypes[i], "");
|
||||||
|
if (parameters[i] && *parameters[i]->annotation().type != *returnTypes[i])
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
parameters[i]->location(),
|
||||||
|
"Invalid type, expected " +
|
||||||
|
returnTypes[i]->toString(false) +
|
||||||
|
" but got " +
|
||||||
|
parameters[i]->annotation().type->toString() +
|
||||||
|
"."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TryCatchClause const* errorClause = nullptr;
|
||||||
|
TryCatchClause const* lowLevelClause = nullptr;
|
||||||
|
for (size_t i = 1; i < _tryStatement.clauses().size(); ++i)
|
||||||
|
{
|
||||||
|
TryCatchClause const& clause = *_tryStatement.clauses()[i];
|
||||||
|
if (clause.errorName() == "")
|
||||||
|
{
|
||||||
|
if (lowLevelClause)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
clause.location(),
|
||||||
|
SecondarySourceLocation{}.append("The first clause is here:", lowLevelClause->location()),
|
||||||
|
"This try statement already has a low-level catch clause."
|
||||||
|
);
|
||||||
|
lowLevelClause = &clause;
|
||||||
|
if (clause.parameters() && !clause.parameters()->parameters().empty())
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
clause.parameters()->parameters().size() != 1 ||
|
||||||
|
*clause.parameters()->parameters().front()->type() != *TypeProvider::bytesMemory()
|
||||||
|
)
|
||||||
|
m_errorReporter.typeError(clause.location(), "Expected `catch (bytes memory ...) { ... }` or `catch { ... }`.");
|
||||||
|
if (!m_evmVersion.supportsReturndata())
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
clause.location(),
|
||||||
|
"This catch clause type cannot be used on the selected EVM version (" +
|
||||||
|
m_evmVersion.name() +
|
||||||
|
"). You need at least a Byzantium-compatible EVM or use `catch { ... }`."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (clause.errorName() == "Error")
|
||||||
|
{
|
||||||
|
if (!m_evmVersion.supportsReturndata())
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
clause.location(),
|
||||||
|
"This catch clause type cannot be used on the selected EVM version (" +
|
||||||
|
m_evmVersion.name() +
|
||||||
|
"). You need at least a Byzantium-compatible EVM or use `catch { ... }`."
|
||||||
|
);
|
||||||
|
|
||||||
|
if (errorClause)
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
clause.location(),
|
||||||
|
SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()),
|
||||||
|
"This try statement already has an \"Error\" catch clause."
|
||||||
|
);
|
||||||
|
errorClause = &clause;
|
||||||
|
if (
|
||||||
|
!clause.parameters() ||
|
||||||
|
clause.parameters()->parameters().size() != 1 ||
|
||||||
|
*clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory()
|
||||||
|
)
|
||||||
|
m_errorReporter.typeError(clause.location(), "Expected `catch Error(string memory ...) { ... }`.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
clause.location(),
|
||||||
|
"Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeChecker::visit(WhileStatement const& _whileStatement)
|
bool TypeChecker::visit(WhileStatement const& _whileStatement)
|
||||||
{
|
{
|
||||||
expectType(_whileStatement.condition(), *TypeProvider::boolean());
|
expectType(_whileStatement.condition(), *TypeProvider::boolean());
|
||||||
|
@ -120,6 +120,7 @@ private:
|
|||||||
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;
|
||||||
|
void endVisit(TryStatement const& _tryStatement) override;
|
||||||
bool visit(WhileStatement const& _whileStatement) override;
|
bool visit(WhileStatement const& _whileStatement) override;
|
||||||
bool visit(ForStatement const& _forStatement) override;
|
bool visit(ForStatement const& _forStatement) override;
|
||||||
void endVisit(Return const& _return) override;
|
void endVisit(Return const& _return) override;
|
||||||
|
@ -452,12 +452,9 @@ bool VariableDeclaration::isLocalVariable() const
|
|||||||
dynamic_cast<ForStatement const*>(s);
|
dynamic_cast<ForStatement const*>(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isCallableParameter() const
|
bool VariableDeclaration::isCallableOrCatchParameter() const
|
||||||
{
|
{
|
||||||
// TODO Adjust for catch params.
|
if (isReturnParameter() || isTryCatchParameter())
|
||||||
// I think catch params should return true here.
|
|
||||||
// some of the error strings would need to be adjusted.
|
|
||||||
if (isReturnParameter() || dynamic_cast<TryCatchClause const*>(scope()))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
|
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
|
||||||
@ -476,8 +473,7 @@ bool VariableDeclaration::isCallableParameter() const
|
|||||||
|
|
||||||
bool VariableDeclaration::isLocalOrReturn() const
|
bool VariableDeclaration::isLocalOrReturn() const
|
||||||
{
|
{
|
||||||
// TODO adjust for catch params
|
return isReturnParameter() || (isLocalVariable() && !isCallableOrCatchParameter());
|
||||||
return isReturnParameter() || (isLocalVariable() && !isCallableParameter());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isReturnParameter() const
|
bool VariableDeclaration::isReturnParameter() const
|
||||||
@ -497,9 +493,14 @@ bool VariableDeclaration::isReturnParameter() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VariableDeclaration::isTryCatchParameter() const
|
||||||
|
{
|
||||||
|
return dynamic_cast<TryCatchClause const*>(scope());
|
||||||
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isExternalCallableParameter() const
|
bool VariableDeclaration::isExternalCallableParameter() const
|
||||||
{
|
{
|
||||||
if (!isCallableParameter())
|
if (!isCallableOrCatchParameter())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
|
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
|
||||||
@ -511,7 +512,7 @@ bool VariableDeclaration::isExternalCallableParameter() const
|
|||||||
|
|
||||||
bool VariableDeclaration::isInternalCallableParameter() const
|
bool VariableDeclaration::isInternalCallableParameter() const
|
||||||
{
|
{
|
||||||
if (!isCallableParameter())
|
if (!isCallableOrCatchParameter())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
|
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
|
||||||
@ -523,7 +524,7 @@ bool VariableDeclaration::isInternalCallableParameter() const
|
|||||||
|
|
||||||
bool VariableDeclaration::isLibraryFunctionParameter() const
|
bool VariableDeclaration::isLibraryFunctionParameter() const
|
||||||
{
|
{
|
||||||
if (!isCallableParameter())
|
if (!isCallableOrCatchParameter())
|
||||||
return false;
|
return false;
|
||||||
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
|
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
|
||||||
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
|
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
|
||||||
@ -559,10 +560,10 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
|
|||||||
locations.insert(Location::Storage);
|
locations.insert(Location::Storage);
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
else if (isCallableParameter())
|
else if (isCallableOrCatchParameter())
|
||||||
{
|
{
|
||||||
set<Location> locations{ Location::Memory };
|
set<Location> locations{ Location::Memory };
|
||||||
if (isInternalCallableParameter() || isLibraryFunctionParameter())
|
if (isInternalCallableParameter() || isLibraryFunctionParameter() || isTryCatchParameter())
|
||||||
locations.insert(Location::Storage);
|
locations.insert(Location::Storage);
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
@ -738,9 +738,12 @@ public:
|
|||||||
/// (or function type name or event) or declared inside a function body.
|
/// (or function type name or event) or declared inside a function body.
|
||||||
bool isLocalVariable() const;
|
bool isLocalVariable() const;
|
||||||
/// @returns true if this variable is a parameter or return parameter of a function.
|
/// @returns true if this variable is a parameter or return parameter of a function.
|
||||||
bool isCallableParameter() const;
|
bool isCallableOrCatchParameter() const;
|
||||||
/// @returns true if this variable is a return parameter of a function.
|
/// @returns true if this variable is a return parameter of a function.
|
||||||
bool isReturnParameter() const;
|
bool isReturnParameter() const;
|
||||||
|
/// @returns true if this variable is a parameter of the success or failure clausse
|
||||||
|
/// of a try/catch statement.
|
||||||
|
bool isTryCatchParameter() const;
|
||||||
/// @returns true if this variable is a local variable or return parameter.
|
/// @returns true if this variable is a local variable or return parameter.
|
||||||
bool isLocalOrReturn() const;
|
bool isLocalOrReturn() const;
|
||||||
/// @returns true if this variable is a parameter (not return parameter) of an external function.
|
/// @returns true if this variable is a parameter (not return parameter) of an external function.
|
||||||
|
Loading…
Reference in New Issue
Block a user