/* 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 . */ // SPDX-License-Identifier: GPL-3.0 /** * Component that verifies overloads, abstract contracts, function clashes and others * checks at contract or function level. */ #include #include #include #include #include #include #include using namespace std; using namespace solidity; using namespace solidity::frontend; using namespace solidity::langutil; using solidity::util::GenericVisitor; using solidity::util::contains_if; using solidity::util::joinHumanReadable; namespace { // Helper struct to do a search by name struct MatchByName { string const& m_name; bool operator()(OverrideProxy const& _item) { return _item.name() == m_name; } }; /** * Construct the override graph for this signature. * Reserve node 0 for the current contract and node * 1 for an artificial top node to which all override paths * connect at the end. */ struct OverrideGraph { OverrideGraph(set const& _baseCallables) { for (auto const& baseFunction: _baseCallables) addEdge(0, visit(baseFunction)); } std::map nodes; std::map nodeInv; std::map> edges; size_t numNodes = 2; void addEdge(int _a, int _b) { edges[_a].insert(_b); edges[_b].insert(_a); } private: /// Completes the graph starting from @a _function and /// @returns the node ID. int visit(OverrideProxy const& _function) { auto it = nodes.find(_function); if (it != nodes.end()) return it->second; int currentNode = static_cast(numNodes++); nodes[_function] = currentNode; nodeInv[currentNode] = _function; if (!_function.baseFunctions().empty()) for (auto const& baseFunction: _function.baseFunctions()) addEdge(currentNode, visit(baseFunction)); else addEdge(currentNode, 1); return currentNode; } }; /** * Detect cut vertices following https://en.wikipedia.org/wiki/Biconnected_component#Pseudocode * Can ignore the root node, since it is never a cut vertex in our case. */ struct CutVertexFinder { CutVertexFinder(OverrideGraph const& _graph): m_graph(_graph) { run(); } std::set const& cutVertices() const { return m_cutVertices; } private: OverrideGraph const& m_graph; std::vector m_visited = std::vector(m_graph.numNodes, false); std::vector m_depths = std::vector(m_graph.numNodes, -1); std::vector m_low = std::vector(m_graph.numNodes, -1); std::vector m_parent = std::vector(m_graph.numNodes, -1); std::set m_cutVertices{}; void run(size_t _u = 0, size_t _depth = 0) { m_visited.at(_u) = true; m_depths.at(_u) = m_low.at(_u) = static_cast(_depth); for (int const v: m_graph.edges.at(static_cast(_u))) { auto const vInd = static_cast(v); if (!m_visited.at(vInd)) { m_parent[vInd] = static_cast(_u); run(vInd, _depth + 1); if (m_low[vInd] >= m_depths[_u] && m_parent[_u] != -1) m_cutVertices.insert(m_graph.nodeInv.at(static_cast(_u))); m_low[_u] = min(m_low[_u], m_low[vInd]); } else if (v != m_parent[_u]) m_low[_u] = min(m_low[_u], m_depths[vInd]); } } }; vector resolveDirectBaseContracts(ContractDefinition const& _contract) { vector resolvedContracts; for (ASTPointer const& specifier: _contract.baseContracts()) { Declaration const* baseDecl = specifier->name().annotation().referencedDeclaration; auto contract = dynamic_cast(baseDecl); if (contract) resolvedContracts.emplace_back(contract); } return resolvedContracts; } vector> sortByContract(vector> const& _list) { auto sorted = _list; stable_sort(sorted.begin(), sorted.end(), [] (ASTPointer _a, ASTPointer _b) { if (!_a || !_b) return _a < _b; Declaration const* aDecl = _a->annotation().referencedDeclaration; Declaration const* bDecl = _b->annotation().referencedDeclaration; if (!aDecl || !bDecl) return aDecl < bDecl; return aDecl->id() < bDecl->id(); } ); return sorted; } OverrideProxy makeOverrideProxy(CallableDeclaration const& _callable) { if (auto const* fun = dynamic_cast(&_callable)) return OverrideProxy{fun}; else if (auto const* mod = dynamic_cast(&_callable)) return OverrideProxy{mod}; else solAssert(false, "Invalid call to makeOverrideProxy."); return {}; } } bool OverrideProxy::operator<(OverrideProxy const& _other) const { return id() < _other.id(); } bool OverrideProxy::isVariable() const { return holds_alternative(m_item); } bool OverrideProxy::isFunction() const { return holds_alternative(m_item); } bool OverrideProxy::isModifier() const { return holds_alternative(m_item); } bool OverrideProxy::CompareBySignature::operator()(OverrideProxy const& _a, OverrideProxy const& _b) const { return _a.overrideComparator() < _b.overrideComparator(); } size_t OverrideProxy::id() const { return std::visit(GenericVisitor{ [&](auto const* _item) -> size_t { return static_cast(_item->id()); } }, m_item); } shared_ptr OverrideProxy::overrides() const { return std::visit(GenericVisitor{ [&](auto const* _item) { return _item->overrides(); } }, m_item); } set OverrideProxy::baseFunctions() const { return std::visit(GenericVisitor{ [&](auto const* _item) -> set { set ret; for (auto const* f: _item->annotation().baseFunctions) ret.insert(makeOverrideProxy(*f)); return ret; } }, m_item); } void OverrideProxy::storeBaseFunction(OverrideProxy const& _base) const { std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { _item->annotation().baseFunctions.emplace(std::get(_base.m_item)); }, [&](ModifierDefinition const* _item) { _item->annotation().baseFunctions.emplace(std::get(_base.m_item)); }, [&](VariableDeclaration const* _item) { _item->annotation().baseFunctions.emplace(std::get(_base.m_item)); } }, m_item); } string const& OverrideProxy::name() const { return std::visit(GenericVisitor{ [&](auto const* _item) -> string const& { return _item->name(); } }, m_item); } ContractDefinition const& OverrideProxy::contract() const { return std::visit(GenericVisitor{ [&](auto const* _item) -> ContractDefinition const& { return dynamic_cast(*_item->scope()); } }, m_item); } string const& OverrideProxy::contractName() const { return contract().name(); } Visibility OverrideProxy::visibility() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return _item->visibility(); }, [&](ModifierDefinition const* _item) { return _item->visibility(); }, [&](VariableDeclaration const*) { return Visibility::External; } }, m_item); } StateMutability OverrideProxy::stateMutability() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return _item->stateMutability(); }, [&](ModifierDefinition const*) { solAssert(false, "Requested state mutability from modifier."); return StateMutability{}; }, [&](VariableDeclaration const* _var) { return _var->isConstant() ? StateMutability::Pure : StateMutability::View; } }, m_item); } bool OverrideProxy::virtualSemantics() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return _item->virtualSemantics(); }, [&](ModifierDefinition const* _item) { return _item->virtualSemantics(); }, [&](VariableDeclaration const*) { return false; } }, m_item); } Token OverrideProxy::functionKind() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return _item->kind(); }, [&](ModifierDefinition const*) { return Token::Function; }, [&](VariableDeclaration const*) { return Token::Function; } }, m_item); } FunctionType const* OverrideProxy::externalFunctionType() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); }, [&](VariableDeclaration const* _item) { return FunctionType(*_item).asExternallyCallableFunction(false); }, [&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested function type of modifier."); return nullptr; } }, m_item); } FunctionType const* OverrideProxy::originalFunctionType() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return TypeProvider::function(*_item); }, [&](VariableDeclaration const*) -> FunctionType const* { solAssert(false, "Requested specific function type of variable."); return nullptr; }, [&](ModifierDefinition const*) -> FunctionType const* { solAssert(false, "Requested specific function type of modifier."); return nullptr; } }, m_item); } ModifierType const* OverrideProxy::modifierType() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const*) -> ModifierType const* { solAssert(false, "Requested modifier type of function."); return nullptr; }, [&](VariableDeclaration const*) -> ModifierType const* { solAssert(false, "Requested modifier type of variable."); return nullptr; }, [&](ModifierDefinition const* _modifier) -> ModifierType const* { return TypeProvider::modifier(*_modifier); } }, m_item); } Declaration const* OverrideProxy::declaration() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _function) -> Declaration const* { return _function; }, [&](VariableDeclaration const* _variable) -> Declaration const* { return _variable; }, [&](ModifierDefinition const* _modifier) -> Declaration const* { return _modifier; } }, m_item); } SourceLocation const& OverrideProxy::location() const { return std::visit(GenericVisitor{ [&](auto const* _item) -> SourceLocation const& { return _item->location(); } }, m_item); } string OverrideProxy::astNodeName() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const*) { return "function"; }, [&](ModifierDefinition const*) { return "modifier"; }, [&](VariableDeclaration const*) { return "public state variable"; }, }, m_item); } string OverrideProxy::astNodeNameCapitalized() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const*) { return "Function"; }, [&](ModifierDefinition const*) { return "Modifier"; }, [&](VariableDeclaration const*) { return "Public state variable"; }, }, m_item); } string OverrideProxy::distinguishingProperty() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const*) { return "name and parameter types"; }, [&](ModifierDefinition const*) { return "name"; }, [&](VariableDeclaration const*) { return "name and parameter types"; }, }, m_item); } bool OverrideProxy::unimplemented() const { return std::visit(GenericVisitor{ [&](FunctionDefinition const* _item) { return !_item->isImplemented(); }, [&](ModifierDefinition const* _item) { return !_item->isImplemented(); }, [&](VariableDeclaration const*) { return false; } }, m_item); } bool OverrideProxy::OverrideComparator::operator<(OverrideComparator const& _other) const { if (name != _other.name) return name < _other.name; if (!functionKind || !_other.functionKind) return false; if (functionKind != _other.functionKind) return *functionKind < *_other.functionKind; // Parameters do not matter for non-regular functions. if (functionKind != Token::Function) return false; if (!parameterTypes || !_other.parameterTypes) return false; return boost::lexicographical_compare(*parameterTypes, *_other.parameterTypes); } OverrideProxy::OverrideComparator const& OverrideProxy::overrideComparator() const { if (!m_comparator) { m_comparator = make_shared(std::visit(GenericVisitor{ [&](FunctionDefinition const* _function) { vector paramTypes; for (Type const* t: externalFunctionType()->parameterTypes()) paramTypes.emplace_back(t->richIdentifier()); return OverrideComparator{ _function->name(), _function->kind(), std::move(paramTypes) }; }, [&](VariableDeclaration const* _var) { vector paramTypes; for (Type const* t: externalFunctionType()->parameterTypes()) paramTypes.emplace_back(t->richIdentifier()); return OverrideComparator{ _var->name(), Token::Function, std::move(paramTypes) }; }, [&](ModifierDefinition const* _mod) { return OverrideComparator{ _mod->name(), {}, {} }; } }, m_item)); } return *m_comparator; } bool OverrideChecker::CompareByID::operator()(ContractDefinition const* _a, ContractDefinition const* _b) const { if (!_a || !_b) return _a < _b; return _a->id() < _b->id(); } void OverrideChecker::check(ContractDefinition const& _contract) { checkIllegalOverrides(_contract); checkAmbiguousOverrides(_contract); } void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract) { OverrideProxyBySignatureMultiSet const& inheritedFuncs = inheritedFunctions(_contract); OverrideProxyBySignatureMultiSet const& inheritedMods = inheritedModifiers(_contract); for (ModifierDefinition const* modifier: _contract.functionModifiers()) { if (contains_if(inheritedFuncs, MatchByName{modifier->name()})) m_errorReporter.typeError( 5631_error, modifier->location(), "Override changes function or public state variable to modifier." ); checkOverrideList(OverrideProxy{modifier}, inheritedMods); } for (FunctionDefinition const* function: _contract.definedFunctions()) { if (function->isConstructor()) continue; if (contains_if(inheritedMods, MatchByName{function->name()})) m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function."); checkOverrideList(OverrideProxy{function}, inheritedFuncs); } for (auto const* stateVar: _contract.stateVariables()) { if (!stateVar->isPublic()) { if (stateVar->overrides()) m_errorReporter.typeError(8022_error, stateVar->location(), "Override can only be used with public state variables."); continue; } if (contains_if(inheritedMods, MatchByName{stateVar->name()})) m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable."); checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs); } } void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverrideProxy const& _super) { solAssert(_super.isModifier() == _overriding.isModifier(), ""); if (_super.isFunction() || _super.isModifier()) _overriding.storeBaseFunction(_super); if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType()) m_errorReporter.typeError( 1078_error, _overriding.location(), "Override changes modifier signature." ); if (!_overriding.overrides() && !(_super.isFunction() && _super.contract().isInterface())) overrideError( _overriding, _super, 9456_error, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.", "Overridden " + _overriding.astNodeName() + " is here:" ); if (_super.isVariable()) overrideError( _super, _overriding, 1452_error, "Cannot override public state variable.", "Overriding " + _overriding.astNodeName() + " is here:" ); else if (!_super.virtualSemantics()) overrideError( _super, _overriding, 4334_error, "Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?", "Overriding " + _overriding.astNodeName() + " is here:" ); if (_overriding.isVariable()) { if (_super.visibility() != Visibility::External) overrideError( _overriding, _super, 5225_error, "Public state variables can only override functions with external visibility.", "Overridden function is here:" ); solAssert(_overriding.visibility() == Visibility::External, ""); } else if (_overriding.visibility() != _super.visibility()) { // Visibility change from external to public is fine. // Any other change is disallowed. if (!( _super.visibility() == Visibility::External && _overriding.visibility() == Visibility::Public )) overrideError( _overriding, _super, 9098_error, "Overriding " + _overriding.astNodeName() + " visibility differs.", "Overridden " + _overriding.astNodeName() + " is here:" ); } if (_overriding.unimplemented() && !_super.unimplemented()) { solAssert(!_overriding.isVariable() || !_overriding.unimplemented(), ""); overrideError( _overriding, _super, 4593_error, "Overriding an implemented " + _super.astNodeName() + " with an unimplemented " + _overriding.astNodeName() + " is not allowed." ); } if (_super.isFunction()) { FunctionType const* functionType = _overriding.externalFunctionType(); FunctionType const* superType = _super.externalFunctionType(); bool returnTypesDifferAlready = false; if (_overriding.functionKind() != Token::Fallback) { solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!"); if (!functionType->hasEqualReturnTypes(*superType)) { returnTypesDifferAlready = true; overrideError( _overriding, _super, 4822_error, "Overriding " + _overriding.astNodeName() + " return types differ.", "Overridden " + _overriding.astNodeName() + " is here:" ); } } // The override proxy considers calldata and memory the same data location. // Here we do a more specific check: // Data locations of parameters and return variables have to match // unless we have a public function overriding an external one. if ( _overriding.isFunction() && !returnTypesDifferAlready && _super.visibility() != Visibility::External && _overriding.functionKind() != Token::Fallback ) { if (!_overriding.originalFunctionType()->hasEqualParameterTypes(*_super.originalFunctionType())) overrideError( _overriding, _super, 7723_error, "Data locations of parameters have to be the same when overriding non-external functions, but they differ.", "Overridden " + _overriding.astNodeName() + " is here:" ); if (!_overriding.originalFunctionType()->hasEqualReturnTypes(*_super.originalFunctionType())) overrideError( _overriding, _super, 1443_error, "Data locations of return variables have to be the same when overriding non-external functions, but they differ.", "Overridden " + _overriding.astNodeName() + " is here:" ); } // Stricter mutability is always okay except when super is Payable if ( (_overriding.isFunction() || _overriding.isVariable()) && ( _overriding.stateMutability() > _super.stateMutability() || _super.stateMutability() == StateMutability::Payable ) && _overriding.stateMutability() != _super.stateMutability() ) overrideError( _overriding, _super, 6959_error, "Overriding " + _overriding.astNodeName() + " changes state mutability from \"" + stateMutabilityToString(_super.stateMutability()) + "\" to \"" + stateMutabilityToString(_overriding.stateMutability()) + "\"." ); } } void OverrideChecker::overrideListError( OverrideProxy const& _item, set _secondary, ErrorId _error, string const& _message1, string const& _message2 ) { // Using a set rather than a vector so the order is always the same set names; SecondarySourceLocation ssl; for (Declaration const* c: _secondary) { ssl.append("This contract: ", c->location()); names.insert("\"" + c->name() + "\""); } string contractSingularPlural = "contract "; if (_secondary.size() > 1) contractSingularPlural = "contracts "; m_errorReporter.typeError( _error, _item.overrides() ? _item.overrides()->location() : _item.location(), ssl, _message1 + contractSingularPlural + _message2 + joinHumanReadable(names, ", ", " and ") + "." ); } void OverrideChecker::overrideError( OverrideProxy const& _overriding, OverrideProxy const& _super, ErrorId _error, string const& _message, optional const& _secondaryMsg ) { m_errorReporter.typeError( _error, _overriding.location(), SecondarySourceLocation().append( _secondaryMsg.value_or("Overridden " + _super.astNodeName() + " is here:"), _super.location() ), _message ); } void OverrideChecker::checkAmbiguousOverrides(ContractDefinition const& _contract) const { { // Fetch inherited functions and sort them by signature. // We get at least one function per signature and direct base contract, which is // enough because we re-construct the inheritance graph later. OverrideProxyBySignatureMultiSet nonOverriddenFunctions = inheritedFunctions(_contract); // Remove all functions that match the signature of a function in the current contract. for (FunctionDefinition const* f: _contract.definedFunctions()) nonOverriddenFunctions.erase(OverrideProxy{f}); for (VariableDeclaration const* v: _contract.stateVariables()) if (v->isPublic()) nonOverriddenFunctions.erase(OverrideProxy{v}); // Walk through the set of functions signature by signature. for (auto it = nonOverriddenFunctions.cbegin(); it != nonOverriddenFunctions.cend();) { std::set baseFunctions; for (auto nextSignature = nonOverriddenFunctions.upper_bound(*it); it != nextSignature; ++it) baseFunctions.insert(*it); checkAmbiguousOverridesInternal(std::move(baseFunctions), _contract.location()); } } { OverrideProxyBySignatureMultiSet modifiers = inheritedModifiers(_contract); for (ModifierDefinition const* mod: _contract.functionModifiers()) modifiers.erase(OverrideProxy{mod}); for (auto it = modifiers.cbegin(); it != modifiers.cend();) { std::set baseModifiers; for (auto next = modifiers.upper_bound(*it); it != next; ++it) baseModifiers.insert(*it); checkAmbiguousOverridesInternal(std::move(baseModifiers), _contract.location()); } } } void OverrideChecker::checkAmbiguousOverridesInternal(set _baseCallables, SourceLocation const& _location) const { if (_baseCallables.size() <= 1) return; OverrideGraph overrideGraph(_baseCallables); CutVertexFinder cutVertexFinder{overrideGraph}; // Remove all base functions overridden by cut vertices (they don't need to be overridden). for (OverrideProxy const& function: cutVertexFinder.cutVertices()) { std::set toTraverse = function.baseFunctions(); while (!toTraverse.empty()) { OverrideProxy base = *toTraverse.begin(); toTraverse.erase(toTraverse.begin()); _baseCallables.erase(base); for (OverrideProxy const& f: base.baseFunctions()) toTraverse.insert(f); } // Remove unimplemented base functions at the cut vertices itself as well. if (function.unimplemented()) _baseCallables.erase(function); } // If more than one function is left, they have to be overridden. if (_baseCallables.size() <= 1) return; SecondarySourceLocation ssl; for (OverrideProxy const& baseFunction: _baseCallables) ssl.append("Definition in \"" + baseFunction.contractName() + "\": ", baseFunction.location()); string callableName = _baseCallables.begin()->astNodeName(); if (_baseCallables.begin()->isVariable()) callableName = "function"; string distinguishigProperty = _baseCallables.begin()->distinguishingProperty(); bool foundVariable = false; for (auto const& base: _baseCallables) if (base.isVariable()) foundVariable = true; string message = "Derived contract must override " + callableName + " \"" + _baseCallables.begin()->name() + "\". Two or more base classes define " + callableName + " with same " + distinguishigProperty + "."; if (foundVariable) message += " Since one of the bases defines a public state variable which cannot be overridden, " "you have to change the inheritance layout or the names of the functions."; m_errorReporter.typeError(6480_error, _location, ssl, message); } set OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const { set resolved; for (ASTPointer const& override: _overrides.overrides()) { Declaration const* decl = override->annotation().referencedDeclaration; solAssert(decl, "Expected declaration to be resolved."); // If it's not a contract it will be caught // in the reference resolver if (ContractDefinition const* contract = dynamic_cast(decl)) resolved.insert(contract); } return resolved; } void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySignatureMultiSet const& _inherited) { set specifiedContracts = _item.overrides() ? resolveOverrideList(*_item.overrides()) : decltype(specifiedContracts){}; // Check for duplicates in override list if (_item.overrides() && specifiedContracts.size() != _item.overrides()->overrides().size()) { // Sort by contract id to find duplicate for error reporting vector> list = sortByContract(_item.overrides()->overrides()); // Find duplicates and output error for (size_t i = 1; i < list.size(); i++) { Declaration const* aDecl = list[i]->annotation().referencedDeclaration; Declaration const* bDecl = list[i-1]->annotation().referencedDeclaration; if (!aDecl || !bDecl) continue; if (aDecl->id() == bDecl->id()) { SecondarySourceLocation ssl; ssl.append("First occurrence here: ", list[i-1]->location()); m_errorReporter.typeError( 4520_error, list[i]->location(), ssl, "Duplicate contract \"" + joinHumanReadable(list[i]->path(), ".") + "\" found in override list of \"" + _item.name() + "\"." ); } } } set expectedContracts; // Build list of expected contracts for (auto [begin, end] = _inherited.equal_range(_item); begin != end; begin++) { // Validate the override checkOverride(_item, *begin); expectedContracts.insert(&begin->contract()); } if (_item.overrides() && expectedContracts.empty()) m_errorReporter.typeError( 7792_error, _item.overrides()->location(), _item.astNodeNameCapitalized() + " has override specified but does not override anything." ); set missingContracts; // If we expect only one contract, no contract needs to be specified if (expectedContracts.size() > 1) missingContracts = expectedContracts - specifiedContracts; if (!missingContracts.empty()) overrideListError( _item, missingContracts, 4327_error, _item.astNodeNameCapitalized() + " needs to specify overridden ", "" ); auto surplusContracts = specifiedContracts - expectedContracts; if (!surplusContracts.empty()) overrideListError( _item, surplusContracts, 2353_error, "Invalid ", "specified in override list: " ); } OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheritedFunctions(ContractDefinition const& _contract) const { if (!m_inheritedFunctions.count(&_contract)) { OverrideProxyBySignatureMultiSet result; for (auto const* base: resolveDirectBaseContracts(_contract)) { set functionsInBase; for (FunctionDefinition const* fun: base->definedFunctions()) if (!fun->isConstructor()) functionsInBase.emplace(OverrideProxy{fun}); for (VariableDeclaration const* var: base->stateVariables()) if (var->isPublic()) functionsInBase.emplace(OverrideProxy{var}); result += functionsInBase; for (OverrideProxy const& func: inheritedFunctions(*base)) if (!functionsInBase.count(func)) result.insert(func); } m_inheritedFunctions[&_contract] = result; } return m_inheritedFunctions[&_contract]; } OverrideChecker::OverrideProxyBySignatureMultiSet const& OverrideChecker::inheritedModifiers(ContractDefinition const& _contract) const { if (!m_inheritedModifiers.count(&_contract)) { OverrideProxyBySignatureMultiSet result; for (auto const* base: resolveDirectBaseContracts(_contract)) { set modifiersInBase; for (ModifierDefinition const* mod: base->functionModifiers()) modifiersInBase.emplace(OverrideProxy{mod}); for (OverrideProxy const& mod: inheritedModifiers(*base)) modifiersInBase.insert(mod); result += modifiersInBase; } m_inheritedModifiers[&_contract] = result; } return m_inheritedModifiers[&_contract]; }