mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			572 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			572 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
	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 <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
// SPDX-License-Identifier: GPL-3.0
 | 
						|
/**
 | 
						|
 * Component that verifies overloads, abstract contracts, function clashes and others
 | 
						|
 * checks at contract or function level.
 | 
						|
 */
 | 
						|
 | 
						|
#include <libsolidity/analysis/ContractLevelChecker.h>
 | 
						|
 | 
						|
#include <libsolidity/ast/AST.h>
 | 
						|
#include <libsolidity/ast/TypeProvider.h>
 | 
						|
#include <libsolidity/analysis/TypeChecker.h>
 | 
						|
#include <libsolutil/FunctionSelector.h>
 | 
						|
#include <liblangutil/ErrorReporter.h>
 | 
						|
 | 
						|
#include <fmt/format.h>
 | 
						|
 | 
						|
#include <range/v3/view/reverse.hpp>
 | 
						|
 | 
						|
using namespace std;
 | 
						|
using namespace solidity;
 | 
						|
using namespace solidity::langutil;
 | 
						|
using namespace solidity::frontend;
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
 | 
						|
template <class T, class B>
 | 
						|
bool hasEqualExternalCallableParameters(T const& _a, B const& _b)
 | 
						|
{
 | 
						|
	return FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes(
 | 
						|
		*FunctionType(_b).asExternallyCallableFunction(false)
 | 
						|
	);
 | 
						|
}
 | 
						|
 | 
						|
template<typename T>
 | 
						|
map<ASTString, vector<T const*>> filterDeclarations(
 | 
						|
	map<ASTString, vector<Declaration const*>> const& _declarations)
 | 
						|
{
 | 
						|
	map<ASTString, vector<T const*>> filteredDeclarations;
 | 
						|
	for (auto const& [name, overloads]: _declarations)
 | 
						|
		for (auto const* declaration: overloads)
 | 
						|
			if (auto typedDeclaration = dynamic_cast<T const*>(declaration))
 | 
						|
				filteredDeclarations[name].push_back(typedDeclaration);
 | 
						|
	return filteredDeclarations;
 | 
						|
}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
bool ContractLevelChecker::check(SourceUnit const& _sourceUnit)
 | 
						|
{
 | 
						|
	bool noErrors = true;
 | 
						|
	findDuplicateDefinitions(
 | 
						|
		filterDeclarations<FunctionDefinition>(*_sourceUnit.annotation().exportedSymbols)
 | 
						|
	);
 | 
						|
	// This check flags duplicate free events when free events become
 | 
						|
	// a Solidity feature
 | 
						|
	findDuplicateDefinitions(
 | 
						|
		filterDeclarations<EventDefinition>(*_sourceUnit.annotation().exportedSymbols)
 | 
						|
	);
 | 
						|
	if (Error::containsErrors(m_errorReporter.errors()))
 | 
						|
		noErrors = false;
 | 
						|
	for (ASTPointer<ASTNode> const& node: _sourceUnit.nodes())
 | 
						|
		if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
 | 
						|
			if (!check(*contract))
 | 
						|
				noErrors = false;
 | 
						|
	return noErrors;
 | 
						|
}
 | 
						|
 | 
						|
