mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #3821 from ethereum/warn-constructor-override
Warn constructor override
This commit is contained in:
commit
b52614116e
@ -9,6 +9,7 @@ Features:
|
||||
* Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc).
|
||||
* Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``.
|
||||
* Optimizer: Optimize across ``mload`` if ``msize()`` is not used.
|
||||
* Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature.
|
||||
* Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature).
|
||||
* General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature.
|
||||
* Inheritance: Error when using empty parenthesis for base class constructors that require arguments as experimental 0.5.0 feature.
|
||||
|
@ -1034,9 +1034,12 @@ the base constructors. This can be done in two ways::
|
||||
constructor(uint _x) public { x = _x; }
|
||||
}
|
||||
|
||||
contract Derived is Base(7) {
|
||||
constructor(uint _y) Base(_y * _y) public {
|
||||
}
|
||||
contract Derived1 is Base(7) {
|
||||
constructor(uint _y) public {}
|
||||
}
|
||||
|
||||
contract Derived2 is Base {
|
||||
constructor(uint _y) Base(_y * _y) public {}
|
||||
}
|
||||
|
||||
One way is directly in the inheritance list (``is Base(7)``). The other is in
|
||||
@ -1046,8 +1049,9 @@ do it is more convenient if the constructor argument is a
|
||||
constant and defines the behaviour of the contract or
|
||||
describes it. The second way has to be used if the
|
||||
constructor arguments of the base depend on those of the
|
||||
derived contract. If, as in this silly example, both places
|
||||
are used, the modifier-style argument takes precedence.
|
||||
derived contract. Arguments have to be given either in the
|
||||
inheritance list or in modifier-style in the derived constuctor.
|
||||
Specifying arguments in both places is an error.
|
||||
|
||||
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
|
||||
|
||||
|
@ -101,7 +101,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
|
||||
checkContractDuplicateEvents(_contract);
|
||||
checkContractIllegalOverrides(_contract);
|
||||
checkContractAbstractFunctions(_contract);
|
||||
checkContractAbstractConstructors(_contract);
|
||||
checkContractBaseConstructorArguments(_contract);
|
||||
|
||||
FunctionDefinition const* function = _contract.constructor();
|
||||
if (function)
|
||||
@ -291,42 +291,90 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract)
|
||||
void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract)
|
||||
{
|
||||
set<ContractDefinition const*> argumentsNeeded;
|
||||
// check that we get arguments for all base constructors that need it.
|
||||
// If not mark the contract as abstract (not fully implemented)
|
||||
|
||||
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
||||
for (ContractDefinition const* contract: bases)
|
||||
if (FunctionDefinition const* constructor = contract->constructor())
|
||||
if (contract != &_contract && !constructor->parameters().empty())
|
||||
argumentsNeeded.insert(contract);
|
||||
|
||||
// Determine the arguments that are used for the base constructors.
|
||||
for (ContractDefinition const* contract: bases)
|
||||
{
|
||||
if (FunctionDefinition const* constructor = contract->constructor())
|
||||
for (auto const& modifier: constructor->modifiers())
|
||||
{
|
||||
auto baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
&dereference(*modifier->name())
|
||||
);
|
||||
if (baseContract)
|
||||
argumentsNeeded.erase(baseContract);
|
||||
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name()));
|
||||
if (baseContract && baseContract->constructor() && !modifier->arguments().empty())
|
||||
annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
|
||||
}
|
||||
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
||||
{
|
||||
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
|
||||
solAssert(baseContract, "");
|
||||
if (base->arguments() && !base->arguments()->empty())
|
||||
argumentsNeeded.erase(baseContract);
|
||||
|
||||
if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
|
||||
annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
|
||||
}
|
||||
}
|
||||
if (!argumentsNeeded.empty())
|
||||
for (ContractDefinition const* contract: argumentsNeeded)
|
||||
_contract.annotation().unimplementedFunctions.push_back(contract->constructor());
|
||||
|
||||
// check that we get arguments for all base constructors that need it.
|
||||
// If not mark the contract as abstract (not fully implemented)
|
||||
for (ContractDefinition const* contract: bases)
|
||||
if (FunctionDefinition const* constructor = contract->constructor())
|
||||
if (contract != &_contract && !constructor->parameters().empty())
|
||||
if (!_contract.annotation().baseConstructorArguments.count(constructor))
|
||||
_contract.annotation().unimplementedFunctions.push_back(constructor);
|
||||
}
|
||||
|
||||
void TypeChecker::annotateBaseConstructorArguments(
|
||||
ContractDefinition const& _currentContract,
|
||||
FunctionDefinition const* _baseConstructor,
|
||||
ASTNode const* _argumentNode
|
||||
)
|
||||
{
|
||||
bool const v050 = _currentContract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||
|
||||
solAssert(_baseConstructor, "");
|
||||
solAssert(_argumentNode, "");
|
||||
|
||||
auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
|
||||
std::make_pair(_baseConstructor, _argumentNode)
|
||||
);
|
||||
if (!insertionResult.second)
|
||||
{
|
||||
ASTNode const* previousNode = insertionResult.first->second;
|
||||
|
||||
SourceLocation const* mainLocation = nullptr;
|
||||
SecondarySourceLocation ssl;
|
||||
|
||||
if (
|
||||
_currentContract.location().contains(previousNode->location()) ||
|
||||
_currentContract.location().contains(_argumentNode->location())
|
||||
)
|
||||
{
|
||||
mainLocation = &previousNode->location();
|
||||
ssl.append("Second constructor call is here:", _argumentNode->location());
|
||||
}
|
||||
else
|
||||
{
|
||||
mainLocation = &_currentContract.location();
|
||||
ssl.append("First constructor call is here: ", _argumentNode->location());
|
||||
ssl.append("Second constructor call is here: ", previousNode->location());
|
||||
}
|
||||
|
||||
if (v050)
|
||||
m_errorReporter.declarationError(
|
||||
*mainLocation,
|
||||
ssl,
|
||||
"Base constructor arguments given twice."
|
||||
);
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
*mainLocation,
|
||||
"Base constructor arguments given twice.",
|
||||
ssl
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
|
||||
|
@ -73,7 +73,12 @@ private:
|
||||
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 checkContractAbstractConstructors(ContractDefinition const& _contract);
|
||||
void checkContractBaseConstructorArguments(ContractDefinition const& _contract);
|
||||
void annotateBaseConstructorArguments(
|
||||
ContractDefinition const& _currentContract,
|
||||
FunctionDefinition const* _baseConstructor,
|
||||
ASTNode const* _argumentNode
|
||||
);
|
||||
/// Checks that different functions with external visibility end up having different
|
||||
/// external argument types (i.e. different signature).
|
||||
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
|
||||
|
@ -90,6 +90,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota
|
||||
/// List of contracts this contract creates, i.e. which need to be compiled first.
|
||||
/// Also includes all contracts from @a linearizedBaseContracts.
|
||||
std::set<ContractDefinition const*> contractDependencies;
|
||||
/// Mapping containing the nodes that define the arguments for base constructors.
|
||||
/// These can either be inheritance specifiers or modifier invocations.
|
||||
std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments;
|
||||
};
|
||||
|
||||
struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
|
||||
|
@ -135,34 +135,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
|
||||
{
|
||||
solAssert(!_contract.isLibrary(), "Tried to initialize library.");
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||
// Determine the arguments that are used for the base constructors.
|
||||
std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
||||
for (ContractDefinition const* contract: bases)
|
||||
{
|
||||
if (FunctionDefinition const* constructor = contract->constructor())
|
||||
for (auto const& modifier: constructor->modifiers())
|
||||
{
|
||||
auto baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
modifier->name()->annotation().referencedDeclaration
|
||||
);
|
||||
if (baseContract && !modifier->arguments().empty())
|
||||
if (m_baseArguments.count(baseContract->constructor()) == 0)
|
||||
m_baseArguments[baseContract->constructor()] = &modifier->arguments();
|
||||
}
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
||||
{
|
||||
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
base->name().annotation().referencedDeclaration
|
||||
);
|
||||
solAssert(baseContract, "");
|
||||
m_baseArguments = &_contract.annotation().baseConstructorArguments;
|
||||
|
||||
if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty())
|
||||
m_baseArguments[baseContract->constructor()] = base->arguments();
|
||||
}
|
||||
}
|
||||
// Initialization of state variables in base-to-derived order.
|
||||
for (ContractDefinition const* contract: boost::adaptors::reverse(bases))
|
||||
for (ContractDefinition const* contract: boost::adaptors::reverse(
|
||||
_contract.annotation().linearizedBaseContracts
|
||||
))
|
||||
initializeStateVariables(*contract);
|
||||
|
||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||
@ -236,8 +215,14 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc
|
||||
FunctionType constructorType(_constructor);
|
||||
if (!constructorType.parameterTypes().empty())
|
||||
{
|
||||
solAssert(m_baseArguments.count(&_constructor), "");
|
||||
std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
|
||||
solAssert(m_baseArguments, "");
|
||||
solAssert(m_baseArguments->count(&_constructor), "");
|
||||
std::vector<ASTPointer<Expression>> const* arguments = nullptr;
|
||||
ASTNode const* baseArgumentNode = m_baseArguments->at(&_constructor);
|
||||
if (auto inheritanceSpecifier = dynamic_cast<InheritanceSpecifier const*>(baseArgumentNode))
|
||||
arguments = inheritanceSpecifier->arguments();
|
||||
else if (auto modifierInvocation = dynamic_cast<ModifierInvocation const*>(baseArgumentNode))
|
||||
arguments = &modifierInvocation->arguments();
|
||||
solAssert(arguments, "");
|
||||
solAssert(arguments->size() == constructorType.parameterTypes().size(), "");
|
||||
for (unsigned i = 0; i < arguments->size(); ++i)
|
||||
|
@ -135,7 +135,7 @@ private:
|
||||
FunctionDefinition const* m_currentFunction = nullptr;
|
||||
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
|
||||
// arguments for base constructors, filled in derived-to-base order
|
||||
std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
|
||||
std::map<FunctionDefinition const*, ASTNode const*> const* m_baseArguments;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -5191,7 +5191,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base)
|
||||
}
|
||||
uint public m_i;
|
||||
}
|
||||
contract Derived is Base(2) {
|
||||
contract Derived is Base {
|
||||
function Derived(uint i) Base(i)
|
||||
{}
|
||||
}
|
||||
@ -5211,10 +5211,10 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base)
|
||||
}
|
||||
uint public m_i;
|
||||
}
|
||||
contract Base1 is Base(3) {
|
||||
contract Base1 is Base {
|
||||
function Base1(uint k) Base(k*k) {}
|
||||
}
|
||||
contract Derived is Base(3), Base1(2) {
|
||||
contract Derived is Base, Base1 {
|
||||
function Derived(uint i) Base(i) Base1(i)
|
||||
{}
|
||||
}
|
||||
@ -5235,7 +5235,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap)
|
||||
uint public m_i;
|
||||
}
|
||||
contract Base1 is Base(3) {}
|
||||
contract Derived is Base(2), Base1 {
|
||||
contract Derived is Base, Base1 {
|
||||
function Derived(uint i) Base(i) {}
|
||||
}
|
||||
contract Final is Derived(4) {
|
||||
|
@ -0,0 +1,3 @@
|
||||
contract A { constructor() public { } }
|
||||
contract B1 is A { constructor() A() public { } }
|
||||
contract B2 is A { constructor() A public { } }
|
@ -0,0 +1,9 @@
|
||||
contract Base {
|
||||
constructor(uint) public { }
|
||||
}
|
||||
contract Base1 is Base(3) {}
|
||||
contract Derived is Base, Base1 {
|
||||
constructor(uint i) Base(i) public {}
|
||||
}
|
||||
// ----
|
||||
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,5 @@
|
||||
contract A { constructor(uint) public { } }
|
||||
contract B is A(2) { constructor() public { } }
|
||||
contract C is B { constructor() A(3) public { } }
|
||||
// ----
|
||||
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,7 @@
|
||||
pragma experimental "v0.5.0";
|
||||
|
||||
contract A { constructor(uint) public { } }
|
||||
contract B is A(2) { constructor() public { } }
|
||||
contract C is B { constructor() A(3) public { } }
|
||||
// ----
|
||||
// DeclarationError: Base constructor arguments given twice.
|
@ -0,0 +1,4 @@
|
||||
contract A { constructor(uint) public { } }
|
||||
contract B is A(2) { constructor() A(3) public { } }
|
||||
// ----
|
||||
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,6 @@
|
||||
pragma experimental "v0.5.0";
|
||||
|
||||
contract A { constructor(uint) public { } }
|
||||
contract B is A(2) { constructor() A(3) public { } }
|
||||
// ----
|
||||
// DeclarationError: Base constructor arguments given twice.
|
@ -0,0 +1,7 @@
|
||||
contract C { constructor(uint) public {} }
|
||||
contract A is C(2) {}
|
||||
contract B is C(2) {}
|
||||
contract D is A, B { constructor() C(3) public {} }
|
||||
// ----
|
||||
// Warning: Base constructor arguments given twice.
|
||||
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,6 @@
|
||||
contract C { constructor(uint) public {} }
|
||||
contract A is C(2) {}
|
||||
contract B is C(2) {}
|
||||
contract D is A, B {}
|
||||
// ----
|
||||
// Warning: Base constructor arguments given twice.
|
@ -0,0 +1,6 @@
|
||||
contract C { constructor(uint) public {} }
|
||||
contract A is C { constructor() C(2) public {} }
|
||||
contract B is C { constructor() C(2) public {} }
|
||||
contract D is A, B { }
|
||||
// ----
|
||||
// Warning: Base constructor arguments given twice.
|
Loading…
Reference in New Issue
Block a user