diff options
author | chriseth <chris@ethereum.org> | 2018-12-03 22:48:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-03 22:48:03 +0800 |
commit | c8a2cb62832afb2dc09ccee6fd42c1516dfdb981 (patch) | |
tree | 7977e9dcbbc215088c05b847f849871ef5d4ae66 /libsolidity/analysis | |
parent | 1d4f565a64988a3400847d2655ca24f73f234bc6 (diff) | |
parent | 590be1d84cea9850ce69b68be3dc5294b39041e5 (diff) | |
download | dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.gz dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.bz2 dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.lz dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.xz dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.zst dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.zip |
Merge pull request #5571 from ethereum/develop
Version 0.5.1
Diffstat (limited to 'libsolidity/analysis')
26 files changed, 888 insertions, 694 deletions
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index f9b00927..9d041ce5 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -22,7 +22,7 @@ #include <libsolidity/analysis/ConstantEvaluator.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> using namespace std; using namespace dev; diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index ac3a24a1..23ca3628 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -24,12 +24,16 @@ #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; class TypeChecker; /** @@ -39,7 +43,7 @@ class ConstantEvaluator: private ASTConstVisitor { public: ConstantEvaluator( - ErrorReporter& _errorReporter, + langutil::ErrorReporter& _errorReporter, size_t _newDepth = 0, std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>() ): @@ -61,7 +65,7 @@ private: void setType(ASTNode const& _node, TypePointer const& _type); TypePointer type(ASTNode const& _node); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Current recursion depth. size_t m_depth = 0; std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types; diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp new file mode 100644 index 00000000..6dc564de --- /dev/null +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -0,0 +1,463 @@ +/* + 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/>. +*/ +/** + * 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 <liblangutil/ErrorReporter.h> + +#include <boost/range/adaptor/reversed.hpp> + + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace dev::solidity; + + +bool ContractLevelChecker::check(ContractDefinition const& _contract) +{ + checkDuplicateFunctions(_contract); + checkDuplicateEvents(_contract); + checkIllegalOverrides(_contract); + checkAbstractFunctions(_contract); + checkBaseConstructorArguments(_contract); + checkConstructor(_contract); + checkFallbackFunction(_contract); + checkExternalTypeClashes(_contract); + checkHashCollisions(_contract); + checkLibraryRequirements(_contract); + + return Error::containsOnlyWarnings(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; + 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::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 (EventDefinition const* event: _contract.events()) + events[event->name()].push_back(event); + + findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); +} + +template <class T> +void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message) +{ + 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 (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 + ); + } + } + } +} + +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 + map<string, vector<FunctionDefinition const*>> functions; + map<string, ModifierDefinition const*> 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 + ); +} + +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. + using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; + map<string, vector<FunTypeAndFlag>> functions; + + 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) + { + 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<FunctionType>(*v), true); + + for (FunctionDefinition const* function: contract->definedFunctions()) + if (!function->isConstructor()) + registerFunction( + *function, + make_shared<FunctionType>(*function)->asCallableFunction(false), + function->isImplemented() + ); + } + + // 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<FunctionDefinition const*>(&funAndFlag.first->declaration()); + solAssert(function, ""); + _contract.annotation().unimplementedFunctions.push_back(function); + break; + } +} + + +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( + 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) + 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." + ); + } + +} + +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."); +} + +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\"."); +} + +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 = make_shared<FunctionType>(*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<FunctionType>(*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." + ); +} + +void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract) +{ + set<FixedHash<4>> 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); + } +} + +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"); +} diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h new file mode 100644 index 00000000..15cbf45d --- /dev/null +++ b/libsolidity/analysis/ContractLevelChecker.h @@ -0,0 +1,87 @@ +/* + 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/>. +*/ +/** + * Component that verifies overloads, abstract contracts, function clashes and others + * checks at contract or function level. + */ + +#pragma once + +#include <libsolidity/ast/ASTForward.h> + +#include <map> + +namespace langutil +{ +class ErrorReporter; +} + +namespace dev +{ +namespace solidity +{ + +/** + * Component that verifies overloads, abstract contracts, function clashes and others + * checks at contract or function level. + */ +class ContractLevelChecker +{ +public: + /// @param _errorReporter provides the error logging functionality. + explicit ContractLevelChecker(langutil::ErrorReporter& _errorReporter): + m_errorReporter(_errorReporter) + {} + + /// Performs checks on the given contract. + /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings + bool check(ContractDefinition const& _contract); + +private: + /// Checks that two functions defined in this contract with the same name have different + /// arguments and that there is at most one constructor. + void checkDuplicateFunctions(ContractDefinition const& _contract); + void checkDuplicateEvents(ContractDefinition const& _contract); + template <class T> + void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message); + void checkIllegalOverrides(ContractDefinition const& _contract); + /// Reports a type error with an appropriate message if overridden function signature differs. + /// Also stores the direct super function in the AST annotations. + void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); + void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); + void checkAbstractFunctions(ContractDefinition const& _contract); + void checkBaseConstructorArguments(ContractDefinition const& _contract); + void annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode + ); + void checkConstructor(ContractDefinition const& _contract); + void checkFallbackFunction(ContractDefinition const& _contract); + /// Checks that different functions with external visibility end up having different + /// external argument types (i.e. different signature). + void checkExternalTypeClashes(ContractDefinition const& _contract); + /// Checks for hash collisions in external function signatures. + void checkHashCollisions(ContractDefinition const& _contract); + /// Checks that all requirements for a library are fulfilled if this is a library. + void checkLibraryRequirements(ContractDefinition const& _contract); + + langutil::ErrorReporter& m_errorReporter; +}; + +} +} diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 8a608552..fe58f0aa 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -16,8 +16,10 @@ */ #include <libsolidity/analysis/ControlFlowAnalyzer.h> +#include <liblangutil/SourceLocation.h> using namespace std; +using namespace langutil; using namespace dev::solidity; bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot) diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h index 43e13fb6..411d57ff 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.h +++ b/libsolidity/analysis/ControlFlowAnalyzer.h @@ -29,12 +29,12 @@ namespace solidity class ControlFlowAnalyzer: private ASTConstVisitor { public: - explicit ControlFlowAnalyzer(CFG const& _cfg, ErrorReporter& _errorReporter): + explicit ControlFlowAnalyzer(CFG const& _cfg, langutil::ErrorReporter& _errorReporter): m_cfg(_cfg), m_errorReporter(_errorReporter) {} bool analyze(ASTNode const& _astRoot); - virtual bool visit(FunctionDefinition const& _function) override; + bool visit(FunctionDefinition const& _function) override; private: static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node); @@ -45,7 +45,7 @@ private: ) const; CFG const& m_cfg; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index e9d96e5f..40605e00 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -46,19 +46,19 @@ public: private: explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow); - virtual bool visit(BinaryOperation const& _operation) override; - virtual bool visit(Conditional const& _conditional) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(Break const&) override; - virtual bool visit(Continue const&) override; - virtual bool visit(Throw const&) override; - virtual bool visit(Block const&) override; - virtual void endVisit(Block const&) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(PlaceholderStatement const&) override; - virtual bool visit(FunctionCall const& _functionCall) override; + bool visit(BinaryOperation const& _operation) override; + bool visit(Conditional const& _conditional) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(ForStatement const& _forStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + bool visit(Break const&) override; + bool visit(Continue const&) override; + bool visit(Throw const&) override; + bool visit(Block const&) override; + void endVisit(Block const&) override; + bool visit(Return const& _return) override; + bool visit(PlaceholderStatement const&) override; + bool visit(FunctionCall const& _functionCall) override; /// Appends the control flow of @a _node to the current control flow. @@ -74,7 +74,7 @@ private: protected: - virtual bool visitNode(ASTNode const& node) override; + bool visitNode(ASTNode const& node) override; private: diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp index 9b3da0eb..b8860158 100644 --- a/libsolidity/analysis/ControlFlowGraph.cpp +++ b/libsolidity/analysis/ControlFlowGraph.cpp @@ -23,6 +23,7 @@ #include <algorithm> using namespace std; +using namespace langutil; using namespace dev::solidity; bool CFG::constructFlow(ASTNode const& _astRoot) @@ -133,4 +134,4 @@ void CFG::applyModifierFlowToFunctionFlow( _functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry]; _functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit]; -}
\ No newline at end of file +} diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index c646e4f1..8fe9fe8e 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -19,7 +19,7 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ASTVisitor.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <map> #include <memory> @@ -101,12 +101,12 @@ struct ModifierFlow: FunctionFlow class CFG: private ASTConstVisitor { public: - explicit CFG(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + explicit CFG(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool constructFlow(ASTNode const& _astRoot); - virtual bool visit(ModifierDefinition const& _modifier) override; - virtual bool visit(FunctionDefinition const& _function) override; + bool visit(ModifierDefinition const& _modifier) override; + bool visit(FunctionDefinition const& _function) override; FunctionFlow const& functionFlow(FunctionDefinition const& _function) const; @@ -133,7 +133,7 @@ private: FunctionFlow* _functionFlow ); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Node container. /// All nodes allocated during the construction of the control flow graph diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index c1b97def..69a7a43c 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -23,11 +23,12 @@ #include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libsolidity/parsing/DocStringParser.h> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit) diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 5d339428..f6b236db 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -25,13 +25,16 @@ #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * Parses and analyses the doc strings. * Stores the parsing results in the AST annotations and reports errors. @@ -39,14 +42,14 @@ class ErrorReporter; class DocStringAnalyser: private ASTConstVisitor { public: - DocStringAnalyser(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + DocStringAnalyser(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool analyseDocStrings(SourceUnit const& _sourceUnit); private: - virtual bool visit(ContractDefinition const& _contract) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(ModifierDefinition const& _modifier) override; - virtual bool visit(EventDefinition const& _event) override; + bool visit(ContractDefinition const& _contract) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(ModifierDefinition const& _modifier) override; + bool visit(EventDefinition const& _event) override; void checkParameters( CallableDeclaration const& _callable, @@ -75,7 +78,7 @@ private: void appendError(std::string const& _description); bool m_errorOccured = false; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index b452a49a..0528a200 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -24,12 +24,13 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/TypeChecker.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libdevcore/StringUtils.h> #include <boost/algorithm/string.hpp> using namespace std; +using namespace langutil; namespace dev { @@ -59,7 +60,7 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode { DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); } - catch (FatalError const&) + catch (langutil::FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. @@ -129,7 +130,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi { return resolveNamesAndTypesInternal(_node, _resolveInsideCode); } - catch (FatalError const&) + catch (langutil::FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. @@ -144,7 +145,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true); solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope."); } - catch (FatalError const&) + catch (langutil::FatalError const&) { if (m_errorReporter.errors().empty()) throw; // Something is weird here, rather throw again. @@ -491,9 +492,9 @@ bool DeclarationRegistrationHelper::registerDeclaration( Declaration const* conflictingDeclaration = _container.conflictingDeclaration(_declaration, _name); solAssert(conflictingDeclaration, ""); bool const comparable = - _errorLocation->sourceName && - conflictingDeclaration->location().sourceName && - *_errorLocation->sourceName == *conflictingDeclaration->location().sourceName; + _errorLocation->source && + conflictingDeclaration->location().source && + _errorLocation->source->name() == conflictingDeclaration->location().source->name(); if (comparable && _errorLocation->start < conflictingDeclaration->location().start) { firstDeclarationLocation = *_errorLocation; diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index a72c21e3..1b034ef4 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -30,13 +30,16 @@ #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTAnnotations.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * Resolves name references, typenames and sets the (explicitly given) types for all variable * declarations. @@ -50,7 +53,7 @@ public: NameAndTypeResolver( std::vector<Declaration const*> const& _globals, std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, - ErrorReporter& _errorReporter + langutil::ErrorReporter& _errorReporter ); /// Registers all declarations found in the AST node, usually a source unit. /// @returns false in case of error. @@ -125,7 +128,7 @@ private: std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; DeclarationContainer* m_currentScope = nullptr; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; /** @@ -142,7 +145,7 @@ public: DeclarationRegistrationHelper( std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, - ErrorReporter& _errorReporter, + langutil::ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -150,10 +153,10 @@ public: DeclarationContainer& _container, Declaration const& _declaration, std::string const* _name, - SourceLocation const* _errorLocation, + langutil::SourceLocation const* _errorLocation, bool _warnOnShadow, bool _inactive, - ErrorReporter& _errorReporter + langutil::ErrorReporter& _errorReporter ); private: @@ -194,7 +197,7 @@ private: std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index 240d7973..27cbcd45 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -18,7 +18,7 @@ #include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/SemVerHandler.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libsolidity/interface/Version.h> #include <libdevcore/Algorithms.h> @@ -29,6 +29,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index 4f9dac6e..e428b81a 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -23,13 +23,17 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * This module performs analyses on the AST that are done after type checking and assignments of types: * - whether there are circular references in constant state variables @@ -39,25 +43,25 @@ class PostTypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - PostTypeChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + PostTypeChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool check(ASTNode const& _astRoot); private: /// Adds a new error to the list of errors. - void typeError(SourceLocation const& _location, std::string const& _description); + void typeError(langutil::SourceLocation const& _location, std::string const& _description); - virtual bool visit(ContractDefinition const& _contract) override; - virtual void endVisit(ContractDefinition const& _contract) override; + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; - virtual bool visit(VariableDeclaration const& _variable) override; - virtual void endVisit(VariableDeclaration const& _variable) override; + bool visit(VariableDeclaration const& _variable) override; + void endVisit(VariableDeclaration const& _variable) override; - virtual bool visit(Identifier const& _identifier) override; + bool visit(Identifier const& _identifier) override; VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; VariableDeclaration const* m_currentConstVariable = nullptr; std::vector<VariableDeclaration const*> m_constVariables; ///< Required for determinism. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 2adc8e77..c4931d98 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -23,12 +23,12 @@ #include <libsolidity/analysis/ReferencesResolver.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/NameAndTypeResolver.h> -#include <libsolidity/interface/Exceptions.h> #include <libsolidity/analysis/ConstantEvaluator.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Exceptions.h> #include <libdevcore/StringUtils.h> @@ -36,9 +36,12 @@ #include <boost/range/adaptor/transformed.hpp> using namespace std; -using namespace dev; -using namespace dev::solidity; +using namespace langutil; +namespace dev +{ +namespace solidity +{ bool ReferencesResolver::resolve(ASTNode const& _root) { @@ -270,7 +273,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) ErrorList errors; ErrorReporter errorsIgnored(errors); yul::ExternalIdentifierAccess::Resolver resolver = - [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { + [&](yul::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); @@ -311,9 +314,9 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information // We use the latest EVM version because we will re-run it anyway. - assembly::AsmAnalysisInfo analysisInfo; + yul::AsmAnalysisInfo analysisInfo; boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); + yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } @@ -454,3 +457,6 @@ void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, m_errorOccurred = true; m_errorReporter.fatalDeclarationError(_location, _description); } + +} +} diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 24ec4643..32c0553f 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -28,12 +28,17 @@ #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTAnnotations.h> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity { -class ErrorReporter; class NameAndTypeResolver; /** @@ -44,7 +49,7 @@ class ReferencesResolver: private ASTConstVisitor { public: ReferencesResolver( - ErrorReporter& _errorReporter, + langutil::ErrorReporter& _errorReporter, NameAndTypeResolver& _resolver, bool _resolveInsideCode = false ): @@ -57,38 +62,38 @@ public: bool resolve(ASTNode const& _root); private: - virtual bool visit(Block const& _block) override; - virtual void endVisit(Block const& _block) override; - virtual bool visit(ForStatement const& _for) override; - virtual void endVisit(ForStatement const& _for) override; - virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; - virtual bool visit(Identifier const& _identifier) override; - virtual bool visit(ElementaryTypeName const& _typeName) override; - virtual bool visit(FunctionDefinition const& _functionDefinition) override; - virtual void endVisit(FunctionDefinition const& _functionDefinition) override; - virtual bool visit(ModifierDefinition const& _modifierDefinition) override; - virtual void endVisit(ModifierDefinition const& _modifierDefinition) override; - virtual void endVisit(UserDefinedTypeName const& _typeName) override; - virtual void endVisit(FunctionTypeName const& _typeName) override; - virtual void endVisit(Mapping const& _typeName) override; - virtual void endVisit(ArrayTypeName const& _typeName) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(Return const& _return) override; - virtual void endVisit(VariableDeclaration const& _variable) override; + bool visit(Block const& _block) override; + void endVisit(Block const& _block) override; + bool visit(ForStatement const& _for) override; + void endVisit(ForStatement const& _for) override; + void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; + bool visit(Identifier const& _identifier) override; + bool visit(ElementaryTypeName const& _typeName) override; + bool visit(FunctionDefinition const& _functionDefinition) override; + void endVisit(FunctionDefinition const& _functionDefinition) override; + bool visit(ModifierDefinition const& _modifierDefinition) override; + void endVisit(ModifierDefinition const& _modifierDefinition) override; + void endVisit(UserDefinedTypeName const& _typeName) override; + void endVisit(FunctionTypeName const& _typeName) override; + void endVisit(Mapping const& _typeName) override; + void endVisit(ArrayTypeName const& _typeName) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(Return const& _return) override; + void endVisit(VariableDeclaration const& _variable) override; /// Adds a new error to the list of errors. - void typeError(SourceLocation const& _location, std::string const& _description); + void typeError(langutil::SourceLocation const& _location, std::string const& _description); /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalTypeError(SourceLocation const& _location, std::string const& _description); + void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description); /// Adds a new error to the list of errors. - void declarationError(SourceLocation const& _location, std::string const& _description); + void declarationError(langutil::SourceLocation const& _location, std::string const& _description); /// Adds a new error to the list of errors and throws to abort reference resolving. - void fatalDeclarationError(SourceLocation const& _location, std::string const& _description); + void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; /// Stack of return parameters. std::vector<ParameterList const*> m_returnParameters; diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h index 03a557c5..80185612 100644 --- a/libsolidity/analysis/SemVerHandler.h +++ b/libsolidity/analysis/SemVerHandler.h @@ -22,8 +22,9 @@ #pragma once -#include <vector> #include <libsolidity/parsing/Token.h> +#include <string> +#include <vector> namespace dev { diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 487a5cca..38391841 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -23,11 +23,12 @@ #include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/ConstantEvaluator.h> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <memory> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 7f5c743a..ff33fa3a 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -28,6 +28,11 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity @@ -44,7 +49,7 @@ class StaticAnalyzer: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - explicit StaticAnalyzer(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} /// Performs static analysis on the given source unit and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings @@ -52,25 +57,25 @@ public: private: - virtual bool visit(ContractDefinition const& _contract) override; - virtual void endVisit(ContractDefinition const& _contract) override; + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual void endVisit(FunctionDefinition const& _function) override; + bool visit(FunctionDefinition const& _function) override; + void endVisit(FunctionDefinition const& _function) override; - virtual bool visit(ExpressionStatement const& _statement) override; - virtual bool visit(VariableDeclaration const& _variable) override; - virtual bool visit(Identifier const& _identifier) override; - virtual bool visit(Return const& _return) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(BinaryOperation const& _operation) override; - virtual bool visit(FunctionCall const& _functionCall) override; + bool visit(ExpressionStatement const& _statement) override; + bool visit(VariableDeclaration const& _variable) override; + bool visit(Identifier const& _identifier) override; + bool visit(Return const& _return) override; + bool visit(MemberAccess const& _memberAccess) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(BinaryOperation const& _operation) override; + bool visit(FunctionCall const& _functionCall) override; /// @returns the size of this type in storage, including all sub-types. static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen); - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Flag that indicates whether the current contract definition is a library. bool m_library = false; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 3f9f8373..a73d7e5c 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -20,7 +20,7 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ExperimentalFeatures.h> #include <libsolidity/analysis/SemVerHandler.h> -#include <libsolidity/interface/ErrorReporter.h> +#include <liblangutil/ErrorReporter.h> #include <libsolidity/interface/Version.h> #include <boost/algorithm/cxx11/all_of.hpp> @@ -29,6 +29,7 @@ using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index f5716bf9..c2463955 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -23,6 +23,11 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity @@ -39,49 +44,49 @@ class SyntaxChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - SyntaxChecker(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + SyntaxChecker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} bool checkSyntax(ASTNode const& _astRoot); private: - virtual bool visit(SourceUnit const& _sourceUnit) override; - virtual void endVisit(SourceUnit const& _sourceUnit) override; - virtual bool visit(PragmaDirective const& _pragma) override; + bool visit(SourceUnit const& _sourceUnit) override; + void endVisit(SourceUnit const& _sourceUnit) override; + bool visit(PragmaDirective const& _pragma) override; - virtual bool visit(ModifierDefinition const& _modifier) override; - virtual void endVisit(ModifierDefinition const& _modifier) override; + bool visit(ModifierDefinition const& _modifier) override; + void endVisit(ModifierDefinition const& _modifier) override; /// Reports an error if _statement is a VariableDeclarationStatement. /// Used by if/while/for to check for single statement variable declarations /// without a block. void checkSingleStatementVariableDeclaration(ASTNode const& _statement); - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual void endVisit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual void endVisit(ForStatement const& _forStatement) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + void endVisit(WhileStatement const& _whileStatement) override; + bool visit(ForStatement const& _forStatement) override; + void endVisit(ForStatement const& _forStatement) override; - virtual bool visit(Continue const& _continueStatement) override; - virtual bool visit(Break const& _breakStatement) override; + bool visit(Continue const& _continueStatement) override; + bool visit(Break const& _breakStatement) override; - virtual bool visit(Throw const& _throwStatement) override; + bool visit(Throw const& _throwStatement) override; - virtual bool visit(UnaryOperation const& _operation) override; + bool visit(UnaryOperation const& _operation) override; - virtual bool visit(PlaceholderStatement const& _placeholderStatement) override; + bool visit(PlaceholderStatement const& _placeholderStatement) override; - virtual bool visit(ContractDefinition const& _contract) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(FunctionTypeName const& _node) override; + bool visit(ContractDefinition const& _contract) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(FunctionTypeName const& _node) override; - virtual bool visit(VariableDeclarationStatement const& _statement) override; + bool visit(VariableDeclarationStatement const& _statement) override; - virtual bool visit(StructDefinition const& _struct) override; - virtual bool visit(Literal const& _literal) override; + bool visit(StructDefinition const& _struct) override; + bool visit(Literal const& _literal) override; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; /// Flag that indicates whether a function modifier actually contains '_'. bool m_placeholderFound = false; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c5e6488b..9350df05 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -21,20 +21,27 @@ */ #include <libsolidity/analysis/TypeChecker.h> -#include <memory> +#include <libsolidity/ast/AST.h> + +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> + +#include <liblangutil/ErrorReporter.h> + +#include <libdevcore/Algorithms.h> +#include <libdevcore/StringUtils.h> + #include <boost/algorithm/cxx11/all_of.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/join.hpp> -#include <boost/range/adaptor/reversed.hpp> -#include <libsolidity/ast/AST.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/interface/ErrorReporter.h> -#include <libdevcore/Algorithms.h> + +#include <memory> +#include <vector> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace @@ -81,418 +88,14 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { m_scope = &_contract; - // We force our own visiting order here. The structs have to be excluded below. - set<ASTNode const*> visited; - for (auto const& s: _contract.definedStructs()) - visited.insert(s); - ASTNode::listAccept(_contract.definedStructs(), *this); ASTNode::listAccept(_contract.baseContracts(), *this); - checkContractDuplicateFunctions(_contract); - checkContractDuplicateEvents(_contract); - checkContractIllegalOverrides(_contract); - checkContractAbstractFunctions(_contract); - checkContractBaseConstructorArguments(_contract); - - FunctionDefinition const* function = _contract.constructor(); - if (function) - { - if (!function->returnParameters().empty()) - m_errorReporter.typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); - if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - function->location(), - "Constructor must be payable or non-payable, but is \"" + - stateMutabilityToString(function->stateMutability()) + - "\"." - ); - if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal) - m_errorReporter.typeError(function->location(), "Constructor must be public or internal."); - } - - for (FunctionDefinition const* function: _contract.definedFunctions()) - if (function->isFallback()) - { - if (_contract.isLibrary()) - m_errorReporter.typeError(function->location(), "Libraries cannot have fallback functions."); - if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - function->location(), - "Fallback function must be payable or non-payable, but is \"" + - stateMutabilityToString(function->stateMutability()) + - "\"." - ); - if (!function->parameters().empty()) - m_errorReporter.typeError(function->parameterList().location(), "Fallback function cannot take parameters."); - if (!function->returnParameters().empty()) - m_errorReporter.typeError(function->returnParameterList()->location(), "Fallback function cannot return values."); - if (function->visibility() != FunctionDefinition::Visibility::External) - m_errorReporter.typeError(function->location(), "Fallback function must be defined as \"external\"."); - } - for (auto const& n: _contract.subNodes()) - if (!visited.count(n.get())) - n->accept(*this); - - checkContractExternalTypeClashes(_contract); - // check for hash collisions in function signatures - set<FixedHash<4>> 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); - } - - if (_contract.isLibrary()) - checkLibraryRequirements(_contract); + n->accept(*this); return false; } -void TypeChecker::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<string, vector<FunctionDefinition const*>> 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 TypeChecker::checkContractDuplicateEvents(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 (EventDefinition const* event: _contract.events()) - events[event->name()].push_back(event); - - findDuplicateDefinitions(events, "Event with same name and arguments defined twice."); -} - -template <class T> -void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message) -{ - 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 (FunctionType(*overloads[i]).hasEqualParameterTypes(FunctionType(*overloads[j]))) - { - 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 - ); - } - } - } -} - -void TypeChecker::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<FunctionTypePointer, bool>; - map<string, vector<FunTypeAndFlag>> 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<FunctionType>(*function); - 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<FunctionDefinition const*>(&funAndFlag.first->declaration()); - solAssert(function, ""); - _contract.annotation().unimplementedFunctions.push_back(function); - break; - } -} - -void TypeChecker::checkContractBaseConstructorArguments(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*>(&dereference(*modifier->name()))) - { - 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<InheritanceSpecifier> const& base: contract->baseContracts()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name())); - 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 TypeChecker::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." - ); - } - -} - -void TypeChecker::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<string, vector<FunctionDefinition const*>> functions; - map<string, ModifierDefinition const*> 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 TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) -{ - FunctionType functionType(function); - FunctionType superType(super); - - if (!functionType.hasEqualParameterTypes(superType)) - return; - - if (!function.annotation().superFunction) - function.annotation().superFunction = &super; - - if (function.visibility() != super.visibility()) - { - // visibility is enforced to be external in interfaces, but a contract can override that with public - if ( - super.inContractKind() == ContractDefinition::ContractKind::Interface && - function.inContractKind() != ContractDefinition::ContractKind::Interface && - function.visibility() == FunctionDefinition::Visibility::Public - ) - return; - overrideError(function, super, "Overriding function visibility differs."); - } - - else if (function.stateMutability() != super.stateMutability()) - overrideError( - function, - super, - "Overriding function changes state mutability from \"" + - stateMutabilityToString(super.stateMutability()) + - "\" to \"" + - stateMutabilityToString(function.stateMutability()) + - "\"." - ); - - else if (functionType != superType) - overrideError(function, super, "Overriding function return types differ."); -} - -void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message) -{ - m_errorReporter.typeError( - function.location(), - SecondarySourceLocation().append("Overridden function is here:", super.location()), - message - ); -} - -void TypeChecker::checkContractExternalTypeClashes(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 = make_shared<FunctionType>(*f); - // under non error circumstances this should be true - if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(f, functionType) - ); - } - for (VariableDeclaration const* v: contract->stateVariables()) - if (v->isPartOfExternalInterface()) - { - auto functionType = make_shared<FunctionType>(*v); - // under non error circumstances this should be true - if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].push_back( - make_pair(v, functionType) - ); - } - } - 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." - ); -} - -void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract) -{ - solAssert(_contract.isLibrary(), ""); - 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"); -} - void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) { TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide())); @@ -697,20 +300,22 @@ bool TypeChecker::visit(FunctionDefinition const& _function) } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { + if (type(*var)->category() == Type::Category::Mapping) + { + if (!type(*var)->dataStoredIn(DataLocation::Storage)) + m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." ); + else if (!isLibraryFunction && _function.isPublic()) + m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions."); + } + else + { + if (!type(*var)->canLiveOutsideStorage() && _function.isPublic()) + m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); + if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction))) + m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); + } if ( - type(*var)->category() == Type::Category::Mapping && - !type(*var)->dataStoredIn(DataLocation::Storage) - ) - m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"."); - else if ( - !type(*var)->canLiveOutsideStorage() && - _function.visibility() > FunctionDefinition::Visibility::Internal - ) - m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); - if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) - m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); - if ( - _function.visibility() > FunctionDefinition::Visibility::Internal && + _function.isPublic() && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && !typeSupportedByOldABIEncoder(*type(*var)) ) @@ -775,15 +380,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); - // Variables can be declared without type (with "var"), in which case the first assignment - // sets the type. - // Note that assignments before the first declaration are legal because of the special scoping - // rules inherited from JavaScript. - // type is filled either by ReferencesResolver directly from the type name or by // TypeChecker at the VariableDeclarationStatement level. TypePointer varType = _variable.annotation().type; - solAssert(!!varType, "Failed to infer variable type."); + solAssert(!!varType, "Variable type not provided."); if (_variable.value()) expectType(*_variable.value(), *varType); @@ -812,11 +412,25 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!varType->canLiveOutsideStorage()) m_errorReporter.typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage."); } - else if ( - _variable.visibility() >= VariableDeclaration::Visibility::Public && - !FunctionType(_variable).interfaceFunctionType() - ) - m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); + else if (_variable.visibility() >= VariableDeclaration::Visibility::Public) + { + FunctionType getter(_variable); + if (!_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)) + { + vector<string> unsupportedTypes; + for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes()) + if (!typeSupportedByOldABIEncoder(*param)) + unsupportedTypes.emplace_back(param->toString()); + if (!unsupportedTypes.empty()) + m_errorReporter.typeError(_variable.location(), + "The following types are only supported for getters in the new experimental ABI encoder: " + + joinHumanReadable(unsupportedTypes) + + ". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature." + ); + } + if (!getter.interfaceFunctionType()) + m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); + } switch (varType->category()) { @@ -953,7 +567,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( - assembly::Identifier const& _identifier, + yul::Identifier const& _identifier, yul::IdentifierContext _context, bool ) @@ -1038,13 +652,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(1); }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); - _inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - assembly::AsmAnalyzer analyzer( + _inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>(); + yul::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, m_evmVersion, Error::Type::SyntaxError, - assembly::AsmFlavour::Loose, + yul::AsmFlavour::Loose, identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) @@ -1590,6 +1204,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { if (!inlineArrayType) m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements."); + else if (!inlineArrayType->canLiveOutsideStorage()) + m_errorReporter.fatalTypeError(_tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); + _tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size()); } else @@ -1806,26 +1423,6 @@ void TypeChecker::typeCheckFunctionCall( "\"staticcall\" is not supported by the VM version." ); - // Check for deprecated function names - if (_functionType->kind() == FunctionType::Kind::KECCAK256) - { - if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) - if (functionName->name() == "sha3") - m_errorReporter.typeError( - _functionCall.location(), - "\"sha3\" has been deprecated in favour of \"keccak256\"" - ); - } - else if (_functionType->kind() == FunctionType::Kind::Selfdestruct) - { - if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) - if (functionName->name() == "suicide") - m_errorReporter.typeError( - _functionCall.location(), - "\"suicide\" has been deprecated in favour of \"selfdestruct\"" - ); - } - // Check for event outside of emit statement if (!m_insideEmitStatement && _functionType->kind() == FunctionType::Kind::Event) m_errorReporter.typeError( @@ -2488,7 +2085,7 @@ bool TypeChecker::visit(IndexAccess const& _access) } else { - expectType(*index, IntegerType(256)); + expectType(*index, IntegerType::uint256()); if (!m_errorReporter.hasErrors()) if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get())) { @@ -2519,7 +2116,7 @@ bool TypeChecker::visit(IndexAccess const& _access) resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType())); else { - expectType(*index, IntegerType(256)); + expectType(*index, IntegerType::uint256()); if (auto length = dynamic_cast<RationalNumberType const*>(type(*index).get())) resultType = make_shared<TypeType>(make_shared<ArrayType>( DataLocation::Memory, @@ -2538,7 +2135,7 @@ bool TypeChecker::visit(IndexAccess const& _access) m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else { - if (!expectType(*index, IntegerType(256))) + if (!expectType(*index, IntegerType::uint256())) m_errorReporter.fatalTypeError(_access.location(), "Index expression cannot be represented as an unsigned integer."); if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) @@ -2621,6 +2218,23 @@ bool TypeChecker::visit(Identifier const& _identifier) else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration)) if (dynamic_cast<FunctionType const*>(annotation.type.get())) annotation.isPure = true; + + // Check for deprecated function names. + // The check is done here for the case without an actual function call. + if (FunctionType const* fType = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get())) + { + if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256) + m_errorReporter.typeError( + _identifier.location(), + "\"sha3\" has been deprecated in favour of \"keccak256\"" + ); + else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct) + m_errorReporter.typeError( + _identifier.location(), + "\"suicide\" has been deprecated in favour of \"selfdestruct\"" + ); + } + return false; } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index c76fa466..ebfcdadc 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -22,20 +22,23 @@ #pragma once -#include <libsolidity/interface/EVMVersion.h> +#include <liblangutil/EVMVersion.h> #include <libsolidity/ast/Types.h> #include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> +namespace langutil +{ +class ErrorReporter; +} + namespace dev { namespace solidity { -class ErrorReporter; - /** * The module that performs type analysis on the AST, checks the applicability of operations on * those types and stores errors for invalid operations. @@ -45,7 +48,7 @@ class TypeChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - TypeChecker(EVMVersion _evmVersion, ErrorReporter& _errorReporter): + TypeChecker(EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter): m_evmVersion(_evmVersion), m_errorReporter(_errorReporter) {} @@ -62,28 +65,7 @@ public: private: - virtual bool visit(ContractDefinition const& _contract) override; - /// Checks that two functions defined in this contract with the same name have different - /// arguments and that there is at most one constructor. - void checkContractDuplicateFunctions(ContractDefinition const& _contract); - void checkContractDuplicateEvents(ContractDefinition const& _contract); - void checkContractIllegalOverrides(ContractDefinition const& _contract); - /// Reports a type error with an appropriate message if overridden function signature differs. - /// Also stores the direct super function in the AST annotations. - 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 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); - /// Checks that all requirements for a library are fulfilled if this is a library. - void checkLibraryRequirements(ContractDefinition const& _contract); + bool visit(ContractDefinition const& _contract) override; /// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage. /// Should only be called if the left hand side is tuple-typed. void checkDoubleStorageAssignment(Assignment const& _assignment); @@ -122,40 +104,37 @@ private: FunctionTypePointer _functionType ); - virtual void endVisit(InheritanceSpecifier const& _inheritance) override; - virtual void endVisit(UsingForDirective const& _usingFor) override; - virtual bool visit(StructDefinition const& _struct) override; - virtual bool visit(FunctionDefinition const& _function) override; - virtual bool visit(VariableDeclaration const& _variable) override; + void endVisit(InheritanceSpecifier const& _inheritance) override; + void endVisit(UsingForDirective const& _usingFor) override; + bool visit(StructDefinition const& _struct) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(VariableDeclaration const& _variable) override; /// We need to do this manually because we want to pass the bases of the current contract in /// case this is a base constructor call. void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases); - virtual bool visit(EventDefinition const& _eventDef) override; - virtual void endVisit(FunctionTypeName const& _funType) override; - virtual bool visit(InlineAssembly const& _inlineAssembly) override; - virtual bool visit(IfStatement const& _ifStatement) override; - virtual bool visit(WhileStatement const& _whileStatement) override; - virtual bool visit(ForStatement const& _forStatement) override; - virtual void endVisit(Return const& _return) override; - virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } - virtual void endVisit(EmitStatement const& _emit) override; - virtual bool visit(VariableDeclarationStatement const& _variable) override; - virtual void endVisit(ExpressionStatement const& _statement) override; - virtual bool visit(Conditional const& _conditional) override; - virtual bool visit(Assignment const& _assignment) override; - virtual bool visit(TupleExpression const& _tuple) override; - virtual void endVisit(BinaryOperation const& _operation) override; - virtual bool visit(UnaryOperation const& _operation) override; - virtual bool visit(FunctionCall const& _functionCall) override; - virtual void endVisit(NewExpression const& _newExpression) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual bool visit(IndexAccess const& _indexAccess) override; - virtual bool visit(Identifier const& _identifier) override; - virtual void endVisit(ElementaryTypeNameExpression const& _expr) override; - virtual void endVisit(Literal const& _literal) override; - - template <class T> - void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message); + bool visit(EventDefinition const& _eventDef) override; + void endVisit(FunctionTypeName const& _funType) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(WhileStatement const& _whileStatement) override; + bool visit(ForStatement const& _forStatement) override; + void endVisit(Return const& _return) override; + bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } + void endVisit(EmitStatement const& _emit) override; + bool visit(VariableDeclarationStatement const& _variable) override; + void endVisit(ExpressionStatement const& _statement) override; + bool visit(Conditional const& _conditional) override; + bool visit(Assignment const& _assignment) override; + bool visit(TupleExpression const& _tuple) override; + void endVisit(BinaryOperation const& _operation) override; + bool visit(UnaryOperation const& _operation) override; + bool visit(FunctionCall const& _functionCall) override; + void endVisit(NewExpression const& _newExpression) override; + bool visit(MemberAccess const& _memberAccess) override; + bool visit(IndexAccess const& _indexAccess) override; + bool visit(Identifier const& _identifier) override; + void endVisit(ElementaryTypeNameExpression const& _expr) override; + void endVisit(Literal const& _literal) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, @@ -183,7 +162,7 @@ private: /// Flag indicating whether we are currently inside a StructDefinition. bool m_insideStruct = false; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index b0cacc43..1112d682 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -19,13 +19,16 @@ #include <libevmasm/SemanticInformation.h> -#include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/ast/ExperimentalFeatures.h> +#include <libyul/AsmData.h> + +#include <liblangutil/ErrorReporter.h> #include <functional> using namespace std; using namespace dev; +using namespace langutil; using namespace dev::solidity; namespace @@ -37,48 +40,48 @@ public: explicit AssemblyViewPureChecker(std::function<void(StateMutability, SourceLocation const&)> _reportMutability): m_reportMutability(_reportMutability) {} - void operator()(assembly::Label const&) { } - void operator()(assembly::Instruction const& _instruction) + void operator()(yul::Label const&) { } + void operator()(yul::Instruction const& _instruction) { checkInstruction(_instruction.location, _instruction.instruction); } - void operator()(assembly::Literal const&) {} - void operator()(assembly::Identifier const&) {} - void operator()(assembly::FunctionalInstruction const& _instr) + void operator()(yul::Literal const&) {} + void operator()(yul::Identifier const&) {} + void operator()(yul::FunctionalInstruction const& _instr) { checkInstruction(_instr.location, _instr.instruction); for (auto const& arg: _instr.arguments) boost::apply_visitor(*this, arg); } - void operator()(assembly::ExpressionStatement const& _expr) + void operator()(yul::ExpressionStatement const& _expr) { boost::apply_visitor(*this, _expr.expression); } - void operator()(assembly::StackAssignment const&) {} - void operator()(assembly::Assignment const& _assignment) + void operator()(yul::StackAssignment const&) {} + void operator()(yul::Assignment const& _assignment) { boost::apply_visitor(*this, *_assignment.value); } - void operator()(assembly::VariableDeclaration const& _varDecl) + void operator()(yul::VariableDeclaration const& _varDecl) { if (_varDecl.value) boost::apply_visitor(*this, *_varDecl.value); } - void operator()(assembly::FunctionDefinition const& _funDef) + void operator()(yul::FunctionDefinition const& _funDef) { (*this)(_funDef.body); } - void operator()(assembly::FunctionCall const& _funCall) + void operator()(yul::FunctionCall const& _funCall) { for (auto const& arg: _funCall.arguments) boost::apply_visitor(*this, arg); } - void operator()(assembly::If const& _if) + void operator()(yul::If const& _if) { boost::apply_visitor(*this, *_if.condition); (*this)(_if.body); } - void operator()(assembly::Switch const& _switch) + void operator()(yul::Switch const& _switch) { boost::apply_visitor(*this, *_switch.expression); for (auto const& _case: _switch.cases) @@ -88,14 +91,14 @@ public: (*this)(_case.body); } } - void operator()(assembly::ForLoop const& _for) + void operator()(yul::ForLoop const& _for) { (*this)(_for.pre); boost::apply_visitor(*this, *_for.condition); (*this)(_for.body); (*this)(_for.post); } - void operator()(assembly::Block const& _block) + void operator()(yul::Block const& _block) { for (auto const& s: _block.statements) boost::apply_visitor(*this, s); diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index faa5b698..fd2432a7 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -21,11 +21,15 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTVisitor.h> -#include <libsolidity/interface/ErrorReporter.h> - #include <map> #include <memory> +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + namespace dev { namespace solidity @@ -34,7 +38,7 @@ namespace solidity class ViewPureChecker: private ASTConstVisitor { public: - ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, ErrorReporter& _errorReporter): + ViewPureChecker(std::vector<std::shared_ptr<ASTNode>> const& _ast, langutil::ErrorReporter& _errorReporter): m_ast(_ast), m_errorReporter(_errorReporter) {} bool check(); @@ -43,34 +47,34 @@ private: struct MutabilityAndLocation { StateMutability mutability; - SourceLocation location; + langutil::SourceLocation location; }; - virtual bool visit(FunctionDefinition const& _funDef) override; - virtual void endVisit(FunctionDefinition const& _funDef) override; - virtual bool visit(ModifierDefinition const& _modifierDef) override; - virtual void endVisit(ModifierDefinition const& _modifierDef) override; - virtual void endVisit(Identifier const& _identifier) override; - virtual bool visit(MemberAccess const& _memberAccess) override; - virtual void endVisit(MemberAccess const& _memberAccess) override; - virtual void endVisit(IndexAccess const& _indexAccess) override; - virtual void endVisit(ModifierInvocation const& _modifier) override; - virtual void endVisit(FunctionCall const& _functionCall) override; - virtual void endVisit(InlineAssembly const& _inlineAssembly) override; + bool visit(FunctionDefinition const& _funDef) override; + void endVisit(FunctionDefinition const& _funDef) override; + bool visit(ModifierDefinition const& _modifierDef) override; + void endVisit(ModifierDefinition const& _modifierDef) override; + void endVisit(Identifier const& _identifier) override; + bool visit(MemberAccess const& _memberAccess) override; + void endVisit(MemberAccess const& _memberAccess) override; + void endVisit(IndexAccess const& _indexAccess) override; + void endVisit(ModifierInvocation const& _modifier) override; + void endVisit(FunctionCall const& _functionCall) override; + void endVisit(InlineAssembly const& _inlineAssembly) override; /// Called when an element of mutability @a _mutability is encountered. /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. void reportMutability( StateMutability _mutability, - SourceLocation const& _location, - boost::optional<SourceLocation> const& _nestedLocation = {} + langutil::SourceLocation const& _location, + boost::optional<langutil::SourceLocation> const& _nestedLocation = {} ); std::vector<std::shared_ptr<ASTNode>> const& m_ast; - ErrorReporter& m_errorReporter; + langutil::ErrorReporter& m_errorReporter; bool m_errors = false; - MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, SourceLocation()}; + MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()}; FunctionDefinition const* m_currentFunction = nullptr; std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability; }; |