bool ContractLevelChecker::check(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	_contract.annotation().unimplementedDeclarations = std::vector<Declaration const*>();
 | 
						|
 | 
						|
	checkDuplicateFunctions(_contract);
 | 
						|
	checkDuplicateEvents(_contract);
 | 
						|
	checkReceiveFunction(_contract);
 | 
						|
	m_overrideChecker.check(_contract);
 | 
						|
	checkBaseConstructorArguments(_contract);
 | 
						|
	checkAbstractDefinitions(_contract);
 | 
						|
	checkExternalTypeClashes(_contract);
 | 
						|
	checkHashCollisions(_contract);
 | 
						|
	checkLibraryRequirements(_contract);
 | 
						|
	checkBaseABICompatibility(_contract);
 | 
						|
	checkPayableFallbackWithoutReceive(_contract);
 | 
						|
	checkStorageSize(_contract);
 | 
						|
 | 
						|
	return !Error::containsErrors(m_errorReporter.errors());
 | 
						|
}
 | 
						|
 | 
						|
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.
 | 
						|
	map<string, vector<FunctionDefinition const*>> functions;
 | 
						|
	FunctionDefinition const* constructor = nullptr;
 | 
						|
	FunctionDefinition const* fallback = nullptr;
 | 
						|
	FunctionDefinition const* receive = nullptr;
 | 
						|
	for (FunctionDefinition const* function: _contract.definedFunctions())
 | 
						|
		if (function->isConstructor())
 | 
						|
		{
 | 
						|
			if (constructor)
 | 
						|
				m_errorReporter.declarationError(
 | 
						|
					7997_error,
 | 
						|
					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(
 | 
						|
					7301_error,
 | 
						|
					function->location(),
 | 
						|
					SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
 | 
						|
					"Only one fallback function is allowed."
 | 
						|
				);
 | 
						|
			fallback = function;
 | 
						|
		}
 | 
						|
		else if (function->isReceive())
 | 
						|
		{
 | 
						|
			if (receive)
 | 
						|
				m_errorReporter.declarationError(
 | 
						|
					4046_error,
 | 
						|
					function->location(),
 | 
						|
					SecondarySourceLocation().append("Another declaration is here:", receive->location()),
 | 
						|
					"Only one receive function is allowed."
 | 
						|
				);
 | 
						|
			receive = function;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			solAssert(!function->name().empty(), "");
 | 
						|
			functions[function->name()].push_back(function);
 | 
						|
		}
 | 
						|
 | 
						|
	findDuplicateDefinitions(functions);
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	/// Checks that two events with the same name defined in this contract have different
 | 
						|
	/// argument types
 | 
						|
	map<string, vector<EventDefinition const*>> events;
 | 
						|
	for (auto const* contract: _contract.annotation().linearizedBaseContracts)
 | 
						|
		for (EventDefinition const* event: contract->events())
 | 
						|
			events[event->name()].push_back(event);
 | 
						|
 | 
						|
	findDuplicateDefinitions(events);
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkReceiveFunction(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	for (FunctionDefinition const* function: _contract.definedFunctions())
 | 
						|
	{
 | 
						|
		solAssert(function, "");
 | 
						|
		if (function->isReceive())
 | 
						|
		{
 | 
						|
			if (function->libraryFunction())
 | 
						|
				m_errorReporter.declarationError(4549_error, function->location(), "Libraries cannot have receive ether functions.");
 | 
						|
 | 
						|
			if (function->stateMutability() != StateMutability::Payable)
 | 
						|
				m_errorReporter.declarationError(
 | 
						|
					7793_error,
 | 
						|
					function->location(),
 | 
						|
					"Receive ether function must be payable, but is \"" +
 | 
						|
					stateMutabilityToString(function->stateMutability()) +
 | 
						|
					"\"."
 | 
						|
				);
 | 
						|
			if (function->visibility() != Visibility::External)
 | 
						|
				m_errorReporter.declarationError(4095_error, function->location(), "Receive ether function must be defined as \"external\".");
 | 
						|
 | 
						|
			if (!function->returnParameters().empty())
 | 
						|
				m_errorReporter.fatalDeclarationError(6899_error, function->returnParameterList()->location(), "Receive ether function cannot return values.");
 | 
						|
			if (!function->parameters().empty())
 | 
						|
				m_errorReporter.fatalDeclarationError(6857_error, function->parameterList().location(), "Receive ether function cannot take parameters.");
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
template <class T>
 | 
						|
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions)
 | 
						|
{
 | 
						|
	for (auto const& it: _definitions)
 | 
						|
	{
 | 
						|
		vector<T> const& overloads = it.second;
 | 
						|
		set<size_t> 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 (hasEqualExternalCallableParameters(*overloads[i], *overloads[j]))
 | 
						|
				{
 | 
						|
					solAssert(
 | 
						|
						(
 | 
						|
							dynamic_cast<ContractDefinition const*>(overloads[i]->scope()) &&
 | 
						|
							dynamic_cast<ContractDefinition const*>(overloads[j]->scope()) &&
 | 
						|
							overloads[i]->name() == overloads[j]->name()
 | 
						|
						) ||
 | 
						|
						(
 | 
						|
							dynamic_cast<SourceUnit const*>(overloads[i]->scope()) &&
 | 
						|
							dynamic_cast<SourceUnit const*>(overloads[j]->scope())
 | 
						|
						),
 | 
						|
						"Override is neither a namesake function/event in contract scope nor "
 | 
						|
						"a free function/event (alias)."
 | 
						|
					);
 | 
						|
					ssl.append("Other declaration is here:", overloads[j]->location());
 | 
						|
					reported.insert(j);
 | 
						|
				}
 | 
						|
 | 
						|
			if (ssl.infos.size() > 0)
 | 
						|
			{
 | 
						|
				ErrorId error;
 | 
						|
				string message;
 | 
						|
				if constexpr (is_same_v<T, FunctionDefinition const*>)
 | 
						|
				{
 | 
						|
					error = 1686_error;
 | 
						|
					message = "Function with same name and parameter types defined twice.";
 | 
						|
				}
 | 
						|
				else
 | 
						|
				{
 | 
						|
					static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\"");
 | 
						|
					error = 5883_error;
 | 
						|
					message = "Event with same name and parameter types defined twice.";
 | 
						|
				}
 | 
						|
 | 
						|
				ssl.limitSize(message);
 | 
						|
 | 
						|
				m_errorReporter.declarationError(
 | 
						|
					error,
 | 
						|
					overloads[i]->location(),
 | 
						|
					ssl,
 | 
						|
					message
 | 
						|
				);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	// Collects functions, static variable getters and modifiers. If they
 | 
						|
	// override (unimplemented) base class ones, they are replaced.
 | 
						|
	set<OverrideProxy, OverrideProxy::CompareBySignature> proxies;
 | 
						|
 | 
						|
	auto registerProxy = [&proxies](OverrideProxy const& _overrideProxy)
 | 
						|
	{
 | 
						|
		// Overwrite an existing proxy, if it exists.
 | 
						|
		if (!_overrideProxy.unimplemented())
 | 
						|
			proxies.erase(_overrideProxy);
 | 
						|
 | 
						|
		proxies.insert(_overrideProxy);
 | 
						|
	};
 | 
						|
 | 
						|
	// Search from base to derived, collect all functions and modifiers and
 | 
						|
	// update proxies.
 | 
						|
	for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts | ranges::views::reverse)
 | 
						|
	{
 | 
						|
		for (VariableDeclaration const* v: contract->stateVariables())
 | 
						|
			if (v->isPartOfExternalInterface())
 | 
						|
				registerProxy(OverrideProxy(v));
 | 
						|
 | 
						|
		for (FunctionDefinition const* function: contract->definedFunctions())
 | 
						|
			if (!function->isConstructor())
 | 
						|
				registerProxy(OverrideProxy(function));
 | 
						|
 | 
						|
		for (ModifierDefinition const* modifier: contract->functionModifiers())
 | 
						|
			registerProxy(OverrideProxy(modifier));
 | 
						|
	}
 | 
						|
 | 
						|
	// Set to not fully implemented if at least one flag is false.
 | 
						|
	for (auto const& proxy: proxies)
 | 
						|
		if (proxy.unimplemented())
 | 
						|
			_contract.annotation().unimplementedDeclarations->push_back(proxy.declaration());
 | 
						|
 | 
						|
	if (_contract.abstract())
 | 
						|
	{
 | 
						|
		if (_contract.contractKind() == ContractKind::Interface)
 | 
						|
			m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
 | 
						|
		else if (_contract.contractKind() == ContractKind::Library)
 | 
						|
			m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract.");
 | 
						|
		else
 | 
						|
			solAssert(_contract.contractKind() == ContractKind::Contract, "");
 | 
						|
	}
 | 
						|
 | 
						|
	// For libraries, we emit errors on function-level, so this is fine as long as we do
 | 
						|
	// not have inheritance for libraries.
 | 
						|
	if (
 | 
						|
		_contract.contractKind() == ContractKind::Contract &&
 | 
						|
		!_contract.abstract() &&
 | 
						|
		!_contract.annotation().unimplementedDeclarations->empty()
 | 
						|
	)
 | 
						|
	{
 | 
						|
		SecondarySourceLocation ssl;
 | 
						|
		for (auto declaration: *_contract.annotation().unimplementedDeclarations)
 | 
						|
			ssl.append("Missing implementation: ", declaration->location());
 | 
						|
		m_errorReporter.typeError(
 | 
						|
			3656_error,
 | 
						|
			_contract.location(),
 | 
						|
			ssl,
 | 
						|
			"Contract \"" + *_contract.annotation().canonicalName + "\" should be marked as abstract."
 | 
						|
		);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	vector<ContractDefinition const*> 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<ContractDefinition const*>(
 | 
						|
					modifier->name().annotation().referencedDeclaration
 | 
						|
				))
 | 
						|
				{
 | 
						|
					if (modifier->arguments())
 | 
						|
					{
 | 
						|
						if (baseContract->constructor())
 | 
						|
							annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
 | 
						|
					}
 | 
						|
					else
 | 
						|
						m_errorReporter.declarationError(
 | 
						|
							1563_error,
 | 
						|
							modifier->location(),
 | 
						|
							"Modifier-style base constructor call without arguments."
 | 
						|
						);
 | 
						|
				}
 | 
						|
 | 
						|
		for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
 | 
						|
		{
 | 
						|
			ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
 | 
						|
				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)
 | 
						|
	if (_contract.contractKind() == ContractKind::Contract && !_contract.abstract())
 | 
						|
		for (ContractDefinition const* baseContract: bases)
 | 
						|
			if (FunctionDefinition const* baseConstructor = baseContract->constructor())
 | 
						|
				if (
 | 
						|
					baseContract != &_contract &&
 | 
						|
					!baseConstructor->parameters().empty() &&
 | 
						|
					_contract.annotation().baseConstructorArguments.count(baseConstructor) == 0
 | 
						|
				)
 | 
						|
					m_errorReporter.typeError(
 | 
						|
						3415_error,
 | 
						|
						_contract.location(),
 | 
						|
						SecondarySourceLocation{}.append(
 | 
						|
							"Base constructor parameters:",
 | 
						|
							baseConstructor->parameterList().location()
 | 
						|
						),
 | 
						|
						fmt::format(
 | 
						|
							"No arguments passed to the base constructor. "
 | 
						|
							"Specify the arguments or mark \"{}\" as abstract.",
 | 
						|
							*_contract.annotation().canonicalName
 | 
						|
						)
 | 
						|
					);
 | 
						|
}
 | 
						|
 | 
						|
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(
 | 
						|
			3364_error,
 | 
						|
			*mainLocation,
 | 
						|
			ssl,
 | 
						|
			"Base constructor arguments given twice."
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
 | 
						|
	for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
 | 
						|
	{
 | 
						|
		for (FunctionDefinition const* f: contract->definedFunctions())
 | 
						|
			if (f->isPartOfExternalInterface())
 | 
						|
			{
 | 
						|
				auto functionType = TypeProvider::function(*f);
 | 
						|
				// under non error circumstances this should be true
 | 
						|
				if (functionType->interfaceFunctionType())
 | 
						|
					externalDeclarations[functionType->externalSignature()].emplace_back(
 | 
						|
						f, functionType->asExternallyCallableFunction(false)
 | 
						|
					);
 | 
						|
			}
 | 
						|
		for (VariableDeclaration const* v: contract->stateVariables())
 | 
						|
			if (v->isPartOfExternalInterface())
 | 
						|
			{
 | 
						|
				auto functionType = TypeProvider::function(*v);
 | 
						|
				// under non error circumstances this should be true
 | 
						|
				if (functionType->interfaceFunctionType())
 | 
						|
					externalDeclarations[functionType->externalSignature()].emplace_back(
 | 
						|
						v, functionType->asExternallyCallableFunction(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(
 | 
						|
						9914_error,
 | 
						|
						it.second[j].first->location(),
 | 
						|
						"Function overload clash during conversion to external types for arguments."
 | 
						|
					);
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	set<util::FixedHash<4>> hashes;
 | 
						|
	for (auto const& it: _contract.interfaceFunctionList())
 | 
						|
	{
 | 
						|
		util::FixedHash<4> const& hash = it.first;
 | 
						|
		if (hashes.count(hash))
 | 
						|
			m_errorReporter.fatalTypeError(
 | 
						|
				1860_error,
 | 
						|
				_contract.location(),
 | 
						|
				string("Function signature hash collision for ") + it.second->externalSignature()
 | 
						|
			);
 | 
						|
		hashes.insert(hash);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	if (!_contract.isLibrary())
 | 
						|
		return;
 | 
						|
 | 
						|
	if (!_contract.baseContracts().empty())
 | 
						|
		m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit.");
 | 
						|
 | 
						|
	for (auto const& var: _contract.stateVariables())
 | 
						|
		if (!var->isConstant())
 | 
						|
			m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables");
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	if (*_contract.sourceUnit().annotation().useABICoderV2)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (_contract.isLibrary())
 | 
						|
	{
 | 
						|
		solAssert(
 | 
						|
			_contract.baseContracts().empty() || m_errorReporter.hasErrors(),
 | 
						|
			"Library is not allowed to inherit"
 | 
						|
		);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	SecondarySourceLocation errors;
 | 
						|
 | 
						|
	// interfaceFunctionList contains all inherited functions as well
 | 
						|
	for (auto const& func: _contract.interfaceFunctionList())
 | 
						|
	{
 | 
						|
		solAssert(func.second->hasDeclaration(), "Function has no declaration?!");
 | 
						|
 | 
						|
		if (!*func.second->declaration().sourceUnit().annotation().useABICoderV2)
 | 
						|
			continue;
 | 
						|
 | 
						|
		auto const& currentLoc = func.second->declaration().location();
 | 
						|
 | 
						|
		for (Type const* paramType: func.second->parameterTypes() + func.second->returnParameterTypes())
 | 
						|
			if (!TypeChecker::typeSupportedByOldABIEncoder(*paramType, false))
 | 
						|
			{
 | 
						|
				errors.append("Type only supported by ABIEncoderV2", currentLoc);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
	}
 | 
						|
 | 
						|
	if (!errors.infos.empty())
 | 
						|
		m_errorReporter.fatalTypeError(
 | 
						|
			6594_error,
 | 
						|
			_contract.location(),
 | 
						|
			errors,
 | 
						|
			std::string("Contract \"") +
 | 
						|
			_contract.name() +
 | 
						|
			"\" does not use ABI coder v2 but wants to inherit from a contract " +
 | 
						|
			"which uses types that require it. " +
 | 
						|
			"Use \"pragma abicoder v2;\" for the inheriting contract as well to enable the feature."
 | 
						|
		);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	if (auto const* fallback = _contract.fallbackFunction())
 | 
						|
		if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
 | 
						|
			m_errorReporter.warning(
 | 
						|
				3628_error,
 | 
						|
				_contract.location(),
 | 
						|
				"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
 | 
						|
				SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())
 | 
						|
			);
 | 
						|
}
 | 
						|
 | 
						|
void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract)
 | 
						|
{
 | 
						|
	bigint size = 0;
 | 
						|
	for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts | ranges::views::reverse)
 | 
						|
		for (VariableDeclaration const* variable: contract->stateVariables())
 | 
						|
			if (!(variable->isConstant() || variable->immutable()))
 | 
						|
			{
 | 
						|
				size += variable->annotation().type->storageSizeUpperBound();
 | 
						|
				if (size >= bigint(1) << 256)
 | 
						|
				{
 | 
						|
					m_errorReporter.typeError(7676_error, _contract.location(), "Contract requires too much storage.");
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
}
 |