mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2581 from federicobond/improve-override-error
Improve override function error messages
This commit is contained in:
commit
52ccc26494
@ -4,6 +4,7 @@ Features:
|
|||||||
* Parser: Display previous visibility specifier in error if multiple are found.
|
* Parser: Display previous visibility specifier in error if multiple are found.
|
||||||
* Syntax Checker: Support ``pragma experimental <feature>;`` to turn on experimental features.
|
* Syntax Checker: Support ``pragma experimental <feature>;`` to turn on experimental features.
|
||||||
* Metadata: Store experimental flag in metadata CBOR.
|
* Metadata: Store experimental flag in metadata CBOR.
|
||||||
|
* Type Checker: More detailed error message for invalid overrides.
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Parser: Enforce commas between array and tuple elements.
|
* Parser: Enforce commas between array and tuple elements.
|
||||||
|
@ -277,21 +277,10 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
|
|||||||
string const& name = function->name();
|
string const& name = function->name();
|
||||||
if (modifiers.count(name))
|
if (modifiers.count(name))
|
||||||
m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
|
m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
|
||||||
FunctionType functionType(*function);
|
|
||||||
// function should not change the return type
|
|
||||||
for (FunctionDefinition const* overriding: functions[name])
|
for (FunctionDefinition const* overriding: functions[name])
|
||||||
{
|
checkFunctionOverride(*overriding, *function);
|
||||||
FunctionType overridingType(*overriding);
|
|
||||||
if (!overridingType.hasEqualArgumentTypes(functionType))
|
|
||||||
continue;
|
|
||||||
if (
|
|
||||||
overriding->visibility() != function->visibility() ||
|
|
||||||
overriding->isDeclaredConst() != function->isDeclaredConst() ||
|
|
||||||
overriding->isPayable() != function->isPayable() ||
|
|
||||||
overridingType != functionType
|
|
||||||
)
|
|
||||||
m_errorReporter.typeError(overriding->location(), "Override changes extended function signature.");
|
|
||||||
}
|
|
||||||
functions[name].push_back(function);
|
functions[name].push_back(function);
|
||||||
}
|
}
|
||||||
for (ModifierDefinition const* modifier: contract->functionModifiers())
|
for (ModifierDefinition const* modifier: contract->functionModifiers())
|
||||||
@ -308,6 +297,42 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super)
|
||||||
|
{
|
||||||
|
FunctionType functionType(function);
|
||||||
|
FunctionType superType(super);
|
||||||
|
|
||||||
|
if (!functionType.hasEqualArgumentTypes(superType))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (function.visibility() != super.visibility())
|
||||||
|
overrideError(function, super, "Overriding function visibility differs.");
|
||||||
|
|
||||||
|
else if (function.isDeclaredConst() && !super.isDeclaredConst())
|
||||||
|
overrideError(function, super, "Overriding function should not be declared constant.");
|
||||||
|
|
||||||
|
else if (!function.isDeclaredConst() && super.isDeclaredConst())
|
||||||
|
overrideError(function, super, "Overriding function should be declared constant.");
|
||||||
|
|
||||||
|
else if (function.isPayable() && !super.isPayable())
|
||||||
|
overrideError(function, super, "Overriding function should not be declared payable.");
|
||||||
|
|
||||||
|
else if (!function.isPayable() && super.isPayable())
|
||||||
|
overrideError(function, super, "Overriding function should be declared payable.");
|
||||||
|
|
||||||
|
else if (functionType != superType)
|
||||||
|
overrideError(function, super, "Overriding function return types differ.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(
|
||||||
|
function.location(),
|
||||||
|
SecondarySourceLocation().append("Overriden function is here:", super.location()),
|
||||||
|
message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
|
void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
|
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
|
||||||
@ -1949,4 +1974,3 @@ void TypeChecker::requireLValue(Expression const& _expression)
|
|||||||
else if (!_expression.annotation().isLValue)
|
else if (!_expression.annotation().isLValue)
|
||||||
m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue.");
|
m_errorReporter.typeError(_expression.location(), "Expression has to be an lvalue.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,9 @@ private:
|
|||||||
/// arguments and that there is at most one constructor.
|
/// arguments and that there is at most one constructor.
|
||||||
void checkContractDuplicateFunctions(ContractDefinition const& _contract);
|
void checkContractDuplicateFunctions(ContractDefinition const& _contract);
|
||||||
void checkContractIllegalOverrides(ContractDefinition const& _contract);
|
void checkContractIllegalOverrides(ContractDefinition const& _contract);
|
||||||
|
/// Reports a type error with an appropiate message if overriden function signature differs.
|
||||||
|
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
|
||||||
|
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
|
||||||
void checkContractAbstractFunctions(ContractDefinition const& _contract);
|
void checkContractAbstractFunctions(ContractDefinition const& _contract);
|
||||||
void checkContractAbstractConstructors(ContractDefinition const& _contract);
|
void checkContractAbstractConstructors(ContractDefinition const& _contract);
|
||||||
/// Checks that different functions with external visibility end up having different
|
/// Checks that different functions with external visibility end up having different
|
||||||
|
@ -371,6 +371,15 @@ string FunctionDefinition::externalSignature() const
|
|||||||
return FunctionType(*this).externalSignature();
|
return FunctionType(*this).externalSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string FunctionDefinition::fullyQualifiedName() const
|
||||||
|
{
|
||||||
|
auto const* contract = dynamic_cast<ContractDefinition const*>(scope());
|
||||||
|
solAssert(contract, "Enclosing scope of function definition was not set.");
|
||||||
|
|
||||||
|
auto fname = name().empty() ? "<fallback>" : name();
|
||||||
|
return sourceUnitName() + ":" + contract->name() + "." + fname;
|
||||||
|
}
|
||||||
|
|
||||||
FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
|
FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
|
||||||
{
|
{
|
||||||
if (!m_annotation)
|
if (!m_annotation)
|
||||||
|
@ -613,6 +613,7 @@ public:
|
|||||||
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
|
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
|
||||||
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
|
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
|
||||||
Block const& body() const { solAssert(m_body, ""); return *m_body; }
|
Block const& body() const { solAssert(m_body, ""); return *m_body; }
|
||||||
|
std::string fullyQualifiedName() const;
|
||||||
virtual bool isVisibleInContract() const override
|
virtual bool isVisibleInContract() const override
|
||||||
{
|
{
|
||||||
return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
|
return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
|
||||||
|
@ -75,8 +75,8 @@ public:
|
|||||||
|
|
||||||
void typeError(
|
void typeError(
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
SecondarySourceLocation const& _secondaryLocation,
|
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
|
||||||
std::string const& _description
|
std::string const& _description = std::string()
|
||||||
);
|
);
|
||||||
|
|
||||||
void typeError(SourceLocation const& _location, std::string const& _description);
|
void typeError(SourceLocation const& _location, std::string const& _description);
|
||||||
|
@ -924,16 +924,25 @@ BOOST_AUTO_TEST_CASE(illegal_override_visibility)
|
|||||||
contract B { function f() internal {} }
|
contract B { function f() internal {} }
|
||||||
contract C is B { function f() public {} }
|
contract C is B { function f() public {} }
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Override changes extended function signature.");
|
CHECK_ERROR(text, TypeError, "Overriding function visibility differs");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(illegal_override_constness)
|
BOOST_AUTO_TEST_CASE(illegal_override_remove_constness)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
contract B { function f() constant {} }
|
contract B { function f() constant {} }
|
||||||
contract C is B { function f() {} }
|
contract C is B { function f() {} }
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Override changes extended function signature.");
|
CHECK_ERROR(text, TypeError, "Overriding function should be declared constant.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(illegal_override_add_constness)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract B { function f() {} }
|
||||||
|
contract C is B { function f() constant {} }
|
||||||
|
)";
|
||||||
|
CHECK_ERROR(text, TypeError, "Overriding function should not be declared constant.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(complex_inheritance)
|
BOOST_AUTO_TEST_CASE(complex_inheritance)
|
||||||
@ -2491,7 +2500,7 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types)
|
|||||||
function f(uint a) returns (uint8) { }
|
function f(uint a) returns (uint8) { }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(sourceCode, TypeError, "Override changes extended function signature.");
|
CHECK_ERROR(sourceCode, TypeError, "Overriding function return types differ");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(multiple_constructors)
|
BOOST_AUTO_TEST_CASE(multiple_constructors)
|
||||||
@ -4732,7 +4741,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable)
|
|||||||
contract B { function f() payable {} }
|
contract B { function f() payable {} }
|
||||||
contract C is B { function f() {} }
|
contract C is B { function f() {} }
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Override changes extended function signature.");
|
CHECK_ERROR(text, TypeError, "Overriding function should be declared payable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
|
BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
|
||||||
@ -4741,7 +4750,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable)
|
|||||||
contract B { function f() {} }
|
contract B { function f() {} }
|
||||||
contract C is B { function f() payable {} }
|
contract C is B { function f() payable {} }
|
||||||
)";
|
)";
|
||||||
CHECK_ERROR(text, TypeError, "Override changes extended function signature.");
|
CHECK_ERROR(text, TypeError, "Overriding function should not be declared payable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_variable_mixin)
|
BOOST_AUTO_TEST_CASE(function_variable_mixin)
|
||||||
|
Loading…
Reference in New Issue
Block a user