From 0bd9db480f54d846abbdb4218e000f08ead4fbd5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 17:35:14 +0100 Subject: Add skeleton for contract level checker. --- libsolidity/analysis/ContractLevelChecker.cpp | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 libsolidity/analysis/ContractLevelChecker.cpp (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp new file mode 100644 index 00000000..4f32eb62 --- /dev/null +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -0,0 +1,37 @@ +/* + 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 . +*/ +/** + * Component that verifies overloads, abstract contracts, function clashes and others + * checks at contract or function level. + */ + +#include +#include + +#include + + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace dev::solidity; + + +bool ContractLevelChecker::check(ContractDefinition const&) +{ + return Error::containsOnlyWarnings(m_errorReporter.errors()); +} -- cgit v1.2.3 From d054a3b85d70508611c4f8a1e093610ab95cfa37 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 17:39:57 +0100 Subject: Move duplication checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 87 ++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 4f32eb62..a2e76edf 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -31,7 +31,92 @@ using namespace langutil; using namespace dev::solidity; -bool ContractLevelChecker::check(ContractDefinition const&) +bool ContractLevelChecker::check(ContractDefinition const& _contract) { + checkContractDuplicateFunctions(_contract); + checkContractDuplicateEvents(_contract); + return Error::containsOnlyWarnings(m_errorReporter.errors()); } + +void ContractLevelChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract) +{ + /// Checks that two functions with the same name defined in this contract have different + /// argument types and that there is at most one constructor. + map> functions; + FunctionDefinition const* constructor = nullptr; + FunctionDefinition const* fallback = nullptr; + for (FunctionDefinition const* function: _contract.definedFunctions()) + if (function->isConstructor()) + { + if (constructor) + m_errorReporter.declarationError( + function->location(), + SecondarySourceLocation().append("Another declaration is here:", constructor->location()), + "More than one constructor defined." + ); + constructor = function; + } + else if (function->isFallback()) + { + if (fallback) + m_errorReporter.declarationError( + function->location(), + SecondarySourceLocation().append("Another declaration is here:", fallback->location()), + "Only one fallback function is allowed." + ); + fallback = function; + } + else + { + solAssert(!function->name().empty(), ""); + functions[function->name()].push_back(function); + } + + findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); +} + +void ContractLevelChecker::checkContractDuplicateEvents(ContractDefinition const& _contract) +{ + /// Checks that two events with the same name defined in this contract have different + /// argument types + map> events; + for (EventDefinition const* event: _contract.events()) + events[event->name()].push_back(event); + + findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); +} + +template +void ContractLevelChecker::findDuplicateDefinitions(map> const& _definitions, string _message) +{ + for (auto const& it: _definitions) + { + vector const& overloads = it.second; + set reported; + for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) + { + SecondarySourceLocation ssl; + + for (size_t j = i + 1; j < overloads.size(); ++j) + if (FunctionType(*overloads[i]).asCallableFunction(false)->hasEqualParameterTypes( + *FunctionType(*overloads[j]).asCallableFunction(false)) + ) + { + ssl.append("Other declaration is here:", overloads[j]->location()); + reported.insert(j); + } + + if (ssl.infos.size() > 0) + { + ssl.limitSize(_message); + + m_errorReporter.declarationError( + overloads[i]->location(), + ssl, + _message + ); + } + } + } +} -- cgit v1.2.3 From 89cf6a5a38b02c3da1377279fcd98d690a58a978 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 17:44:38 +0100 Subject: Move override checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 83 +++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index a2e76edf..bf132d63 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -35,6 +35,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) { checkContractDuplicateFunctions(_contract); checkContractDuplicateEvents(_contract); + checkContractIllegalOverrides(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -120,3 +121,85 @@ void ContractLevelChecker::findDuplicateDefinitions(map> const } } } + +void ContractLevelChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) +{ + // TODO unify this at a later point. for this we need to put the constness and the access specifier + // into the types + map> functions; + map modifiers; + + // We search from derived to base, so the stored item causes the error. + for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) + { + for (FunctionDefinition const* function: contract->definedFunctions()) + { + if (function->isConstructor()) + continue; // constructors can neither be overridden nor override anything + string const& name = function->name(); + if (modifiers.count(name)) + m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier."); + + for (FunctionDefinition const* overriding: functions[name]) + checkFunctionOverride(*overriding, *function); + + functions[name].push_back(function); + } + for (ModifierDefinition const* modifier: contract->functionModifiers()) + { + string const& name = modifier->name(); + ModifierDefinition const*& override = modifiers[name]; + if (!override) + override = modifier; + else if (ModifierType(*override) != ModifierType(*modifier)) + m_errorReporter.typeError(override->location(), "Override changes modifier signature."); + if (!functions[name].empty()) + m_errorReporter.typeError(override->location(), "Override changes modifier to function."); + } + } +} + +void ContractLevelChecker::checkFunctionOverride(FunctionDefinition const& _function, FunctionDefinition const& _super) +{ + FunctionTypePointer functionType = FunctionType(_function).asCallableFunction(false); + FunctionTypePointer superType = FunctionType(_super).asCallableFunction(false); + + if (!functionType->hasEqualParameterTypes(*superType)) + return; + if (!functionType->hasEqualReturnTypes(*superType)) + overrideError(_function, _super, "Overriding function return types differ."); + + if (!_function.annotation().superFunction) + _function.annotation().superFunction = &_super; + + if (_function.visibility() != _super.visibility()) + { + // Visibility change from external to public is fine. + // Any other change is disallowed. + if (!( + _super.visibility() == FunctionDefinition::Visibility::External && + _function.visibility() == FunctionDefinition::Visibility::Public + )) + overrideError(_function, _super, "Overriding function visibility differs."); + } + if (_function.stateMutability() != _super.stateMutability()) + overrideError( + _function, + _super, + "Overriding function changes state mutability from \"" + + stateMutabilityToString(_super.stateMutability()) + + "\" to \"" + + stateMutabilityToString(_function.stateMutability()) + + "\"." + ); +} + +void ContractLevelChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message) +{ + m_errorReporter.typeError( + function.location(), + SecondarySourceLocation().append("Overridden function is here:", super.location()), + message + ); +} + -- cgit v1.2.3 From 57a62429c9d0be4b3a8a9cc98fa7eb46a6015165 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 17:51:57 +0100 Subject: Move abstract function check. --- libsolidity/analysis/ContractLevelChecker.cpp | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index bf132d63..74ac665f 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -24,6 +24,8 @@ #include +#include + using namespace std; using namespace dev; @@ -36,6 +38,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkContractDuplicateFunctions(_contract); checkContractDuplicateEvents(_contract); checkContractIllegalOverrides(_contract); + checkContractAbstractFunctions(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -203,3 +206,45 @@ void ContractLevelChecker::overrideError(FunctionDefinition const& function, Fun ); } +void ContractLevelChecker::checkContractAbstractFunctions(ContractDefinition const& _contract) +{ + // Mapping from name to function definition (exactly one per argument type equality class) and + // flag to indicate whether it is fully implemented. + using FunTypeAndFlag = std::pair; + map> functions; + + // Search from base to derived + for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) + for (FunctionDefinition const* function: contract->definedFunctions()) + { + // Take constructors out of overload hierarchy + if (function->isConstructor()) + continue; + auto& overloads = functions[function->name()]; + FunctionTypePointer funType = make_shared(*function)->asCallableFunction(false); + auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) + { + return funType->hasEqualParameterTypes(*_funAndFlag.first); + }); + if (it == overloads.end()) + overloads.push_back(make_pair(funType, function->isImplemented())); + else if (it->second) + { + if (!function->isImplemented()) + m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract"); + } + else if (function->isImplemented()) + it->second = true; + } + + // Set to not fully implemented if at least one flag is false. + for (auto const& it: functions) + for (auto const& funAndFlag: it.second) + if (!funAndFlag.second) + { + FunctionDefinition const* function = dynamic_cast(&funAndFlag.first->declaration()); + solAssert(function, ""); + _contract.annotation().unimplementedFunctions.push_back(function); + break; + } +} -- cgit v1.2.3 From 2c2c976697d22400d37f3b26064b6b1f6fc91dba Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 17:58:15 +0100 Subject: Move base constructor argument checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 91 +++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 74ac665f..f41e569f 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -39,6 +39,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkContractDuplicateEvents(_contract); checkContractIllegalOverrides(_contract); checkContractAbstractFunctions(_contract); + checkContractBaseConstructorArguments(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -248,3 +249,93 @@ void ContractLevelChecker::checkContractAbstractFunctions(ContractDefinition con break; } } + + +void ContractLevelChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) +{ + vector const& bases = _contract.annotation().linearizedBaseContracts; + + // 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()) + if (auto baseContract = dynamic_cast( + modifier->name()->annotation().referencedDeclaration + )) + { + if (modifier->arguments()) + { + if (baseContract->constructor()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); + } + else + m_errorReporter.declarationError( + modifier->location(), + "Modifier-style base constructor call without arguments." + ); + } + + for (ASTPointer const& base: contract->baseContracts()) + { + ContractDefinition const* baseContract = dynamic_cast( + base->name().annotation().referencedDeclaration + ); + solAssert(baseContract, ""); + + if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); + } + } + + // 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 ContractLevelChecker::annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode +) +{ + 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()); + } + + m_errorReporter.declarationError( + *mainLocation, + ssl, + "Base constructor arguments given twice." + ); + } + +} -- cgit v1.2.3 From b610be4882ca77defb73e35eda93996ca790f9f4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 18:23:58 +0100 Subject: Rename functions. --- libsolidity/analysis/ContractLevelChecker.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index f41e569f..0ddb198f 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -35,16 +35,16 @@ using namespace dev::solidity; bool ContractLevelChecker::check(ContractDefinition const& _contract) { - checkContractDuplicateFunctions(_contract); - checkContractDuplicateEvents(_contract); - checkContractIllegalOverrides(_contract); - checkContractAbstractFunctions(_contract); - checkContractBaseConstructorArguments(_contract); + checkDuplicateFunctions(_contract); + checkDuplicateEvents(_contract); + checkIllegalOverrides(_contract); + checkAbstractFunctions(_contract); + checkBaseConstructorArguments(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } -void ContractLevelChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract) +void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _contract) { /// Checks that two functions with the same name defined in this contract have different /// argument types and that there is at most one constructor. @@ -81,7 +81,7 @@ void ContractLevelChecker::checkContractDuplicateFunctions(ContractDefinition co findDuplicateDefinitions(functions, "Function with same name and arguments defined twice."); } -void ContractLevelChecker::checkContractDuplicateEvents(ContractDefinition const& _contract) +void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract) { /// Checks that two events with the same name defined in this contract have different /// argument types @@ -126,7 +126,7 @@ void ContractLevelChecker::findDuplicateDefinitions(map> const } } -void ContractLevelChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) +void ContractLevelChecker::checkIllegalOverrides(ContractDefinition const& _contract) { // TODO unify this at a later point. for this we need to put the constness and the access specifier // into the types @@ -207,7 +207,7 @@ void ContractLevelChecker::overrideError(FunctionDefinition const& function, Fun ); } -void ContractLevelChecker::checkContractAbstractFunctions(ContractDefinition const& _contract) +void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _contract) { // Mapping from name to function definition (exactly one per argument type equality class) and // flag to indicate whether it is fully implemented. @@ -251,7 +251,7 @@ void ContractLevelChecker::checkContractAbstractFunctions(ContractDefinition con } -void ContractLevelChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) +void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract) { vector const& bases = _contract.annotation().linearizedBaseContracts; -- cgit v1.2.3 From 2a85152463e3990d4695978613efddbcebe29b5b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 18:26:53 +0100 Subject: Move constructor checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 0ddb198f..879903de 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -40,6 +40,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkIllegalOverrides(_contract); checkAbstractFunctions(_contract); checkBaseConstructorArguments(_contract); + checkConstructor(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -339,3 +340,22 @@ void ContractLevelChecker::annotateBaseConstructorArguments( } } + +void ContractLevelChecker::checkConstructor(ContractDefinition const& _contract) +{ + FunctionDefinition const* constructor = _contract.constructor(); + if (!constructor) + return; + + if (!constructor->returnParameters().empty()) + m_errorReporter.typeError(constructor->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); + if (constructor->stateMutability() != StateMutability::NonPayable && constructor->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + constructor->location(), + "Constructor must be payable or non-payable, but is \"" + + stateMutabilityToString(constructor->stateMutability()) + + "\"." + ); + if (constructor->visibility() != FunctionDefinition::Visibility::Public && constructor->visibility() != FunctionDefinition::Visibility::Internal) + m_errorReporter.typeError(constructor->location(), "Constructor must be public or internal."); +} -- cgit v1.2.3 From 4f4f623273052de6ede71bae2696f2388f1aa713 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 18:29:51 +0100 Subject: Move fallback function checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 879903de..dd08e181 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -41,6 +41,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkAbstractFunctions(_contract); checkBaseConstructorArguments(_contract); checkConstructor(_contract); + checkFallbackFunction(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -359,3 +360,26 @@ void ContractLevelChecker::checkConstructor(ContractDefinition const& _contract) if (constructor->visibility() != FunctionDefinition::Visibility::Public && constructor->visibility() != FunctionDefinition::Visibility::Internal) m_errorReporter.typeError(constructor->location(), "Constructor must be public or internal."); } + +void ContractLevelChecker::checkFallbackFunction(ContractDefinition const& _contract) +{ + FunctionDefinition const* fallback = _contract.fallbackFunction(); + if (!fallback) + return; + + if (_contract.isLibrary()) + m_errorReporter.typeError(fallback->location(), "Libraries cannot have fallback functions."); + if (fallback->stateMutability() != StateMutability::NonPayable && fallback->stateMutability() != StateMutability::Payable) + m_errorReporter.typeError( + fallback->location(), + "Fallback function must be payable or non-payable, but is \"" + + stateMutabilityToString(fallback->stateMutability()) + + "\"." + ); + if (!fallback->parameters().empty()) + m_errorReporter.typeError(fallback->parameterList().location(), "Fallback function cannot take parameters."); + if (!fallback->returnParameters().empty()) + m_errorReporter.typeError(fallback->returnParameterList()->location(), "Fallback function cannot return values."); + if (fallback->visibility() != FunctionDefinition::Visibility::External) + m_errorReporter.typeError(fallback->location(), "Fallback function must be defined as \"external\"."); +} -- cgit v1.2.3 From 6d1644e55c03850341e6bfcc4ae46f8de264a039 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 18:32:06 +0100 Subject: Move external type clash check. --- libsolidity/analysis/ContractLevelChecker.cpp | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index dd08e181..e2208f3f 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -42,6 +42,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkBaseConstructorArguments(_contract); checkConstructor(_contract); checkFallbackFunction(_contract); + checkExternalTypeClashes(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -383,3 +384,39 @@ void ContractLevelChecker::checkFallbackFunction(ContractDefinition const& _cont if (fallback->visibility() != FunctionDefinition::Visibility::External) m_errorReporter.typeError(fallback->location(), "Fallback function must be defined as \"external\"."); } + +void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract) +{ + map>> externalDeclarations; + for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) + { + for (FunctionDefinition const* f: contract->definedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared(*f); + // under non error circumstances this should be true + if (functionType->interfaceFunctionType()) + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(f, functionType->asCallableFunction(false)) + ); + } + for (VariableDeclaration const* v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared(*v); + // under non error circumstances this should be true + if (functionType->interfaceFunctionType()) + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(v, functionType->asCallableFunction(false)) + ); + } + } + for (auto const& it: externalDeclarations) + for (size_t i = 0; i < it.second.size(); ++i) + for (size_t j = i + 1; j < it.second.size(); ++j) + if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) + m_errorReporter.typeError( + it.second[j].first->location(), + "Function overload clash during conversion to external types for arguments." + ); +} -- cgit v1.2.3 From 4f992298c6381b3c7ea41462bce749850b5846e5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 18:33:54 +0100 Subject: Move hash collisions checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index e2208f3f..419ebe93 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -43,6 +43,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkConstructor(_contract); checkFallbackFunction(_contract); checkExternalTypeClashes(_contract); + checkHashCollisions(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -420,3 +421,18 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c "Function overload clash during conversion to external types for arguments." ); } + +void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract) +{ + set> hashes; + for (auto const& it: _contract.interfaceFunctionList()) + { + FixedHash<4> const& hash = it.first; + if (hashes.count(hash)) + m_errorReporter.typeError( + _contract.location(), + string("Function signature hash collision for ") + it.second->externalSignature() + ); + hashes.insert(hash); + } +} -- cgit v1.2.3 From 33d6a24c47f4cfd95e8bb880d5254fb7dd6ea21a Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Nov 2018 18:36:26 +0100 Subject: Move library related checks. --- libsolidity/analysis/ContractLevelChecker.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 419ebe93..58dcfe4d 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -44,6 +44,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkFallbackFunction(_contract); checkExternalTypeClashes(_contract); checkHashCollisions(_contract); + checkLibraryRequirements(_contract); return Error::containsOnlyWarnings(m_errorReporter.errors()); } @@ -436,3 +437,16 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra hashes.insert(hash); } } + +void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract) +{ + if (!_contract.isLibrary()) + return; + + if (!_contract.baseContracts().empty()) + m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit."); + + for (auto const& var: _contract.stateVariables()) + if (!var->isConstant()) + m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables"); +} -- cgit v1.2.3 From 0668a9ecfb60ab0e00bfb2c4a4a97ce523860832 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 1 Dec 2018 00:08:42 +0100 Subject: Public state variables are implementing external functions. --- libsolidity/analysis/ContractLevelChecker.cpp | 53 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 21 deletions(-) (limited to 'libsolidity/analysis/ContractLevelChecker.cpp') diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 58dcfe4d..6dc564de 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -219,29 +219,40 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con using FunTypeAndFlag = std::pair; map> functions; - // Search from base to derived - for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) - for (FunctionDefinition const* function: contract->definedFunctions()) + auto registerFunction = [&](Declaration const& _declaration, FunctionTypePointer const& _type, bool _implemented) + { + auto& overloads = functions[_declaration.name()]; + auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) { - // Take constructors out of overload hierarchy - if (function->isConstructor()) - continue; - auto& overloads = functions[function->name()]; - FunctionTypePointer funType = make_shared(*function)->asCallableFunction(false); - auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) - { - return funType->hasEqualParameterTypes(*_funAndFlag.first); - }); - if (it == overloads.end()) - overloads.push_back(make_pair(funType, function->isImplemented())); - else if (it->second) - { - if (!function->isImplemented()) - m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract"); - } - else if (function->isImplemented()) - it->second = true; + return _type->hasEqualParameterTypes(*_funAndFlag.first); + }); + if (it == overloads.end()) + overloads.push_back(make_pair(_type, _implemented)); + else if (it->second) + { + if (!_implemented) + m_errorReporter.typeError(_declaration.location(), "Redeclaring an already implemented function as abstract"); } + else if (_implemented) + it->second = true; + }; + + // Search from base to derived, collect all functions and update + // the 'implemented' flag. + for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) + { + for (VariableDeclaration const* v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + registerFunction(*v, make_shared(*v), true); + + for (FunctionDefinition const* function: contract->definedFunctions()) + if (!function->isConstructor()) + registerFunction( + *function, + make_shared(*function)->asCallableFunction(false), + function->isImplemented() + ); + } // Set to not fully implemented if at least one flag is false. for (auto const& it: functions) -- cgit v1.2.3