diff --git a/CMakeLists.txt b/CMakeLists.txt index 8da44a9..4fec256 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ include(ViteUtils) include(EthToolchains) -option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) +option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" ON) option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF) # Let's find our dependencies @@ -37,7 +37,7 @@ find_package(Threads) include(EthPolicy) eth_policy() -set(PROJECT_VERSION "0.8.0") +set(PROJECT_VERSION "0.8.1") # Figure out what compiler and system are we using include(ViteCompilerSettings) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 6b664f5..3d2c713 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -317,7 +317,7 @@ static std::map const c_instructionInfo = { Instruction::CALLCODE, { "CALLCODE", 0, 5, 1, true, Tier::Special } }, { Instruction::RETURN, { "RETURN", 0, 2, 0, true, Tier::Zero } }, - { Instruction::DELEGATECALL, { "DELEGATECALL", 0, 6, 1, true, Tier::Special } }, + { Instruction::DELEGATECALL, { "DELEGATECALL", 0, 5, 1, true, Tier::Special } }, // Solidity++: { Instruction::SYNCCALL, { "SYNCCALL", 0, 6, 0, true, Tier::Special } }, diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 65733e2..0c2fe22 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -128,10 +128,6 @@ namespace solidity::langutil K(Send, "send", 0) \ \ K(Event, "event", 0) \ - /* Solidity++: message keyword */ \ - K(Message, "message", 0) \ - /* Solidity++: offchain keyword */ \ - K(Offchain, "offchain", 0) \ \ K(External, "external", 0) \ K(Fallback, "fallback", 0) \ @@ -250,8 +246,6 @@ namespace solidity::langutil K(Var, "var", 0) \ \ /* Solidity++ keywords */ \ - K(OnMessage, "onMessage", 0) \ - K(Getter, "getter", 0) \ K(Async, "async", 0) \ K(Await, "await", 0) \ \ @@ -294,7 +288,7 @@ namespace TokenTraits constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; } constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); } constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; } - constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External || op == Token::Offchain; } // Solidity++: add offchain visibility + constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } constexpr bool isStateMutabilitySpecifier(Token op) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 6a4d7dd..549ee65 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -2,8 +2,8 @@ set(ORIGINAL_SOURCE_DIR ${PROJECT_SOURCE_DIR}/solidity/libsolidity) set(sources ${ORIGINAL_SOURCE_DIR}/analysis/ConstantEvaluator.cpp ${ORIGINAL_SOURCE_DIR}/analysis/ConstantEvaluator.h - analysis/ContractLevelChecker.cpp - analysis/ContractLevelChecker.h + ${ORIGINAL_SOURCE_DIR}/analysis/ContractLevelChecker.cpp + ${ORIGINAL_SOURCE_DIR}/analysis/ContractLevelChecker.h ${ORIGINAL_SOURCE_DIR}/analysis/ControlFlowAnalyzer.cpp ${ORIGINAL_SOURCE_DIR}/analysis/ControlFlowAnalyzer.h ${ORIGINAL_SOURCE_DIR}/analysis/ControlFlowBuilder.cpp @@ -34,25 +34,34 @@ set(sources ${ORIGINAL_SOURCE_DIR}/analysis/Scoper.h ${ORIGINAL_SOURCE_DIR}/analysis/StaticAnalyzer.cpp ${ORIGINAL_SOURCE_DIR}/analysis/StaticAnalyzer.h - analysis/SyntaxChecker.cpp - ${ORIGINAL_SOURCE_DIR}/analysis/SyntaxChecker.h - analysis/TypeChecker.cpp + analysis/SolidityppSyntaxChecker.cpp + analysis/SolidityppSyntaxChecker.h + ${ORIGINAL_SOURCE_DIR}/analysis/SyntaxChecker.cpp + analysis/SyntaxChecker.h + analysis/SolidityppTypeChecker.cpp + analysis/SolidityppTypeChecker.h + ${ORIGINAL_SOURCE_DIR}/analysis/TypeChecker.cpp analysis/TypeChecker.h ${ORIGINAL_SOURCE_DIR}/analysis/ViewPureChecker.cpp ${ORIGINAL_SOURCE_DIR}/analysis/ViewPureChecker.h ast/AST.cpp ast/AST.h - ast/AST_accept.h + ast/SolidityppAST.cpp + ast/SolidityppAST.h + ${ORIGINAL_SOURCE_DIR}/ast/AST_accept.h + ast/SolidityppAST_accept.h ${ORIGINAL_SOURCE_DIR}/ast/ASTAnnotations.cpp ast/ASTAnnotations.h ast/ASTEnums.h ast/ASTForward.h - ast/ASTJsonConverter.cpp + ast/SolidityppASTJsonConverter.cpp + ast/SolidityppASTJsonConverter.h + ${ORIGINAL_SOURCE_DIR}/ast/ASTJsonConverter.cpp ast/ASTJsonConverter.h ${ORIGINAL_SOURCE_DIR}/ast/ASTUtils.cpp ${ORIGINAL_SOURCE_DIR}/ast/ASTUtils.h - ast/ASTJsonImporter.cpp - ast/ASTJsonImporter.h + ${ORIGINAL_SOURCE_DIR}/ast/ASTJsonImporter.cpp + ${ORIGINAL_SOURCE_DIR}/ast/ASTJsonImporter.h ast/ASTVisitor.h ${ORIGINAL_SOURCE_DIR}/ast/ExperimentalFeatures.h ast/Types.cpp @@ -122,8 +131,8 @@ set(sources ${ORIGINAL_SOURCE_DIR}/formal/SymbolicVariables.h ${ORIGINAL_SOURCE_DIR}/formal/VariableUsage.cpp ${ORIGINAL_SOURCE_DIR}/formal/VariableUsage.h - interface/ABI.cpp - interface/ABI.h + ${ORIGINAL_SOURCE_DIR}/interface/ABI.cpp + ${ORIGINAL_SOURCE_DIR}/interface/ABI.h interface/CompilerStack.cpp interface/CompilerStack.h ${ORIGINAL_SOURCE_DIR}/interface/DebugSettings.h @@ -137,8 +146,8 @@ set(sources ${ORIGINAL_SOURCE_DIR}/interface/StandardCompiler.h ${ORIGINAL_SOURCE_DIR}/interface/StorageLayout.cpp ${ORIGINAL_SOURCE_DIR}/interface/StorageLayout.h - ${ORIGINAL_SOURCE_DIR}/interface/Version.cpp - ${ORIGINAL_SOURCE_DIR}/interface/Version.h + interface/Version.cpp + interface/Version.h ${ORIGINAL_SOURCE_DIR}/parsing/DocStringParser.cpp ${ORIGINAL_SOURCE_DIR}/parsing/DocStringParser.h parsing/Parser.cpp diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp deleted file mode 100644 index 0b28181..0000000 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -// SPDX-License-Identifier: GPL-3.0 -/** - * Component that verifies overloads, abstract contracts, function clashes and others - * checks at contract or function level. - */ - -#include - -#include -#include -#include -#include -#include - - -using namespace std; -using namespace solidity; -using namespace solidity::langutil; -using namespace solidity::frontend; - -namespace -{ - -template -bool hasEqualParameters(T const& _a, B const& _b) -{ - return FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes( - *FunctionType(_b).asExternallyCallableFunction(false) - ); -} - -template -map> filterDeclarations( - map> const& _declarations) -{ - map> filteredDeclarations; - for (auto const& [name, overloads]: _declarations) - for (auto const* declaration: overloads) - if (auto typedDeclaration = dynamic_cast(declaration)) - filteredDeclarations[name].push_back(typedDeclaration); - return filteredDeclarations; -} - -} - -bool ContractLevelChecker::check(SourceUnit const& _sourceUnit) -{ - bool noErrors = true; - findDuplicateDefinitions( - filterDeclarations(*_sourceUnit.annotation().exportedSymbols) - ); - // This check flags duplicate free events when free events become - // a Solidity feature - findDuplicateDefinitions( - filterDeclarations(*_sourceUnit.annotation().exportedSymbols) - ); - if (!Error::containsOnlyWarnings(m_errorReporter.errors())) - noErrors = false; - for (ASTPointer const& node: _sourceUnit.nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (!check(*contract)) - noErrors = false; - return noErrors; -} - -bool ContractLevelChecker::check(ContractDefinition const& _contract) -{ - _contract.annotation().unimplementedDeclarations = std::vector(); - - checkDuplicateFunctions(_contract); - checkDuplicateEvents(_contract); - m_overrideChecker.check(_contract); - checkBaseConstructorArguments(_contract); - checkAbstractDefinitions(_contract); - checkExternalTypeClashes(_contract); - checkHashCollisions(_contract); - checkLibraryRequirements(_contract); - checkBaseABICompatibility(_contract); - checkPayableFallbackWithoutReceive(_contract); - checkStorageSize(_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. - /// Solidity++: An onchain function and an offchain function is allowed to have the same signature - map> functions; -// map> offchainFunctions; // Solidity++ - FunctionDefinition const* constructor = nullptr; - FunctionDefinition const* fallback = nullptr; - FunctionDefinition const* receive = nullptr; - for (FunctionDefinition const* function: _contract.definedFunctions()) - if (function->isConstructor()) - { - if (constructor) - m_errorReporter.declarationError( - 7997_error, - function->location(), - SecondarySourceLocation().append("Another declaration is here:", constructor->location()), - "More than one constructor defined." - ); - constructor = function; - } - else if (function->isFallback()) - { - if (fallback) - m_errorReporter.declarationError( - 7301_error, - function->location(), - SecondarySourceLocation().append("Another declaration is here:", fallback->location()), - "Only one fallback function is allowed." - ); - fallback = function; - } - else if (function->isReceive()) - { - if (receive) - m_errorReporter.declarationError( - 4046_error, - function->location(), - SecondarySourceLocation().append("Another declaration is here:", receive->location()), - "Only one receive function is allowed." - ); - receive = function; - } -// else if (function->isOffchain()) // Solidity++ -// { -// solAssert(!function->name().empty(), ""); -// offchainFunctions[function->name()].push_back(function); -// } - else - { - solAssert(!function->name().empty(), ""); - functions[function->name()].push_back(function); - } - - findDuplicateDefinitions(functions); -// findDuplicateDefinitions(offchainFunctions); // Solidity++ -} - -void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract) -{ - /// Checks that two events with the same name defined in this contract have different - /// argument types - map> events; - for (auto const* contract: _contract.annotation().linearizedBaseContracts) - for (EventDefinition const* event: contract->events()) - events[event->name()].push_back(event); - - findDuplicateDefinitions(events); -} - -template -void ContractLevelChecker::findDuplicateDefinitions(map> const& _definitions) -{ - for (auto const& it: _definitions) - { - vector const& overloads = it.second; - set reported; - for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i) - { - SecondarySourceLocation ssl; - - for (size_t j = i + 1; j < overloads.size(); ++j) - if (hasEqualParameters(*overloads[i], *overloads[j])) - { - solAssert( - ( - dynamic_cast(overloads[i]->scope()) && - dynamic_cast(overloads[j]->scope()) && - overloads[i]->name() == overloads[j]->name() - ) || - ( - dynamic_cast(overloads[i]->scope()) && - dynamic_cast(overloads[j]->scope()) - ), - "Override is neither a namesake function/event in contract scope nor " - "a free function/event (alias)." - ); - ssl.append("Other declaration is here:", overloads[j]->location()); - reported.insert(j); - } - - if (ssl.infos.size() > 0) - { - ErrorId error; - string message; - if constexpr (is_same_v) - { - error = 1686_error; - message = "Function with same name and parameter types defined twice."; - } - else - { - static_assert(is_same_v, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\""); - error = 5883_error; - message = "Event with same name and parameter types defined twice."; - } - - ssl.limitSize(message); - - m_errorReporter.declarationError( - error, - overloads[i]->location(), - ssl, - message - ); - } - } - } -} - -void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _contract) -{ - // Collects functions, static variable getters and modifiers. If they - // override (unimplemented) base class ones, they are replaced. - set proxies; - - auto registerProxy = [&proxies](OverrideProxy const& _overrideProxy) - { - // Overwrite an existing proxy, if it exists. - if (!_overrideProxy.unimplemented()) - proxies.erase(_overrideProxy); - - proxies.insert(_overrideProxy); - }; - - // Search from base to derived, collect all functions and modifiers and - // update proxies. - for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) - { - for (VariableDeclaration const* v: contract->stateVariables()) - if (v->isPartOfExternalInterface()) - registerProxy(OverrideProxy(v)); - - for (FunctionDefinition const* function: contract->definedFunctions()) - if (!function->isConstructor()) - registerProxy(OverrideProxy(function)); - - for (ModifierDefinition const* modifier: contract->functionModifiers()) - registerProxy(OverrideProxy(modifier)); - } - - // Set to not fully implemented if at least one flag is false. - // Note that `_contract.annotation().unimplementedDeclarations` has already been - // pre-filled by `checkBaseConstructorArguments`. - // - for (auto const& proxy: proxies) - if (proxy.unimplemented()) - _contract.annotation().unimplementedDeclarations->push_back(proxy.declaration()); - - if (_contract.abstract()) - { - if (_contract.contractKind() == ContractKind::Interface) - m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly."); - else if (_contract.contractKind() == ContractKind::Library) - m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract."); - else - solAssert(_contract.contractKind() == ContractKind::Contract, ""); - } - - // For libraries, we emit errors on function-level, so this is fine as long as we do - // not have inheritance for libraries. - if ( - _contract.contractKind() == ContractKind::Contract && - !_contract.abstract() && - !_contract.annotation().unimplementedDeclarations->empty() - ) - { - SecondarySourceLocation ssl; - for (auto declaration: *_contract.annotation().unimplementedDeclarations) - ssl.append("Missing implementation: ", declaration->location()); - m_errorReporter.typeError( - 3656_error, - _contract.location(), - ssl, - "Contract \"" + *_contract.annotation().canonicalName + "\" should be marked as abstract." - ); - } -} - - -void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract) -{ - vector const& bases = _contract.annotation().linearizedBaseContracts; - - // Determine the arguments that are used for the base constructors. - for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->constructor()) - for (auto const& modifier: constructor->modifiers()) - if (auto baseContract = dynamic_cast( - modifier->name().annotation().referencedDeclaration - )) - { - if (modifier->arguments()) - { - if (baseContract->constructor()) - annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); - } - else - m_errorReporter.declarationError( - 1563_error, - modifier->location(), - "Modifier-style base constructor call without arguments." - ); - } - - for (ASTPointer const& base: contract->baseContracts()) - { - ContractDefinition const* baseContract = dynamic_cast( - base->name().annotation().referencedDeclaration - ); - solAssert(baseContract, ""); - - if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) - annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); - } - } - - // check that we get arguments for all base constructors that need it. - // If not mark the contract as abstract (not fully implemented) - for (ContractDefinition const* contract: bases) - if (FunctionDefinition const* constructor = contract->constructor()) - if (contract != &_contract && !constructor->parameters().empty()) - if (!_contract.annotation().baseConstructorArguments.count(constructor)) - _contract.annotation().unimplementedDeclarations->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( - 3364_error, - *mainLocation, - ssl, - "Base constructor arguments given twice." - ); - } - -} - -void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract) -{ - map>> externalDeclarations; - for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) - { - for (FunctionDefinition const* f: contract->definedFunctions()) - if (f->isPartOfExternalInterface()) - { - auto functionType = TypeProvider::function(*f); - // under non error circumstances this should be true - if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].emplace_back( - f, functionType->asExternallyCallableFunction(false) - ); - } - for (VariableDeclaration const* v: contract->stateVariables()) - if (v->isPartOfExternalInterface()) - { - auto functionType = TypeProvider::function(*v); - // under non error circumstances this should be true - if (functionType->interfaceFunctionType()) - externalDeclarations[functionType->externalSignature()].emplace_back( - v, functionType->asExternallyCallableFunction(false) - ); - } - } - for (auto const& it: externalDeclarations) - for (size_t i = 0; i < it.second.size(); ++i) - for (size_t j = i + 1; j < it.second.size(); ++j) - if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) - m_errorReporter.typeError( - 9914_error, - it.second[j].first->location(), - "Function overload clash during conversion to external types for arguments." - ); -} - -void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract) -{ - set> hashes; - for (auto const& it: _contract.interfaceFunctionList()) - { - util::FixedHash<4> const& hash = it.first; - if (hashes.count(hash)) - m_errorReporter.typeError( - 1860_error, - _contract.location(), - string("Function signature hash collision for ") + it.second->externalSignature() - ); - hashes.insert(hash); - } -} - -void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract) -{ - if (!_contract.isLibrary()) - return; - - if (!_contract.baseContracts().empty()) - m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit."); - - for (auto const& var: _contract.stateVariables()) - if (!var->isConstant()) - m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables"); -} - -void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract) -{ - if (*_contract.sourceUnit().annotation().useABICoderV2) - return; - - if (_contract.isLibrary()) - { - solAssert( - _contract.baseContracts().empty() || m_errorReporter.hasErrors(), - "Library is not allowed to inherit" - ); - return; - } - - SecondarySourceLocation errors; - - // interfaceFunctionList contains all inherited functions as well - for (auto const& func: _contract.interfaceFunctionList()) - { - solAssert(func.second->hasDeclaration(), "Function has no declaration?!"); - - if (!*func.second->declaration().sourceUnit().annotation().useABICoderV2) - continue; - - auto const& currentLoc = func.second->declaration().location(); - - for (TypePointer const& paramType: func.second->parameterTypes() + func.second->returnParameterTypes()) - if (!TypeChecker::typeSupportedByOldABIEncoder(*paramType, false)) - { - errors.append("Type only supported by ABIEncoderV2", currentLoc); - break; - } - } - - if (!errors.infos.empty()) - m_errorReporter.fatalTypeError( - 6594_error, - _contract.location(), - errors, - std::string("Contract \"") + - _contract.name() + - "\" does not use ABI coder v2 but wants to inherit from a contract " + - "which uses types that require it. " + - "Use \"pragma abicoder v2;\" for the inheriting contract as well to enable the feature." - ); - -} - -void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition const& _contract) -{ - if (auto const* fallback = _contract.fallbackFunction()) - if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction()) - m_errorReporter.warning( - 3628_error, - _contract.location(), - "This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.", - SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location()) - ); -} - -void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract) -{ - bigint size = 0; - for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) - for (VariableDeclaration const* variable: contract->stateVariables()) - if (!(variable->isConstant() || variable->immutable())) - { - size += variable->annotation().type->storageSizeUpperBound(); - if (size >= bigint(1) << 256) - { - m_errorReporter.typeError(7676_error, _contract.location(), "Contract requires too much storage."); - break; - } - } -} diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h deleted file mode 100644 index 5f89068..0000000 --- a/libsolidity/analysis/ContractLevelChecker.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -// SPDX-License-Identifier: GPL-3.0 -/** - * Component that verifies overloads, abstract contracts, function clashes and others - * checks at contract or function level. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace solidity::langutil -{ -class ErrorReporter; -} - -namespace solidity::frontend -{ - -/** - * Component that verifies overloads, abstract contracts, function clashes and others - * checks at file, contract, or function level. - */ -class ContractLevelChecker -{ -public: - - /// @param _errorReporter provides the error logging functionality. - explicit ContractLevelChecker(langutil::ErrorReporter& _errorReporter): - m_overrideChecker{_errorReporter}, - m_errorReporter(_errorReporter) - {} - - /// Performs checks on the given source ast. - /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings - bool check(SourceUnit const& _sourceUnit); - -private: - /// 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); - /// 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 - void findDuplicateDefinitions(std::map> const& _definitions); - /// Checks for unimplemented functions and modifiers. - void checkAbstractDefinitions(ContractDefinition const& _contract); - /// Checks that the base constructor arguments are properly provided. - /// Fills the list of unimplemented functions in _contract's annotations. - void checkBaseConstructorArguments(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 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); - /// Checks base contracts for ABI compatibility - void checkBaseABICompatibility(ContractDefinition const& _contract); - - /// Warns if the contract has a payable fallback, but no receive ether function. - void checkPayableFallbackWithoutReceive(ContractDefinition const& _contract); - /// Error if the contract requires too much storage - void checkStorageSize(ContractDefinition const& _contract); - - OverrideChecker m_overrideChecker; - langutil::ErrorReporter& m_errorReporter; -}; - -} diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index bb17599..1bd44eb 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -358,13 +358,6 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) typeLoc = DataLocation::Memory; } - // Solidity++: - else if (_variable.isMessageParameter()) - { - solAssert(varLoc == Location::Unspecified, ""); - typeLoc = DataLocation::Memory; - } - else if (_variable.isFileLevelVariable()) { solAssert(varLoc == Location::Unspecified, ""); diff --git a/libsolidity/analysis/SolidityppSyntaxChecker.cpp b/libsolidity/analysis/SolidityppSyntaxChecker.cpp new file mode 100644 index 0000000..39d8346 --- /dev/null +++ b/libsolidity/analysis/SolidityppSyntaxChecker.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Charles + * @date 2021 + * Solidity++ syntax checker + * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + */ + +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::util; + +void SolidityppSyntaxChecker::endVisit(SourceUnit const& _sourceUnit) +{ + if (!m_versionPragmaFound) + { + string errorString("Source file does not specify required compiler version!"); + SemVerVersion recommendedVersion{string(VersionString)}; + if (!recommendedVersion.isPrerelease()) + errorString += + " Consider adding \"pragma soliditypp ^" + + to_string(recommendedVersion.major()) + + string(".") + + to_string(recommendedVersion.minor()) + + string(".") + + to_string(recommendedVersion.patch()) + + string(";\""); + + // when reporting the warning, print the source name only + m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); + } + if (!m_sourceUnit->annotation().useABICoderV2.set()) + m_sourceUnit->annotation().useABICoderV2 = true; + + m_sourceUnit = nullptr; +} + +bool SolidityppSyntaxChecker::visit(PragmaDirective const& _pragma) +{ + solAssert(!_pragma.tokens().empty(), ""); + solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); + if (_pragma.tokens()[0] != Token::Identifier) + m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); + else if (_pragma.literals()[0] == "experimental") + { + solAssert(m_sourceUnit, ""); + vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); + if (literals.empty()) + m_errorReporter.syntaxError( + 9679_error, + _pragma.location(), + "Experimental feature name is missing." + ); + else if (literals.size() > 1) + m_errorReporter.syntaxError( + 6022_error, + _pragma.location(), + "Stray arguments." + ); + else + { + string const literal = literals[0]; + if (literal.empty()) + m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid."); + else if (!ExperimentalFeatureNames.count(literal)) + m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name."); + else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal))) + m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name."); + else + { + auto feature = ExperimentalFeatureNames.at(literal); + m_sourceUnit->annotation().experimentalFeatures.insert(feature); + if (!ExperimentalFeatureWithoutWarning.count(feature)) + m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + + if (feature == ExperimentalFeature::ABIEncoderV2) + { + if (m_sourceUnit->annotation().useABICoderV2.set()) + { + if (!*m_sourceUnit->annotation().useABICoderV2) + m_errorReporter.syntaxError( + 8273_error, + _pragma.location(), + "ABI coder v1 has already been selected through \"pragma abicoder v1\"." + ); + } + else + m_sourceUnit->annotation().useABICoderV2 = true; + } + } + } + } + else if (_pragma.literals()[0] == "abicoder") + { + solAssert(m_sourceUnit, ""); + if ( + _pragma.literals().size() != 2 || + !set{"v1", "v2"}.count(_pragma.literals()[1]) + ) + m_errorReporter.syntaxError( + 2745_error, + _pragma.location(), + "Expected either \"pragma abicoder v1\" or \"pragma abicoder v2\"." + ); + else if (m_sourceUnit->annotation().useABICoderV2.set()) + m_errorReporter.syntaxError( + 3845_error, + _pragma.location(), + "ABI coder has already been selected for this source unit." + ); + else + m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2"); + } + else if ( _pragma.literals()[0] == "soliditypp" || _pragma.literals()[0] == "solidity") + { + vector tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); + vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); + SemVerMatchExpressionParser parser(tokens, literals); + auto matchExpression = parser.parse(); + // An unparsable version pragma is an unrecoverable fatal error in the parser. + solAssert(matchExpression.has_value(), ""); + static SemVerVersion const currentVersion{string(VersionString)}; + if (!matchExpression->matches(currentVersion)) + m_errorReporter.syntaxError( + 3997_error, + _pragma.location(), + "Source file requires different compiler version (current compiler is " + + string(VersionString) + ") - note that nightly builds are considered to be " + "strictly less than the released version" + ); + m_versionPragmaFound = true; + } + else + m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); + + return true; +} diff --git a/libsolidity/analysis/SolidityppSyntaxChecker.h b/libsolidity/analysis/SolidityppSyntaxChecker.h new file mode 100644 index 0000000..9703a5f --- /dev/null +++ b/libsolidity/analysis/SolidityppSyntaxChecker.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Charles + * @date 2021 + * Solidity++ syntax checker + * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend +{ + +/** + * The module that performs syntax analysis for Solidity++ + */ +class SolidityppSyntaxChecker: public SyntaxChecker +{ +public: + /// @param _errorReporter provides the error logging functionality. + SolidityppSyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer): + SyntaxChecker(_errorReporter, _useYulOptimizer) + {} + +protected: + void endVisit(SourceUnit const& _sourceUnit) override; + bool visit(PragmaDirective const& _pragma) override; +}; + +} diff --git a/libsolidity/analysis/SolidityppTypeChecker.cpp b/libsolidity/analysis/SolidityppTypeChecker.cpp new file mode 100644 index 0000000..53c91f1 --- /dev/null +++ b/libsolidity/analysis/SolidityppTypeChecker.cpp @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Charles + * @date 2021 + * Solidity++ Type analyzer and checker. + * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::frontend; + +bool SolidityppTypeChecker::visit(SourceUnit const& _sourceUnit) +{ + m_sourceLanguage = *_sourceUnit.annotation().sourceLanguage; + return true; +} + +bool SolidityppTypeChecker::visit(FunctionDefinition const& _function) +{ + if (_function.markedVirtual()) + { + if (_function.isFree()) + m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual."); + else if (_function.isConstructor()) + m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); + else if (_function.annotation().contract->isInterface()) + m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); + else if (_function.visibility() == Visibility::Private) + m_errorReporter.typeError(3942_error, _function.location(), "\"virtual\" and \"private\" cannot be used together."); + else if (_function.libraryFunction()) + m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); + } + if (_function.overrides() && _function.isFree()) + m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override."); + + if (!_function.modifiers().empty() && _function.isFree()) + m_errorReporter.syntaxError(5811_error, _function.location(), "Free functions cannot have modifiers."); + + if (_function.isPayable()) + { + if (_function.libraryFunction()) + m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); + else if (_function.isFree()) + m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable."); + else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) + m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); + } + + // Solidity++: check async functions +// if (_function.isAsync()) +// { +// if (_function.libraryFunction()) +// m_errorReporter.typeError(100301_error, _function.location(), "Library functions cannot be async."); +// else if (_function.isFree()) +// m_errorReporter.typeError(100302_error, _function.location(), "Free functions cannot be async."); +// else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) +// m_errorReporter.typeError(100303_error, _function.location(), "\"internal\" and \"private\" functions cannot be async."); +// } + // Solidity++: check externally visible functions (must be async) +// else if (_function.isPartOfExternalInterface()) +// { +// if (_function.isOrdinary() && !_function.libraryFunction()) +// m_errorReporter.typeError(100304_error, _function.location(), "\"external\" and \"public\" functions must be async."); +// } + + vector internalParametersInConstructor; + + auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& _var) { + if (type(_var)->containsNestedMapping()) + if (_var.referenceLocation() == VariableDeclaration::Location::Storage) + solAssert( + _function.libraryFunction() || _function.isConstructor() || !_function.isPublic(), + "Mapping types for parameters or return variables " + "can only be used in internal or library functions." + ); + bool functionIsExternallyVisible = + (!_function.isConstructor() && _function.isPublic()) || + (_function.isConstructor() && !m_currentContract->abstract()); + if ( + _function.isConstructor() && + _var.referenceLocation() == VariableDeclaration::Location::Storage && + !m_currentContract->abstract() + ) + m_errorReporter.typeError( + 3644_error, + _var.location(), + "This parameter has a type that can only be used internally. " + "You can make the contract abstract to avoid this problem." + ); + else if (functionIsExternallyVisible) + { + auto iType = type(_var)->interfaceType(_function.libraryFunction()); + + if (!iType) + { + string message = iType.message(); + solAssert(!message.empty(), "Expected detailed error message!"); + if (_function.isConstructor()) + message += " You can make the contract abstract to avoid this problem."; + m_errorReporter.typeError(4103_error, _var.location(), message); + } + else if ( + !useABICoderV2() && + !typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction()) + ) + { + string message = + "This type is only supported in ABI coder v2. " + "Use \"pragma abicoder v2;\" to enable the feature."; + if (_function.isConstructor()) + message += + " Alternatively, make the contract abstract and supply the " + "constructor arguments from a derived contract."; + m_errorReporter.typeError( + 4957_error, + _var.location(), + message + ); + } + } + }; + for (ASTPointer const& var: _function.parameters()) + { + checkArgumentAndReturnParameter(*var); + var->accept(*this); + } + for (ASTPointer const& var: _function.returnParameters()) + { + checkArgumentAndReturnParameter(*var); + var->accept(*this); + } + + set modifiers; + for (ASTPointer const& modifier: _function.modifiers()) + { + vector baseContracts; + if (auto contract = dynamic_cast(_function.scope())) + { + baseContracts = contract->annotation().linearizedBaseContracts; + // Delete first base which is just the main contract itself + baseContracts.erase(baseContracts.begin()); + } + + visitManually( + *modifier, + _function.isConstructor() ? baseContracts : vector() + ); + Declaration const* decl = &dereference(modifier->name()); + if (modifiers.count(decl)) + { + if (dynamic_cast(decl)) + m_errorReporter.declarationError(1697_error, modifier->location(), "Base constructor already provided."); + } + else + modifiers.insert(decl); + } + + solAssert(_function.isFree() == !m_currentContract, ""); + if (!m_currentContract) + { + solAssert(!_function.isConstructor(), ""); + solAssert(!_function.isFallback(), ""); + solAssert(!_function.isReceive(), ""); + } + else if (m_currentContract->isInterface()) + { + if (_function.isImplemented()) + m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); + + if (_function.isConstructor()) + m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces."); + else if (_function.visibility() != Visibility::External) + m_errorReporter.typeError(1560_error, _function.location(), "Functions in interfaces must be declared external."); + } + else if (m_currentContract->contractKind() == ContractKind::Library) + if (_function.isConstructor()) + m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); + + if (_function.isImplemented()) + _function.body().accept(*this); + else if (_function.isConstructor()) + m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared."); + else if (_function.libraryFunction()) + m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); + else if (!_function.virtualSemantics()) + { + if (_function.isFree()) + solAssert(m_errorReporter.hasErrors(), ""); + else + m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + } + + + if (_function.isFallback()) + typeCheckFallbackFunction(_function); + else if (_function.isReceive()) + typeCheckReceiveFunction(_function); + else if (_function.isConstructor()) + typeCheckConstructor(_function); + + return false; +} + +bool SolidityppTypeChecker::visit(FunctionCall const& _functionCall) +{ + vector> const& arguments = _functionCall.arguments(); + bool argumentsArePure = true; + + // We need to check arguments' type first as they will be needed for overload resolution. + for (ASTPointer const& argument: arguments) + { + argument->accept(*this); + if (!*argument->annotation().isPure) + argumentsArePure = false; + } + + // Store argument types - and names if given - for overload resolution + { + FuncCallArguments funcCallArgs; + + funcCallArgs.names = _functionCall.names(); + + for (ASTPointer const& argument: arguments) + funcCallArgs.types.push_back(type(*argument)); + + _functionCall.expression().annotation().arguments = std::move(funcCallArgs); + } + + _functionCall.expression().accept(*this); + + Type const* expressionType = type(_functionCall.expression()); + + // Determine function call kind and function type for this FunctionCall node + FunctionCallAnnotation& funcCallAnno = _functionCall.annotation(); + FunctionTypePointer functionType = nullptr; + funcCallAnno.isConstant = false; + + bool isLValue = false; + + // Determine and assign function call kind, lvalue, purity and function type for this FunctionCall node + switch (expressionType->category()) + { + case Type::Category::Function: + functionType = dynamic_cast(expressionType); + funcCallAnno.kind = FunctionCallKind::FunctionCall; + + // Solidity++: determine function call synchrony (sync / async) + if (m_sourceLanguage == SourceLanguage::Solidity) + funcCallAnno.async = false; // All function calls in Solidity are synchronous + + if (auto memberAccess = dynamic_cast(&_functionCall.expression())) + { + if (dynamic_cast(memberAccess->annotation().referencedDeclaration)) + _functionCall.expression().annotation().calledDirectly = true; + } + else if (auto identifier = dynamic_cast(&_functionCall.expression())) + if (dynamic_cast(identifier->annotation().referencedDeclaration)) + _functionCall.expression().annotation().calledDirectly = true; + + // Purity for function calls also depends upon the callee and its FunctionType + funcCallAnno.isPure = + argumentsArePure && + *_functionCall.expression().annotation().isPure && + functionType->isPure(); + + if ( + functionType->kind() == FunctionType::Kind::ArrayPush || + functionType->kind() == FunctionType::Kind::ByteArrayPush + ) + isLValue = functionType->parameterTypes().empty(); + + break; + + case Type::Category::TypeType: + { + // Determine type for type conversion or struct construction expressions + TypePointer const& actualType = dynamic_cast(*expressionType).actualType(); + solAssert(!!actualType, ""); + + if (actualType->category() == Type::Category::Struct) + { + if (actualType->containsNestedMapping()) + m_errorReporter.fatalTypeError( + 9515_error, + _functionCall.location(), + "Struct containing a (nested) mapping cannot be constructed." + ); + functionType = dynamic_cast(*actualType).constructorType(); + funcCallAnno.kind = FunctionCallKind::StructConstructorCall; + } + else + { + if (auto const* contractType = dynamic_cast(actualType)) + if (contractType->isSuper()) + m_errorReporter.fatalTypeError( + 1744_error, + _functionCall.location(), + "Cannot convert to the super type." + ); + funcCallAnno.kind = FunctionCallKind::TypeConversion; + } + + funcCallAnno.isPure = argumentsArePure; + + break; + } + + default: + m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable"); + // Unreachable, because fatalTypeError throws. We don't set kind, but that's okay because the switch below + // is never reached. And, even if it was, SetOnce would trigger an assertion violation and not UB. + funcCallAnno.isPure = argumentsArePure; + break; + } + + funcCallAnno.isLValue = isLValue; + + // Determine return types + switch (*funcCallAnno.kind) + { + case FunctionCallKind::TypeConversion: + funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); + break; + + case FunctionCallKind::StructConstructorCall: // fall-through + case FunctionCallKind::FunctionCall: + { + TypePointers returnTypes; + + switch (functionType->kind()) + { + case FunctionType::Kind::ABIDecode: + { + returnTypes = typeCheckABIDecodeAndRetrieveReturnType( + _functionCall, + useABICoderV2() + ); + break; + } + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: + { + typeCheckABIEncodeFunctions(_functionCall, functionType); + returnTypes = functionType->returnParameterTypes(); + break; + } + case FunctionType::Kind::MetaType: + returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall); + break; + default: + { + typeCheckFunctionCall(_functionCall, functionType); + returnTypes = m_evmVersion.supportsReturndata() ? + functionType->returnParameterTypes() : + functionType->returnParameterTypesWithoutDynamicTypes(); + break; + } + } + + funcCallAnno.type = returnTypes.size() == 1 ? + move(returnTypes.front()) : + TypeProvider::tuple(move(returnTypes)); + + break; + } + + default: + // for non-callables, ensure error reported and annotate node to void function + solAssert(m_errorReporter.hasErrors(), ""); + funcCallAnno.kind = FunctionCallKind::FunctionCall; + funcCallAnno.type = TypeProvider::emptyTuple(); + break; + } + + return false; +} + +bool SolidityppTypeChecker::visit(FunctionCallOptions const& _functionCallOptions) +{ + solAssert(_functionCallOptions.options().size() == _functionCallOptions.names().size(), "Lengths of name & value arrays differ!"); + + _functionCallOptions.expression().annotation().arguments = _functionCallOptions.annotation().arguments; + + _functionCallOptions.expression().accept(*this); + + _functionCallOptions.annotation().isPure = false; + _functionCallOptions.annotation().isConstant = false; + _functionCallOptions.annotation().isLValue = false; + + auto expressionFunctionType = dynamic_cast(type(_functionCallOptions.expression())); + if (!expressionFunctionType) + { + m_errorReporter.fatalTypeError(2622_error, _functionCallOptions.location(), "Expected callable expression before call options."); + return false; + } + + bool setSalt = false; + bool setValue = false; + bool setGas = false; + bool setTokenId = false; + + FunctionType::Kind kind = expressionFunctionType->kind(); + if ( + kind != FunctionType::Kind::Creation && + kind != FunctionType::Kind::External && + kind != FunctionType::Kind::BareCall && + kind != FunctionType::Kind::BareCallCode && + kind != FunctionType::Kind::BareDelegateCall && + kind != FunctionType::Kind::BareStaticCall + ) + { + m_errorReporter.fatalTypeError( + 2193_error, + _functionCallOptions.location(), + "Function call options can only be set on external function calls or contract creations." + ); + return false; + } + + if ( + expressionFunctionType->valueSet() || + expressionFunctionType->tokenSet() || + expressionFunctionType->gasSet() || + expressionFunctionType->saltSet() + ) + m_errorReporter.typeError( + 1645_error, + _functionCallOptions.location(), + "Function call options have already been set, you have to combine them into a single " + "{...}-option." + ); + + auto setCheckOption = [&](bool& _option, string const& _name) + { + if (_option) + m_errorReporter.typeError( + 9886_error, + _functionCallOptions.location(), + "Duplicate option \"" + std::move(_name) + "\"." + ); + + _option = true; + }; + + for (size_t i = 0; i < _functionCallOptions.names().size(); ++i) + { + string const& name = *(_functionCallOptions.names()[i]); + + // Solidity++: disable ethereum options of "salt" and "gas" + if (name == "gas" || name == "salt") + { + m_errorReporter.typeError( + 100201_error, + _functionCallOptions.location(), + "Cannot set option \"" + name + "\" in Solidity++. Valid options are \"token\" and \"value\"." + ); + } + else if (name == "value") + { + if (kind == FunctionType::Kind::BareDelegateCall) + m_errorReporter.typeError( + 6189_error, + _functionCallOptions.location(), + "Cannot set option \"value\" for delegatecall." + ); + else if (kind == FunctionType::Kind::BareStaticCall) + m_errorReporter.typeError( + 2842_error, + _functionCallOptions.location(), + "Cannot set option \"value\" for staticcall." + ); + else if (!expressionFunctionType->isPayable()) + m_errorReporter.typeError( + 7006_error, + _functionCallOptions.location(), + kind == FunctionType::Kind::Creation ? + "Cannot set option \"value\", since the constructor of " + + expressionFunctionType->returnParameterTypes().front()->toString() + + " is not payable." : + "Cannot set option \"value\" on a non-payable function type." + ); + else + { + expectType(*_functionCallOptions.options()[i], *TypeProvider::uint256()); + + setCheckOption(setValue, "value"); + } + } + // Solidity++: + else if (name == "token") + { + if (kind == FunctionType::Kind::BareDelegateCall) + m_errorReporter.typeError( + 6189_error, + _functionCallOptions.location(), + "Cannot set option \"token\" for delegatecall." + ); + else if (kind == FunctionType::Kind::BareStaticCall) + m_errorReporter.typeError( + 2842_error, + _functionCallOptions.location(), + "Cannot set option \"token\" for staticcall." + ); + else if (!expressionFunctionType->isPayable()) + m_errorReporter.typeError( + 7006_error, + _functionCallOptions.location(), + kind == FunctionType::Kind::Creation ? + "Cannot set option \"token\", since the constructor of " + + expressionFunctionType->returnParameterTypes().front()->toString() + + " is not payable." : + "Cannot set option \"token\" on a non-payable function type." + ); + else + { + expectType(*_functionCallOptions.options()[i], *TypeProvider::viteTokenId()); + + setCheckOption(setTokenId, "token"); + } + } + + else + m_errorReporter.typeError( + 9318_error, + _functionCallOptions.location(), + "Unknown call option \"" + name + "\". Valid options are \"token\" and \"value\"." + ); + } + + if (setSalt && !m_evmVersion.hasCreate2()) + m_errorReporter.typeError( + 5189_error, + _functionCallOptions.location(), + "Unsupported call option \"salt\" (requires Constantinople-compatible VMs)." + ); + + _functionCallOptions.annotation().type = expressionFunctionType->copyAndSetCallOptions(setGas, setValue, setSalt, setTokenId); + return false; +} + +// Solidity++: +bool SolidityppTypeChecker::visit(AwaitExpression const& _awaitExpression) +{ + auto call = dynamic_cast(&_awaitExpression.expression()); + solAssert(call, "fail to convert function call in await expression: " + _awaitExpression.location().text()); + + call->accept(*this); + _awaitExpression.annotation().type = _awaitExpression.expression().annotation().type; + _awaitExpression.annotation().isPure = false; + + call->annotation().async = false; // set to sync + + return false; +} + +void SolidityppTypeChecker::endVisit(Literal const& _literal) +{ + if (_literal.looksLikeViteAddress()) // Solidity++: check vite address literal + { + _literal.annotation().type = TypeProvider::address(); + + string msg; + + if (!_literal.passesViteAddressChecksum()) + { + msg = "This looks like a Vite address but has an invalid checksum."; + } + + if (!msg.empty()) + m_errorReporter.syntaxError( + 9429_error, + _literal.location(), + msg + ); + } + + if (_literal.looksLikeViteTokenId()) // Solidity++: check token id literal + { + _literal.annotation().type = TypeProvider::viteTokenId(); + + string msg; + + if (!_literal.passesViteTokenIdChecksum()) + { + msg = "This looks like a Vite Token Id but has an invalid checksum."; + } + + if (!msg.empty()) + m_errorReporter.syntaxError( + 9429_error, + _literal.location(), + msg + ); + } + + if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None) + m_errorReporter.fatalTypeError( + 5145_error, + _literal.location(), + "Hexadecimal numbers cannot be used with unit denominations. " + "You can use an expression of the form \"0x1234 * 1 day\" instead." + ); + + if (_literal.subDenomination() == Literal::SubDenomination::Year) + m_errorReporter.typeError( + 4820_error, + _literal.location(), + "Using \"years\" as a unit denomination is deprecated." + ); + + if (!_literal.annotation().type) + _literal.annotation().type = TypeProvider::forLiteral(_literal); + + if (!_literal.annotation().type) + m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value."); + + _literal.annotation().isPure = true; + _literal.annotation().isLValue = false; + _literal.annotation().isConstant = false; +} diff --git a/libsolidity/analysis/SolidityppTypeChecker.h b/libsolidity/analysis/SolidityppTypeChecker.h new file mode 100644 index 0000000..2ecf1fb --- /dev/null +++ b/libsolidity/analysis/SolidityppTypeChecker.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Charles + * @date 2021 + * Solidity++ Type analyzer and checker. + * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend +{ + +/** + * The module that performs type analysis on the AST, checks the applicability of operations on + * those types and stores errors for invalid operations. + * Provides a way to retrieve the type of an AST node. + */ +class SolidityppTypeChecker: public TypeChecker +{ +public: + /// @param _errorReporter provides the error logging functionality. + SolidityppTypeChecker(langutil::EVMVersion _evmVersion, langutil::ErrorReporter& _errorReporter): + TypeChecker(_evmVersion, _errorReporter) + {} + +protected: + using TypeChecker::visit; + using TypeChecker::endVisit; + + bool visit(SourceUnit const& _sourceUnit) override; + bool visit(AwaitExpression const& _awaitExpression) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(FunctionCall const& _functionCall) override; + bool visit(FunctionCallOptions const& _functionCallOptions) override; + void endVisit(Literal const& _literal) override; + + SourceLanguage m_sourceLanguage; + +}; + +} diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp deleted file mode 100644 index c12a2f9..0000000 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ /dev/null @@ -1,438 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -/** - * @author Charles - * @date 2021 - * Solidity++ syntax checker - * Solidity++ is modified from Solidity under the terms of the GNU General Public License. - */ - -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#include - -#include - -#include -#include - -using namespace std; -using namespace solidity; -using namespace solidity::langutil; -using namespace solidity::frontend; -using namespace solidity::util; - -bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) -{ - _astRoot.accept(*this); - return Error::containsOnlyWarnings(m_errorReporter.errors()); -} - -bool SyntaxChecker::visit(SourceUnit const& _sourceUnit) -{ - m_versionPragmaFound = false; - m_sourceUnit = &_sourceUnit; - return true; -} - -void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) -{ - if (!m_versionPragmaFound) - { - string errorString("Source file does not specify required compiler version!"); - SemVerVersion recommendedVersion{string(VersionString)}; - if (!recommendedVersion.isPrerelease()) - errorString += - " Consider adding \"pragma solidity ^" + - to_string(recommendedVersion.major()) + - string(".") + - to_string(recommendedVersion.minor()) + - string(".") + - to_string(recommendedVersion.patch()) + - string(";\""); - - // when reporting the warning, print the source name only - m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); - } - if (!m_sourceUnit->annotation().useABICoderV2.set()) - m_sourceUnit->annotation().useABICoderV2 = true; - m_sourceUnit = nullptr; -} - -bool SyntaxChecker::visit(PragmaDirective const& _pragma) -{ - solAssert(!_pragma.tokens().empty(), ""); - solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); - if (_pragma.tokens()[0] != Token::Identifier) - m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\""); - else if (_pragma.literals()[0] == "experimental") - { - solAssert(m_sourceUnit, ""); - vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); - if (literals.empty()) - m_errorReporter.syntaxError( - 9679_error, - _pragma.location(), - "Experimental feature name is missing." - ); - else if (literals.size() > 1) - m_errorReporter.syntaxError( - 6022_error, - _pragma.location(), - "Stray arguments." - ); - else - { - string const literal = literals[0]; - if (literal.empty()) - m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid."); - else if (!ExperimentalFeatureNames.count(literal)) - m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name."); - else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal))) - m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name."); - else - { - auto feature = ExperimentalFeatureNames.at(literal); - m_sourceUnit->annotation().experimentalFeatures.insert(feature); - if (!ExperimentalFeatureWithoutWarning.count(feature)) - m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); - - if (feature == ExperimentalFeature::ABIEncoderV2) - { - if (m_sourceUnit->annotation().useABICoderV2.set()) - { - if (!*m_sourceUnit->annotation().useABICoderV2) - m_errorReporter.syntaxError( - 8273_error, - _pragma.location(), - "ABI coder v1 has already been selected through \"pragma abicoder v1\"." - ); - } - else - m_sourceUnit->annotation().useABICoderV2 = true; - } - } - } - } - else if (_pragma.literals()[0] == "abicoder") - { - solAssert(m_sourceUnit, ""); - if ( - _pragma.literals().size() != 2 || - !set{"v1", "v2"}.count(_pragma.literals()[1]) - ) - m_errorReporter.syntaxError( - 2745_error, - _pragma.location(), - "Expected either \"pragma abicoder v1\" or \"pragma abicoder v2\"." - ); - else if (m_sourceUnit->annotation().useABICoderV2.set()) - m_errorReporter.syntaxError( - 3845_error, - _pragma.location(), - "ABI coder has already been selected for this source unit." - ); - else - m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2"); - } - // else if (_pragma.literals()[0] == "solidity") - // { - // m_errorReporter.syntaxError(3997_error, _pragma.location(), "Expected \"pragma soliditypp\"."); - // } - else if ( _pragma.literals()[0] == "soliditypp" || _pragma.literals()[0] == "solidity") - { - vector tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); - vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); - SemVerMatchExpressionParser parser(tokens, literals); - auto matchExpression = parser.parse(); - // An unparsable version pragma is an unrecoverable fatal error in the parser. - solAssert(matchExpression.has_value(), ""); - static SemVerVersion const currentVersion{string(VersionString)}; - if (!matchExpression->matches(currentVersion)) - m_errorReporter.syntaxError( - 3997_error, - _pragma.location(), - "Source file requires different compiler version (current compiler is " + - string(VersionString) + ") - note that nightly builds are considered to be " - "strictly less than the released version" - ); - m_versionPragmaFound = true; - } - else - m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); - - return true; -} - -bool SyntaxChecker::visit(ModifierDefinition const&) -{ - m_placeholderFound = false; - return true; -} - -void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) -{ - if (_modifier.isImplemented() && !m_placeholderFound) - m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'."); - m_placeholderFound = false; -} - -void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _statement) -{ - auto varDecl = dynamic_cast(&_statement); - if (varDecl) - m_errorReporter.syntaxError(9079_error, _statement.location(), "Variable declarations can only be used inside blocks."); -} - -bool SyntaxChecker::visit(IfStatement const& _ifStatement) -{ - checkSingleStatementVariableDeclaration(_ifStatement.trueStatement()); - if (Statement const* _statement = _ifStatement.falseStatement()) - checkSingleStatementVariableDeclaration(*_statement); - return true; -} - -bool SyntaxChecker::visit(WhileStatement const& _whileStatement) -{ - m_inLoopDepth++; - checkSingleStatementVariableDeclaration(_whileStatement.body()); - return true; -} - -void SyntaxChecker::endVisit(WhileStatement const&) -{ - m_inLoopDepth--; -} - -bool SyntaxChecker::visit(ForStatement const& _forStatement) -{ - m_inLoopDepth++; - checkSingleStatementVariableDeclaration(_forStatement.body()); - return true; -} - -void SyntaxChecker::endVisit(ForStatement const&) -{ - m_inLoopDepth--; -} - -bool SyntaxChecker::visit(Block const& _block) -{ - if (_block.unchecked()) - { - if (m_uncheckedArithmetic) - m_errorReporter.syntaxError( - 1941_error, - _block.location(), - "\"unchecked\" blocks cannot be nested." - ); - - m_uncheckedArithmetic = true; - } - return true; -} - -void SyntaxChecker::endVisit(Block const& _block) -{ - if (_block.unchecked()) - m_uncheckedArithmetic = false; -} - -bool SyntaxChecker::visit(Continue const& _continueStatement) -{ - if (m_inLoopDepth <= 0) - // we're not in a for/while loop, report syntax error - m_errorReporter.syntaxError(4123_error, _continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop."); - return true; -} - -bool SyntaxChecker::visit(Break const& _breakStatement) -{ - if (m_inLoopDepth <= 0) - // we're not in a for/while loop, report syntax error - m_errorReporter.syntaxError(6102_error, _breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop."); - return true; -} - -bool SyntaxChecker::visit(Throw const& _throwStatement) -{ - m_errorReporter.syntaxError( - 4538_error, - _throwStatement.location(), - "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." - ); - - return true; -} - -bool SyntaxChecker::visit(Literal const& _literal) -{ - size_t invalidSequence; - if ((_literal.token() == Token::UnicodeStringLiteral) && !validateUTF8(_literal.value(), invalidSequence)) - m_errorReporter.syntaxError( - 8452_error, - _literal.location(), - "Contains invalid UTF-8 sequence at position " + toString(invalidSequence) + "." - ); - - if (_literal.token() != Token::Number) - return true; - - ASTString const& value = _literal.value(); - solAssert(!value.empty(), ""); - - // Generic checks no matter what base this number literal is of: - if (value.back() == '_') - { - m_errorReporter.syntaxError(2090_error, _literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed."); - return true; - } - - if (value.find("__") != ASTString::npos) - { - m_errorReporter.syntaxError(2990_error, _literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed."); - return true; - } - - if (!_literal.isHexNumber()) // decimal literal - { - if (value.find("._") != ASTString::npos) - m_errorReporter.syntaxError(3891_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); - - if (value.find("_.") != ASTString::npos) - m_errorReporter.syntaxError(1023_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); - - if (value.find("_e") != ASTString::npos) - m_errorReporter.syntaxError(6415_error, _literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed."); - - if (value.find("e_") != ASTString::npos) - m_errorReporter.syntaxError(6165_error, _literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed."); - } - - return true; -} - -bool SyntaxChecker::visit(UnaryOperation const& _operation) -{ - if (_operation.getOperator() == Token::Add) - m_errorReporter.syntaxError(9636_error, _operation.location(), "Use of unary + is disallowed."); - - return true; -} - -bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly) -{ - if (!m_useYulOptimizer) - return false; - - if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations())) - m_errorReporter.syntaxError( - 6553_error, - _inlineAssembly.location(), - "The msize instruction cannot be used when the Yul optimizer is activated because " - "it can change its semantics. Either disable the Yul optimizer or do not use the instruction." - ); - - return false; -} - -bool SyntaxChecker::visit(PlaceholderStatement const& _placeholder) -{ - if (m_uncheckedArithmetic) - m_errorReporter.syntaxError( - 2573_error, - _placeholder.location(), - "The placeholder statement \"_\" cannot be used inside an \"unchecked\" block." - ); - - m_placeholderFound = true; - return true; -} - -bool SyntaxChecker::visit(ContractDefinition const& _contract) -{ - m_currentContractKind = _contract.contractKind(); - - ASTString const& contractName = _contract.name(); - for (FunctionDefinition const* function: _contract.definedFunctions()) - if (function->name() == contractName) - m_errorReporter.syntaxError( - 5796_error, - function->location(), - "Functions are not allowed to have the same name as the contract. " - "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it." - ); - return true; -} - -void SyntaxChecker::endVisit(ContractDefinition const&) -{ - m_currentContractKind = std::nullopt; -} - -bool SyntaxChecker::visit(FunctionDefinition const& _function) -{ - solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); - - if (!_function.isFree() && !_function.isConstructor() && !_function.isOnMessage() && !_function.isOffchain() && _function.noVisibilitySpecified()) - { - string suggestedVisibility = - _function.isFallback() || - _function.isReceive() || - m_currentContractKind == ContractKind::Interface - ? "external" : "public"; - m_errorReporter.syntaxError( - 4937_error, - _function.location(), - "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" - ); - } - else if (_function.isFree()) - { - if (!_function.noVisibilitySpecified()) - m_errorReporter.syntaxError( - 4126_error, - _function.location(), - "Free functions cannot have visibility." - ); - if (!_function.isImplemented()) - m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented."); - } - - if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty()) - m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers."); - else if (!_function.isImplemented() && !_function.modifiers().empty()) - m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); - - return true; -} - -bool SyntaxChecker::visit(FunctionTypeName const& _node) -{ - for (auto const& decl: _node.parameterTypeList()->parameters()) - if (!decl->name().empty()) - m_errorReporter.warning(6162_error, decl->location(), "Naming function type parameters is deprecated."); - - for (auto const& decl: _node.returnParameterTypeList()->parameters()) - if (!decl->name().empty()) - m_errorReporter.syntaxError(7304_error, decl->location(), "Return parameters in function types may not be named."); - - return true; -} - -bool SyntaxChecker::visit(StructDefinition const& _struct) -{ - if (_struct.members().empty()) - m_errorReporter.syntaxError(5306_error, _struct.location(), "Defining empty structs is disallowed."); - - return true; -} diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h new file mode 100644 index 0000000..9accd88 --- /dev/null +++ b/libsolidity/analysis/SyntaxChecker.h @@ -0,0 +1,116 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend +{ + +/** + * The module that performs syntax analysis on the AST: + * - whether continue/break is in a for/while loop. + * - whether a modifier contains at least one '_' + * - issues deprecation warnings for unary '+' + * - issues deprecation warning for throw + * - whether the msize instruction is used and the Yul optimizer is enabled at the same time. + * - selection of the ABI coder through pragmas. + */ +class SyntaxChecker: public ASTConstVisitor +{ +public: + /// @param _errorReporter provides the error logging functionality. + SyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer): + m_errorReporter(_errorReporter), + m_useYulOptimizer(_useYulOptimizer) + {} + + bool checkSyntax(ASTNode const& _astRoot); + +protected: + + bool visit(SourceUnit const& _sourceUnit) override; + void endVisit(SourceUnit const& _sourceUnit) override; + bool visit(PragmaDirective const& _pragma) 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); + + 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; + + bool visit(Block const& _block) override; + void endVisit(Block const& _block) override; + + bool visit(Continue const& _continueStatement) override; + bool visit(Break const& _breakStatement) override; + + bool visit(Throw const& _throwStatement) override; + + bool visit(UnaryOperation const& _operation) override; + + bool visit(InlineAssembly const& _inlineAssembly) override; + + bool visit(PlaceholderStatement const& _placeholderStatement) override; + + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; + bool visit(FunctionDefinition const& _function) override; + bool visit(FunctionTypeName const& _node) override; + + bool visit(StructDefinition const& _struct) override; + bool visit(Literal const& _literal) override; + + langutil::ErrorReporter& m_errorReporter; + + bool m_useYulOptimizer = false; + + /// Flag that indicates whether a function modifier actually contains '_'. + bool m_placeholderFound = false; + + /// Flag that indicates whether some version pragma was present. + bool m_versionPragmaFound = false; + + /// Flag that indicates whether we are inside an unchecked block. + bool m_uncheckedArithmetic = false; + + int m_inLoopDepth = 0; + std::optional m_currentContractKind; + + SourceUnit const* m_sourceUnit = nullptr; +}; + +} diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp deleted file mode 100644 index e3af90b..0000000 --- a/libsolidity/analysis/TypeChecker.cpp +++ /dev/null @@ -1,3594 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -/** - * @author Charles - * @date 2021 - * Solidity++ Type analyzer and checker. - * Solidity++ is modified from Solidity under the terms of the GNU General Public License. - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -using namespace std; -using namespace solidity; -using namespace solidity::util; -using namespace solidity::langutil; -using namespace solidity::frontend; - -bool TypeChecker::typeSupportedByOldABIEncoder(Type const& _type, bool _isLibraryCall) -{ - if (_isLibraryCall && _type.dataStoredIn(DataLocation::Storage)) - return true; - if (_type.category() == Type::Category::Struct) - return false; - if (_type.category() == Type::Category::Array) - { - auto const& arrayType = dynamic_cast(_type); - auto base = arrayType.baseType(); - if (!typeSupportedByOldABIEncoder(*base, _isLibraryCall) || (base->category() == Type::Category::Array && base->isDynamicallySized())) - return false; - } - return true; -} - -bool TypeChecker::checkTypeRequirements(SourceUnit const& _source) -{ - m_currentSourceUnit = &_source; - _source.accept(*this); - m_currentSourceUnit = nullptr; - return Error::containsOnlyWarnings(m_errorReporter.errors()); -} - -TypePointer const& TypeChecker::type(Expression const& _expression) const -{ - solAssert(!!_expression.annotation().type, "Type requested but not present."); - return _expression.annotation().type; -} - -TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const -{ - solAssert(!!_variable.annotation().type, "Type requested but not present."); - return _variable.annotation().type; -} - -bool TypeChecker::visit(ContractDefinition const& _contract) -{ - m_currentContract = &_contract; - - ASTNode::listAccept(_contract.baseContracts(), *this); - - for (auto const& n: _contract.subNodes()) - n->accept(*this); - - m_currentContract = nullptr; - - return false; -} - -void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) -{ - TupleType const& lhs = dynamic_cast(*type(_assignment.leftHandSide())); - TupleType const& rhs = dynamic_cast(*type(_assignment.rightHandSide())); - - if (lhs.components().size() != rhs.components().size()) - { - solAssert(m_errorReporter.hasErrors(), ""); - return; - } - - size_t storageToStorageCopies = 0; - size_t toStorageCopies = 0; - for (size_t i = 0; i < lhs.components().size(); ++i) - { - ReferenceType const* ref = dynamic_cast(lhs.components()[i]); - if (!ref || !ref->dataStoredIn(DataLocation::Storage) || ref->isPointer()) - continue; - toStorageCopies++; - if (rhs.components()[i]->dataStoredIn(DataLocation::Storage)) - storageToStorageCopies++; - } - if (storageToStorageCopies >= 1 && toStorageCopies >= 2) - m_errorReporter.warning( - 7238_error, - _assignment.location(), - "This assignment performs two copies to storage. Since storage copies do not first " - "copy to a temporary location, one of them might be overwritten before the second " - "is executed and thus may have unexpected effects. It is safer to perform the copies " - "separately or assign to storage pointers first." - ); -} - -TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2) -{ - vector> arguments = _functionCall.arguments(); - if (arguments.size() != 2) - m_errorReporter.typeError( - 5782_error, - _functionCall.location(), - "This function takes two arguments, but " + - toString(arguments.size()) + - " were provided." - ); - - if (arguments.size() >= 1) - if ( - !type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) && - !type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()) - ) - m_errorReporter.typeError( - 1956_error, - arguments.front()->location(), - "The first argument to \"abi.decode\" must be implicitly convertible to " - "bytes memory or bytes calldata, but is of type " + - type(*arguments.front())->toString() + - "." - ); - - if (arguments.size() < 2) - return {}; - - // The following is a rather syntactic restriction, but we check it here anyway: - // The second argument has to be a tuple expression containing type names. - TupleExpression const* tupleExpression = dynamic_cast(arguments[1].get()); - if (!tupleExpression) - { - m_errorReporter.typeError( - 6444_error, - arguments[1]->location(), - "The second argument to \"abi.decode\" has to be a tuple of types." - ); - return {}; - } - - TypePointers components; - for (auto const& typeArgument: tupleExpression->components()) - { - solAssert(typeArgument, ""); - if (TypeType const* argTypeType = dynamic_cast(type(*typeArgument))) - { - TypePointer actualType = argTypeType->actualType(); - solAssert(actualType, ""); - // We force memory because the parser currently cannot handle - // data locations. Furthermore, storage can be a little dangerous and - // calldata is not really implemented anyway. - actualType = TypeProvider::withLocationIfReference(DataLocation::Memory, actualType); - // We force address payable for address types. - if (actualType->category() == Type::Category::Address) - actualType = TypeProvider::payableAddress(); - solAssert( - !actualType->dataStoredIn(DataLocation::CallData) && - !actualType->dataStoredIn(DataLocation::Storage), - "" - ); - if (!actualType->fullEncodingType(false, _abiEncoderV2, false)) - m_errorReporter.typeError( - 9611_error, - typeArgument->location(), - "Decoding type " + actualType->toString(false) + " not supported." - ); - - if (auto referenceType = dynamic_cast(actualType)) - { - auto result = referenceType->validForLocation(referenceType->location()); - if (!result) - m_errorReporter.typeError( - 6118_error, - typeArgument->location(), - result.message() - ); - } - - components.push_back(actualType); - } - else - { - m_errorReporter.typeError(1039_error, typeArgument->location(), "Argument has to be a type name."); - components.push_back(TypeProvider::emptyTuple()); - } - } - return components; -} - -TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall) -{ - vector> arguments = _functionCall.arguments(); - if (arguments.size() != 1) - m_errorReporter.fatalTypeError( - 8885_error, - _functionCall.location(), - "This function takes one argument, but " + - toString(arguments.size()) + - " were provided." - ); - TypePointer firstArgType = type(*arguments.front()); - - bool wrongType = false; - if (firstArgType->category() == Type::Category::TypeType) - { - TypeType const* typeTypePtr = dynamic_cast(firstArgType); - Type::Category typeCategory = typeTypePtr->actualType()->category(); - if (auto const* contractType = dynamic_cast(typeTypePtr->actualType())) - wrongType = contractType->isSuper(); - else if (typeCategory != Type::Category::Integer) - wrongType = true; - } - else - wrongType = true; - - if (wrongType) - m_errorReporter.fatalTypeError( - 4259_error, - arguments.front()->location(), - "Invalid type for argument in the function call. " - "A contract type or an integer type is required, but " + - type(*arguments.front())->toString(true) + " provided." - ); - - return {TypeProvider::meta(dynamic_cast(*firstArgType).actualType())}; -} - -void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) -{ - auto base = dynamic_cast(&dereference(_inheritance.name())); - solAssert(base, "Base contract not available."); - solAssert(m_currentContract, ""); - - if (m_currentContract->isInterface() && !base->isInterface()) - m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); - - auto const& arguments = _inheritance.arguments(); - TypePointers parameterTypes; - if (!base->isInterface()) - // Interfaces do not have constructors, so there are zero parameters. - parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); - - if (arguments) - { - if (parameterTypes.size() != arguments->size()) - { - m_errorReporter.typeError( - 7927_error, - _inheritance.location(), - "Wrong argument count for constructor call: " + - toString(arguments->size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - ". Remove parentheses if you do not want to provide arguments here." - ); - } - for (size_t i = 0; i < std::min(arguments->size(), parameterTypes.size()); ++i) - { - BoolResult result = type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i]); - if (!result) - m_errorReporter.typeErrorConcatenateDescriptions( - 9827_error, - (*arguments)[i]->location(), - "Invalid type for argument in constructor call. " - "Invalid implicit conversion from " + - type(*(*arguments)[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested.", - result.message() - ); - } - } -} - -void TypeChecker::endVisit(ModifierDefinition const& _modifier) -{ - if (_modifier.virtualSemantics()) - if (auto const* contractDef = dynamic_cast(_modifier.scope())) - if (contractDef->isLibrary()) - m_errorReporter.typeError( - 3275_error, - _modifier.location(), - "Modifiers in a library cannot be virtual." - ); - - if (!_modifier.isImplemented() && !_modifier.virtualSemantics()) - m_errorReporter.typeError(8063_error, _modifier.location(), "Modifiers without implementation must be marked virtual."); -} - -bool TypeChecker::visit(FunctionDefinition const& _function) -{ - if (_function.markedVirtual()) - { - if (_function.isFree()) - m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual."); - else if (_function.isConstructor()) - m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); - else if (_function.annotation().contract->isInterface()) - m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); - else if (_function.visibility() == Visibility::Private) - m_errorReporter.typeError(3942_error, _function.location(), "\"virtual\" and \"private\" cannot be used together."); - else if (_function.libraryFunction()) - m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); - } - if (_function.overrides() && _function.isFree()) - m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override."); - - if (!_function.modifiers().empty() && _function.isFree()) - m_errorReporter.syntaxError(5811_error, _function.location(), "Free functions cannot have modifiers."); - - if (_function.isPayable()) - { - if (_function.libraryFunction()) - m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); - else if (_function.isFree()) - m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable."); - else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) - m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); - } - - // Solidity++: check async functions - if (_function.isAsync()) - { - if (_function.libraryFunction()) - m_errorReporter.typeError(100301_error, _function.location(), "Library functions cannot be async."); - else if (_function.isFree()) - m_errorReporter.typeError(100302_error, _function.location(), "Free functions cannot be async."); - else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) - m_errorReporter.typeError(100303_error, _function.location(), "\"internal\" and \"private\" functions cannot be async."); - } - // Solidity++: check externally visible functions (must be async) - else if (_function.isPartOfExternalInterface()) - { - if (_function.isOrdinary()) - m_errorReporter.typeError(100304_error, _function.location(), "\"external\" and \"public\" functions must be async."); - } - - vector internalParametersInConstructor; - - auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& _var) { - if (type(_var)->containsNestedMapping()) - if (_var.referenceLocation() == VariableDeclaration::Location::Storage) - solAssert( - _function.libraryFunction() || _function.isConstructor() || !_function.isPublic(), - "Mapping types for parameters or return variables " - "can only be used in internal or library functions." - ); - bool functionIsExternallyVisible = - (!_function.isConstructor() && _function.isPublic()) || - (_function.isConstructor() && !m_currentContract->abstract()); - if ( - _function.isConstructor() && - _var.referenceLocation() == VariableDeclaration::Location::Storage && - !m_currentContract->abstract() - ) - m_errorReporter.typeError( - 3644_error, - _var.location(), - "This parameter has a type that can only be used internally. " - "You can make the contract abstract to avoid this problem." - ); - else if (functionIsExternallyVisible) - { - auto iType = type(_var)->interfaceType(_function.libraryFunction()); - - if (!iType) - { - string message = iType.message(); - solAssert(!message.empty(), "Expected detailed error message!"); - if (_function.isConstructor()) - message += " You can make the contract abstract to avoid this problem."; - m_errorReporter.typeError(4103_error, _var.location(), message); - } - else if ( - !useABICoderV2() && - !typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction()) - ) - { - string message = - "This type is only supported in ABI coder v2. " - "Use \"pragma abicoder v2;\" to enable the feature."; - if (_function.isConstructor()) - message += - " Alternatively, make the contract abstract and supply the " - "constructor arguments from a derived contract."; - m_errorReporter.typeError( - 4957_error, - _var.location(), - message - ); - } - } - }; - for (ASTPointer const& var: _function.parameters()) - { - checkArgumentAndReturnParameter(*var); - var->accept(*this); - } - for (ASTPointer const& var: _function.returnParameters()) - { - checkArgumentAndReturnParameter(*var); - var->accept(*this); - } - - set modifiers; - for (ASTPointer const& modifier: _function.modifiers()) - { - vector baseContracts; - if (auto contract = dynamic_cast(_function.scope())) - { - baseContracts = contract->annotation().linearizedBaseContracts; - // Delete first base which is just the main contract itself - baseContracts.erase(baseContracts.begin()); - } - - visitManually( - *modifier, - _function.isConstructor() ? baseContracts : vector() - ); - Declaration const* decl = &dereference(modifier->name()); - if (modifiers.count(decl)) - { - if (dynamic_cast(decl)) - m_errorReporter.declarationError(1697_error, modifier->location(), "Base constructor already provided."); - } - else - modifiers.insert(decl); - } - - solAssert(_function.isFree() == !m_currentContract, ""); - if (!m_currentContract) - { - solAssert(!_function.isConstructor(), ""); - solAssert(!_function.isFallback(), ""); - solAssert(!_function.isReceive(), ""); - } - else if (m_currentContract->isInterface()) - { - if (_function.isImplemented()) - m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); - - if (_function.isConstructor()) - m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces."); - else if (_function.visibility() != Visibility::External && _function.visibility() != Visibility::Offchain) - m_errorReporter.typeError(1560_error, _function.location(), "Functions in interfaces must be declared external or offchain."); - } - else if (m_currentContract->contractKind() == ContractKind::Library) - if (_function.isConstructor()) - m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); - - if (_function.isImplemented()) - _function.body().accept(*this); - else if (_function.isConstructor()) - m_errorReporter.typeError(5700_error, _function.location(), "Constructor must be implemented if declared."); - else if (_function.libraryFunction()) - m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); - else if (!_function.virtualSemantics()) - { - if (_function.isFree()) - solAssert(m_errorReporter.hasErrors(), ""); - else - m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); - } - - - if (_function.isFallback()) - typeCheckFallbackFunction(_function); - else if (_function.isReceive()) - typeCheckReceiveFunction(_function); - else if (_function.isConstructor()) - typeCheckConstructor(_function); - - return false; -} - -bool TypeChecker::visit(VariableDeclaration const& _variable) -{ - _variable.typeName().accept(*this); - - // 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, "Variable type not provided."); - - if (_variable.value()) - { - if (_variable.isStateVariable() && varType->containsNestedMapping()) - { - m_errorReporter.typeError( - 6280_error, - _variable.location(), - "Types in storage containing (nested) mappings cannot be assigned to." - ); - _variable.value()->accept(*this); - } - else - expectType(*_variable.value(), *varType); - } - if (_variable.isConstant()) - { - if (!_variable.value()) - m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable."); - else if (!*_variable.value()->annotation().isPure) - m_errorReporter.typeError( - 8349_error, - _variable.value()->location(), - "Initial value for constant variable has to be compile-time constant." - ); - } - else if (_variable.immutable()) - { - if (!_variable.type()->isValueType()) - m_errorReporter.typeError(6377_error, _variable.location(), "Immutable variables cannot have a non-value type."); - if ( - auto const* functionType = dynamic_cast(_variable.type()); - functionType && functionType->kind() == FunctionType::Kind::External - ) - m_errorReporter.typeError(3366_error, _variable.location(), "Immutable variables of external function type are not yet supported."); - solAssert(_variable.type()->sizeOnStack() == 1 || m_errorReporter.hasErrors(), ""); - } - - if (!_variable.isStateVariable()) - { - if ( - _variable.referenceLocation() == VariableDeclaration::Location::CallData || - _variable.referenceLocation() == VariableDeclaration::Location::Memory - ) - if (varType->containsNestedMapping()) - m_errorReporter.fatalTypeError( - 4061_error, - _variable.location(), - "Type " + varType->toString(true) + " is only valid in storage because it contains a (nested) mapping." - ); - } - else if (_variable.visibility() >= Visibility::Public) - { - FunctionType getter(_variable); - if (!useABICoderV2()) - { - vector unsupportedTypes; - for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes()) - if (!typeSupportedByOldABIEncoder(*param, false /* isLibrary */)) - unsupportedTypes.emplace_back(param->toString()); - if (!unsupportedTypes.empty()) - m_errorReporter.typeError( - 2763_error, - _variable.location(), - "The following types are only supported for getters in ABI coder v2: " + - joinHumanReadable(unsupportedTypes) + - ". Either remove \"public\" or use \"pragma abicoder v2;\" to enable the feature." - ); - } - if (!getter.interfaceFunctionType()) - m_errorReporter.typeError(6744_error, _variable.location(), "Internal or recursive type is not allowed for public state variables."); - } - - bool isStructMemberDeclaration = dynamic_cast(_variable.scope()) != nullptr; - if (isStructMemberDeclaration) - return false; - - if (auto referenceType = dynamic_cast(varType)) - { - auto result = referenceType->validForLocation(referenceType->location()); - if (result) - { - bool isLibraryStorageParameter = (_variable.isLibraryFunctionParameter() && referenceType->location() == DataLocation::Storage); - bool callDataCheckRequired = ((_variable.isConstructorParameter() || _variable.isPublicCallableParameter()) && !isLibraryStorageParameter); - if (callDataCheckRequired) - result = referenceType->validForLocation(DataLocation::CallData); - } - if (!result) - { - solAssert(!result.message().empty(), "Expected detailed error message"); - m_errorReporter.typeError(1534_error, _variable.location(), result.message()); - return false; - } - } - - return false; -} - -void TypeChecker::visitManually( - ModifierInvocation const& _modifier, - vector const& _bases -) -{ - std::vector> const& arguments = - _modifier.arguments() ? *_modifier.arguments() : std::vector>(); - for (ASTPointer const& argument: arguments) - argument->accept(*this); - - _modifier.name().accept(*this); - - auto const* declaration = &dereference(_modifier.name()); - vector> emptyParameterList; - vector> const* parameters = nullptr; - if (auto modifierDecl = dynamic_cast(declaration)) - { - parameters = &modifierDecl->parameters(); - if (auto const* modifierContract = dynamic_cast(modifierDecl->scope())) - if (m_currentContract) - { - if (!contains(m_currentContract->annotation().linearizedBaseContracts, modifierContract)) - m_errorReporter.typeError( - 9428_error, - _modifier.location(), - "Can only use modifiers defined in the current contract or in base contracts." - ); - } - } - else - // check parameters for Base constructors - for (ContractDefinition const* base: _bases) - if (declaration == base) - { - if (auto referencedConstructor = base->constructor()) - parameters = &referencedConstructor->parameters(); - else - parameters = &emptyParameterList; - break; - } - if (!parameters) - { - m_errorReporter.typeError(4659_error, _modifier.location(), "Referenced declaration is neither modifier nor base class."); - return; - } - if (parameters->size() != arguments.size()) - { - m_errorReporter.typeError( - 2973_error, - _modifier.location(), - "Wrong argument count for modifier invocation: " + - toString(arguments.size()) + - " arguments given but expected " + - toString(parameters->size()) + - "." - ); - return; - } - for (size_t i = 0; i < arguments.size(); ++i) - { - BoolResult result = type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])); - if (!result) - m_errorReporter.typeErrorConcatenateDescriptions( - 4649_error, - arguments[i]->location(), - "Invalid type for argument in modifier invocation. " - "Invalid implicit conversion from " + - type(*arguments[i])->toString() + - " to " + - type(*(*parameters)[i])->toString() + - " requested.", - result.message() - ); - } -} - -bool TypeChecker::visit(EventDefinition const& _eventDef) -{ - solAssert(_eventDef.visibility() > Visibility::Internal, ""); - unsigned numIndexed = 0; - for (ASTPointer const& var: _eventDef.parameters()) - { - if (var->isIndexed()) - numIndexed++; - if (type(*var)->containsNestedMapping()) - m_errorReporter.typeError( - 3448_error, - var->location(), - "Type containing a (nested) mapping is not allowed as event parameter type." - ); - if (!type(*var)->interfaceType(false)) - m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); - if ( - !useABICoderV2() && - !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) - ) - m_errorReporter.typeError( - 3061_error, - var->location(), - "This type is only supported in ABI coder v2. " - "Use \"pragma abicoder v2;\" to enable the feature." - ); - } - if (_eventDef.isAnonymous() && numIndexed > 4) - m_errorReporter.typeError(8598_error, _eventDef.location(), "More than 4 indexed arguments for anonymous event."); - else if (!_eventDef.isAnonymous() && numIndexed > 3) - m_errorReporter.typeError(7249_error, _eventDef.location(), "More than 3 indexed arguments for event."); - return true; -} - -// Solidity++: check message definition type -bool TypeChecker::visit(MessageDefinition const& _msgDef) -{ - solAssert(_msgDef.visibility() > Visibility::Internal, ""); - unsigned numIndexed = 0; - for (ASTPointer const& var: _msgDef.parameters()) - { - if (var->isIndexed()) - numIndexed++; - if (type(*var)->containsNestedMapping()) - m_errorReporter.typeError( - 3448_error, - var->location(), - "Type containing a (nested) mapping is not allowed as message parameter type." - ); - if (!type(*var)->interfaceType(false)) - m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as message parameter type."); - if ( - !useABICoderV2() && - !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) - ) - m_errorReporter.typeError( - 3061_error, - var->location(), - "This type is only supported in ABI coder v2. " - "Use \"pragma abicoder v2;\" to enable the feature." - ); - } - - return true; -} - -void TypeChecker::endVisit(FunctionTypeName const& _funType) -{ - FunctionType const& fun = dynamic_cast(*_funType.annotation().type); - if (fun.kind() == FunctionType::Kind::External) - { - for (auto const& t: _funType.parameterTypes() + _funType.returnParameterTypes()) - { - solAssert(t->annotation().type, "Type not set for parameter."); - if (!t->annotation().type->interfaceType(false).get()) - m_errorReporter.typeError(2582_error, t->location(), "Internal type cannot be used for external function type."); - } - solAssert(fun.interfaceType(false), "External function type uses internal types."); - } -} - -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 = [&]( - yul::Identifier const& _identifier, - yul::IdentifierContext _context, - bool - ) - { - auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); - if (ref == _inlineAssembly.annotation().externalReferences.end()) - return false; - InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; - Declaration const* declaration = identifierInfo.declaration; - solAssert(!!declaration, ""); - if (auto var = dynamic_cast(declaration)) - { - solAssert(var->type(), "Expected variable type!"); - if (var->immutable()) - { - m_errorReporter.typeError(3773_error, _identifier.location, "Assembly access to immutable variables is not supported."); - return false; - } - if (var->isConstant()) - { - if (isConstantVariableRecursive(*var)) - { - m_errorReporter.typeError( - 3558_error, - _identifier.location, - "Constant variable is circular." - ); - return false; - } - - var = rootConstVariableDeclaration(*var); - - if (var && !var->value()) - { - m_errorReporter.typeError(3224_error, _identifier.location, "Constant has no value."); - return false; - } - else if (_context == yul::IdentifierContext::LValue) - { - m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to."); - return false; - } - else if (!identifierInfo.suffix.empty()) - { - m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); - return false; - } - else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast(var->value().get())) - { - m_errorReporter.typeError( - 2249_error, - _identifier.location, - "Constant variables with non-literal values cannot be forward referenced from inline assembly." - ); - return false; - } - else if (!var || !type(*var)->isValueType() || ( - !dynamic_cast(var->value().get()) && - type(*var->value())->category() != Type::Category::RationalNumber - )) - { - m_errorReporter.typeError(7615_error, _identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); - return false; - } - } - - solAssert(!dynamic_cast(var->type()), "FixedPointType not implemented."); - - if (!identifierInfo.suffix.empty()) - { - string const& suffix = identifierInfo.suffix; - solAssert((set{"offset", "slot", "length"}).count(suffix), ""); - if (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)) - { - if (suffix != "slot" && suffix != "offset") - { - m_errorReporter.typeError(4656_error, _identifier.location, "State variables only support \".slot\" and \".offset\"."); - return false; - } - else if (_context == yul::IdentifierContext::LValue) - { - if (var->isStateVariable()) - { - m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); - return false; - } - else if (suffix != "slot") - { - m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); - return false; - } - } - } - else if ( - auto const* arrayType = dynamic_cast(var->type()); - arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) - ) - { - if (suffix != "offset" && suffix != "length") - { - m_errorReporter.typeError(1536_error, _identifier.location, "Calldata variables only support \".offset\" and \".length\"."); - return false; - } - } - else - { - m_errorReporter.typeError(3622_error, _identifier.location, "The suffix \"." + suffix + "\" is not supported by this variable or type."); - return false; - } - } - else if (!var->isConstant() && var->isStateVariable()) - { - m_errorReporter.typeError( - 1408_error, - _identifier.location, - "Only local variables are supported. To access storage variables, use the \".slot\" and \".offset\" suffixes." - ); - return false; - } - else if (var->type()->dataStoredIn(DataLocation::Storage)) - { - m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); - return false; - } - else if (var->type()->sizeOnStack() != 1) - { - if ( - auto const* arrayType = dynamic_cast(var->type()); - arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) - ) - m_errorReporter.typeError(1397_error, _identifier.location, "Call data elements cannot be accessed directly. Use \".offset\" and \".length\" to access the calldata offset and length of this array and then use \"calldatacopy\"."); - else - { - solAssert(!var->type()->dataStoredIn(DataLocation::CallData), ""); - m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported."); - } - return false; - } - } - else if (!identifierInfo.suffix.empty()) - { - m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); - return false; - } - else if (_context == yul::IdentifierContext::LValue) - { - if (dynamic_cast(declaration)) - return false; - - m_errorReporter.typeError(1990_error, _identifier.location, "Only local variables can be assigned to in inline assembly."); - return false; - } - - if (_context == yul::IdentifierContext::RValue) - { - solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); - if (dynamic_cast(declaration)) - { - m_errorReporter.declarationError(2025_error, _identifier.location, "Access to functions is not allowed in inline assembly."); - return false; - } - else if (dynamic_cast(declaration)) - { - } - else if (auto contract = dynamic_cast(declaration)) - { - if (!contract->isLibrary()) - { - m_errorReporter.typeError(4977_error, _identifier.location, "Expected a library."); - return false; - } - } - else - return false; - } - identifierInfo.valueSize = 1; - return true; - }; - solAssert(!_inlineAssembly.annotation().analysisInfo, ""); - _inlineAssembly.annotation().analysisInfo = make_shared(); - yul::AsmAnalyzer analyzer( - *_inlineAssembly.annotation().analysisInfo, - m_errorReporter, - _inlineAssembly.dialect(), - identifierAccess - ); - if (!analyzer.analyze(_inlineAssembly.operations())) - return false; - return true; -} - -bool TypeChecker::visit(IfStatement const& _ifStatement) -{ - expectType(_ifStatement.condition(), *TypeProvider::boolean()); - _ifStatement.trueStatement().accept(*this); - if (_ifStatement.falseStatement()) - _ifStatement.falseStatement()->accept(*this); - return false; -} - -void TypeChecker::endVisit(TryStatement const& _tryStatement) -{ - FunctionCall const* externalCall = dynamic_cast(&_tryStatement.externalCall()); - if (!externalCall || *externalCall->annotation().kind != FunctionCallKind::FunctionCall) - { - m_errorReporter.typeError( - 5347_error, - _tryStatement.externalCall().location(), - "Try can only be used with external function calls and contract creation calls." - ); - return; - } - - FunctionType const& functionType = dynamic_cast(*externalCall->expression().annotation().type); - if ( - functionType.kind() != FunctionType::Kind::External && - functionType.kind() != FunctionType::Kind::Creation && - functionType.kind() != FunctionType::Kind::DelegateCall - ) - { - m_errorReporter.typeError( - 2536_error, - _tryStatement.externalCall().location(), - "Try can only be used with external function calls and contract creation calls." - ); - return; - } - - externalCall->annotation().tryCall = true; - - solAssert(_tryStatement.clauses().size() >= 2, ""); - solAssert(_tryStatement.clauses().front(), ""); - - TryCatchClause const& successClause = *_tryStatement.clauses().front(); - if (successClause.parameters()) - { - TypePointers returnTypes = - m_evmVersion.supportsReturndata() ? - functionType.returnParameterTypes() : - functionType.returnParameterTypesWithoutDynamicTypes(); - std::vector> const& parameters = - successClause.parameters()->parameters(); - if (returnTypes.size() != parameters.size()) - m_errorReporter.typeError( - 2800_error, - successClause.location(), - "Function returns " + - to_string(functionType.returnParameterTypes().size()) + - " values, but returns clause has " + - to_string(parameters.size()) + - " variables." - ); - for (auto&& [parameter, returnType]: ranges::views::zip(parameters, returnTypes)) - { - solAssert(returnType, ""); - if (parameter && *parameter->annotation().type != *returnType) - m_errorReporter.typeError( - 6509_error, - parameter->location(), - "Invalid type, expected " + - returnType->toString(false) + - " but got " + - parameter->annotation().type->toString() + - "." - ); - } - } - - TryCatchClause const* panicClause = nullptr; - TryCatchClause const* errorClause = nullptr; - TryCatchClause const* lowLevelClause = nullptr; - for (auto const& clause: _tryStatement.clauses() | ranges::views::drop_exactly(1) | views::dereferenceChecked) - { - if (clause.errorName() == "") - { - if (lowLevelClause) - m_errorReporter.typeError( - 5320_error, - clause.location(), - SecondarySourceLocation{}.append("The first clause is here:", lowLevelClause->location()), - "This try statement already has a low-level catch clause." - ); - lowLevelClause = &clause; - if (clause.parameters() && !clause.parameters()->parameters().empty()) - { - if ( - clause.parameters()->parameters().size() != 1 || - *clause.parameters()->parameters().front()->type() != *TypeProvider::bytesMemory() - ) - m_errorReporter.typeError(6231_error, clause.location(), "Expected `catch (bytes memory ...) { ... }` or `catch { ... }`."); - if (!m_evmVersion.supportsReturndata()) - m_errorReporter.typeError( - 9908_error, - clause.location(), - "This catch clause type cannot be used on the selected EVM version (" + - m_evmVersion.name() + - "). You need at least a Byzantium-compatible EVM or use `catch { ... }`." - ); - } - } - else if (clause.errorName() == "Error" || clause.errorName() == "Panic") - { - if (!m_evmVersion.supportsReturndata()) - m_errorReporter.typeError( - 1812_error, - clause.location(), - "This catch clause type cannot be used on the selected EVM version (" + - m_evmVersion.name() + - "). You need at least a Byzantium-compatible EVM or use `catch { ... }`." - ); - - if (clause.errorName() == "Error") - { - if (errorClause) - m_errorReporter.typeError( - 1036_error, - clause.location(), - SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()), - "This try statement already has an \"Error\" catch clause." - ); - errorClause = &clause; - if ( - !clause.parameters() || - clause.parameters()->parameters().size() != 1 || - *clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory() - ) - m_errorReporter.typeError(2943_error, clause.location(), "Expected `catch Error(string memory ...) { ... }`."); - } - else - { - if (panicClause) - m_errorReporter.typeError( - 6732_error, - clause.location(), - SecondarySourceLocation{}.append("The first clause is here:", panicClause->location()), - "This try statement already has a \"Panic\" catch clause." - ); - panicClause = &clause; - if ( - !clause.parameters() || - clause.parameters()->parameters().size() != 1 || - *clause.parameters()->parameters().front()->type() != *TypeProvider::uint256() - ) - m_errorReporter.typeError(1271_error, clause.location(), "Expected `catch Panic(uint ...) { ... }`."); - } - } - else - m_errorReporter.typeError( - 3542_error, - clause.location(), - "Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`." - ); - } -} - -bool TypeChecker::visit(WhileStatement const& _whileStatement) -{ - expectType(_whileStatement.condition(), *TypeProvider::boolean()); - _whileStatement.body().accept(*this); - return false; -} - -bool TypeChecker::visit(ForStatement const& _forStatement) -{ - if (_forStatement.initializationExpression()) - _forStatement.initializationExpression()->accept(*this); - if (_forStatement.condition()) - expectType(*_forStatement.condition(), *TypeProvider::boolean()); - if (_forStatement.loopExpression()) - _forStatement.loopExpression()->accept(*this); - _forStatement.body().accept(*this); - return false; -} - -void TypeChecker::endVisit(Return const& _return) -{ - ParameterList const* params = _return.annotation().functionReturnParameters; - if (!_return.expression()) - { - if (params && !params->parameters().empty()) - m_errorReporter.typeError(6777_error, _return.location(), "Return arguments required."); - return; - } - if (!params) - { - m_errorReporter.typeError(7552_error, _return.location(), "Return arguments not allowed."); - return; - } - TypePointers returnTypes; - for (auto const& var: params->parameters()) - returnTypes.push_back(type(*var)); - if (auto tupleType = dynamic_cast(type(*_return.expression()))) - { - if (tupleType->components().size() != params->parameters().size()) - m_errorReporter.typeError(5132_error, _return.location(), "Different number of arguments in return statement than in returns declaration."); - else - { - BoolResult result = tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)); - if (!result) - m_errorReporter.typeErrorConcatenateDescriptions( - 5992_error, - _return.expression()->location(), - "Return argument type " + - type(*_return.expression())->toString() + - " is not implicitly convertible to expected type " + - TupleType(returnTypes).toString(false) + ".", - result.message() - ); - } - } - else if (params->parameters().size() != 1) - m_errorReporter.typeError(8863_error, _return.location(), "Different number of arguments in return statement than in returns declaration."); - else - { - TypePointer const& expected = type(*params->parameters().front()); - BoolResult result = type(*_return.expression())->isImplicitlyConvertibleTo(*expected); - if (!result) - m_errorReporter.typeErrorConcatenateDescriptions( - 6359_error, - _return.expression()->location(), - "Return argument type " + - type(*_return.expression())->toString() + - " is not implicitly convertible to expected type (type of first return variable) " + - expected->toString() + ".", - result.message() - ); - } -} - -void TypeChecker::endVisit(EmitStatement const& _emit) -{ - if ( - *_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || - type(_emit.eventCall().expression())->category() != Type::Category::Function || - dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event - ) - m_errorReporter.typeError(9292_error, _emit.eventCall().expression().location(), "Expression has to be an event invocation."); -} - -bool TypeChecker::visit(VariableDeclarationStatement const& _statement) -{ - if (!_statement.initialValue()) - { - // No initial value is only permitted for single variables with specified type. - // This usually already results in a parser error. - if (_statement.declarations().size() != 1 || !_statement.declarations().front()) - { - solAssert(m_errorReporter.hasErrors(), ""); - - // It is okay to return here, as there are no named components on the - // left-hand-side that could cause any damage later. - return false; - } - - VariableDeclaration const& varDecl = *_statement.declarations().front(); - solAssert(varDecl.annotation().type, ""); - - if (dynamic_cast(type(varDecl))) - m_errorReporter.typeError( - 4182_error, - varDecl.location(), - "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." - ); - varDecl.accept(*this); - return false; - } - - // Here we have an initial value and might have to derive some types before we can visit - // the variable declaration(s). - - _statement.initialValue()->accept(*this); - TypePointers valueTypes; - if (auto tupleType = dynamic_cast(type(*_statement.initialValue()))) - valueTypes = tupleType->components(); - else - valueTypes = TypePointers{type(*_statement.initialValue())}; - - vector> const& variables = _statement.declarations(); - if (variables.empty()) - // We already have an error for this in the SyntaxChecker. - solAssert(m_errorReporter.hasErrors(), ""); - else if (valueTypes.size() != variables.size()) - m_errorReporter.typeError( - 7364_error, - _statement.location(), - "Different number of components on the left hand side (" + - toString(variables.size()) + - ") than on the right hand side (" + - toString(valueTypes.size()) + - ")." - ); - - for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i) - { - if (!variables[i]) - continue; - VariableDeclaration const& var = *variables[i]; - solAssert(!var.value(), "Value has to be tied to statement."); - TypePointer const& valueComponentType = valueTypes[i]; - solAssert(!!valueComponentType, ""); - solAssert(var.annotation().type, ""); - - var.accept(*this); - BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type); - if (!result) - { - auto errorMsg = "Type " + - valueComponentType->toString() + - " is not implicitly convertible to expected type " + - var.annotation().type->toString(); - if ( - valueComponentType->category() == Type::Category::RationalNumber && - dynamic_cast(*valueComponentType).isFractional() && - valueComponentType->mobileType() - ) - { - if (var.annotation().type->operator==(*valueComponentType->mobileType())) - m_errorReporter.typeError( - 5107_error, - _statement.location(), - errorMsg + ", but it can be explicitly converted." - ); - else - m_errorReporter.typeError( - 4486_error, - _statement.location(), - errorMsg + - ". Try converting to type " + - valueComponentType->mobileType()->toString() + - " or use an explicit conversion." - ); - } - else - m_errorReporter.typeErrorConcatenateDescriptions( - 9574_error, - _statement.location(), - errorMsg + ".", - result.message() - ); - } - } - - if (valueTypes.size() != variables.size()) - { - solAssert(m_errorReporter.hasErrors(), "Should have errors!"); - for (auto const& var: variables) - if (var && !var->annotation().type) - BOOST_THROW_EXCEPTION(FatalError()); - } - - return false; -} - -void TypeChecker::endVisit(ExpressionStatement const& _statement) -{ - if (type(_statement.expression())->category() == Type::Category::RationalNumber) - if (!dynamic_cast(*type(_statement.expression())).mobileType()) - m_errorReporter.typeError(3757_error, _statement.expression().location(), "Invalid rational number."); - - if (auto call = dynamic_cast(&_statement.expression())) - { - if (auto callType = dynamic_cast(type(call->expression()))) - { - auto kind = callType->kind(); - if ( - kind == FunctionType::Kind::BareCall || - kind == FunctionType::Kind::BareCallCode || - kind == FunctionType::Kind::BareDelegateCall || - kind == FunctionType::Kind::BareStaticCall - ) - m_errorReporter.warning(9302_error, _statement.location(), "Return value of low-level calls not used."); - else if (kind == FunctionType::Kind::Send) - m_errorReporter.warning(5878_error, _statement.location(), "Failure condition of 'send' ignored. Consider using 'transfer' instead."); - } - } -} - -bool TypeChecker::visit(Conditional const& _conditional) -{ - expectType(_conditional.condition(), *TypeProvider::boolean()); - - _conditional.trueExpression().accept(*this); - _conditional.falseExpression().accept(*this); - - TypePointer trueType = type(_conditional.trueExpression())->mobileType(); - TypePointer falseType = type(_conditional.falseExpression())->mobileType(); - - TypePointer commonType = nullptr; - - if (!trueType) - m_errorReporter.typeError(9717_error, _conditional.trueExpression().location(), "Invalid mobile type in true expression."); - else - commonType = trueType; - - if (!falseType) - m_errorReporter.typeError(3703_error, _conditional.falseExpression().location(), "Invalid mobile type in false expression."); - else - commonType = falseType; - - if (!trueType && !falseType) - BOOST_THROW_EXCEPTION(FatalError()); - else if (trueType && falseType) - { - commonType = Type::commonType(trueType, falseType); - - if (!commonType) - { - m_errorReporter.typeError( - 1080_error, - _conditional.location(), - "True expression's type " + - trueType->toString() + - " does not match false expression's type " + - falseType->toString() + - "." - ); - // even we can't find a common type, we have to set a type here, - // otherwise the upper statement will not be able to check the type. - commonType = trueType; - } - } - - _conditional.annotation().isConstant = false; - _conditional.annotation().type = commonType; - _conditional.annotation().isPure = - *_conditional.condition().annotation().isPure && - *_conditional.trueExpression().annotation().isPure && - *_conditional.falseExpression().annotation().isPure; - - _conditional.annotation().isLValue = false; - - if (_conditional.annotation().willBeWrittenTo) - m_errorReporter.typeError( - 2212_error, - _conditional.location(), - "Conditional expression as left value is not supported yet." - ); - - return false; -} - -void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& _expression) -{ - if (auto const* tupleExpression = dynamic_cast(&_expression)) - { - if (tupleExpression->components().empty()) - m_errorReporter.typeError(5547_error, _expression.location(), "Empty tuple on the left hand side."); - - auto const* tupleType = dynamic_cast(&_type); - auto const& types = tupleType && tupleExpression->components().size() != 1 ? tupleType->components() : vector { &_type }; - - solAssert( - tupleExpression->components().size() == types.size() || m_errorReporter.hasErrors(), - "Array sizes don't match and no errors generated." - ); - - for (size_t i = 0; i < min(tupleExpression->components().size(), types.size()); i++) - if (types[i]) - { - solAssert(!!tupleExpression->components()[i], ""); - checkExpressionAssignment(*types[i], *tupleExpression->components()[i]); - } - } - else if (_type.nameable() && _type.containsNestedMapping()) - { - bool isLocalOrReturn = false; - if (auto const* identifier = dynamic_cast(&_expression)) - if (auto const *variableDeclaration = dynamic_cast(identifier->annotation().referencedDeclaration)) - if (variableDeclaration->isLocalOrReturn()) - isLocalOrReturn = true; - if (!isLocalOrReturn) - m_errorReporter.typeError(9214_error, _expression.location(), "Types in storage containing (nested) mappings cannot be assigned to."); - } -} - -bool TypeChecker::visit(Assignment const& _assignment) -{ - requireLValue( - _assignment.leftHandSide(), - _assignment.assignmentOperator() == Token::Assign - ); - TypePointer t = type(_assignment.leftHandSide()); - _assignment.annotation().type = t; - _assignment.annotation().isPure = false; - _assignment.annotation().isLValue = false; - _assignment.annotation().isConstant = false; - - checkExpressionAssignment(*t, _assignment.leftHandSide()); - - if (TupleType const* tupleType = dynamic_cast(t)) - { - if (_assignment.assignmentOperator() != Token::Assign) - m_errorReporter.typeError( - 4289_error, - _assignment.location(), - "Compound assignment is not allowed for tuple types." - ); - // Sequenced assignments of tuples is not valid, make the result a "void" type. - _assignment.annotation().type = TypeProvider::emptyTuple(); - - expectType(_assignment.rightHandSide(), *tupleType); - - // expectType does not cause fatal errors, so we have to check again here. - if (dynamic_cast(type(_assignment.rightHandSide()))) - checkDoubleStorageAssignment(_assignment); - } - else if (_assignment.assignmentOperator() == Token::Assign) - expectType(_assignment.rightHandSide(), *t); - else - { - // compound assignment - _assignment.rightHandSide().accept(*this); - TypePointer resultType = t->binaryOperatorResult( - TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()), - type(_assignment.rightHandSide()) - ); - if (!resultType || *resultType != *t) - m_errorReporter.typeError( - 7366_error, - _assignment.location(), - "Operator " + - string(TokenTraits::toString(_assignment.assignmentOperator())) + - " not compatible with types " + - t->toString() + - " and " + - type(_assignment.rightHandSide())->toString() - ); - } - return false; -} - -bool TypeChecker::visit(TupleExpression const& _tuple) -{ - _tuple.annotation().isConstant = false; - vector> const& components = _tuple.components(); - TypePointers types; - - if (_tuple.annotation().willBeWrittenTo) - { - if (_tuple.isInlineArray()) - m_errorReporter.fatalTypeError(3025_error, _tuple.location(), "Inline array type cannot be declared as LValue."); - for (auto const& component: components) - if (component) - { - requireLValue( - *component, - _tuple.annotation().lValueOfOrdinaryAssignment - ); - types.push_back(type(*component)); - } - else - types.push_back(TypePointer()); - if (components.size() == 1) - _tuple.annotation().type = type(*components[0]); - else - _tuple.annotation().type = TypeProvider::tuple(move(types)); - // If some of the components are not LValues, the error is reported above. - _tuple.annotation().isLValue = true; - _tuple.annotation().isPure = false; - } - else - { - bool isPure = true; - TypePointer inlineArrayType = nullptr; - - for (size_t i = 0; i < components.size(); ++i) - { - if (!components[i]) - m_errorReporter.fatalTypeError(8381_error, _tuple.location(), "Tuple component cannot be empty."); - - components[i]->accept(*this); - types.push_back(type(*components[i])); - - if (types[i]->category() == Type::Category::Tuple) - if (dynamic_cast(*types[i]).components().empty()) - { - if (_tuple.isInlineArray()) - m_errorReporter.fatalTypeError(5604_error, components[i]->location(), "Array component cannot be empty."); - m_errorReporter.typeError(6473_error, components[i]->location(), "Tuple component cannot be empty."); - } - - // Note: code generation will visit each of the expression even if they are not assigned from. - if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1) - if (!dynamic_cast(*types[i]).mobileType()) - m_errorReporter.fatalTypeError(3390_error, components[i]->location(), "Invalid rational number."); - - if (_tuple.isInlineArray()) - { - solAssert(!!types[i], "Inline array cannot have empty components"); - - if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) - m_errorReporter.fatalTypeError(9563_error, components[i]->location(), "Invalid mobile type."); - - if (i == 0) - inlineArrayType = types[i]->mobileType(); - else if (inlineArrayType) - inlineArrayType = Type::commonType(inlineArrayType, types[i]); - } - if (!*components[i]->annotation().isPure) - isPure = false; - } - _tuple.annotation().isPure = isPure; - if (_tuple.isInlineArray()) - { - if (!inlineArrayType) - m_errorReporter.fatalTypeError(6378_error, _tuple.location(), "Unable to deduce common type for array elements."); - else if (!inlineArrayType->nameable()) - m_errorReporter.fatalTypeError( - 9656_error, - _tuple.location(), - "Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element." - ); - else if (inlineArrayType->containsNestedMapping()) - m_errorReporter.fatalTypeError( - 1545_error, - _tuple.location(), - "Type " + inlineArrayType->toString(true) + " is only valid in storage." - ); - - _tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size()); - } - else - { - if (components.size() == 1) - _tuple.annotation().type = type(*components[0]); - else - _tuple.annotation().type = TypeProvider::tuple(move(types)); - } - - _tuple.annotation().isLValue = false; - } - return false; -} - -bool TypeChecker::visit(UnaryOperation const& _operation) -{ - // Inc, Dec, Add, Sub, Not, BitNot, Delete - Token op = _operation.getOperator(); - bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete); - if (modifying) - requireLValue(_operation.subExpression(), false); - else - _operation.subExpression().accept(*this); - TypePointer const& subExprType = type(_operation.subExpression()); - TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op); - if (!t) - { - string description = "Unary operator " + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->toString(); - if (modifying) - // Cannot just report the error, ignore the unary operator, and continue, - // because the sub-expression was already processed with requireLValue() - m_errorReporter.fatalTypeError(9767_error, _operation.location(), description); - else - m_errorReporter.typeError(4907_error, _operation.location(), description); - t = subExprType; - } - _operation.annotation().type = t; - _operation.annotation().isConstant = false; - _operation.annotation().isPure = !modifying && *_operation.subExpression().annotation().isPure; - _operation.annotation().isLValue = false; - - return false; -} - -void TypeChecker::endVisit(BinaryOperation const& _operation) -{ - TypePointer const& leftType = type(_operation.leftExpression()); - TypePointer const& rightType = type(_operation.rightExpression()); - TypeResult result = leftType->binaryOperatorResult(_operation.getOperator(), rightType); - TypePointer commonType = result.get(); - if (!commonType) - { - m_errorReporter.typeError( - 2271_error, - _operation.location(), - "Operator " + - string(TokenTraits::toString(_operation.getOperator())) + - " not compatible with types " + - leftType->toString() + - " and " + - rightType->toString() + - (!result.message().empty() ? ". " + result.message() : "") - ); - commonType = leftType; - } - _operation.annotation().commonType = commonType; - _operation.annotation().type = - TokenTraits::isCompareOp(_operation.getOperator()) ? - TypeProvider::boolean() : - commonType; - _operation.annotation().isPure = - *_operation.leftExpression().annotation().isPure && - *_operation.rightExpression().annotation().isPure; - _operation.annotation().isLValue = false; - _operation.annotation().isConstant = false; - - if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL) - { - string operation = _operation.getOperator() == Token::Exp ? "exponentiation" : "shift"; - if ( - leftType->category() == Type::Category::RationalNumber && - rightType->category() != Type::Category::RationalNumber - ) - if (( - commonType->category() == Type::Category::Integer && - dynamic_cast(*commonType).numBits() != 256 - ) || ( - commonType->category() == Type::Category::FixedPoint && - dynamic_cast(*commonType).numBits() != 256 - )) - m_errorReporter.warning( - 9085_error, - _operation.location(), - "Result of " + operation + " has type " + commonType->toString() + " and thus " - "might overflow. Silence this warning by converting the literal to the " - "expected type." - ); - if ( - commonType->category() == Type::Category::Integer && - rightType->category() == Type::Category::Integer && - dynamic_cast(*commonType).numBits() < - dynamic_cast(*rightType).numBits() - ) - m_errorReporter.warning( - 3149_error, - _operation.location(), - "The result type of the " + - operation + - " operation is equal to the type of the first operand (" + - commonType->toString() + - ") ignoring the (larger) type of the second operand (" + - rightType->toString() + - ") which might be unexpected. Silence this warning by either converting " - "the first or the second operand to the type of the other." - ); - } -} - -TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( - FunctionCall const& _functionCall -) -{ - solAssert(*_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); - TypePointer const& expressionType = type(_functionCall.expression()); - - vector> const& arguments = _functionCall.arguments(); - bool const isPositionalCall = _functionCall.names().empty(); - - TypePointer resultType = dynamic_cast(*expressionType).actualType(); - if (arguments.size() != 1) - m_errorReporter.typeError( - 2558_error, - _functionCall.location(), - "Exactly one argument expected for explicit type conversion." - ); - else if (!isPositionalCall) - m_errorReporter.typeError( - 5153_error, - _functionCall.location(), - "Type conversion cannot allow named arguments." - ); - else - { - Type const* argType = type(*arguments.front()); - // Resulting data location is memory unless we are converting from a reference - // type with a different data location. - // (data location cannot yet be specified for type conversions) - DataLocation dataLoc = DataLocation::Memory; - if (auto argRefType = dynamic_cast(argType)) - dataLoc = argRefType->location(); - if (auto type = dynamic_cast(resultType)) - resultType = TypeProvider::withLocation(type, dataLoc, type->isPointer()); - BoolResult result = argType->isExplicitlyConvertibleTo(*resultType); - if (result) - { - if (auto argArrayType = dynamic_cast(argType)) - { - auto resultArrayType = dynamic_cast(resultType); - solAssert(!!resultArrayType, ""); - solAssert( - argArrayType->location() != DataLocation::Storage || - ( - ( - resultArrayType->isPointer() || - (argArrayType->isByteArray() && resultArrayType->isByteArray()) - ) && - resultArrayType->location() == DataLocation::Storage - ), - "Invalid explicit conversion to storage type." - ); - } - } - else - { - if ( - resultType->category() == Type::Category::Contract && - argType->category() == Type::Category::Address - ) - { - solAssert(dynamic_cast(resultType)->isPayable(), ""); - solAssert( - dynamic_cast(argType)->stateMutability() < - StateMutability::Payable, - "" - ); - SecondarySourceLocation ssl; - if ( - auto const* identifier = dynamic_cast(arguments.front().get()) - ) - if ( - auto const* variableDeclaration = dynamic_cast( - identifier->annotation().referencedDeclaration - ) - ) - ssl.append( - "Did you mean to declare this variable as \"address payable\"?", - variableDeclaration->location() - ); - m_errorReporter.typeError( - 7398_error, - _functionCall.location(), - ssl, - "Explicit type conversion not allowed from non-payable \"address\" to \"" + - resultType->toString() + - "\", which has a payable fallback function." - ); - } - else if ( - auto const* functionType = dynamic_cast(argType); - functionType && - functionType->kind() == FunctionType::Kind::External && - resultType->category() == Type::Category::Address - ) - m_errorReporter.typeError( - 5030_error, - _functionCall.location(), - "Explicit type conversion not allowed from \"" + - argType->toString() + - "\" to \"" + - resultType->toString() + - "\". To obtain the address of the contract of the function, " + - "you can use the .address member of the function." - ); - else - m_errorReporter.typeErrorConcatenateDescriptions( - 9640_error, - _functionCall.location(), - "Explicit type conversion not allowed from \"" + - argType->toString() + - "\" to \"" + - resultType->toString() + - "\".", - result.message() - ); - } - } - return resultType; -} - -void TypeChecker::typeCheckFunctionCall( - FunctionCall const& _functionCall, - FunctionTypePointer _functionType -) -{ - // Actual function call or struct constructor call. - - solAssert(!!_functionType, ""); - solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); - - if (_functionType->kind() == FunctionType::Kind::Declaration) - { - solAssert(_functionType->declaration().annotation().contract, ""); - if ( - m_currentContract && - m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && - !dynamic_cast(_functionType->declaration()).isImplemented() - ) - m_errorReporter.typeError( - 7501_error, - _functionCall.location(), - "Cannot call unimplemented base function." - ); - else - m_errorReporter.typeError( - 3419_error, - _functionCall.location(), - "Cannot call function via contract type name." - ); - return; - } - - // Check for unsupported use of bare static call - if ( - _functionType->kind() == FunctionType::Kind::BareStaticCall && - !m_evmVersion.hasStaticCall() - ) - m_errorReporter.typeError( - 5052_error, - _functionCall.location(), - "\"staticcall\" is not supported by the VM version." - ); - - // Perform standard function call type checking - typeCheckFunctionGeneralChecks(_functionCall, _functionType); -} - -void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function) -{ - solAssert(_function.isFallback(), ""); - - if (_function.libraryFunction()) - m_errorReporter.typeError(5982_error, _function.location(), "Libraries cannot have fallback functions."); - if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - 4575_error, - _function.location(), - "Fallback function must be payable or non-payable, but is \"" + - stateMutabilityToString(_function.stateMutability()) + - "\"." - ); - if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\"."); - - if (!_function.returnParameters().empty() || !_function.parameters().empty()) - { - if ( - _function.returnParameters().size() != 1 || - *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory() || - _function.parameters().size() != 1 || - *type(*_function.parameters().front()) != *TypeProvider::bytesCalldata() - ) - m_errorReporter.typeError( - 5570_error, - _function.returnParameterList()->location(), - "Fallback function either has to have the signature \"fallback()\" or \"fallback(bytes calldata) returns (bytes memory)\"." - ); - } -} - -void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) -{ - solAssert(_function.isReceive(), ""); - - if (_function.libraryFunction()) - m_errorReporter.typeError(4549_error, _function.location(), "Libraries cannot have receive ether functions."); - - if (_function.stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - 7793_error, - _function.location(), - "Receive ether function must be payable, but is \"" + - stateMutabilityToString(_function.stateMutability()) + - "\"." - ); - if (_function.visibility() != Visibility::External) - m_errorReporter.typeError(4095_error, _function.location(), "Receive ether function must be defined as \"external\"."); - if (!_function.returnParameters().empty()) - m_errorReporter.typeError(6899_error, _function.returnParameterList()->location(), "Receive ether function cannot return values."); - if (!_function.parameters().empty()) - m_errorReporter.typeError(6857_error, _function.parameterList().location(), "Receive ether function cannot take parameters."); -} - - -void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function) -{ - solAssert(_function.isConstructor(), ""); - if (_function.overrides()) - m_errorReporter.typeError(1209_error, _function.location(), "Constructors cannot override."); - if (!_function.returnParameters().empty()) - m_errorReporter.typeError(9712_error, _function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); - if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) - m_errorReporter.typeError( - 1558_error, - _function.location(), - "Constructor must be payable or non-payable, but is \"" + - stateMutabilityToString(_function.stateMutability()) + - "\"." - ); - if (!_function.noVisibilitySpecified()) - { - auto const& contract = dynamic_cast(*_function.scope()); - if (_function.visibility() != Visibility::Public && _function.visibility() != Visibility::Internal) - m_errorReporter.typeError(9239_error, _function.location(), "Constructor cannot have visibility."); - else if (_function.isPublic() && contract.abstract()) - m_errorReporter.declarationError( - 8295_error, - _function.location(), - "Abstract contracts cannot have public constructors. Remove the \"public\" keyword to fix this." - ); - else if (!_function.isPublic() && !contract.abstract()) - m_errorReporter.declarationError( - 1845_error, - _function.location(), - "Non-abstract contracts cannot have internal constructors. Remove the \"internal\" keyword and make the contract abstract to fix this." - ); - else - m_errorReporter.warning( - 2462_error, - _function.location(), - "Visibility for constructor is ignored. If you want the contract to be non-deployable, making it \"abstract\" is sufficient." - ); - } -} - -void TypeChecker::typeCheckABIEncodeFunctions( - FunctionCall const& _functionCall, - FunctionTypePointer _functionType -) -{ - solAssert(!!_functionType, ""); - solAssert( - _functionType->kind() == FunctionType::Kind::ABIEncode || - _functionType->kind() == FunctionType::Kind::ABIEncodePacked || - _functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || - _functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature, - "ABI function has unexpected FunctionType::Kind." - ); - solAssert(_functionType->takesArbitraryParameters(), "ABI functions should be variadic."); - - bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; - solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding"); - - bool const abiEncoderV2 = useABICoderV2(); - - // Check for named arguments - if (!_functionCall.names().empty()) - { - m_errorReporter.typeError( - 2627_error, - _functionCall.location(), - "Named arguments cannot be used for functions that take arbitrary parameters." - ); - return; - } - - // Perform standard function call type checking - typeCheckFunctionGeneralChecks(_functionCall, _functionType); - - // Check additional arguments for variadic functions - vector> const& arguments = _functionCall.arguments(); - for (size_t i = 0; i < arguments.size(); ++i) - { - auto const& argType = type(*arguments[i]); - - if (argType->category() == Type::Category::RationalNumber) - { - auto const& rationalType = dynamic_cast(*argType); - if (rationalType.isFractional()) - { - m_errorReporter.typeError( - 6090_error, - arguments[i]->location(), - "Fractional numbers cannot yet be encoded." - ); - continue; - } - else if (!argType->mobileType()) - { - m_errorReporter.typeError( - 8009_error, - arguments[i]->location(), - "Invalid rational number (too large or division by zero)." - ); - continue; - } - else if (isPacked) - { - m_errorReporter.typeError( - 7279_error, - arguments[i]->location(), - "Cannot perform packed encoding for a literal." - " Please convert it to an explicit type first." - ); - continue; - } - } - - if (isPacked && !typeSupportedByOldABIEncoder(*argType, false /* isLibrary */)) - { - m_errorReporter.typeError( - 9578_error, - arguments[i]->location(), - "Type not supported in packed mode." - ); - continue; - } - - if (!argType->fullEncodingType(false, abiEncoderV2, !_functionType->padArguments())) - m_errorReporter.typeError( - 2056_error, - arguments[i]->location(), - "This type cannot be encoded." - ); - } -} - -void TypeChecker::typeCheckFunctionGeneralChecks( - FunctionCall const& _functionCall, - FunctionTypePointer _functionType -) -{ - // Actual function call or struct constructor call. - - solAssert(!!_functionType, ""); - solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); - - bool const isPositionalCall = _functionCall.names().empty(); - bool const isVariadic = _functionType->takesArbitraryParameters(); - - auto functionCallKind = *_functionCall.annotation().kind; - - solAssert( - !isVariadic || functionCallKind == FunctionCallKind::FunctionCall, - "Struct constructor calls cannot be variadic." - ); - - TypePointers const& parameterTypes = _functionType->parameterTypes(); - vector> const& arguments = _functionCall.arguments(); - vector> const& argumentNames = _functionCall.names(); - - // Check number of passed in arguments - if ( - arguments.size() < parameterTypes.size() || - (!isVariadic && arguments.size() > parameterTypes.size()) - ) - { - bool const isStructConstructorCall = - functionCallKind == FunctionCallKind::StructConstructorCall; - - auto [errorId, description] = [&]() -> tuple { - string msg = isVariadic ? - "Need at least " + - toString(parameterTypes.size()) + - " arguments for " + - string(isStructConstructorCall ? "struct constructor" : "function call") + - ", but provided only " + - toString(arguments.size()) + - "." - : - "Wrong argument count for " + - string(isStructConstructorCall ? "struct constructor" : "function call") + - ": " + - toString(arguments.size()) + - " arguments given but " + - string(isVariadic ? "need at least " : "expected ") + - toString(parameterTypes.size()) + - "."; - - if (isStructConstructorCall) - return { isVariadic ? 1123_error : 9755_error, msg }; - else if ( - _functionType->kind() == FunctionType::Kind::BareCall || - _functionType->kind() == FunctionType::Kind::BareCallCode || - _functionType->kind() == FunctionType::Kind::BareDelegateCall || - _functionType->kind() == FunctionType::Kind::BareStaticCall - ) - { - if (arguments.empty()) - return { - isVariadic ? 7653_error : 6138_error, - msg + - " This function requires a single bytes argument." - " Use \"\" as argument to provide empty calldata." - }; - else - return { - isVariadic ? 9390_error : 8922_error, - msg + - " This function requires a single bytes argument." - " If all your arguments are value types, you can use" - " abi.encode(...) to properly generate it." - }; - } - else if ( - _functionType->kind() == FunctionType::Kind::KECCAK256 || - _functionType->kind() == FunctionType::Kind::SHA256 || - _functionType->kind() == FunctionType::Kind::RIPEMD160 - ) - return { - isVariadic ? 1220_error : 4323_error, - msg + - " This function requires a single bytes argument." - " Use abi.encodePacked(...) to obtain the pre-0.5.0" - " behaviour or abi.encode(...) to use ABI encoding." - }; - else - return { isVariadic ? 9308_error : 6160_error, msg }; - }(); - - m_errorReporter.typeError(errorId, _functionCall.location(), description); - return; - } - - // Parameter to argument map - std::vector paramArgMap(parameterTypes.size()); - - // Map parameters to arguments - trivially for positional calls, less so for named calls - if (isPositionalCall) - for (size_t i = 0; i < paramArgMap.size(); ++i) - paramArgMap[i] = arguments[i].get(); - else - { - auto const& parameterNames = _functionType->parameterNames(); - - solAssert( - parameterNames.size() == argumentNames.size(), - "Unexpected parameter length mismatch!" - ); - - // Check for duplicate argument names - { - bool duplication = false; - for (size_t i = 0; i < argumentNames.size(); i++) - for (size_t j = i + 1; j < argumentNames.size(); j++) - if (*argumentNames[i] == *argumentNames[j]) - { - duplication = true; - m_errorReporter.typeError( - 6995_error, - arguments[i]->location(), - "Duplicate named argument \"" + *argumentNames[i] + "\"." - ); - } - if (duplication) - return; - } - - // map parameter names to argument names - { - bool not_all_mapped = false; - - for (size_t i = 0; i < argumentNames.size(); i++) - { - size_t j; - for (j = 0; j < parameterNames.size(); j++) - if (parameterNames[j] == *argumentNames[i]) - break; - - if (j < parameterNames.size()) - paramArgMap[j] = arguments[i].get(); - else - { - not_all_mapped = true; - m_errorReporter.typeError( - 4974_error, - _functionCall.location(), - "Named argument \"" + - *argumentNames[i] + - "\" does not match function declaration." - ); - } - } - - if (not_all_mapped) - return; - } - } - - // Check for compatible types between arguments and parameters - for (size_t i = 0; i < paramArgMap.size(); ++i) - { - solAssert(!!paramArgMap[i], "unmapped parameter"); - BoolResult result = type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i]); - if (!result) - { - auto [errorId, description] = [&]() -> tuple { - string msg = - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - type(*paramArgMap[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested."; - if (!result.message().empty()) - msg += " " + result.message(); - if ( - _functionType->kind() == FunctionType::Kind::BareCall || - _functionType->kind() == FunctionType::Kind::BareCallCode || - _functionType->kind() == FunctionType::Kind::BareDelegateCall || - _functionType->kind() == FunctionType::Kind::BareStaticCall - ) - return { - 8051_error, - msg + - " This function requires a single bytes argument." - " If all your arguments are value types, you can" - " use abi.encode(...) to properly generate it." - }; - else if ( - _functionType->kind() == FunctionType::Kind::KECCAK256 || - _functionType->kind() == FunctionType::Kind::SHA256 || - _functionType->kind() == FunctionType::Kind::RIPEMD160 - ) - return { - 7556_error, - msg + - " This function requires a single bytes argument." - " Use abi.encodePacked(...) to obtain the pre-0.5.0" - " behaviour or abi.encode(...) to use ABI encoding." - }; - else - return { 9553_error, msg }; - }(); - m_errorReporter.typeError(errorId, paramArgMap[i]->location(), description); - } - } - - TypePointers const& returnParameterTypes = _functionType->returnParameterTypes(); - bool isLibraryCall = (_functionType->kind() == FunctionType::Kind::DelegateCall); - bool callRequiresABIEncoding = - // ABIEncode/ABIDecode calls not included because they should have been already validated - // at this point and they have variadic arguments so they need special handling. - _functionType->kind() == FunctionType::Kind::DelegateCall || - _functionType->kind() == FunctionType::Kind::External || - _functionType->kind() == FunctionType::Kind::Creation || - _functionType->kind() == FunctionType::Kind::Event; - - if (callRequiresABIEncoding && !useABICoderV2()) - { - solAssert(!isVariadic, ""); - solAssert(parameterTypes.size() == arguments.size(), ""); - solAssert(!_functionType->isBareCall(), ""); - solAssert(*_functionCall.annotation().kind == FunctionCallKind::FunctionCall, ""); - - for (size_t i = 0; i < parameterTypes.size(); ++i) - { - solAssert(parameterTypes[i], ""); - - if (!typeSupportedByOldABIEncoder(*parameterTypes[i], isLibraryCall)) - m_errorReporter.typeError( - 2443_error, - paramArgMap[i]->location(), - "The type of this parameter, " + parameterTypes[i]->toString(true) + ", " - "is only supported in ABI coder v2. " - "Use \"pragma abicoder v2;\" to enable the feature." - ); - } - - for (size_t i = 0; i < returnParameterTypes.size(); ++i) - { - solAssert(returnParameterTypes[i], ""); - - if (!typeSupportedByOldABIEncoder(*returnParameterTypes[i], isLibraryCall)) - m_errorReporter.typeError( - 2428_error, - _functionCall.location(), - "The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", " - "is only supported in ABI coder v2. " - "Use \"pragma abicoder v2;\" to enable the feature." - ); - } - } -} - -bool TypeChecker::visit(FunctionCall const& _functionCall) -{ - vector> const& arguments = _functionCall.arguments(); - bool argumentsArePure = true; - - // We need to check arguments' type first as they will be needed for overload resolution. - for (ASTPointer const& argument: arguments) - { - argument->accept(*this); - if (!*argument->annotation().isPure) - argumentsArePure = false; - } - - // Store argument types - and names if given - for overload resolution - { - FuncCallArguments funcCallArgs; - - funcCallArgs.names = _functionCall.names(); - - for (ASTPointer const& argument: arguments) - funcCallArgs.types.push_back(type(*argument)); - - _functionCall.expression().annotation().arguments = std::move(funcCallArgs); - } - - _functionCall.expression().accept(*this); - - Type const* expressionType = type(_functionCall.expression()); - - // Determine function call kind and function type for this FunctionCall node - FunctionCallAnnotation& funcCallAnno = _functionCall.annotation(); - FunctionTypePointer functionType = nullptr; - funcCallAnno.isConstant = false; - - bool isLValue = false; - - // Determine and assign function call kind, lvalue, purity and function type for this FunctionCall node - switch (expressionType->category()) - { - case Type::Category::Function: - functionType = dynamic_cast(expressionType); - funcCallAnno.kind = FunctionCallKind::FunctionCall; - - if (auto memberAccess = dynamic_cast(&_functionCall.expression())) - { - if (dynamic_cast(memberAccess->annotation().referencedDeclaration)) - _functionCall.expression().annotation().calledDirectly = true; - } - else if (auto identifier = dynamic_cast(&_functionCall.expression())) - if (dynamic_cast(identifier->annotation().referencedDeclaration)) - _functionCall.expression().annotation().calledDirectly = true; - - // Purity for function calls also depends upon the callee and its FunctionType - funcCallAnno.isPure = - argumentsArePure && - *_functionCall.expression().annotation().isPure && - functionType->isPure(); - - if ( - functionType->kind() == FunctionType::Kind::ArrayPush || - functionType->kind() == FunctionType::Kind::ByteArrayPush - ) - isLValue = functionType->parameterTypes().empty(); - - break; - - case Type::Category::TypeType: - { - // Determine type for type conversion or struct construction expressions - TypePointer const& actualType = dynamic_cast(*expressionType).actualType(); - solAssert(!!actualType, ""); - - if (actualType->category() == Type::Category::Struct) - { - if (actualType->containsNestedMapping()) - m_errorReporter.fatalTypeError( - 9515_error, - _functionCall.location(), - "Struct containing a (nested) mapping cannot be constructed." - ); - functionType = dynamic_cast(*actualType).constructorType(); - funcCallAnno.kind = FunctionCallKind::StructConstructorCall; - } - else - { - if (auto const* contractType = dynamic_cast(actualType)) - if (contractType->isSuper()) - m_errorReporter.fatalTypeError( - 1744_error, - _functionCall.location(), - "Cannot convert to the super type." - ); - funcCallAnno.kind = FunctionCallKind::TypeConversion; - } - - funcCallAnno.isPure = argumentsArePure; - - break; - } - - default: - m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable"); - // Unreachable, because fatalTypeError throws. We don't set kind, but that's okay because the switch below - // is never reached. And, even if it was, SetOnce would trigger an assertion violation and not UB. - funcCallAnno.isPure = argumentsArePure; - break; - } - - funcCallAnno.isLValue = isLValue; - - // Determine return types - switch (*funcCallAnno.kind) - { - case FunctionCallKind::TypeConversion: - funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); - break; - - case FunctionCallKind::StructConstructorCall: // fall-through - case FunctionCallKind::FunctionCall: - { - TypePointers returnTypes; - - switch (functionType->kind()) - { - case FunctionType::Kind::ABIDecode: - { - returnTypes = typeCheckABIDecodeAndRetrieveReturnType( - _functionCall, - useABICoderV2() - ); - break; - } - case FunctionType::Kind::ABIEncode: - case FunctionType::Kind::ABIEncodePacked: - case FunctionType::Kind::ABIEncodeWithSelector: - case FunctionType::Kind::ABIEncodeWithSignature: - { - typeCheckABIEncodeFunctions(_functionCall, functionType); - returnTypes = functionType->returnParameterTypes(); - break; - } - case FunctionType::Kind::MetaType: - returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall); - break; - default: - { - typeCheckFunctionCall(_functionCall, functionType); - returnTypes = m_evmVersion.supportsReturndata() ? - functionType->returnParameterTypes() : - functionType->returnParameterTypesWithoutDynamicTypes(); - break; - } - } - - funcCallAnno.type = returnTypes.size() == 1 ? - move(returnTypes.front()) : - TypeProvider::tuple(move(returnTypes)); - - break; - } - - default: - // for non-callables, ensure error reported and annotate node to void function - solAssert(m_errorReporter.hasErrors(), ""); - funcCallAnno.kind = FunctionCallKind::FunctionCall; - funcCallAnno.type = TypeProvider::emptyTuple(); - break; - } - - return false; -} - -bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) -{ - solAssert(_functionCallOptions.options().size() == _functionCallOptions.names().size(), "Lengths of name & value arrays differ!"); - - _functionCallOptions.expression().annotation().arguments = _functionCallOptions.annotation().arguments; - - _functionCallOptions.expression().accept(*this); - - _functionCallOptions.annotation().isPure = false; - _functionCallOptions.annotation().isConstant = false; - _functionCallOptions.annotation().isLValue = false; - - auto expressionFunctionType = dynamic_cast(type(_functionCallOptions.expression())); - if (!expressionFunctionType) - { - m_errorReporter.fatalTypeError(2622_error, _functionCallOptions.location(), "Expected callable expression before call options."); - return false; - } - - bool setSalt = false; - bool setValue = false; - bool setGas = false; - bool setTokenId = false; - bool setAmount = false; - - FunctionType::Kind kind = expressionFunctionType->kind(); - if ( - kind != FunctionType::Kind::Creation && - kind != FunctionType::Kind::External && - kind != FunctionType::Kind::BareCall && - kind != FunctionType::Kind::BareCallCode && - kind != FunctionType::Kind::BareDelegateCall && - kind != FunctionType::Kind::BareStaticCall && - kind != FunctionType::Kind::SendMessage // Solidity++ - ) - { - m_errorReporter.fatalTypeError( - 2193_error, - _functionCallOptions.location(), - "Function call options can only be set on external function calls or contract creations." - ); - return false; - } - - if ( - expressionFunctionType->valueSet() || - expressionFunctionType->gasSet() || - expressionFunctionType->saltSet() - ) - m_errorReporter.typeError( - 1645_error, - _functionCallOptions.location(), - "Function call options have already been set, you have to combine them into a single " - "{...}-option." - ); - - auto setCheckOption = [&](bool& _option, string const& _name) - { - if (_option) - m_errorReporter.typeError( - 9886_error, - _functionCallOptions.location(), - "Duplicate option \"" + std::move(_name) + "\"." - ); - - _option = true; - }; - - for (size_t i = 0; i < _functionCallOptions.names().size(); ++i) - { - string const& name = *(_functionCallOptions.names()[i]); - - // Solidity++: disable ethereum options of "value", "salt" and "gas" - if (name == "value" || name == "gas" || name == "salt") - { - m_errorReporter.typeError( - 100201_error, - _functionCallOptions.location(), - "Cannot set option \"" + name + "\" in Solidity++. Valid options are \"token\" and \"amount\"." - ); - } - // Solidity++: - else if (name == "token") - { - if (kind == FunctionType::Kind::BareDelegateCall) - m_errorReporter.typeError( - 6189_error, - _functionCallOptions.location(), - "Cannot set option \"tokenId\" for delegatecall." - ); - else if (kind == FunctionType::Kind::BareStaticCall) - m_errorReporter.typeError( - 2842_error, - _functionCallOptions.location(), - "Cannot set option \"tokenId\" for staticcall." - ); - else if (!expressionFunctionType->isPayable()) - m_errorReporter.typeError( - 7006_error, - _functionCallOptions.location(), - kind == FunctionType::Kind::Creation ? - "Cannot set option \"tokenId\", since the constructor of " + - expressionFunctionType->returnParameterTypes().front()->toString() + - " is not payable." : - "Cannot set option \"tokenId\" on a non-payable function type." - ); - else - { - expectType(*_functionCallOptions.options()[i], *TypeProvider::viteTokenId()); - setCheckOption(setTokenId, "tokenId"); - } - } - else if (name == "amount") - { - if (kind == FunctionType::Kind::BareDelegateCall) - m_errorReporter.typeError( - 6189_error, - _functionCallOptions.location(), - "Cannot set option \"amount\" for delegatecall." - ); - else if (kind == FunctionType::Kind::BareStaticCall) - m_errorReporter.typeError( - 2842_error, - _functionCallOptions.location(), - "Cannot set option \"amount\" for staticcall." - ); - else if (!expressionFunctionType->isPayable()) - m_errorReporter.typeError( - 7006_error, - _functionCallOptions.location(), - kind == FunctionType::Kind::Creation ? - "Cannot set option \"amount\", since the constructor of " + - expressionFunctionType->returnParameterTypes().front()->toString() + - " is not payable." : - "Cannot set option \"amount\" on a non-payable function type." - ); - else - { - expectType(*_functionCallOptions.options()[i], *TypeProvider::uint256()); - setCheckOption(setAmount, "amount"); - } - } - - else - m_errorReporter.typeError( - 9318_error, - _functionCallOptions.location(), - "Unknown call option \"" + name + "\". Valid options are \"token\" and \"amount\"." - ); - } - - if (setSalt && !m_evmVersion.hasCreate2()) - m_errorReporter.typeError( - 5189_error, - _functionCallOptions.location(), - "Unsupported call option \"salt\" (requires Constantinople-compatible VMs)." - ); - - setValue = setTokenId && setAmount; // Solidity++ - - _functionCallOptions.annotation().type = expressionFunctionType->copyAndSetCallOptions(setGas, setValue, setSalt); - return false; -} - -void TypeChecker::endVisit(NewExpression const& _newExpression) -{ - TypePointer type = _newExpression.typeName().annotation().type; - solAssert(!!type, "Type name not resolved."); - - _newExpression.annotation().isConstant = false; - _newExpression.annotation().isLValue = false; - - if (auto contractName = dynamic_cast(&_newExpression.typeName())) - { - auto contract = dynamic_cast(&dereference(contractName->pathNode())); - - if (!contract) - m_errorReporter.fatalTypeError(5540_error, _newExpression.location(), "Identifier is not a contract."); - if (contract->isInterface()) - m_errorReporter.fatalTypeError(2971_error, _newExpression.location(), "Cannot instantiate an interface."); - if (contract->abstract()) - m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); - - if (m_currentContract) - { - // TODO this is not properly detecting creation-cycles if they go through - // internal library functions or free functions. It will be caught at - // code generation time, but it would of course be better to catch it here. - m_currentContract->annotation().contractDependencies.insert(contract); - solAssert( - !contract->annotation().linearizedBaseContracts.empty(), - "Linearized base contracts not yet available." - ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4579_error, - _newExpression.location(), - "Circular reference for contract creation (cannot create instance of derived or same contract)." - ); - } - - _newExpression.annotation().type = FunctionType::newExpressionType(*contract); - _newExpression.annotation().isPure = false; - } - else if (type->category() == Type::Category::Array) - { - if (type->containsNestedMapping()) - m_errorReporter.fatalTypeError( - 1164_error, - _newExpression.typeName().location(), - "Array containing a (nested) mapping cannot be constructed in memory." - ); - if (!type->isDynamicallySized()) - m_errorReporter.typeError( - 3904_error, - _newExpression.typeName().location(), - "Length has to be placed in parentheses after the array type for new expression." - ); - type = TypeProvider::withLocationIfReference(DataLocation::Memory, type); - _newExpression.annotation().type = TypeProvider::function( - TypePointers{TypeProvider::uint256()}, - TypePointers{type}, - strings(1, ""), - strings(1, ""), - FunctionType::Kind::ObjectCreation, - false, - StateMutability::Pure - ); - _newExpression.annotation().isPure = true; - } - else - { - _newExpression.annotation().isPure = false; - m_errorReporter.fatalTypeError(8807_error, _newExpression.location(), "Contract or array type expected."); - } -} - -// Solidity++: -bool TypeChecker::visit(AwaitExpression const& _awaitExpression) -{ - _awaitExpression.expression().accept(*this); - _awaitExpression.annotation().type = _awaitExpression.expression().annotation().type; - _awaitExpression.annotation().isPure = false; - // pass the await id to function call - _awaitExpression.expression().annotation().awaitId = _awaitExpression.id(); - - return false; -} - -bool TypeChecker::visit(MemberAccess const& _memberAccess) -{ - _memberAccess.expression().accept(*this); - TypePointer exprType = type(_memberAccess.expression()); - ASTString const& memberName = _memberAccess.memberName(); - - auto& annotation = _memberAccess.annotation(); - - // Retrieve the types of the arguments if this is used to call a function. - auto const& arguments = annotation.arguments; - MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName); - size_t const initialMemberCount = possibleMembers.size(); - if (initialMemberCount > 1 && arguments) - { - // do overload resolution - for (auto it = possibleMembers.begin(); it != possibleMembers.end();) - if ( - it->type->category() == Type::Category::Function && - !dynamic_cast(*it->type).canTakeArguments(*arguments, exprType) - ) - it = possibleMembers.erase(it); - else - ++it; - } - - annotation.isConstant = false; - - if (possibleMembers.empty()) - { - if (initialMemberCount == 0 && !dynamic_cast(exprType)) - { - // Try to see if the member was removed because it is only available for storage types. - auto storageType = TypeProvider::withLocationIfReference( - DataLocation::Storage, - exprType - ); - if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty()) - m_errorReporter.fatalTypeError( - 4994_error, - _memberAccess.location(), - "Member \"" + memberName + "\" is not available in " + - exprType->toString() + - " outside of storage." - ); - } - - auto [errorId, description] = [&]() -> tuple { - string errorMsg = "Member \"" + memberName + "\" not found or not visible " - "after argument-dependent lookup in " + exprType->toString() + "."; - - if (auto const* funType = dynamic_cast(exprType)) - { - TypePointers const& t = funType->returnParameterTypes(); - - if (memberName == "value") - { - if (funType->kind() == FunctionType::Kind::Creation) - return { - 8827_error, - "Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available." - }; - else if ( - funType->kind() == FunctionType::Kind::DelegateCall || - funType->kind() == FunctionType::Kind::BareDelegateCall - ) - return { 8477_error, "Member \"value\" is not allowed in delegated calls due to \"msg.value\" persisting." }; - else - return { 8820_error, "Member \"value\" is only available for payable functions." }; - } - else if ( - t.size() == 1 && ( - t.front()->category() == Type::Category::Struct || - t.front()->category() == Type::Category::Contract - ) - ) - return { 6005_error, errorMsg + " Did you intend to call the function?" }; - } - else if (exprType->category() == Type::Category::Contract) - { - for (MemberList::Member const& addressMember: TypeProvider::payableAddress()->nativeMembers(nullptr)) - if (addressMember.name == memberName) - { - auto const* var = dynamic_cast(&_memberAccess.expression()); - string varName = var ? var->name() : "..."; - errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member."; - return { 3125_error, errorMsg }; - } - } - else if (auto const* addressType = dynamic_cast(exprType)) - { - // Trigger error when using send or transfer with a non-payable fallback function. - if (memberName == "send" || memberName == "transfer") - { - solAssert( - addressType->stateMutability() != StateMutability::Payable, - "Expected address not-payable as members were not found" - ); - - return { 9862_error, "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\"." }; - } - } - - return { 9582_error, errorMsg }; - }(); - - m_errorReporter.fatalTypeError( - errorId, - _memberAccess.location(), - description - ); - } - else if (possibleMembers.size() > 1) - m_errorReporter.fatalTypeError( - 6675_error, - _memberAccess.location(), - "Member \"" + memberName + "\" not unique " - "after argument-dependent lookup in " + exprType->toString() + - (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".") - ); - - annotation.referencedDeclaration = possibleMembers.front().declaration; - annotation.type = possibleMembers.front().type; - - VirtualLookup requiredLookup = VirtualLookup::Static; - - if (auto funType = dynamic_cast(annotation.type)) - { - solAssert( - !funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()), - "Function \"" + memberName + "\" cannot be called on an object of type " + - exprType->toString() + " (expected " + funType->selfType()->toString() + ")." - ); - - if ( - dynamic_cast(exprType) && - !annotation.referencedDeclaration && - (memberName == "value" || memberName == "gas") - ) - m_errorReporter.typeError( - 1621_error, - _memberAccess.location(), - "Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead." - ); - - if ( - funType->kind() == FunctionType::Kind::ArrayPush && - arguments.value().numArguments() != 0 && - exprType->containsNestedMapping() - ) - m_errorReporter.typeError( - 8871_error, - _memberAccess.location(), - "Storage arrays with nested mappings do not support .push()." - ); - - if (!funType->bound()) - if (auto typeType = dynamic_cast(exprType)) - { - auto contractType = dynamic_cast(typeType->actualType()); - if (contractType && contractType->isSuper()) - requiredLookup = VirtualLookup::Super; - } - } - - annotation.requiredLookup = requiredLookup; - - if (auto const* structType = dynamic_cast(exprType)) - annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData); - else if (exprType->category() == Type::Category::Array) - annotation.isLValue = false; - else if (exprType->category() == Type::Category::FixedBytes) - annotation.isLValue = false; - else if (TypeType const* typeType = dynamic_cast(exprType)) - { - if (ContractType const* contractType = dynamic_cast(typeType->actualType())) - { - annotation.isLValue = annotation.referencedDeclaration->isLValue(); - if ( - auto const* functionType = dynamic_cast(annotation.type); - functionType && - functionType->kind() == FunctionType::Kind::Declaration - ) - annotation.isPure = *_memberAccess.expression().annotation().isPure; - } - else - annotation.isLValue = false; - } - else if (exprType->category() == Type::Category::Module) - { - annotation.isPure = *_memberAccess.expression().annotation().isPure; - annotation.isLValue = false; - } - else - annotation.isLValue = false; - - // TODO some members might be pure, but for example `address(0x123).balance` is not pure - // although every subexpression is, so leaving this limited for now. - if (auto tt = dynamic_cast(exprType)) - if (tt->actualType()->category() == Type::Category::Enum) - annotation.isPure = true; - if ( - auto const* functionType = dynamic_cast(exprType); - functionType && - functionType->hasDeclaration() && - dynamic_cast(&functionType->declaration()) && - memberName == "selector" - ) - if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) - { - bool isPure = *parentAccess->expression().annotation().isPure; - if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) - if (exprInt->name() == "this" || exprInt->name() == "super") - isPure = true; - - annotation.isPure = isPure; - } - - if (auto magicType = dynamic_cast(exprType)) - { - if (magicType->kind() == MagicType::Kind::ABI) - annotation.isPure = true; - else if (magicType->kind() == MagicType::Kind::MetaType && ( - memberName == "creationCode" || memberName == "runtimeCode" - )) - { - annotation.isPure = true; - ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); - solAssert(!accessedContractType.isSuper(), ""); - if ( - memberName == "runtimeCode" && - !accessedContractType.immutableVariables().empty() - ) - m_errorReporter.typeError( - 9274_error, - _memberAccess.location(), - "\"runtimeCode\" is not available for contracts containing immutable variables." - ); - if (m_currentContract) - { - // TODO in the same way as with ``new``, - // this is not properly detecting creation-cycles if they go through - // internal library functions or free functions. It will be caught at - // code generation time, but it would of course be better to catch it here. - - m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4224_error, - _memberAccess.location(), - "Circular reference for contract code access." - ); - } - } - else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name") - annotation.isPure = true; - else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "interfaceId") - annotation.isPure = true; - else if ( - magicType->kind() == MagicType::Kind::MetaType && - (memberName == "min" || memberName == "max") - ) - annotation.isPure = true; - else if (magicType->kind() == MagicType::Kind::Block && memberName == "chainid" && !m_evmVersion.hasChainID()) - m_errorReporter.typeError( - 3081_error, - _memberAccess.location(), - "\"chainid\" is not supported by the VM version." - ); - } - - if ( - _memberAccess.expression().annotation().type->category() == Type::Category::Address && - memberName == "codehash" && - !m_evmVersion.hasExtCodeHash() - ) - m_errorReporter.typeError( - 7598_error, - _memberAccess.location(), - "\"codehash\" is not supported by the VM version." - ); - - if (!annotation.isPure.set()) - annotation.isPure = false; - - return false; -} - -bool TypeChecker::visit(IndexAccess const& _access) -{ - _access.annotation().isConstant = false; - _access.baseExpression().accept(*this); - TypePointer baseType = type(_access.baseExpression()); - TypePointer resultType = nullptr; - bool isLValue = false; - bool isPure = *_access.baseExpression().annotation().isPure; - Expression const* index = _access.indexExpression(); - switch (baseType->category()) - { - case Type::Category::ArraySlice: - { - auto const& arrayType = dynamic_cast(*baseType).arrayType(); - if (arrayType.location() != DataLocation::CallData || !arrayType.isDynamicallySized()) - m_errorReporter.typeError(4802_error, _access.location(), "Index access is only implemented for slices of dynamic calldata arrays."); - baseType = &arrayType; - [[fallthrough]]; - } - case Type::Category::Array: - { - ArrayType const& actualType = dynamic_cast(*baseType); - if (!index) - m_errorReporter.typeError(9689_error, _access.location(), "Index expression cannot be omitted."); - else if (actualType.isString()) - { - m_errorReporter.typeError(9961_error, _access.location(), "Index access for string is not possible."); - index->accept(*this); - } - else - { - expectType(*index, *TypeProvider::uint256()); - if (!m_errorReporter.hasErrors()) - if (auto numberType = dynamic_cast(type(*index))) - { - solAssert(!numberType->isFractional(), ""); - if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) - m_errorReporter.typeError(3383_error, _access.location(), "Out of bounds array access."); - } - } - resultType = actualType.baseType(); - isLValue = actualType.location() != DataLocation::CallData; - break; - } - case Type::Category::Mapping: - { - MappingType const& actualType = dynamic_cast(*baseType); - if (!index) - m_errorReporter.typeError(1267_error, _access.location(), "Index expression cannot be omitted."); - else - expectType(*index, *actualType.keyType()); - resultType = actualType.valueType(); - isLValue = true; - break; - } - case Type::Category::TypeType: - { - TypeType const& typeType = dynamic_cast(*baseType); - if (auto const* contractType = dynamic_cast(typeType.actualType())) - if (contractType->contractDefinition().isLibrary()) - m_errorReporter.typeError(2876_error, _access.location(), "Index access for library types and arrays of libraries are not possible."); - if (!index) - resultType = TypeProvider::typeType(TypeProvider::array(DataLocation::Memory, typeType.actualType())); - else - { - u256 length = 1; - if (expectType(*index, *TypeProvider::uint256())) - { - if (auto indexValue = dynamic_cast(type(*index))) - length = indexValue->literalValue(nullptr); - else - m_errorReporter.fatalTypeError(3940_error, index->location(), "Integer constant expected."); - } - else - solAssert(m_errorReporter.hasErrors(), "Expected errors as expectType returned false"); - - resultType = TypeProvider::typeType(TypeProvider::array( - DataLocation::Memory, - typeType.actualType(), - length - )); - } - break; - } - case Type::Category::FixedBytes: - { - FixedBytesType const& bytesType = dynamic_cast(*baseType); - if (!index) - m_errorReporter.typeError(8830_error, _access.location(), "Index expression cannot be omitted."); - else - { - if (!expectType(*index, *TypeProvider::uint256())) - m_errorReporter.fatalTypeError(6318_error, _access.location(), "Index expression cannot be represented as an unsigned integer."); - if (auto integerType = dynamic_cast(type(*index))) - if (bytesType.numBytes() <= integerType->literalValue(nullptr)) - m_errorReporter.typeError(1859_error, _access.location(), "Out of bounds array access."); - } - resultType = TypeProvider::fixedBytes(1); - isLValue = false; // @todo this heavily depends on how it is embedded - break; - } - default: - m_errorReporter.fatalTypeError( - 2614_error, - _access.baseExpression().location(), - "Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")" - ); - } - _access.annotation().type = resultType; - _access.annotation().isLValue = isLValue; - if (index && !*index->annotation().isPure) - isPure = false; - _access.annotation().isPure = isPure; - - return false; -} - -bool TypeChecker::visit(IndexRangeAccess const& _access) -{ - _access.annotation().isConstant = false; - _access.baseExpression().accept(*this); - - bool isLValue = false; // TODO: set this correctly when implementing slices for memory and storage arrays - bool isPure = *_access.baseExpression().annotation().isPure; - - if (Expression const* start = _access.startExpression()) - { - expectType(*start, *TypeProvider::uint256()); - if (!*start->annotation().isPure) - isPure = false; - } - if (Expression const* end = _access.endExpression()) - { - expectType(*end, *TypeProvider::uint256()); - if (!*end->annotation().isPure) - isPure = false; - } - - _access.annotation().isLValue = isLValue; - _access.annotation().isPure = isPure; - - TypePointer exprType = type(_access.baseExpression()); - if (exprType->category() == Type::Category::TypeType) - { - m_errorReporter.typeError(1760_error, _access.location(), "Types cannot be sliced."); - _access.annotation().type = exprType; - return false; - } - - ArrayType const* arrayType = nullptr; - if (auto const* arraySlice = dynamic_cast(exprType)) - arrayType = &arraySlice->arrayType(); - else if (!(arrayType = dynamic_cast(exprType))) - m_errorReporter.fatalTypeError(4781_error, _access.location(), "Index range access is only possible for arrays and array slices."); - - if (arrayType->location() != DataLocation::CallData || !arrayType->isDynamicallySized()) - m_errorReporter.typeError(1227_error, _access.location(), "Index range access is only supported for dynamic calldata arrays."); - else if (arrayType->baseType()->isDynamicallyEncoded()) - m_errorReporter.typeError(2148_error, _access.location(), "Index range access is not supported for arrays with dynamically encoded base types."); - _access.annotation().type = TypeProvider::arraySlice(*arrayType); - - return false; -} - -vector TypeChecker::cleanOverloadedDeclarations( - Identifier const& _identifier, - vector const& _candidates -) -{ - solAssert(_candidates.size() > 1, ""); - vector uniqueDeclarations; - - for (Declaration const* declaration: _candidates) - { - solAssert(declaration, ""); - // the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1 - solAssert( - dynamic_cast(declaration) || - dynamic_cast(declaration) || - dynamic_cast(declaration) || // Solidity++ - dynamic_cast(declaration) || - dynamic_cast(declaration), - "Found overloading involving something not a function, event or a (magic) variable." - ); - - FunctionTypePointer functionType {declaration->functionType(false)}; - if (!functionType) - functionType = declaration->functionType(true); - solAssert(functionType, "Failed to determine the function type of the overloaded."); - - for (TypePointer parameter: functionType->parameterTypes() + functionType->returnParameterTypes()) - if (!parameter) - m_errorReporter.fatalDeclarationError(3893_error, _identifier.location(), "Function type can not be used in this context."); - - if (uniqueDeclarations.end() == find_if( - uniqueDeclarations.begin(), - uniqueDeclarations.end(), - [&](Declaration const* d) - { - FunctionType const* newFunctionType = d->functionType(false); - if (!newFunctionType) - newFunctionType = d->functionType(true); - return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType); - } - )) - uniqueDeclarations.push_back(declaration); - } - return uniqueDeclarations; -} - -bool TypeChecker::visit(Identifier const& _identifier) -{ - IdentifierAnnotation& annotation = _identifier.annotation(); - - if (!annotation.referencedDeclaration) - { - annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations); - if (annotation.overloadedDeclarations.empty()) - m_errorReporter.fatalTypeError(7593_error, _identifier.location(), "No candidates for overload resolution found."); - else if (annotation.overloadedDeclarations.size() == 1) - annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin(); - else if (!annotation.arguments) - { - // The identifier should be a public state variable shadowing other functions - vector candidates; - - for (Declaration const* declaration: annotation.overloadedDeclarations) - { - if (VariableDeclaration const* variableDeclaration = dynamic_cast(declaration)) - candidates.push_back(declaration); - } - if (candidates.empty()) - m_errorReporter.fatalTypeError(2144_error, _identifier.location(), "No matching declaration found after variable lookup."); - else if (candidates.size() == 1) - annotation.referencedDeclaration = candidates.front(); - else - m_errorReporter.fatalTypeError(7589_error, _identifier.location(), "No unique declaration found after variable lookup."); - } - else - { - vector candidates; - - for (Declaration const* declaration: annotation.overloadedDeclarations) - { - FunctionTypePointer functionType = declaration->functionType(true); - solAssert(!!functionType, "Requested type not present."); - if (functionType->canTakeArguments(*annotation.arguments)) - candidates.push_back(declaration); - } - if (candidates.size() == 1) - annotation.referencedDeclaration = candidates.front(); - else - { - SecondarySourceLocation ssl; - - for (Declaration const* declaration: annotation.overloadedDeclarations) - if (!declaration->location().isValid()) - { - // Try to re-construct function definition - string description; - for (auto const& param: declaration->functionType(true)->parameterTypes()) - description += (description.empty() ? "" : ", ") + param->toString(false); - description = "function " + _identifier.name() + "(" + description + ")"; - - ssl.append("Candidate: " + description, declaration->location()); - } - else - ssl.append("Candidate:", declaration->location()); - if (candidates.empty()) - m_errorReporter.fatalTypeError(9322_error, _identifier.location(), ssl, "No matching declaration found after argument-dependent lookup."); - else - m_errorReporter.fatalTypeError(4487_error, _identifier.location(), ssl, "No unique declaration found after argument-dependent lookup."); - } - } - } - solAssert( - !!annotation.referencedDeclaration, - "Referenced declaration is null after overload resolution." - ); - bool isConstant = false; - annotation.isLValue = annotation.referencedDeclaration->isLValue(); - annotation.type = annotation.referencedDeclaration->type(); - solAssert(annotation.type, "Declaration referenced before type could be determined."); - if (auto variableDeclaration = dynamic_cast(annotation.referencedDeclaration)) - annotation.isPure = isConstant = variableDeclaration->isConstant(); - else if (dynamic_cast(annotation.referencedDeclaration)) - annotation.isPure = dynamic_cast(annotation.type); - else if (dynamic_cast(annotation.type)) - annotation.isPure = true; - else if (dynamic_cast(annotation.type)) - annotation.isPure = true; - else - annotation.isPure = false; - - annotation.isConstant = isConstant; - - annotation.requiredLookup = - dynamic_cast(annotation.referencedDeclaration) ? - VirtualLookup::Virtual : VirtualLookup::Static; - - // Check for deprecated function names. - // The check is done here for the case without an actual function call. - if (FunctionType const* fType = dynamic_cast(_identifier.annotation().type)) - { - if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256) - m_errorReporter.typeError( - 3557_error, - _identifier.location(), - "\"sha3\" has been deprecated in favour of \"keccak256\"." - ); - else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct) - m_errorReporter.typeError( - 8050_error, - _identifier.location(), - "\"suicide\" has been deprecated in favour of \"selfdestruct\"." - ); - } - - if ( - MagicVariableDeclaration const* magicVar = - dynamic_cast(annotation.referencedDeclaration) - ) - if (magicVar->type()->category() == Type::Category::Integer) - { - solAssert(_identifier.name() == "now", ""); - m_errorReporter.typeError( - 7359_error, - _identifier.location(), - "\"now\" has been deprecated. Use \"block.timestamp\" instead." - ); - } - - return false; -} - -void TypeChecker::endVisit(IdentifierPath const& _identifierPath) -{ - if ( - dynamic_cast(_identifierPath.annotation().referencedDeclaration) && - _identifierPath.path().size() == 1 - ) - _identifierPath.annotation().requiredLookup = VirtualLookup::Virtual; - else - _identifierPath.annotation().requiredLookup = VirtualLookup::Static; -} - -void TypeChecker::endVisit(UserDefinedTypeName const& _userDefinedTypeName) -{ - if (!_userDefinedTypeName.annotation().type) - _userDefinedTypeName.annotation().type = _userDefinedTypeName.pathNode().annotation().referencedDeclaration->type(); -} - -void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) -{ - _expr.annotation().type = TypeProvider::typeType(TypeProvider::fromElementaryTypeName(_expr.type().typeName(), _expr.type().stateMutability())); - _expr.annotation().isPure = true; - _expr.annotation().isLValue = false; - _expr.annotation().isConstant = false; -} - -void TypeChecker::endVisit(Literal const& _literal) -{ - if (_literal.looksLikeViteAddress()) // Solidity++: check vite address literal - { - // Solidity++: default mutability of address is payable - _literal.annotation().type = TypeProvider::payableAddress(); - - string msg; - - if (!_literal.passesViteAddressChecksum()) - { - msg = "This looks like a Vite address but has an invalid checksum."; - } - - if (!msg.empty()) - m_errorReporter.syntaxError( - 9429_error, - _literal.location(), - msg - ); - } - - if (_literal.looksLikeViteTokenId()) // Solidity++: check token id literal - { - _literal.annotation().type = TypeProvider::viteTokenId(); - - string msg; - - if (!_literal.passesViteTokenIdChecksum()) - { - msg = "This looks like a Vite Token Id but has an invalid checksum."; - } - - if (!msg.empty()) - m_errorReporter.syntaxError( - 9429_error, - _literal.location(), - msg - ); - } - - if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None) - m_errorReporter.fatalTypeError( - 5145_error, - _literal.location(), - "Hexadecimal numbers cannot be used with unit denominations. " - "You can use an expression of the form \"0x1234 * 1 day\" instead." - ); - - if (_literal.subDenomination() == Literal::SubDenomination::Year) - m_errorReporter.typeError( - 4820_error, - _literal.location(), - "Using \"years\" as a unit denomination is deprecated." - ); - - if (!_literal.annotation().type) - _literal.annotation().type = TypeProvider::forLiteral(_literal); - - if (!_literal.annotation().type) - m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value."); - - _literal.annotation().isPure = true; - _literal.annotation().isLValue = false; - _literal.annotation().isConstant = false; -} - -void TypeChecker::endVisit(UsingForDirective const& _usingFor) -{ - if (m_currentContract->isInterface()) - m_errorReporter.typeError( - 9088_error, - _usingFor.location(), - "The \"using for\" directive is not allowed inside interfaces." - ); -} - -bool TypeChecker::contractDependenciesAreCyclic( - ContractDefinition const& _contract, - std::set const& _seenContracts -) const -{ - // Naive depth-first search that remembers nodes already seen. - if (_seenContracts.count(&_contract)) - return true; - set seen(_seenContracts); - seen.insert(&_contract); - for (auto const* c: _contract.annotation().contractDependencies) - if (contractDependenciesAreCyclic(*c, seen)) - return true; - return false; -} - -Declaration const& TypeChecker::dereference(Identifier const& _identifier) const -{ - solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored."); - return *_identifier.annotation().referencedDeclaration; -} - -Declaration const& TypeChecker::dereference(IdentifierPath const& _path) const -{ - solAssert(!!_path.annotation().referencedDeclaration, "Declaration not stored."); - return *_path.annotation().referencedDeclaration; -} - -bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) -{ - _expression.accept(*this); - BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType); - if (!result) - { - auto errorMsg = "Type " + - type(_expression)->toString() + - " is not implicitly convertible to expected type " + - _expectedType.toString(); - if ( - type(_expression)->category() == Type::Category::RationalNumber && - dynamic_cast(type(_expression))->isFractional() && - type(_expression)->mobileType() - ) - { - if (_expectedType.operator==(*type(_expression)->mobileType())) - m_errorReporter.typeError( - 4426_error, - _expression.location(), - errorMsg + ", but it can be explicitly converted." - ); - else - m_errorReporter.typeErrorConcatenateDescriptions( - 2326_error, - _expression.location(), - errorMsg + - ". Try converting to type " + - type(_expression)->mobileType()->toString() + - " or use an explicit conversion.", - result.message() - ); - } - else - m_errorReporter.typeErrorConcatenateDescriptions( - 7407_error, - _expression.location(), - errorMsg + ".", - result.message() - ); - return false; - } - return true; -} - -void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment) -{ - _expression.annotation().willBeWrittenTo = true; - _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; - _expression.accept(*this); - - if (*_expression.annotation().isLValue) - return; - - auto [errorId, description] = [&]() -> tuple { - if (*_expression.annotation().isConstant) - return { 6520_error, "Cannot assign to a constant variable." }; - - if (auto indexAccess = dynamic_cast(&_expression)) - { - if (type(indexAccess->baseExpression())->category() == Type::Category::FixedBytes) - return { 4360_error, "Single bytes in fixed bytes arrays cannot be modified." }; - else if (auto arrayType = dynamic_cast(type(indexAccess->baseExpression()))) - if (arrayType->dataStoredIn(DataLocation::CallData)) - return { 6182_error, "Calldata arrays are read-only." }; - } - - if (auto memberAccess = dynamic_cast(&_expression)) - { - if (auto structType = dynamic_cast(type(memberAccess->expression()))) - { - if (structType->dataStoredIn(DataLocation::CallData)) - return { 4156_error, "Calldata structs are read-only." }; - } - else if (dynamic_cast(type(memberAccess->expression()))) - if (memberAccess->memberName() == "length") - return { 7567_error, "Member \"length\" is read-only and cannot be used to resize arrays." }; - } - - if (auto identifier = dynamic_cast(&_expression)) - if (auto varDecl = dynamic_cast(identifier->annotation().referencedDeclaration)) - if (varDecl->isExternalCallableParameter() && dynamic_cast(identifier->annotation().type)) - return { 7128_error, "External function arguments of reference type are read-only." }; - - return { 4247_error, "Expression has to be an lvalue." }; - }(); - - m_errorReporter.typeError(errorId, _expression.location(), description); -} - -bool TypeChecker::useABICoderV2() const -{ - solAssert(m_currentSourceUnit, ""); - if (m_currentContract) - solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), ""); - return *m_currentSourceUnit->annotation().useABICoderV2; - -} diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 746a485..2abbdaf 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -1,9 +1,24 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ // SPDX-License-Identifier: GPL-3.0 /** - * @author Charles - * @date 2021 - * Solidity++ Type analyzer and checker. - * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + * @author Christian + * @date 2015 + * Type analyzer and checker. */ #pragma once @@ -28,7 +43,7 @@ namespace solidity::frontend * those types and stores errors for invalid operations. * Provides a way to retrieve the type of an AST node. */ -class TypeChecker: private ASTConstVisitor +class TypeChecker: public ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. @@ -49,7 +64,7 @@ class TypeChecker: private ASTConstVisitor static bool typeSupportedByOldABIEncoder(Type const& _type, bool _isLibraryCall); -private: +protected: bool visit(ContractDefinition const& _contract) override; /// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage. @@ -103,11 +118,7 @@ class TypeChecker: private ASTConstVisitor /// 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 const& _bases); - bool visit(EventDefinition const& _eventDef) override; - /// Solidity++: - bool visit(MessageDefinition const& _msgDef) override; - void endVisit(FunctionTypeName const& _funType) override; bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(IfStatement const& _ifStatement) override; @@ -126,7 +137,6 @@ class TypeChecker: private ASTConstVisitor bool visit(FunctionCall const& _functionCall) override; bool visit(FunctionCallOptions const& _functionCallOptions) override; void endVisit(NewExpression const& _newExpression) override; - bool visit(AwaitExpression const& _awaitExpression) override; // Solidity++ bool visit(MemberAccess const& _memberAccess) override; bool visit(IndexAccess const& _indexAccess) override; bool visit(IndexRangeAccess const& _indexRangeAccess) override; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 4a26f89..5682f5e 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -1,9 +1,24 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ // SPDX-License-Identifier: GPL-3.0 /** - * @author Charles - * @date 2021 - * Solidity++ Abstract Syntax Tree. - * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + * @author Christian + * @date 2014 + * Solidity abstract syntax tree. */ #include @@ -11,6 +26,7 @@ #include #include #include +#include #include #include @@ -90,23 +106,6 @@ map, FunctionTypePointer> ContractDefinition::interfaceFuncti return exportedFunctions; } -// Solidity++: get offchain functions -map, FunctionTypePointer> ContractDefinition::offchainFunctions(bool _includeInheritedFunctions) const -{ - auto exportedFunctionList = offchainFunctionList(_includeInheritedFunctions); - - map, FunctionTypePointer> exportedFunctions; - for (auto const& it: exportedFunctionList) - exportedFunctions.insert(it); - - solAssert( - exportedFunctionList.size() == exportedFunctions.size(), - "Hash collision at Function Definition Hash calculation" - ); - - return exportedFunctions; -} - FunctionDefinition const* ContractDefinition::constructor() const { for (FunctionDefinition const* f: definedFunctions()) @@ -201,48 +200,6 @@ vector, FunctionTypePointer>> const& ContractDefinition: }); } -// Solidity++: get offchain function list -vector, FunctionTypePointer>> const& ContractDefinition::offchainFunctionList(bool _includeInheritedFunctions) const -{ - return m_offchainFunctionList[_includeInheritedFunctions].init([&]{ - set signaturesSeen; - vector, FunctionTypePointer>> interfaceFunctionList; - - for (ContractDefinition const* contract: annotation().linearizedBaseContracts) - { - if (_includeInheritedFunctions == false && contract != this) - continue; - vector functions; - // User defined offchain functions - for (FunctionDefinition const* f: contract->definedFunctions()) - if (f->isOffchain()) - functions.push_back(TypeProvider::function(*f, FunctionType::Kind::External)); // TODO: offchain kind? - - // Solidity++: generate offchain getter automatically for public state variables - for (VariableDeclaration const* v: contract->stateVariables()) - if (v->isPartOfExternalInterface()) - functions.push_back(TypeProvider::function(*v)); - - for (FunctionTypePointer const& fun: functions) - { - if (!fun->interfaceFunctionType()) - // Fails hopefully because we already registered the error - continue; - string functionSignature = fun->externalSignature(); - if (signaturesSeen.count(functionSignature) == 0) - { - signaturesSeen.insert(functionSignature); - // Solidity++: Use blake2b instead of Keccak256 - util::FixedHash<4> hash(util::blake2b(functionSignature)); - interfaceFunctionList.emplace_back(hash, fun); - } - } - } - - return interfaceFunctionList; - }); -} - uint32_t ContractDefinition::interfaceId() const { uint32_t result{0}; @@ -351,7 +308,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const case Visibility::Public: return TypeProvider::function(*this, FunctionType::Kind::Internal); case Visibility::External: - case Visibility::Offchain: return {}; } } @@ -366,7 +322,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const return {}; case Visibility::Public: case Visibility::External: - case Visibility::Offchain: return TypeProvider::function(*this, FunctionType::Kind::External); } } @@ -497,27 +452,6 @@ EventDefinitionAnnotation& EventDefinition::annotation() const return initAnnotation(); } -// Solidity++: -TypePointer MessageDefinition::type() const -{ - return TypeProvider::function(*this); -} - -// Solidity++: -FunctionTypePointer MessageDefinition::functionType(bool _internal) const -{ - if (_internal) - return TypeProvider::function(*this); - else - return nullptr; -} - -// Solidity++: -MessageDefinitionAnnotation& MessageDefinition::annotation() const -{ - return initAnnotation(); -} - SourceUnit const& Scopable::sourceUnit() const { ASTNode const* s = scope(); @@ -566,12 +500,6 @@ bool Declaration::isEventParameter() const return dynamic_cast(scope()); } -bool Declaration::isMessageParameter() const -{ - solAssert(scope(), ""); - return dynamic_cast(scope()); -} - DeclarationAnnotation& Declaration::annotation() const { return initAnnotation(); @@ -715,7 +643,7 @@ set VariableDeclaration::allowedDataLocations() c { using Location = VariableDeclaration::Location; - if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter() || isMessageParameter()) + if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter()) return set{ Location::Unspecified }; else if (isCallableOrCatchParameter()) { @@ -746,15 +674,6 @@ string VariableDeclaration::externalIdentifierHex() const return TypeProvider::function(*this)->externalIdentifierHex(); } -// Solidity++: -string VariableDeclaration::toString() const -{ - auto name = this->name(); - auto type = this->type()->toString(); - auto varString = type + " " + name; - return varString; -} - TypePointer VariableDeclaration::type() const { return annotation().type; @@ -773,7 +692,6 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const return nullptr; case Visibility::Public: case Visibility::External: - case Visibility::Offchain: return TypeProvider::function(*this); } @@ -902,53 +820,10 @@ bool Literal::looksLikeAddress() const return abs(int(valueWithoutUnderscores().length()) - 42) <= 1; } -// Solidity++: -bool Literal::looksLikeViteAddress() const -{ - if (!boost::starts_with(value(), "vite_")) - return false; - - if (subDenomination() != SubDenomination::None) - return false; - - return int(value().length()) == 55; -} - -// Solidity++: -bool Literal::looksLikeViteTokenId() const -{ - if (!boost::starts_with(value(), "tti_")) - return false; - - if (subDenomination() != SubDenomination::None) - return false; - - return int(value().length()) == 28; -} - -// Solidity++: check vite address checksum -bool Literal::passesViteAddressChecksum() const +bool Literal::passesAddressChecksum() const { - return util::passesViteAddressChecksum(value()); -} - -// Solidity++: check vite address checksum - -bool Literal::passesViteTokenIdChecksum() const -{ - return util::passesViteTokenIdChecksum(value()); -} - -// Solidity++: get vite address hex value -ASTString Literal::getViteAddressHex() const -{ - return util::getViteAddressHex(value()); -} - -// Solidity++: get vite token id hex value -ASTString Literal::getViteTokenIdHex() const -{ - return util::getViteTokenIdHex(value()); + solAssert(isHexNumber(), "Expected hex number"); + return util::passesAddressChecksum(valueWithoutUnderscores(), true); } string Literal::getChecksummedAddress() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a51d59c..501068b 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1,9 +1,24 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ // SPDX-License-Identifier: GPL-3.0 /** - * @author Charles - * @date 2021 - * Solidity++ Abstract Syntax Tree. - * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + * @author Christian + * @date 2014 + * Solidity abstract syntax tree. */ #pragma once @@ -219,8 +234,6 @@ class Declaration: public ASTNode, public Scopable return "private"; case Visibility::External: return "external"; - case Visibility::Offchain: - return "offchain"; default: solAssert(false, "Invalid visibility specifier."); } @@ -239,10 +252,10 @@ class Declaration: public ASTNode, public Scopable ASTString const& name() const { return *m_name; } bool noVisibilitySpecified() const { return m_visibility == Visibility::Default; } Visibility visibility() const { return m_visibility == Visibility::Default ? defaultVisibility() : m_visibility; } - bool isPublic() const { return visibility() >= Visibility::Public && visibility() < Visibility::Offchain; } // Solidity++ - virtual bool isVisibleInContract() const { return visibility() != Visibility::External && visibility() != Visibility::Offchain; } // Solidity++ + bool isPublic() const { return visibility() >= Visibility::Public; } + virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } virtual bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } - bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal && visibility() < Visibility::Offchain; } // Solidity++ + bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; } virtual bool isVisibleViaContractTypeAccess() const { return false; } virtual bool isLValue() const { return false; } @@ -255,10 +268,6 @@ class Declaration: public ASTNode, public Scopable /// @returns true if this is a declaration of a parameter of an event. bool isEventParameter() const; - /// Solidity++: - /// @returns true if this is a declaration of a parameter of a message. - bool isMessageParameter() const; - /// @returns the type of expressions referencing this declaration. /// This can only be called once types of variable declarations have already been resolved. virtual TypePointer type() const = 0; @@ -504,15 +513,7 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. std::map, FunctionTypePointer> interfaceFunctions(bool _includeInheritedFunctions = true) const; - - // Solidity++: get offchain functions - std::map, FunctionTypePointer> offchainFunctions(bool _includeInheritedFunctions = true) const; - std::vector, FunctionTypePointer>> const& interfaceFunctionList(bool _includeInheritedFunctions = true) const; - - // Solidity++: get offchain function list - std::vector, FunctionTypePointer>> const& offchainFunctionList(bool _includeInheritedFunctions = true) const; - /// @returns the EIP-165 compatible interface identifier. This will exclude inherited functions. uint32_t interfaceId() const; @@ -545,15 +546,13 @@ class ContractDefinition: public Declaration, public StructurallyDocumented, pub /// @returns the next constructor in the inheritance hierarchy. FunctionDefinition const* nextConstructor(ContractDefinition const& _mostDerivedContract) const; -private: +protected: std::vector> m_baseContracts; std::vector> m_subNodes; ContractKind m_contractKind; bool m_abstract{false}; util::LazyInit, FunctionTypePointer>>> m_interfaceFunctionList[2]; - // Solidity++: offchain functions list - util::LazyInit, FunctionTypePointer>>> m_offchainFunctionList[2]; util::LazyInit> m_interfaceEvents; }; @@ -818,7 +817,6 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ASTPointer const& _name, Visibility _visibility, StateMutability _stateMutability, - ExecutionBehavior _executionBehavior, // Solidity++ bool _free, Token _kind, bool _isVirtual, @@ -833,31 +831,24 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), - m_executionBehavior(_executionBehavior), // Solidity++ m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), m_body(_body) { - solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive || _kind == Token::OnMessage || _kind == Token::Getter, ""); - solAssert((isOrdinary() || isOnMessage() || isOffchain()) == !name().empty(), ""); + solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, ""); + solAssert(isOrdinary() == !name().empty(), ""); } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; StateMutability stateMutability() const { return m_stateMutability; } - - ExecutionBehavior executionBehavior() const { return m_executionBehavior;} // Solidity++ - bool libraryFunction() const; bool isOrdinary() const { return m_kind == Token::Function; } bool isConstructor() const { return m_kind == Token::Constructor; } bool isFallback() const { return m_kind == Token::Fallback; } bool isReceive() const { return m_kind == Token::Receive; } - bool isOnMessage() const { return m_kind == Token::OnMessage; } // Solidity++ - bool isOffchain() const { return !isConstructor() && (m_kind == Token::Getter || visibility() == Visibility::Offchain); } // Solidity++ - bool isAsync() const { return m_executionBehavior == ExecutionBehavior::Async; } // Solidity++ bool isFree() const { return m_free; } Token kind() const { return m_kind; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } @@ -873,8 +864,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen solAssert(!isFree(), ""); return isOrdinary() && visibility() >= Visibility::Public; } - // Solidity++: onMessage function is a part of external interface - bool isPartOfExternalInterface() const override { return (isOrdinary() || isOnMessage()) && isPublic(); } + bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); } /// @returns the external signature of the function /// That consists of the name of the function followed by the types of the @@ -905,9 +895,8 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ContractDefinition const* _searchStart = nullptr ) const override; -private: +protected: StateMutability m_stateMutability; - ExecutionBehavior m_executionBehavior; // Solidity++ bool m_free; Token const m_kind; std::vector> m_functionModifiers; @@ -1019,9 +1008,6 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented VariableDeclarationAnnotation& annotation() const override; - // Solidity++: - std::string toString() const; - protected: Visibility defaultVisibility() const override { return Visibility::Internal; } @@ -1157,50 +1143,6 @@ class EventDefinition: public CallableDeclaration, public StructurallyDocumented bool m_anonymous = false; }; - -/** - * Solidity++: - * Definition of a message call (async external function in another contract). - */ -class MessageDefinition: public CallableDeclaration, public StructurallyDocumented, public ScopeOpener -{ -public: - MessageDefinition( - int64_t _id, - SourceLocation const& _location, - ASTPointer const& _name, - ASTPointer const& _documentation, - ASTPointer const& _parameters - ): - CallableDeclaration(_id, _location, _name, Visibility::Default, _parameters), - StructurallyDocumented(_documentation) - { - } - - void accept(ASTVisitor& _visitor) override; - void accept(ASTConstVisitor& _visitor) const override; - - TypePointer type() const override; - FunctionTypePointer functionType(bool /*_internal*/) const override; - - bool isVisibleInDerivedContracts() const override { return true; } - bool isVisibleViaContractTypeAccess() const override { return false; /* TODO */ } - - MessageDefinitionAnnotation& annotation() const override; - - CallableDeclaration const& resolveVirtual( - ContractDefinition const&, - ContractDefinition const* - ) const override - { - return *this; - } - -private: - -}; - - /** * Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global * functions when such an identifier is encountered. Will never have a valid location in the source code @@ -1746,32 +1688,6 @@ class EmitStatement: public Statement ASTPointer m_eventCall; }; -/** - * Solidity++: - * The send statement is used to send messages: send MessageName(arg1, ..., argn) - */ -class SendStatement: public Statement -{ -public: - explicit SendStatement( - int64_t _id, - SourceLocation const& _location, - ASTPointer const& _docString, - ASTPointer const& _address, - ASTPointer _expression - ): - Statement(_id, _location, _docString), m_address(_address), m_expression(std::move(_expression)) {} - void accept(ASTVisitor& _visitor) override; - void accept(ASTConstVisitor& _visitor) const override; - - Expression const& address() const { return *m_address; } - Expression const& expression() const { return *m_expression; } - -private: - ASTPointer m_address; - ASTPointer m_expression; -}; - /** * Definition of one or more variables as a statement inside a function. * If multiple variables are declared, a value has to be assigned directly. @@ -2091,28 +2007,6 @@ class NewExpression: public Expression ASTPointer m_typeName; }; -/** - * Solidity++: - * The await expression is used to wait for an asynchronous message call to return: await foo(arg1, ..., argn) - */ -class AwaitExpression: public Expression -{ -public: - AwaitExpression( - int64_t _id, - SourceLocation const& _location, - ASTPointer _expression - ): - Expression(_id, _location), m_expression(std::move(_expression)) {} - void accept(ASTVisitor& _visitor) override; - void accept(ASTConstVisitor& _visitor) const override; - - Expression const& expression() const { return *m_expression; } - -private: - ASTPointer m_expression; -}; - /** * Access to a member of an object. Example: x.name */ @@ -2266,6 +2160,7 @@ class Literal: public PrimaryExpression Day = static_cast(Token::SubDay), Week = static_cast(Token::SubWeek), Year = static_cast(Token::SubYear), + // Solidity++: Attov = static_cast(Token::SubAttov), Vite = static_cast(Token::SubVite) }; @@ -2293,6 +2188,10 @@ class Literal: public PrimaryExpression /// @returns true if this looks like a checksummed address. bool looksLikeAddress() const; + /// @returns true if it passes the address checksum test. + bool passesAddressChecksum() const; + /// @returns the checksummed version of an address (or empty string if not valid) + std::string getChecksummedAddress() const; /// @returns true if this looks like a checksummed vite address. /// Solidity++: @@ -2306,7 +2205,7 @@ class Literal: public PrimaryExpression /// Solidity++: bool passesViteAddressChecksum() const; - // @returns true if it passes the vite token id checksum test. + /// @returns true if it passes the vite token id checksum test. /// Solidity++: bool passesViteTokenIdChecksum() const; @@ -2316,11 +2215,6 @@ class Literal: public PrimaryExpression /// Solidity++: get vite token id in hex ASTString getViteTokenIdHex() const; - /// @returns true if it passes the address checksum test. - bool passesAddressChecksum() const; - /// @returns the checksummed version of an address (or empty string if not valid) - std::string getChecksummedAddress() const; - private: Token m_token; ASTPointer m_value; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index a81ff57..94f627d 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -80,6 +80,8 @@ struct SourceUnitAnnotation: ASTAnnotation std::set experimentalFeatures; /// Using the new ABI coder. Set to `false` if using ABI coder v1. SetOnce useABICoderV2; + /// Solidity++: The programing language of this source unit + SetOnce sourceLanguage; }; struct ScopableAnnotation @@ -253,9 +255,6 @@ struct ExpressionAnnotation: ASTAnnotation /// that is called, used for overload resolution std::optional arguments; - /// Solidity++: Id of await expression, used for callback signature of a function call - int64_t awaitId = 0; - /// True if the expression consists solely of the name of the function and the function is called immediately /// instead of being stored or processed. The name may be qualified with the name of a contract, library /// module, etc., that clarifies the scope. For example: `m.L.f()`, where `m` is a module, `L` is a library @@ -303,6 +302,8 @@ enum class FunctionCallKind struct FunctionCallAnnotation: ExpressionAnnotation { util::SetOnce kind; + /// If true, this is an async call. + bool async = true; /// If true, this is the external call of a try statement. bool tryCall = false; }; diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index 4d30849..05c58d8 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -22,24 +22,9 @@ enum class VirtualLookup { Static, Virtual, Super }; enum class StateMutability { Pure, View, NonPayable, Payable }; /// Visibility ordered from restricted to unrestricted. -enum class Visibility { Default, Private, Internal, Public, External, Offchain }; - -/// Solidity++: How a function will be executed in VM, synchronous or asynchronous -enum class ExecutionBehavior {Sync, Async}; - -inline std::string executionBehaviorToString(ExecutionBehavior const& _executionBehavior) -{ - switch (_executionBehavior) - { - case ExecutionBehavior::Sync: - return "sync"; - case ExecutionBehavior::Async: - return "async"; - default: - solAssert(false, "Unknown execution behavior."); - } -} +enum class Visibility { Default, Private, Internal, Public, External }; +enum class SourceLanguage {Solidity, Soliditypp}; // Solidity++ enum class Arithmetic { Checked, Wrapping }; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 0d43999..f74f4a9 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -41,7 +41,6 @@ class VariableDeclaration; class ModifierDefinition; class ModifierInvocation; class EventDefinition; -class MessageDefinition; // Solidity++ class MagicVariableDeclaration; class TypeName; class ElementaryTypeName; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp deleted file mode 100644 index 33f45ef..0000000 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ /dev/null @@ -1,966 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -/** - * @date 2017 - * Converts the AST into json format - */ - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -using namespace std; -using namespace solidity::langutil; - -namespace -{ - -template typename C> -void addIfSet(std::vector>& _attributes, string const& _name, C const& _value) -{ - if constexpr (std::is_same_v, solidity::util::SetOnce>) - { - if (!_value.set()) - return; - } - else if constexpr (std::is_same_v, optional>) - { - if (!_value.has_value()) - return; - } - - _attributes.emplace_back(_name, *_value); -} - -} - -namespace solidity::frontend -{ - -ASTJsonConverter::ASTJsonConverter(CompilerStack::State _stackState, map _sourceIndices): - m_stackState(_stackState), - m_sourceIndices(std::move(_sourceIndices)) -{ -} - - -void ASTJsonConverter::setJsonNode( - ASTNode const& _node, - string const& _nodeName, - initializer_list>&& _attributes -) -{ - ASTJsonConverter::setJsonNode( - _node, - _nodeName, - std::vector>(std::move(_attributes)) - ); -} - -void ASTJsonConverter::setJsonNode( - ASTNode const& _node, - string const& _nodeType, - std::vector>&& _attributes -) -{ - m_currentValue = Json::objectValue; - m_currentValue["id"] = nodeId(_node); - m_currentValue["src"] = sourceLocationToString(_node.location()); - m_currentValue["nodeType"] = _nodeType; - for (auto& e: _attributes) - m_currentValue[e.first] = std::move(e.second); -} - -size_t ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const -{ - if (_location.source && m_sourceIndices.count(_location.source->name())) - return m_sourceIndices.at(_location.source->name()); - else - return numeric_limits::max(); -} - -string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const -{ - size_t sourceIndex = sourceIndexFromLocation(_location); - int length = -1; - if (_location.start >= 0 && _location.end >= 0) - length = _location.end - _location.start; - return to_string(_location.start) + ":" + to_string(length) + ":" + to_string(sourceIndex); -} - -string ASTJsonConverter::namePathToString(std::vector const& _namePath) -{ - return boost::algorithm::join(_namePath, "."); -} - -Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short) -{ - Json::Value typeDescriptions(Json::objectValue); - typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString(_short)) : Json::nullValue; - typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue; - return typeDescriptions; - -} -Json::Value ASTJsonConverter::typePointerToJson(std::optional const& _tps) -{ - if (_tps) - { - Json::Value arguments(Json::arrayValue); - for (auto const& tp: _tps->types) - appendMove(arguments, typePointerToJson(tp)); - return arguments; - } - else - return Json::nullValue; -} - -void ASTJsonConverter::appendExpressionAttributes( - std::vector>& _attributes, - ExpressionAnnotation const& _annotation -) -{ - std::vector> exprAttributes = { - make_pair("typeDescriptions", typePointerToJson(_annotation.type)), - make_pair("argumentTypes", typePointerToJson(_annotation.arguments)) - }; - - addIfSet(exprAttributes, "isLValue", _annotation.isLValue); - addIfSet(exprAttributes, "isPure", _annotation.isPure); - addIfSet(exprAttributes, "isConstant", _annotation.isConstant); - - if (m_stackState > CompilerStack::State::ParsedAndImported) - exprAttributes.emplace_back("lValueRequested", _annotation.willBeWrittenTo); - - _attributes += exprAttributes; -} - -Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair _info) const -{ - Json::Value tuple(Json::objectValue); - tuple["src"] = sourceLocationToString(_info.first->location); - tuple["declaration"] = idOrNull(_info.second.declaration); - tuple["isSlot"] = Json::Value(_info.second.suffix == "slot"); - tuple["isOffset"] = Json::Value(_info.second.suffix == "offset"); - if (!_info.second.suffix.empty()) - tuple["suffix"] = Json::Value(_info.second.suffix); - tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize)); - return tuple; -} - -void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node) -{ - _stream << util::jsonPrettyPrint(toJson(_node)); -} - -Json::Value ASTJsonConverter::toJson(ASTNode const& _node) -{ - _node.accept(*this); - return util::removeNullMembers(std::move(m_currentValue)); -} - -bool ASTJsonConverter::visit(SourceUnit const& _node) -{ - std::vector> attributes = { - make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue), - make_pair("nodes", toJson(_node.nodes())) - }; - - if (_node.annotation().exportedSymbols.set()) - { - Json::Value exportedSymbols = Json::objectValue; - for (auto const& sym: *_node.annotation().exportedSymbols) - { - exportedSymbols[sym.first] = Json::arrayValue; - for (Declaration const* overload: sym.second) - exportedSymbols[sym.first].append(nodeId(*overload)); - } - - attributes.emplace_back("exportedSymbols", exportedSymbols); - }; - - addIfSet(attributes, "absolutePath", _node.annotation().path); - - setJsonNode(_node, "SourceUnit", std::move(attributes)); - - return false; -} - -bool ASTJsonConverter::visit(PragmaDirective const& _node) -{ - Json::Value literals(Json::arrayValue); - for (auto const& literal: _node.literals()) - literals.append(literal); - setJsonNode(_node, "PragmaDirective", { - make_pair("literals", std::move(literals)) - }); - return false; -} - -bool ASTJsonConverter::visit(ImportDirective const& _node) -{ - std::vector> attributes = { - make_pair("file", _node.path()), - make_pair("sourceUnit", idOrNull(_node.annotation().sourceUnit)), - make_pair("scope", idOrNull(_node.scope())) - }; - - addIfSet(attributes, "absolutePath", _node.annotation().absolutePath); - - attributes.emplace_back("unitAlias", _node.name()); - Json::Value symbolAliases(Json::arrayValue); - for (auto const& symbolAlias: _node.symbolAliases()) - { - Json::Value tuple(Json::objectValue); - solAssert(symbolAlias.symbol, ""); - tuple["foreign"] = toJson(*symbolAlias.symbol); - tuple["local"] = symbolAlias.alias ? Json::Value(*symbolAlias.alias) : Json::nullValue; - symbolAliases.append(tuple); - } - attributes.emplace_back("symbolAliases", std::move(symbolAliases)); - setJsonNode(_node, "ImportDirective", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(ContractDefinition const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("contractKind", contractKind(_node.contractKind())), - make_pair("abstract", _node.abstract()), - make_pair("baseContracts", toJson(_node.baseContracts())), - make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies, true)), - make_pair("nodes", toJson(_node.subNodes())), - make_pair("scope", idOrNull(_node.scope())) - }; - - if (_node.annotation().unimplementedDeclarations.has_value()) - attributes.emplace_back("fullyImplemented", _node.annotation().unimplementedDeclarations->empty()); - if (!_node.annotation().linearizedBaseContracts.empty()) - attributes.emplace_back("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)); - - setJsonNode(_node, "ContractDefinition", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(IdentifierPath const& _node) -{ - setJsonNode(_node, "IdentifierPath", { - make_pair("name", namePathToString(_node.path())), - make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)) - }); - return false; -} - -bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) -{ - setJsonNode(_node, "InheritanceSpecifier", { - make_pair("baseName", toJson(_node.name())), - make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) - }); - return false; -} - -bool ASTJsonConverter::visit(UsingForDirective const& _node) -{ - setJsonNode(_node, "UsingForDirective", { - make_pair("libraryName", toJson(_node.libraryName())), - make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue) - }); - return false; -} - -bool ASTJsonConverter::visit(StructDefinition const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.name()), - make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("members", toJson(_node.members())), - make_pair("scope", idOrNull(_node.scope())) - }; - - addIfSet(attributes,"canonicalName", _node.annotation().canonicalName); - - setJsonNode(_node, "StructDefinition", std::move(attributes)); - - return false; -} - -bool ASTJsonConverter::visit(EnumDefinition const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.name()), - make_pair("members", toJson(_node.members())) - }; - - addIfSet(attributes,"canonicalName", _node.annotation().canonicalName); - - setJsonNode(_node, "EnumDefinition", std::move(attributes)); - - return false; -} - -bool ASTJsonConverter::visit(EnumValue const& _node) -{ - setJsonNode(_node, "EnumValue", { - make_pair("name", _node.name()) - }); - return false; -} - -bool ASTJsonConverter::visit(ParameterList const& _node) -{ - setJsonNode(_node, "ParameterList", { - make_pair("parameters", toJson(_node.parameters())) - }); - return false; -} - -bool ASTJsonConverter::visit(OverrideSpecifier const& _node) -{ - setJsonNode(_node, "OverrideSpecifier", { - make_pair("overrides", toJson(_node.overrides())) - }); - return false; -} - -bool ASTJsonConverter::visit(FunctionDefinition const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())), - make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), - make_pair("executionBehavior", executionBehaviorToString(_node.executionBehavior())), - make_pair("virtual", _node.markedVirtual()), - make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), - make_pair("parameters", toJson(_node.parameterList())), - make_pair("returnParameters", toJson(*_node.returnParameterList())), - make_pair("modifiers", toJson(_node.modifiers())), - make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue), - make_pair("implemented", _node.isImplemented()), - make_pair("scope", idOrNull(_node.scope())) - }; - - optional visibility; - if (_node.isConstructor()) - { - if (_node.annotation().contract) - visibility = _node.annotation().contract->abstract() ? Visibility::Internal : Visibility::Public; - } - else - visibility = _node.visibility(); - - if (visibility) - attributes.emplace_back("visibility", Declaration::visibilityToString(*visibility)); - - if (_node.isPartOfExternalInterface() && m_stackState > CompilerStack::State::ParsedAndImported) - attributes.emplace_back("functionSelector", _node.externalIdentifierHex()); - if (!_node.annotation().baseFunctions.empty()) - attributes.emplace_back(make_pair("baseFunctions", getContainerIds(_node.annotation().baseFunctions, true))); - setJsonNode(_node, "FunctionDefinition", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(VariableDeclaration const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.name()), - make_pair("typeName", toJson(_node.typeName())), - make_pair("constant", _node.isConstant()), - make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())), - make_pair("stateVariable", _node.isStateVariable()), - make_pair("storageLocation", location(_node.referenceLocation())), - make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), - make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue), - make_pair("scope", idOrNull(_node.scope())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }; - if (_node.isStateVariable() && _node.isPublic()) - attributes.emplace_back("functionSelector", _node.externalIdentifierHex()); - if (_node.isStateVariable() && _node.documentation()) - attributes.emplace_back("documentation", toJson(*_node.documentation())); - if (m_inEvent) - attributes.emplace_back("indexed", _node.isIndexed()); - if (!_node.annotation().baseFunctions.empty()) - attributes.emplace_back(make_pair("baseFunctions", getContainerIds(_node.annotation().baseFunctions, true))); - setJsonNode(_node, "VariableDeclaration", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(ModifierDefinition const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("parameters", toJson(_node.parameterList())), - make_pair("virtual", _node.markedVirtual()), - make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), - make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue) - }; - if (!_node.annotation().baseFunctions.empty()) - attributes.emplace_back(make_pair("baseModifiers", getContainerIds(_node.annotation().baseFunctions, true))); - setJsonNode(_node, "ModifierDefinition", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(ModifierInvocation const& _node) -{ - setJsonNode(_node, "ModifierInvocation", { - make_pair("modifierName", toJson(_node.name())), - make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) - }); - return false; -} - -bool ASTJsonConverter::visit(EventDefinition const& _node) -{ - m_inEvent = true; - setJsonNode(_node, "EventDefinition", { - make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("parameters", toJson(_node.parameterList())), - make_pair("anonymous", _node.isAnonymous()) - }); - return false; -} - -// Solidity++: -bool ASTJsonConverter::visit(MessageDefinition const& _node) -{ - setJsonNode(_node, "MessageDefinition", { - make_pair("name", _node.name()), - make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("parameters", toJson(_node.parameterList())) - }); - return false; -} - -bool ASTJsonConverter::visit(ElementaryTypeName const& _node) -{ - std::vector> attributes = { - make_pair("name", _node.typeName().toString()), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }; - - if (_node.stateMutability()) - attributes.emplace_back(make_pair("stateMutability", stateMutabilityToString(*_node.stateMutability()))); - - setJsonNode(_node, "ElementaryTypeName", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) -{ - setJsonNode(_node, "UserDefinedTypeName", { - make_pair("pathNode", toJson(_node.pathNode())), - make_pair("referencedDeclaration", idOrNull(_node.pathNode().annotation().referencedDeclaration)), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); - return false; -} - -bool ASTJsonConverter::visit(FunctionTypeName const& _node) -{ - setJsonNode(_node, "FunctionTypeName", { - make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), - make_pair("parameterTypes", toJson(*_node.parameterTypeList())), - make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); - return false; -} - -bool ASTJsonConverter::visit(Mapping const& _node) -{ - setJsonNode(_node, "Mapping", { - make_pair("keyType", toJson(_node.keyType())), - make_pair("valueType", toJson(_node.valueType())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); - return false; -} - -bool ASTJsonConverter::visit(ArrayTypeName const& _node) -{ - setJsonNode(_node, "ArrayTypeName", { - make_pair("baseType", toJson(_node.baseType())), - make_pair("length", toJsonOrNull(_node.length())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); - return false; -} - -bool ASTJsonConverter::visit(InlineAssembly const& _node) -{ - vector> externalReferences; - - for (auto const& it: _node.annotation().externalReferences) - if (it.first) - externalReferences.emplace_back(make_pair( - it.first->name.str(), - inlineAssemblyIdentifierToJson(it) - )); - - Json::Value externalReferencesJson = Json::arrayValue; - - for (auto&& it: boost::range::sort(externalReferences)) - externalReferencesJson.append(std::move(it.second)); - - setJsonNode(_node, "InlineAssembly", { - make_pair("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))), - make_pair("externalReferences", std::move(externalReferencesJson)), - make_pair("evmVersion", dynamic_cast(_node.dialect()).evmVersion().name()) - }); - - return false; -} - -bool ASTJsonConverter::visit(Block const& _node) -{ - setJsonNode(_node, _node.unchecked() ? "UncheckedBlock" : "Block", { - make_pair("statements", toJson(_node.statements())) - }); - return false; -} - -bool ASTJsonConverter::visit(PlaceholderStatement const& _node) -{ - setJsonNode(_node, "PlaceholderStatement", {}); - return false; -} - -bool ASTJsonConverter::visit(IfStatement const& _node) -{ - setJsonNode(_node, "IfStatement", { - make_pair("condition", toJson(_node.condition())), - make_pair("trueBody", toJson(_node.trueStatement())), - make_pair("falseBody", toJsonOrNull(_node.falseStatement())) - }); - return false; -} - -bool ASTJsonConverter::visit(TryCatchClause const& _node) -{ - setJsonNode(_node, "TryCatchClause", { - make_pair("errorName", _node.errorName()), - make_pair("parameters", toJsonOrNull(_node.parameters())), - make_pair("block", toJson(_node.block())) - }); - return false; -} - -bool ASTJsonConverter::visit(TryStatement const& _node) -{ - setJsonNode(_node, "TryStatement", { - make_pair("externalCall", toJson(_node.externalCall())), - make_pair("clauses", toJson(_node.clauses())) - }); - return false; -} - -bool ASTJsonConverter::visit(WhileStatement const& _node) -{ - setJsonNode( - _node, - _node.isDoWhile() ? "DoWhileStatement" : "WhileStatement", - { - make_pair("condition", toJson(_node.condition())), - make_pair("body", toJson(_node.body())) - } - ); - return false; -} - -bool ASTJsonConverter::visit(ForStatement const& _node) -{ - setJsonNode(_node, "ForStatement", { - make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())), - make_pair("condition", toJsonOrNull(_node.condition())), - make_pair("loopExpression", toJsonOrNull(_node.loopExpression())), - make_pair("body", toJson(_node.body())) - }); - return false; -} - -bool ASTJsonConverter::visit(Continue const& _node) -{ - setJsonNode(_node, "Continue", {}); - return false; -} - -bool ASTJsonConverter::visit(Break const& _node) -{ - setJsonNode(_node, "Break", {}); - return false; -} - -bool ASTJsonConverter::visit(Return const& _node) -{ - setJsonNode(_node, "Return", { - make_pair("expression", toJsonOrNull(_node.expression())), - make_pair("functionReturnParameters", idOrNull(_node.annotation().functionReturnParameters)) - }); - return false; -} - -bool ASTJsonConverter::visit(Throw const& _node) -{ - setJsonNode(_node, "Throw", {}); - return false; -} - -bool ASTJsonConverter::visit(EmitStatement const& _node) -{ - setJsonNode(_node, "EmitStatement", { - make_pair("eventCall", toJson(_node.eventCall())) - }); - return false; -} - -// Solidity++: -bool ASTJsonConverter::visit(SendStatement const& _node) -{ - setJsonNode(_node, "SendStatement", { - make_pair("expression", toJson(_node.expression())) - }); - return false; -} - -bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) -{ - Json::Value varDecs(Json::arrayValue); - for (auto const& v: _node.declarations()) - appendMove(varDecs, idOrNull(v.get())); - setJsonNode(_node, "VariableDeclarationStatement", { - make_pair("assignments", std::move(varDecs)), - make_pair("declarations", toJson(_node.declarations())), - make_pair("initialValue", toJsonOrNull(_node.initialValue())) - }); - return false; -} - -bool ASTJsonConverter::visit(ExpressionStatement const& _node) -{ - setJsonNode(_node, "ExpressionStatement", { - make_pair("expression", toJson(_node.expression())) - }); - return false; -} - -bool ASTJsonConverter::visit(Conditional const& _node) -{ - std::vector> attributes = { - make_pair("condition", toJson(_node.condition())), - make_pair("trueExpression", toJson(_node.trueExpression())), - make_pair("falseExpression", toJson(_node.falseExpression())) - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "Conditional", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(Assignment const& _node) -{ - std::vector> attributes = { - make_pair("operator", TokenTraits::toString(_node.assignmentOperator())), - make_pair("leftHandSide", toJson(_node.leftHandSide())), - make_pair("rightHandSide", toJson(_node.rightHandSide())) - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "Assignment", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(TupleExpression const& _node) -{ - std::vector> attributes = { - make_pair("isInlineArray", Json::Value(_node.isInlineArray())), - make_pair("components", toJson(_node.components())), - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "TupleExpression", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(UnaryOperation const& _node) -{ - std::vector> attributes = { - make_pair("prefix", _node.isPrefixOperation()), - make_pair("operator", TokenTraits::toString(_node.getOperator())), - make_pair("subExpression", toJson(_node.subExpression())) - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "UnaryOperation", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(BinaryOperation const& _node) -{ - std::vector> attributes = { - make_pair("operator", TokenTraits::toString(_node.getOperator())), - make_pair("leftExpression", toJson(_node.leftExpression())), - make_pair("rightExpression", toJson(_node.rightExpression())), - make_pair("commonType", typePointerToJson(_node.annotation().commonType)), - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "BinaryOperation", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(FunctionCall const& _node) -{ - Json::Value names(Json::arrayValue); - for (auto const& name: _node.names()) - names.append(Json::Value(*name)); - std::vector> attributes = { - make_pair("expression", toJson(_node.expression())), - make_pair("names", std::move(names)), - make_pair("arguments", toJson(_node.arguments())), - make_pair("tryCall", _node.annotation().tryCall) - }; - - if (_node.annotation().kind.set()) - { - FunctionCallKind nodeKind = *_node.annotation().kind; - attributes.emplace_back("kind", functionCallKind(nodeKind)); - } - - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "FunctionCall", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(FunctionCallOptions const& _node) -{ - Json::Value names(Json::arrayValue); - for (auto const& name: _node.names()) - names.append(Json::Value(*name)); - - std::vector> attributes = { - make_pair("expression", toJson(_node.expression())), - make_pair("names", std::move(names)), - make_pair("options", toJson(_node.options())), - }; - appendExpressionAttributes(attributes, _node.annotation()); - - setJsonNode(_node, "FunctionCallOptions", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(NewExpression const& _node) -{ - std::vector> attributes = { - make_pair("typeName", toJson(_node.typeName())) - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "NewExpression", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(MemberAccess const& _node) -{ - std::vector> attributes = { - make_pair("memberName", _node.memberName()), - make_pair("expression", toJson(_node.expression())), - make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "MemberAccess", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(IndexAccess const& _node) -{ - std::vector> attributes = { - make_pair("baseExpression", toJson(_node.baseExpression())), - make_pair("indexExpression", toJsonOrNull(_node.indexExpression())), - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "IndexAccess", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(IndexRangeAccess const& _node) -{ - std::vector> attributes = { - make_pair("baseExpression", toJson(_node.baseExpression())), - make_pair("startExpression", toJsonOrNull(_node.startExpression())), - make_pair("endExpression", toJsonOrNull(_node.endExpression())), - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "IndexRangeAccess", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(Identifier const& _node) -{ - Json::Value overloads(Json::arrayValue); - for (auto const& dec: _node.annotation().overloadedDeclarations) - overloads.append(nodeId(*dec)); - setJsonNode(_node, "Identifier", { - make_pair("name", _node.name()), - make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), - make_pair("overloadedDeclarations", overloads), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)), - make_pair("argumentTypes", typePointerToJson(_node.annotation().arguments)) - }); - return false; -} - -bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) -{ - std::vector> attributes = { - make_pair("typeName", toJson(_node.type())) - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "ElementaryTypeNameExpression", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(Literal const& _node) -{ - Json::Value value{_node.value()}; - if (!util::validateUTF8(_node.value())) - value = Json::nullValue; - Token subdenomination = Token(_node.subDenomination()); - std::vector> attributes = { - make_pair("kind", literalTokenKind(_node.token())), - make_pair("value", value), - make_pair("hexValue", util::toHex(util::asBytes(_node.value()))), - make_pair( - "subdenomination", - subdenomination == Token::Illegal ? - Json::nullValue : - Json::Value{TokenTraits::toString(subdenomination)} - ) - }; - appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode(_node, "Literal", std::move(attributes)); - return false; -} - -bool ASTJsonConverter::visit(StructuredDocumentation const& _node) -{ - Json::Value text{*_node.text()}; - std::vector> attributes = { - make_pair("text", text) - }; - setJsonNode(_node, "StructuredDocumentation", std::move(attributes)); - return false; -} - -// Solidity++: -bool ASTJsonConverter::visit(AwaitExpression const& _node) -{ - std::vector> attributes = { - make_pair("expression", toJson(_node.expression())), - }; - setJsonNode(_node, "AwaitExpression", std::move(attributes)); - return false; -} - -void ASTJsonConverter::endVisit(EventDefinition const&) -{ - m_inEvent = false; -} - -string ASTJsonConverter::location(VariableDeclaration::Location _location) -{ - switch (_location) - { - case VariableDeclaration::Location::Unspecified: - return "default"; - case VariableDeclaration::Location::Storage: - return "storage"; - case VariableDeclaration::Location::Memory: - return "memory"; - case VariableDeclaration::Location::CallData: - return "calldata"; - } - // To make the compiler happy - return {}; -} - -string ASTJsonConverter::contractKind(ContractKind _kind) -{ - switch (_kind) - { - case ContractKind::Interface: - return "interface"; - case ContractKind::Contract: - return "contract"; - case ContractKind::Library: - return "library"; - } - - // To make the compiler happy - return {}; -} - -string ASTJsonConverter::functionCallKind(FunctionCallKind _kind) -{ - switch (_kind) - { - case FunctionCallKind::FunctionCall: - return "functionCall"; - case FunctionCallKind::TypeConversion: - return "typeConversion"; - case FunctionCallKind::StructConstructorCall: - return "structConstructorCall"; - default: - solAssert(false, "Unknown kind of function call."); - } -} - -string ASTJsonConverter::literalTokenKind(Token _token) -{ - switch (_token) - { - case Token::Number: - return "number"; - case Token::StringLiteral: - return "string"; - case Token::UnicodeStringLiteral: - return "unicodeString"; - case Token::HexStringLiteral: - return "hexString"; - case Token::TrueLiteral: - case Token::FalseLiteral: - return "bool"; - default: - solAssert(false, "Unknown kind of literal token."); - } -} - -string ASTJsonConverter::type(Expression const& _expression) -{ - return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown"; -} - -string ASTJsonConverter::type(VariableDeclaration const& _varDecl) -{ - return _varDecl.annotation().type ? _varDecl.annotation().type->toString() : "Unknown"; -} - -} diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 9fa4ae7..91a81e0 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -1,3 +1,19 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ // SPDX-License-Identifier: GPL-3.0 /** * @author Lefteris @@ -72,7 +88,6 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(ModifierDefinition const& _node) override; bool visit(ModifierInvocation const& _node) override; bool visit(EventDefinition const& _node) override; - bool visit(MessageDefinition const& _node) override; // Solidity++ bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(FunctionTypeName const& _node) override; @@ -91,7 +106,6 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(Return const& _node) override; bool visit(Throw const& _node) override; bool visit(EmitStatement const& _node) override; - bool visit(SendStatement const& _node) override; // Solidity++ bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; @@ -109,11 +123,10 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(ElementaryTypeNameExpression const& _node) override; bool visit(Literal const& _node) override; bool visit(StructuredDocumentation const& _node) override; - bool visit(AwaitExpression const& _node) override; // Solidity++ void endVisit(EventDefinition const&) override; -private: +protected: void setJsonNode( ASTNode const& _node, std::string const& _nodeName, diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp deleted file mode 100644 index 48ab2f7..0000000 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ /dev/null @@ -1,1073 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -/** - * @author julius - * @date 2019 - *Component that imports an AST from json format to the internal format - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; - -namespace solidity::frontend -{ - -using SourceLocation = langutil::SourceLocation; - -template -ASTPointer ASTJsonImporter::nullOrCast(Json::Value const& _json) -{ - if (_json.isNull()) - return nullptr; - else - return dynamic_pointer_cast(convertJsonToASTNode(_json)); -} - - -// ============ public =========================== - -map> ASTJsonImporter::jsonToSourceUnit(map const& _sourceList) -{ - m_sourceList = _sourceList; - for (auto const& src: _sourceList) - m_sourceLocations.emplace_back(make_shared(src.first)); - for (auto const& srcPair: m_sourceList) - { - astAssert(!srcPair.second.isNull(), ""); - astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'."); - m_currentSourceName = srcPair.first; - m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first); - } - return m_sourceUnits; -} - -// ============ private =========================== - -// =========== general creation functions ============== -template -ASTPointer ASTJsonImporter::createASTNode(Json::Value const& _node, Args&&... _args) -{ - astAssert(member(_node, "id").isInt64(), "'id'-field must be 64bit integer."); - - int64_t id = _node["id"].asInt64(); - - astAssert(m_usedIDs.insert(id).second, "Found duplicate node ID!"); - - auto n = make_shared( - id, - createSourceLocation(_node), - forward(_args)... - ); - return n; -} - -SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _node) -{ - astAssert(member(_node, "src").isString(), "'src' must be a string"); - - return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_currentSourceName, m_sourceLocations.size()); -} - -template -ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _node) -{ - ASTPointer ret = dynamic_pointer_cast(convertJsonToASTNode(_node)); - astAssert(ret, "cast of converted json-node must not be nullptr"); - return ret; -} - - -ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _json) -{ - astAssert(_json["nodeType"].isString() && _json.isMember("id"), "JSON-Node needs to have 'nodeType' and 'id' fields."); - string nodeType = _json["nodeType"].asString(); - if (nodeType == "PragmaDirective") - return createPragmaDirective(_json); - if (nodeType == "ImportDirective") - return createImportDirective(_json); - if (nodeType == "ContractDefinition") - return createContractDefinition(_json); - if (nodeType == "IdentifierPath") - return createIdentifierPath(_json); - if (nodeType == "InheritanceSpecifier") - return createInheritanceSpecifier(_json); - if (nodeType == "UsingForDirective") - return createUsingForDirective(_json); - if (nodeType == "StructDefinition") - return createStructDefinition(_json); - if (nodeType == "EnumDefinition") - return createEnumDefinition(_json); - if (nodeType == "EnumValue") - return createEnumValue(_json); - if (nodeType == "ParameterList") - return createParameterList(_json); - if (nodeType == "OverrideSpecifier") - return createOverrideSpecifier(_json); - if (nodeType == "FunctionDefinition") - return createFunctionDefinition(_json); - if (nodeType == "VariableDeclaration") - return createVariableDeclaration(_json); - if (nodeType == "ModifierDefinition") - return createModifierDefinition(_json); - if (nodeType == "ModifierInvocation") - return createModifierInvocation(_json); - if (nodeType == "EventDefinition") - return createEventDefinition(_json); - if (nodeType == "ElementaryTypeName") - return createElementaryTypeName(_json); - if (nodeType == "UserDefinedTypeName") - return createUserDefinedTypeName(_json); - if (nodeType == "FunctionTypeName") - return createFunctionTypeName(_json); - if (nodeType == "Mapping") - return createMapping(_json); - if (nodeType == "ArrayTypeName") - return createArrayTypeName(_json); - if (nodeType == "InlineAssembly") - return createInlineAssembly(_json); - if (nodeType == "Block") - return createBlock(_json, false); - if (nodeType == "UncheckedBlock") - return createBlock(_json, true); - if (nodeType == "PlaceholderStatement") - return createPlaceholderStatement(_json); - if (nodeType == "IfStatement") - return createIfStatement(_json); - if (nodeType == "TryCatchClause") - return createTryCatchClause(_json); - if (nodeType == "TryStatement") - return createTryStatement(_json); - if (nodeType == "WhileStatement") - return createWhileStatement(_json, false); - if (nodeType == "DoWhileStatement") - return createWhileStatement(_json, true); - if (nodeType == "ForStatement") - return createForStatement(_json); - if (nodeType == "Continue") - return createContinue(_json); - if (nodeType == "Break") - return createBreak(_json); - if (nodeType == "Return") - return createReturn(_json); - if (nodeType == "EmitStatement") - return createEmitStatement(_json); - if (nodeType == "Throw") - return createThrow(_json); - if (nodeType == "VariableDeclarationStatement") - return createVariableDeclarationStatement(_json); - if (nodeType == "ExpressionStatement") - return createExpressionStatement(_json); - if (nodeType == "Conditional") - return createConditional(_json); - if (nodeType == "Assignment") - return createAssignment(_json); - if (nodeType == "TupleExpression") - return createTupleExpression(_json); - if (nodeType == "UnaryOperation") - return createUnaryOperation(_json); - if (nodeType == "BinaryOperation") - return createBinaryOperation(_json); - if (nodeType == "FunctionCall") - return createFunctionCall(_json); - if (nodeType == "FunctionCallOptions") - return createFunctionCallOptions(_json); - if (nodeType == "NewExpression") - return createNewExpression(_json); - if (nodeType == "MemberAccess") - return createMemberAccess(_json); - if (nodeType == "IndexAccess") - return createIndexAccess(_json); - if (nodeType == "IndexRangeAccess") - return createIndexRangeAccess(_json); - if (nodeType == "Identifier") - return createIdentifier(_json); - if (nodeType == "ElementaryTypeNameExpression") - return createElementaryTypeNameExpression(_json); - if (nodeType == "Literal") - return createLiteral(_json); - if (nodeType == "StructuredDocumentation") - return createDocumentation(_json); - else - astAssert(false, "Unknown type of ASTNode: " + nodeType); -} - -// ============ functions to instantiate the AST-Nodes from Json-Nodes ============== - -ASTPointer ASTJsonImporter::createSourceUnit(Json::Value const& _node, string const& _srcName) -{ - optional license; - if (_node.isMember("license") && !_node["license"].isNull()) - license = _node["license"].asString(); - - vector> nodes; - for (auto& child: member(_node, "nodes")) - nodes.emplace_back(convertJsonToASTNode(child)); - - ASTPointer tmp = createASTNode(_node, license, nodes); - tmp->annotation().path = _srcName; - return tmp; -} - -ASTPointer ASTJsonImporter::createPragmaDirective(Json::Value const& _node) -{ - vector tokens; - vector literals; - for (auto const& lit: member(_node, "literals")) - { - string l = lit.asString(); - literals.push_back(l); - tokens.push_back(scanSingleToken(l)); - } - return createASTNode(_node, tokens, literals); -} - -ASTPointer ASTJsonImporter::createImportDirective(Json::Value const& _node) -{ - ASTPointer unitAlias = memberAsASTString(_node, "unitAlias"); - ASTPointer path = memberAsASTString(_node, "file"); - ImportDirective::SymbolAliasList symbolAliases; - - for (auto& tuple: member(_node, "symbolAliases")) - { - astAssert(tuple["local"].isNull() || tuple["local"].isString(), "expected 'local' to be a string or null!"); - - symbolAliases.push_back({ - createIdentifier(tuple["foreign"]), - tuple["local"].isNull() ? nullptr : make_shared(tuple["local"].asString()), - createSourceLocation(tuple["foreign"])} - ); - } - ASTPointer tmp = createASTNode( - _node, - path, - unitAlias, - move(symbolAliases) - ); - - astAssert(_node["absolutePath"].isString(), "Expected 'absolutePath' to be a string!"); - - tmp->annotation().absolutePath = _node["absolutePath"].asString(); - return tmp; -} - -ASTPointer ASTJsonImporter::createContractDefinition(Json::Value const& _node) -{ - astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); - - std::vector> baseContracts; - - for (auto& base: _node["baseContracts"]) - baseContracts.push_back(createInheritanceSpecifier(base)); - - std::vector> subNodes; - - for (auto& subnode: _node["nodes"]) - subNodes.push_back(convertJsonToASTNode(subnode)); - - return createASTNode( - _node, - make_shared(_node["name"].asString()), - _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), - baseContracts, - subNodes, - contractKind(_node), - memberAsBool(_node, "abstract") - ); -} - -ASTPointer ASTJsonImporter::createIdentifierPath(Json::Value const& _node) -{ - astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); - - vector namePath; - vector strs; - string nameString = member(_node, "name").asString(); - boost::algorithm::split(strs, nameString, boost::is_any_of(".")); - astAssert(!strs.empty(), "Expected at least one element in IdentifierPath."); - for (string s: strs) - { - astAssert(!s.empty(), "Expected non-empty string for IdentifierPath element."); - namePath.emplace_back(s); - } - return createASTNode(_node, namePath); -} - -ASTPointer ASTJsonImporter::createInheritanceSpecifier(Json::Value const& _node) -{ - std::vector> arguments; - for (auto& arg: member(_node, "arguments")) - arguments.push_back(convertJsonToASTNode(arg)); - return createASTNode( - _node, - createIdentifierPath(member(_node, "baseName")), - member(_node, "arguments").isNull() ? nullptr : make_unique>>(arguments) - ); -} - -ASTPointer ASTJsonImporter::createUsingForDirective(Json::Value const& _node) -{ - return createASTNode( - _node, - createIdentifierPath(member(_node, "libraryName")), - _node["typeName"].isNull() ? nullptr : convertJsonToASTNode(_node["typeName"]) - ); -} - -ASTPointer ASTJsonImporter::createStructDefinition(Json::Value const& _node) -{ - std::vector> members; - for (auto& member: _node["members"]) - members.push_back(createVariableDeclaration(member)); - return createASTNode( - _node, - memberAsASTString(_node, "name"), - members - ); -} - -ASTPointer ASTJsonImporter::createEnumDefinition(Json::Value const& _node) -{ - std::vector> members; - for (auto& member: _node["members"]) - members.push_back(createEnumValue(member)); - return createASTNode( - _node, - memberAsASTString(_node, "name"), - members - ); -} - -ASTPointer ASTJsonImporter::createEnumValue(Json::Value const& _node) -{ - return createASTNode( - _node, - memberAsASTString(_node, "name") - ); -} - -ASTPointer ASTJsonImporter::createParameterList(Json::Value const& _node) -{ - std::vector> parameters; - for (auto& param: _node["parameters"]) - parameters.push_back(createVariableDeclaration(param)); - return createASTNode( - _node, - parameters - ); -} - -ASTPointer ASTJsonImporter::createOverrideSpecifier(Json::Value const& _node) -{ - std::vector> overrides; - - for (auto& param: _node["overrides"]) - overrides.push_back(createIdentifierPath(param)); - - return createASTNode( - _node, - overrides - ); -} - -ASTPointer ASTJsonImporter::createFunctionDefinition(Json::Value const& _node) -{ - astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!"); - - Token kind; - bool freeFunction = false; - string kindStr = member(_node, "kind").asString(); - - if (kindStr == "constructor") - kind = Token::Constructor; - else if (kindStr == "function") - kind = Token::Function; - else if (kindStr == "fallback") - kind = Token::Fallback; - else if (kindStr == "receive") - kind = Token::Receive; - else if (kindStr == "freeFunction") - { - kind = Token::Function; - freeFunction = true; - } - else - astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]"); - - std::vector> modifiers; - for (auto& mod: member(_node, "modifiers")) - modifiers.push_back(createModifierInvocation(mod)); - - Visibility vis = Visibility::Default; - if (!freeFunction) - vis = visibility(_node); - return createASTNode( - _node, - memberAsASTString(_node, "name"), - vis, - stateMutability(_node), - executionBehavior(_node), - freeFunction, - kind, - memberAsBool(_node, "virtual"), - _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), - _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), - createParameterList(member(_node, "parameters")), - modifiers, - createParameterList(member(_node, "returnParameters")), - memberAsBool(_node, "implemented") ? createBlock(member(_node, "body"), false) : nullptr - ); -} - -ASTPointer ASTJsonImporter::createVariableDeclaration(Json::Value const& _node) -{ - astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); - - VariableDeclaration::Mutability mutability{}; - astAssert(member(_node, "mutability").isString(), "'mutability' expected to be string."); - string const mutabilityStr = member(_node, "mutability").asString(); - if (mutabilityStr == "constant") - { - mutability = VariableDeclaration::Mutability::Constant; - astAssert(memberAsBool(_node, "constant"), ""); - } - else - { - astAssert(!memberAsBool(_node, "constant"), ""); - if (mutabilityStr == "mutable") - mutability = VariableDeclaration::Mutability::Mutable; - else if (mutabilityStr == "immutable") - mutability = VariableDeclaration::Mutability::Immutable; - else - astAssert(false, ""); - } - - return createASTNode( - _node, - nullOrCast(member(_node, "typeName")), - make_shared(member(_node, "name").asString()), - nullOrCast(member(_node, "value")), - visibility(_node), - _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), - _node.isMember("indexed") ? memberAsBool(_node, "indexed") : false, - mutability, - _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), - location(_node) - ); -} - -ASTPointer ASTJsonImporter::createModifierDefinition(Json::Value const& _node) -{ - return createASTNode( - _node, - memberAsASTString(_node, "name"), - _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), - createParameterList(member(_node, "parameters")), - memberAsBool(_node, "virtual"), - _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), - _node["body"].isNull() ? nullptr: createBlock(member(_node, "body"), false) - ); -} - -ASTPointer ASTJsonImporter::createModifierInvocation(Json::Value const& _node) -{ - std::vector> arguments; - for (auto& arg: member(_node, "arguments")) - arguments.push_back(convertJsonToASTNode(arg)); - return createASTNode( - _node, - createIdentifierPath(member(_node, "modifierName")), - member(_node, "arguments").isNull() ? nullptr : make_unique>>(arguments) - ); -} - -ASTPointer ASTJsonImporter::createEventDefinition(Json::Value const& _node) -{ - return createASTNode( - _node, - memberAsASTString(_node, "name"), - _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), - createParameterList(member(_node, "parameters")), - memberAsBool(_node, "anonymous") - ); -} - -ASTPointer ASTJsonImporter::createElementaryTypeName(Json::Value const& _node) -{ - unsigned short firstNum; - unsigned short secondNum; - - astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); - - string name = member(_node, "name").asString(); - Token token; - tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(name); - ElementaryTypeNameToken elem(token, firstNum, secondNum); - - std::optional mutability = {}; - if (_node.isMember("stateMutability")) - mutability = stateMutability(_node); - - return createASTNode(_node, elem, mutability); -} - -ASTPointer ASTJsonImporter::createUserDefinedTypeName(Json::Value const& _node) -{ - return createASTNode( - _node, - createIdentifierPath(member(_node, "pathNode")) - ); -} - -ASTPointer ASTJsonImporter::createFunctionTypeName(Json::Value const& _node) -{ - return createASTNode( - _node, - createParameterList(member(_node, "parameterTypes")), - createParameterList(member(_node, "returnParameterTypes")), - visibility(_node), - stateMutability(_node) - ); -} - -ASTPointer ASTJsonImporter::createMapping(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "keyType")), - convertJsonToASTNode(member(_node, "valueType")) - ); -} - -ASTPointer ASTJsonImporter::createArrayTypeName(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "baseType")), - nullOrCast(member(_node, "length")) - ); -} - -ASTPointer ASTJsonImporter::createInlineAssembly(Json::Value const& _node) -{ - astAssert(_node["evmVersion"].isString(), "Expected evmVersion to be a string!"); - auto evmVersion = langutil::EVMVersion::fromString(_node["evmVersion"].asString()); - astAssert(evmVersion.has_value(), "Invalid EVM version!"); - astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!"); - - yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value()); - shared_ptr operations = make_shared(yul::AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST"))); - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - dialect, - operations - ); -} - -ASTPointer ASTJsonImporter::createBlock(Json::Value const& _node, bool _unchecked) -{ - std::vector> statements; - for (auto& stat: member(_node, "statements")) - statements.push_back(convertJsonToASTNode(stat)); - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - _unchecked, - statements - ); -} - -ASTPointer ASTJsonImporter::createPlaceholderStatement(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation") - ); -} - -ASTPointer ASTJsonImporter::createIfStatement(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - convertJsonToASTNode(member(_node, "condition")), - convertJsonToASTNode(member(_node, "trueBody")), - nullOrCast(member(_node, "falseBody")) - ); -} - -ASTPointer ASTJsonImporter::createTryCatchClause(Json::Value const& _node) -{ - return createASTNode( - _node, - memberAsASTString(_node, "errorName"), - nullOrCast(member(_node, "parameters")), - convertJsonToASTNode(member(_node, "block")) - ); -} - -ASTPointer ASTJsonImporter::createTryStatement(Json::Value const& _node) -{ - vector> clauses; - - for (auto& param: _node["clauses"]) - clauses.emplace_back(createTryCatchClause(param)); - - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - convertJsonToASTNode(member(_node, "externalCall")), - clauses - ); -} - -ASTPointer ASTJsonImporter::createWhileStatement(Json::Value const& _node, bool _isDoWhile=false) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - convertJsonToASTNode(member(_node, "condition")), - convertJsonToASTNode(member(_node, "body")), - _isDoWhile - ); -} - -ASTPointer ASTJsonImporter::createForStatement(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - nullOrCast(member(_node, "initializationExpression")), - nullOrCast(member(_node, "condition")), - nullOrCast(member(_node, "loopExpression")), - convertJsonToASTNode(member(_node, "body")) - ); -} - -ASTPointer ASTJsonImporter::createContinue(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation") - ); -} - -ASTPointer ASTJsonImporter::createBreak(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation") - ); -} - -ASTPointer ASTJsonImporter::createReturn(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - nullOrCast(member(_node, "expression")) - ); -} - -ASTPointer ASTJsonImporter::createThrow(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation") - ); -} - -ASTPointer ASTJsonImporter::createEmitStatement(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - createFunctionCall(member(_node, "eventCall")) - ); -} - -ASTPointer ASTJsonImporter::createVariableDeclarationStatement(Json::Value const& _node) -{ - std::vector> variables; - for (auto& var: member(_node, "declarations")) - variables.push_back(var.isNull() ? nullptr : createVariableDeclaration(var)); //unnamed components are empty pointers - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - variables, - nullOrCast(member(_node, "initialValue")) - ); -} - -ASTPointer ASTJsonImporter::createExpressionStatement(Json::Value const& _node) -{ - return createASTNode( - _node, - nullOrASTString(_node, "documentation"), - convertJsonToASTNode(member(_node, "expression")) - ); -} - -ASTPointer ASTJsonImporter::createConditional(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "condition")), - convertJsonToASTNode(member(_node, "trueExpression")), - convertJsonToASTNode(member(_node, "falseExpression")) - ); -} - -ASTPointer ASTJsonImporter::createAssignment(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "leftHandSide")), - scanSingleToken(member(_node, "operator")), - convertJsonToASTNode(member(_node, "rightHandSide")) - ); -} - -ASTPointer ASTJsonImporter::createTupleExpression(Json::Value const& _node) -{ - std::vector> components; - for (auto& comp: member(_node, "components")) - components.push_back(nullOrCast(comp)); - return createASTNode( - _node, - components, - memberAsBool(_node, "isInlineArray") - ); -} - -ASTPointer ASTJsonImporter::createUnaryOperation(Json::Value const& _node) -{ - return createASTNode( - _node, - scanSingleToken(member(_node, "operator")), - convertJsonToASTNode(member(_node, "subExpression")), - memberAsBool(_node, "prefix") - ); -} - -ASTPointer ASTJsonImporter::createBinaryOperation(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "leftExpression")), - scanSingleToken(member(_node, "operator")), - convertJsonToASTNode(member(_node, "rightExpression")) - ); -} - -ASTPointer ASTJsonImporter::createFunctionCall(Json::Value const& _node) -{ - std::vector> arguments; - for (auto& arg: member(_node, "arguments")) - arguments.push_back(convertJsonToASTNode(arg)); - std::vector> names; - for (auto& name: member(_node, "names")) - { - astAssert(name.isString(), "Expected 'names' members to be strings!"); - names.push_back(make_shared(name.asString())); - } - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "expression")), - arguments, - names - ); -} - -ASTPointer ASTJsonImporter::createFunctionCallOptions(Json::Value const& _node) -{ - std::vector> options; - for (auto& option: member(_node, "options")) - options.push_back(convertJsonToASTNode(option)); - std::vector> names; - for (auto& name: member(_node, "names")) - { - astAssert(name.isString(), "Expected 'names' members to be strings!"); - names.push_back(make_shared(name.asString())); - } - - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "expression")), - options, - names - ); -} - -ASTPointer ASTJsonImporter::createNewExpression(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "typeName")) - ); -} - -ASTPointer ASTJsonImporter::createMemberAccess(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "expression")), - memberAsASTString(_node, "memberName") - ); -} - -ASTPointer ASTJsonImporter::createIndexAccess(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "baseExpression")), - nullOrCast(member(_node, "indexExpression")) - ); -} - -ASTPointer ASTJsonImporter::createIndexRangeAccess(Json::Value const& _node) -{ - return createASTNode( - _node, - convertJsonToASTNode(member(_node, "baseExpression")), - nullOrCast(member(_node, "startExpression")), - nullOrCast(member(_node, "endExpression")) - ); -} - -ASTPointer ASTJsonImporter::createIdentifier(Json::Value const& _node) -{ - return createASTNode(_node, memberAsASTString(_node, "name")); -} - -ASTPointer ASTJsonImporter::createElementaryTypeNameExpression(Json::Value const& _node) -{ - return createASTNode( - _node, - createElementaryTypeName(member(_node, "typeName")) - ); -} - -ASTPointer ASTJsonImporter::createLiteral(Json::Value const& _node) -{ - static string const valStr = "value"; - static string const hexValStr = "hexValue"; - - astAssert(member(_node, valStr).isString() || member(_node, hexValStr).isString(), "Literal-value is unset."); - - ASTPointer value = _node.isMember(hexValStr) ? - make_shared(util::asString(util::fromHex(_node[hexValStr].asString()))) : - make_shared(_node[valStr].asString()); - - return createASTNode( - _node, - literalTokenKind(_node), - value, - member(_node, "subdenomination").isNull() ? Literal::SubDenomination::None : subdenomination(_node) - ); -} - -ASTPointer ASTJsonImporter::createDocumentation(Json::Value const& _node) -{ - static string const textString = "text"; - - astAssert(member(_node, textString).isString(), "'text' must be a string"); - - return createASTNode( - _node, - make_shared(_node[textString].asString()) - ); -} - -// ===== helper functions ========== - -Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name) -{ - if (!_node.isMember(_name)) - return Json::nullValue; - return _node[_name]; -} - -Token ASTJsonImporter::scanSingleToken(Json::Value const& _node) -{ - langutil::Scanner scanner{langutil::CharStream(_node.asString(), "")}; - astAssert(scanner.peekNextToken() == Token::EOS, "Token string is too long."); - return scanner.currentToken(); -} - -ASTPointer ASTJsonImporter::nullOrASTString(Json::Value const& _json, string const& _name) -{ - return _json[_name].isString() ? memberAsASTString(_json, _name) : nullptr; -} - -ASTPointer ASTJsonImporter::memberAsASTString(Json::Value const& _node, string const& _name) -{ - Json::Value value = member(_node, _name); - astAssert(value.isString(), "field " + _name + " must be of type string."); - return make_shared(_node[_name].asString()); -} - -bool ASTJsonImporter::memberAsBool(Json::Value const& _node, string const& _name) -{ - Json::Value value = member(_node, _name); - astAssert(value.isBool(), "field " + _name + " must be of type boolean."); - return _node[_name].asBool(); -} - - -// =========== JSON to definition helpers ======================= - -ContractKind ASTJsonImporter::contractKind(Json::Value const& _node) -{ - ContractKind kind; - astAssert(!member(_node, "contractKind").isNull(), "'Contract-kind' can not be null."); - if (_node["contractKind"].asString() == "interface") - kind = ContractKind::Interface; - else if (_node["contractKind"].asString() == "contract") - kind = ContractKind::Contract; - else if (_node["contractKind"].asString() == "library") - kind = ContractKind::Library; - else - astAssert(false, "Unknown ContractKind"); - return kind; -} - -Token ASTJsonImporter::literalTokenKind(Json::Value const& _node) -{ - astAssert(member(_node, "kind").isString(), "Token-'kind' expected to be a string."); - Token tok; - if (_node["kind"].asString() == "number") - tok = Token::Number; - else if (_node["kind"].asString() == "string") - tok = Token::StringLiteral; - else if (_node["kind"].asString() == "unicodeString") - tok = Token::UnicodeStringLiteral; - else if (_node["kind"].asString() == "hexString") - tok = Token::HexStringLiteral; - else if (_node["kind"].asString() == "bool") - tok = (member(_node, "value").asString() == "true") ? Token::TrueLiteral : Token::FalseLiteral; - else - astAssert(false, "Unknown kind of literalString"); - return tok; -} - -Visibility ASTJsonImporter::visibility(Json::Value const& _node) -{ - Json::Value visibility = member(_node, "visibility"); - astAssert(visibility.isString(), "'visibility' expected to be a string."); - - string const visibilityStr = visibility.asString(); - - if (visibilityStr == "default") - return Visibility::Default; - else if (visibilityStr == "private") - return Visibility::Private; - else if ( visibilityStr == "internal") - return Visibility::Internal; - else if (visibilityStr == "public") - return Visibility::Public; - else if (visibilityStr == "external") - return Visibility::External; - else - astAssert(false, "Unknown visibility declaration"); -} - -VariableDeclaration::Location ASTJsonImporter::location(Json::Value const& _node) -{ - Json::Value storageLoc = member(_node, "storageLocation"); - astAssert(storageLoc.isString(), "'storageLocation' expected to be a string."); - - string const storageLocStr = storageLoc.asString(); - - if (storageLocStr == "default") - return VariableDeclaration::Location::Unspecified; - else if (storageLocStr == "storage") - return VariableDeclaration::Location::Storage; - else if (storageLocStr == "memory") - return VariableDeclaration::Location::Memory; - else if (storageLocStr == "calldata") - return VariableDeclaration::Location::CallData; - else - astAssert(false, "Unknown location declaration"); -} - -Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _node) -{ - Json::Value subDen = member(_node, "subdenomination"); - - if (subDen.isNull()) - return Literal::SubDenomination::None; - - astAssert(subDen.isString(), "'subDenomination' expected to be string."); - - string const subDenStr = subDen.asString(); - - if (subDenStr == "wei") - return Literal::SubDenomination::Wei; - else if (subDenStr == "gwei") - return Literal::SubDenomination::Gwei; - else if (subDenStr == "ether") - return Literal::SubDenomination::Ether; - else if (subDenStr == "seconds") - return Literal::SubDenomination::Second; - else if (subDenStr == "minutes") - return Literal::SubDenomination::Minute; - else if (subDenStr == "hours") - return Literal::SubDenomination::Hour; - else if (subDenStr == "days") - return Literal::SubDenomination::Day; - else if (subDenStr == "weeks") - return Literal::SubDenomination::Week; - else if (subDenStr == "years") - return Literal::SubDenomination::Year; - else - astAssert(false, "Unknown subdenomination"); -} - -StateMutability ASTJsonImporter::stateMutability(Json::Value const& _node) -{ - astAssert(member(_node, "stateMutability").isString(), "StateMutability' expected to be string."); - string const mutabilityStr = member(_node, "stateMutability").asString(); - - if (mutabilityStr == "pure") - return StateMutability::Pure; - else if (mutabilityStr == "view") - return StateMutability::View; - else if (mutabilityStr == "nonpayable") - return StateMutability::NonPayable; - else if (mutabilityStr == "payable") - return StateMutability::Payable; - else - astAssert(false, "Unknown stateMutability"); -} - -// Solidity++: parse execution behavior -ExecutionBehavior ASTJsonImporter::executionBehavior(Json::Value const& _node) -{ - astAssert(member(_node, "executionBehavior").isString(), "ExecutionBehavior' expected to be string."); - string const str = member(_node, "executionBehavior").asString(); - - if (str == "sync") - return ExecutionBehavior::Sync; - else if (str == "async") - return ExecutionBehavior::Async; - else - astAssert(false, "Unknown executionBehavior"); -} - -} diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h deleted file mode 100644 index 2943021..0000000 --- a/libsolidity/ast/ASTJsonImporter.h +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -/** - * @author julius - * @date 2019 - * Converts the AST from JSON format to ASTNode - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace solidity::frontend -{ - -/** - * Component that imports an AST from json format to the internal format - */ -class ASTJsonImporter -{ -public: - ASTJsonImporter(langutil::EVMVersion _evmVersion) - :m_evmVersion(_evmVersion) - {} - - /// Converts the AST from JSON-format to ASTPointer - /// @a _sourceList used to provide source names for the ASTs - /// @returns map of sourcenames to their respective ASTs - std::map> jsonToSourceUnit(std::map const& _sourceList); - -private: - - // =========== general creation functions ============== - - /// Sets the source location and nodeID - /// @returns the ASTNode Object class of the respective JSON node, - template - ASTPointer createASTNode(Json::Value const& _node, Args&&... _args); - /// @returns the sourceLocation-object created from the string in the JSON node - langutil::SourceLocation const createSourceLocation(Json::Value const& _node); - /// Creates an ASTNode for a given JSON-ast of unknown type - /// @returns Pointer to a new created ASTNode - ASTPointer convertJsonToASTNode(Json::Value const& _ast); - /// @returns a pointer to the more specific subclass of ASTNode - /// as indicated by the nodeType field of the json - template - ASTPointer convertJsonToASTNode(Json::Value const& _node); - - - /// \defgroup nodeCreators JSON to AST-Nodes - ///@{ - ASTPointer createSourceUnit(Json::Value const& _node, std::string const& _srcName); - ASTPointer createPragmaDirective(Json::Value const& _node); - ASTPointer createImportDirective(Json::Value const& _node); - ASTPointer createContractDefinition(Json::Value const& _node); - ASTPointer createIdentifierPath(Json::Value const& _node); - ASTPointer createInheritanceSpecifier(Json::Value const& _node); - ASTPointer createUsingForDirective(Json::Value const& _node); - ASTPointer createStructDefinition(Json::Value const& _node); - ASTPointer createEnumDefinition(Json::Value const& _node); - ASTPointer createEnumValue(Json::Value const& _node); - ASTPointer createParameterList(Json::Value const& _node); - ASTPointer createOverrideSpecifier(Json::Value const& _node); - ASTPointer createFunctionDefinition(Json::Value const& _node); - ASTPointer createVariableDeclaration(Json::Value const& _node); - ASTPointer createModifierDefinition(Json::Value const& _node); - ASTPointer createModifierInvocation(Json::Value const& _node); - ASTPointer createEventDefinition(Json::Value const& _node); - ASTPointer createElementaryTypeName(Json::Value const& _node); - ASTPointer createUserDefinedTypeName(Json::Value const& _node); - ASTPointer createFunctionTypeName(Json::Value const& _node); - ASTPointer createMapping(Json::Value const& _node); - ASTPointer createArrayTypeName(Json::Value const& _node); - ASTPointer createInlineAssembly(Json::Value const& _node); - ASTPointer createBlock(Json::Value const& _node, bool _unchecked); - ASTPointer createPlaceholderStatement(Json::Value const& _node); - ASTPointer createIfStatement(Json::Value const& _node); - ASTPointer createTryCatchClause(Json::Value const& _node); - ASTPointer createTryStatement(Json::Value const& _node); - ASTPointer createWhileStatement(Json::Value const& _node, bool _isDoWhile); - ASTPointer createForStatement(Json::Value const& _node); - ASTPointer createContinue(Json::Value const& _node); - ASTPointer createBreak(Json::Value const& _node); - ASTPointer createReturn(Json::Value const& _node); - ASTPointer createThrow(Json::Value const& _node); - ASTPointer createEmitStatement(Json::Value const& _node); - ASTPointer createVariableDeclarationStatement(Json::Value const& _node); - ASTPointer createExpressionStatement(Json::Value const& _node); - ASTPointer createConditional(Json::Value const& _node); - ASTPointer createAssignment(Json::Value const& _node); - ASTPointer createTupleExpression(Json::Value const& _node); - ASTPointer createUnaryOperation(Json::Value const& _node); - ASTPointer createBinaryOperation(Json::Value const& _node); - ASTPointer createFunctionCall(Json::Value const& _node); - ASTPointer createFunctionCallOptions(Json::Value const& _node); - ASTPointer createNewExpression(Json::Value const& _node); - ASTPointer createMemberAccess(Json::Value const& _node); - ASTPointer createIndexAccess(Json::Value const& _node); - ASTPointer createIndexRangeAccess(Json::Value const& _node); - ASTPointer createIdentifier(Json::Value const& _node); - ASTPointer createElementaryTypeNameExpression(Json::Value const& _node); - ASTPointer createLiteral(Json::Value const& _node); - ASTPointer createDocumentation(Json::Value const& _node); - ///@} - - // =============== general helper functions =================== - /// @returns the member of a given JSON object, throws if member does not exist - Json::Value member(Json::Value const& _node, std::string const& _name); - /// @returns the appropriate TokenObject used in parsed Strings (pragma directive or operator) - Token scanSingleToken(Json::Value const& _node); - template - ///@returns nullptr or an ASTPointer cast to a specific Class - ASTPointer nullOrCast(Json::Value const& _json); - /// @returns nullptr or ASTString, given an JSON string or an empty field - ASTPointer nullOrASTString(Json::Value const& _json, std::string const& _name); - - // ============== JSON to definition helpers =============== - /// \defgroup typeHelpers Json to ast-datatype helpers - /// {@ - ASTPointer memberAsASTString(Json::Value const& _node, std::string const& _name); - bool memberAsBool(Json::Value const& _node, std::string const& _name); - Visibility visibility(Json::Value const& _node); - StateMutability stateMutability(Json::Value const& _node); - ExecutionBehavior executionBehavior(Json::Value const& _node); // Solidity++ - VariableDeclaration::Location location(Json::Value const& _node); - ContractKind contractKind(Json::Value const& _node); - Token literalTokenKind(Json::Value const& _node); - Literal::SubDenomination subdenomination(Json::Value const& _node); - ///@} - - // =========== member variables =============== - /// Stores filepath as sourcenames to AST in JSON format - std::map m_sourceList; - /// list of filepaths (used as sourcenames) - std::vector> m_sourceLocations; - /// filepath to AST - std::map> m_sourceUnits; - std::string m_currentSourceName; - /// IDs already used by the nodes - std::set m_usedIDs; - /// Configured EVM version - langutil::EVMVersion m_evmVersion; -}; - -} diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index d1f351b..037d313 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -7,7 +7,7 @@ #pragma once -#include +#include #include #include @@ -91,8 +91,6 @@ class ASTVisitor virtual bool visit(Literal& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); } // Solidity++: - virtual bool visit(MessageDefinition& _node) { return visitNode(_node); } - virtual bool visit(SendStatement& _node) { return visitNode(_node); } virtual bool visit(AwaitExpression& _node) { return visitNode(_node); } virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } @@ -148,8 +146,6 @@ class ASTVisitor virtual void endVisit(Literal& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); } // Solidity++: - virtual void endVisit(MessageDefinition& _node) { endVisitNode(_node); } - virtual void endVisit(SendStatement& _node) { endVisitNode(_node); } virtual void endVisit(AwaitExpression& _node) { endVisitNode(_node); } protected: @@ -227,11 +223,8 @@ class ASTConstVisitor virtual bool visit(Literal const& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); } // Solidity++: - virtual bool visit(MessageDefinition const& _node) { return visitNode(_node); } - virtual bool visit(SendStatement const& _node) { return visitNode(_node); } virtual bool visit(AwaitExpression const& _node) { return visitNode(_node); } - virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); } @@ -285,8 +278,6 @@ class ASTConstVisitor virtual void endVisit(Literal const& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); } // Solidity++: - virtual void endVisit(MessageDefinition const& _node) { endVisitNode(_node); } - virtual void endVisit(SendStatement const& _node) { endVisitNode(_node); } virtual void endVisit(AwaitExpression const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h deleted file mode 100644 index f7a3650..0000000 --- a/libsolidity/ast/AST_accept.h +++ /dev/null @@ -1,1015 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -/** - * @author Christian - * @date 2014 - * Implementation of the accept functions of AST nodes, included by AST.cpp to not clutter that - * file with these mechanical implementations. - */ - -#pragma once - -#include -#include - -namespace solidity::frontend -{ - -void SourceUnit::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_nodes, _visitor); - _visitor.endVisit(*this); -} - -void SourceUnit::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_nodes, _visitor); - _visitor.endVisit(*this); -} - -void PragmaDirective::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void PragmaDirective::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ImportDirective::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ImportDirective::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void StructuredDocumentation::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void StructuredDocumentation::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ContractDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - listAccept(m_baseContracts, _visitor); - listAccept(m_subNodes, _visitor); - } - _visitor.endVisit(*this); -} - -void ContractDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - listAccept(m_baseContracts, _visitor); - listAccept(m_subNodes, _visitor); - } - _visitor.endVisit(*this); -} - -void IdentifierPath::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void IdentifierPath::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void InheritanceSpecifier::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_baseName->accept(_visitor); - if (m_arguments) - listAccept(*m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_baseName->accept(_visitor); - if (m_arguments) - listAccept(*m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void EnumDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void EnumDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void EnumValue::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void EnumValue::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void UsingForDirective::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_libraryName->accept(_visitor); - if (m_typeName) - m_typeName->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void UsingForDirective::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_libraryName->accept(_visitor); - if (m_typeName) - m_typeName->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void StructDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void StructDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_members, _visitor); - _visitor.endVisit(*this); -} - -void ParameterList::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_parameters, _visitor); - _visitor.endVisit(*this); -} - -void ParameterList::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_parameters, _visitor); - _visitor.endVisit(*this); -} - -void OverrideSpecifier::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_overrides, _visitor); - _visitor.endVisit(*this); -} - -void OverrideSpecifier::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_overrides, _visitor); - _visitor.endVisit(*this); -} - -void FunctionDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - if (m_overrides) - m_overrides->accept(_visitor); - m_parameters->accept(_visitor); - if (m_returnParameters) - m_returnParameters->accept(_visitor); - listAccept(m_functionModifiers, _visitor); - if (m_body) - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void FunctionDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - if (m_overrides) - m_overrides->accept(_visitor); - m_parameters->accept(_visitor); - if (m_returnParameters) - m_returnParameters->accept(_visitor); - listAccept(m_functionModifiers, _visitor); - if (m_body) - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void VariableDeclaration::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_typeName) - m_typeName->accept(_visitor); - if (m_overrides) - m_overrides->accept(_visitor); - if (m_value) - m_value->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void VariableDeclaration::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_typeName) - m_typeName->accept(_visitor); - if (m_overrides) - m_overrides->accept(_visitor); - if (m_value) - m_value->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ModifierDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - m_parameters->accept(_visitor); - if (m_overrides) - m_overrides->accept(_visitor); - if (m_body) - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ModifierDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - m_parameters->accept(_visitor); - if (m_overrides) - m_overrides->accept(_visitor); - if (m_body) - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ModifierInvocation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_modifierName->accept(_visitor); - if (m_arguments) - listAccept(*m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void ModifierInvocation::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_modifierName->accept(_visitor); - if (m_arguments) - listAccept(*m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void EventDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - m_parameters->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void EventDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - m_parameters->accept(_visitor); - } - _visitor.endVisit(*this); -} - -// Solidity++: -void MessageDefinition::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - m_parameters->accept(_visitor); - } - _visitor.endVisit(*this); -} - -// Solidity++: -void MessageDefinition::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_documentation) - m_documentation->accept(_visitor); - m_parameters->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ElementaryTypeName::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void UserDefinedTypeName::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - this->pathNode().accept(_visitor); - _visitor.endVisit(*this); -} - -void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - this->pathNode().accept(_visitor); - _visitor.endVisit(*this); -} - -void FunctionTypeName::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_parameterTypes->accept(_visitor); - m_returnTypes->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void FunctionTypeName::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_parameterTypes->accept(_visitor); - m_returnTypes->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Mapping::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_keyType->accept(_visitor); - m_valueType->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Mapping::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_keyType->accept(_visitor); - m_valueType->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ArrayTypeName::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_baseType->accept(_visitor); - if (m_length) - m_length->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ArrayTypeName::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_baseType->accept(_visitor); - if (m_length) - m_length->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void InlineAssembly::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void InlineAssembly::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Block::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - listAccept(m_statements, _visitor); - _visitor.endVisit(*this); -} - -void Block::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - listAccept(m_statements, _visitor); - _visitor.endVisit(*this); -} - -void PlaceholderStatement::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void PlaceholderStatement::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void IfStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueBody->accept(_visitor); - if (m_falseBody) - m_falseBody->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void IfStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueBody->accept(_visitor); - if (m_falseBody) - m_falseBody->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void TryCatchClause::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_parameters) - m_parameters->accept(_visitor); - m_block->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void TryCatchClause::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_parameters) - m_parameters->accept(_visitor); - m_block->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void TryStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_externalCall->accept(_visitor); - listAccept(m_clauses, _visitor); - } - _visitor.endVisit(*this); -} - -void TryStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_externalCall->accept(_visitor); - listAccept(m_clauses, _visitor); - } - _visitor.endVisit(*this); -} - -void WhileStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void WhileStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ForStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - if (m_initExpression) - m_initExpression->accept(_visitor); - if (m_condExpression) - m_condExpression->accept(_visitor); - if (m_loopExpression) - m_loopExpression->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ForStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - if (m_initExpression) - m_initExpression->accept(_visitor); - if (m_condExpression) - m_condExpression->accept(_visitor); - if (m_loopExpression) - m_loopExpression->accept(_visitor); - m_body->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Continue::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Continue::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Break::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Break::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Return::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void Return::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void Throw::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Throw::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void EmitStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_eventCall->accept(_visitor); - _visitor.endVisit(*this); -} - -void EmitStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_eventCall->accept(_visitor); - _visitor.endVisit(*this); -} - -// Solidity++: -void SendStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_address->accept(_visitor); - m_expression->accept(_visitor); - } - _visitor.endVisit(*this); -} - -// Solidity++: -void SendStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_address->accept(_visitor); - m_expression->accept(_visitor); - } - _visitor.endVisit(*this); -} - -// Solidity++: -void AwaitExpression::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - } - _visitor.endVisit(*this); -} - -// Solidity++: -void AwaitExpression::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void ExpressionStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void ExpressionStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - if (m_expression) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void VariableDeclarationStatement::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - for (ASTPointer const& var: m_variables) - if (var) - var->accept(_visitor); - if (m_initialValue) - m_initialValue->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - for (ASTPointer const& var: m_variables) - if (var) - var->accept(_visitor); - if (m_initialValue) - m_initialValue->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Conditional::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueExpression->accept(_visitor); - m_falseExpression->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Conditional::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_condition->accept(_visitor); - m_trueExpression->accept(_visitor); - m_falseExpression->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Assignment::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_leftHandSide->accept(_visitor); - m_rightHandSide->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Assignment::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_leftHandSide->accept(_visitor); - m_rightHandSide->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void TupleExpression::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - for (auto const& component: m_components) - if (component) - component->accept(_visitor); - _visitor.endVisit(*this); -} - -void TupleExpression::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - for (auto const& component: m_components) - if (component) - component->accept(_visitor); - _visitor.endVisit(*this); -} - -void UnaryOperation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_subExpression->accept(_visitor); - _visitor.endVisit(*this); -} - -void UnaryOperation::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_subExpression->accept(_visitor); - _visitor.endVisit(*this); -} - -void BinaryOperation::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_left->accept(_visitor); - m_right->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void BinaryOperation::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_left->accept(_visitor); - m_right->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCall::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCall::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_arguments, _visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCallOptions::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_options, _visitor); - } - _visitor.endVisit(*this); -} - -void FunctionCallOptions::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_expression->accept(_visitor); - listAccept(m_options, _visitor); - } - _visitor.endVisit(*this); -} - -void NewExpression::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_typeName->accept(_visitor); - _visitor.endVisit(*this); -} - -void NewExpression::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_typeName->accept(_visitor); - _visitor.endVisit(*this); -} - -void MemberAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void MemberAccess::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - m_expression->accept(_visitor); - _visitor.endVisit(*this); -} - -void IndexAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - if (m_index) - m_index->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void IndexAccess::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - if (m_index) - m_index->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void IndexRangeAccess::accept(ASTVisitor& _visitor) -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - if (m_start) - m_start->accept(_visitor); - if (m_end) - m_end->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void IndexRangeAccess::accept(ASTConstVisitor& _visitor) const -{ - if (_visitor.visit(*this)) - { - m_base->accept(_visitor); - if (m_start) - m_start->accept(_visitor); - if (m_end) - m_end->accept(_visitor); - } - _visitor.endVisit(*this); -} - -void Identifier::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Identifier::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void ElementaryTypeNameExpression::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Literal::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -void Literal::accept(ASTConstVisitor& _visitor) const -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - -} diff --git a/libsolidity/ast/SolidityppAST.cpp b/libsolidity/ast/SolidityppAST.cpp new file mode 100644 index 0000000..4448b9a --- /dev/null +++ b/libsolidity/ast/SolidityppAST.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Charles + * @date 2021 + * Solidity++ Abstract Syntax Tree. + * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + */ + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; + +// Solidity++: +bool Literal::looksLikeViteAddress() const +{ + if (!boost::starts_with(value(), "vite_")) + return false; + + if (subDenomination() != SubDenomination::None) + return false; + + return int(value().length()) == 55; +} + +// Solidity++: +bool Literal::looksLikeViteTokenId() const +{ + if (!boost::starts_with(value(), "tti_")) + return false; + + if (subDenomination() != SubDenomination::None) + return false; + + return int(value().length()) == 28; +} + +// Solidity++: check vite address checksum +bool Literal::passesViteAddressChecksum() const +{ + return util::passesViteAddressChecksum(value()); +} + +// Solidity++: check vite address checksum + +bool Literal::passesViteTokenIdChecksum() const +{ + return util::passesViteTokenIdChecksum(value()); +} + +// Solidity++: get vite address hex value +ASTString Literal::getViteAddressHex() const +{ + return util::getViteAddressHex(value()); +} + +// Solidity++: get vite token id hex value +ASTString Literal::getViteTokenIdHex() const +{ + return util::getViteTokenIdHex(value()); +} diff --git a/libsolidity/ast/SolidityppAST.h b/libsolidity/ast/SolidityppAST.h new file mode 100644 index 0000000..bdd4d01 --- /dev/null +++ b/libsolidity/ast/SolidityppAST.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 +/** + * @author Charles + * @date 2021 + * Solidity++ Abstract Syntax Tree. + * Solidity++ is modified from Solidity under the terms of the GNU General Public License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace solidity::frontend +{ + +class ASTVisitor; +class ASTConstVisitor; + +/** + * Solidity++: + * The await expression is used to wait for an asynchronous message call to return: await foo(arg1, ..., argn) + */ +class AwaitExpression: public Expression +{ +public: + AwaitExpression( + int64_t _id, + SourceLocation const& _location, + ASTPointer _expression + ): + Expression(_id, _location), m_expression(std::move(_expression)) {} + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + Expression const& expression() const { return *m_expression; } + +private: + ASTPointer m_expression; +}; + +} diff --git a/libsolidity/ast/SolidityppASTJsonConverter.cpp b/libsolidity/ast/SolidityppASTJsonConverter.cpp new file mode 100644 index 0000000..f9c39a8 --- /dev/null +++ b/libsolidity/ast/SolidityppASTJsonConverter.cpp @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0 +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::langutil; + +namespace solidity::frontend +{ + + SolidityppASTJsonConverter::SolidityppASTJsonConverter(CompilerStack::State _stackState, map _sourceIndices): + ASTJsonConverter(_stackState, _sourceIndices) +{ +} + +bool SolidityppASTJsonConverter::visit(FunctionCall const& _node) +{ + Json::Value names(Json::arrayValue); + for (auto const& name: _node.names()) + names.append(Json::Value(*name)); + std::vector> attributes = { + make_pair("expression", toJson(_node.expression())), + make_pair("names", std::move(names)), + make_pair("arguments", toJson(_node.arguments())), + make_pair("tryCall", _node.annotation().tryCall), + make_pair("async", _node.annotation().async) // Solidity++: synchrony of the function call + }; + + if (_node.annotation().kind.set()) + { + FunctionCallKind nodeKind = *_node.annotation().kind; + attributes.emplace_back("kind", functionCallKind(nodeKind)); + } + + appendExpressionAttributes(attributes, _node.annotation()); + setJsonNode(_node, "FunctionCall", std::move(attributes)); + return false; +} + +bool SolidityppASTJsonConverter::visit(AwaitExpression const& _node) +{ + std::vector> attributes = { + make_pair("expression", toJson(_node.expression())), + }; + setJsonNode(_node, "AwaitExpression", std::move(attributes)); + return false; +} +} diff --git a/libsolidity/ast/SolidityppASTJsonConverter.h b/libsolidity/ast/SolidityppASTJsonConverter.h new file mode 100644 index 0000000..90493eb --- /dev/null +++ b/libsolidity/ast/SolidityppASTJsonConverter.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ +struct SourceLocation; +} + +namespace solidity::frontend +{ + +class SolidityppASTJsonConverter: public ASTJsonConverter +{ +public: + explicit SolidityppASTJsonConverter( + CompilerStack::State _stackState, + std::map _sourceIndices = std::map() + ); + + using ASTJsonConverter::visit; + + bool visit(FunctionCall const& _node) override; + bool visit(AwaitExpression const& _node) override; +}; + +} diff --git a/libsolidity/ast/SolidityppAST_accept.h b/libsolidity/ast/SolidityppAST_accept.h new file mode 100644 index 0000000..bfad35c --- /dev/null +++ b/libsolidity/ast/SolidityppAST_accept.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +namespace solidity::frontend +{ + void AwaitExpression::accept(ASTVisitor& _visitor) + { + if (_visitor.visit(*this)) + { + m_expression->accept(_visitor); + } + _visitor.endVisit(*this); + } + + void AwaitExpression::accept(ASTConstVisitor& _visitor) const + { + if (_visitor.visit(*this)) + { + m_expression->accept(_visitor); + } + _visitor.endVisit(*this); + } +} diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index 9347c30..2ff285f 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -237,8 +237,7 @@ Type const* TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken const& solAssert(*_stateMutability == StateMutability::Payable, ""); return payableAddress(); } - // Solidity++: default address state mutability is payable - return payableAddress(); + return address(); } // Solidity++: convert tokenId type case Token::TokenId: @@ -434,12 +433,6 @@ FunctionType const* TypeProvider::function(EventDefinition const& _def) return createAndGet(_def); } -// Solidity++: -FunctionType const* TypeProvider::function(MessageDefinition const& _def) -{ - return createAndGet(_def); -} - FunctionType const* TypeProvider::function(FunctionTypeName const& _typeName) { return createAndGet(_typeName); @@ -467,12 +460,12 @@ FunctionType const* TypeProvider::function( FunctionType::Kind _kind, bool _arbitraryParameters, StateMutability _stateMutability, - ExecutionBehavior _executionBehavior, // Solidity++ Declaration const* _declaration, bool _gasSet, bool _valueSet, - bool _bound, - bool _saltSet + bool _tokenSet, + bool _saltSet, + bool _bound ) { return createAndGet( @@ -483,29 +476,15 @@ FunctionType const* TypeProvider::function( _kind, _arbitraryParameters, _stateMutability, - _executionBehavior, _declaration, _gasSet, _valueSet, - _bound, - _saltSet + _tokenSet, + _saltSet, + _bound // @FIXME: _saltSet <-> _bound ??? ); } -// Solidity++: -FunctionType const* TypeProvider::callbackFromFunctionCall( - FunctionType const* callType -) -{ - callType->returnParameterTypes(); - return createAndGet( - callType->returnParameterTypes(), - TypePointers{}, - callType->returnParameterNames(), - strings{} - ); -} - RationalNumberType const* TypeProvider::rationalNumber(rational const& _value, Type const* _compatibleBytesType) { return createAndGet(_value, _compatibleBytesType); diff --git a/libsolidity/ast/TypeProvider.h b/libsolidity/ast/TypeProvider.h index 3e0c8ca..7cc92d7 100644 --- a/libsolidity/ast/TypeProvider.h +++ b/libsolidity/ast/TypeProvider.h @@ -132,10 +132,6 @@ class TypeProvider /// @returns the function type of an event. static FunctionType const* function(EventDefinition const& _event); - /// Solidity++: - /// @returns the function type of a message. - static FunctionType const* function(MessageDefinition const& _message); - /// @returns the type of a function type name. static FunctionType const* function(FunctionTypeName const& _typeName); @@ -157,21 +153,14 @@ class TypeProvider FunctionType::Kind _kind = FunctionType::Kind::Internal, bool _arbitraryParameters = false, StateMutability _stateMutability = StateMutability::NonPayable, - ExecutionBehavior _executionBehavior = ExecutionBehavior::Sync, // Solidity++ Declaration const* _declaration = nullptr, bool _gasSet = false, bool _valueSet = false, - bool _bound = false, - bool _saltSet = false + bool _tokenSet = false, // Solidity++ + bool _saltSet = false, + bool _bound = false ); - /// Solidity++: get callback function type of an asynchronous function call - /// @param callType the asynchronous function call type - /// @returns function type of the callback of the function call - static FunctionType const* callbackFromFunctionCall( - FunctionType const* callType - ); - /// Auto-detect the proper type for a literal. @returns an empty pointer if the literal does /// not fit any type. static TypePointer forLiteral(Literal const& _literal); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 75cc34a..a955f29 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -415,6 +415,7 @@ u256 AddressType::literalValue(Literal const* _literal) const { solAssert(_literal, ""); solAssert(_literal->value().substr(0, 5) == "vite_", "Vite address must begin with vite_"); + return u256(_literal->getViteAddressHex()); } @@ -477,6 +478,7 @@ u256 ViteTokenIdType::literalValue(Literal const* _literal) const { solAssert(_literal, ""); solAssert(_literal->value().substr(0, 4) == "tti_", "Vite Token Id must begin with tti_"); + return u256(_literal->getViteTokenIdHex()); } @@ -879,40 +881,39 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal { return make_tuple(false, rational(0)); } - switch (_literal.subDenomination()) - { - case Literal::SubDenomination::None: - case Literal::SubDenomination::Wei: - case Literal::SubDenomination::Second: - break; - case Literal::SubDenomination::Gwei: - value *= bigint("1000000000"); - break; - case Literal::SubDenomination::Ether: - value *= bigint("1000000000000000000"); - break; - case Literal::SubDenomination::Minute: - value *= bigint("60"); - break; - case Literal::SubDenomination::Hour: - value *= bigint("3600"); - break; - case Literal::SubDenomination::Day: - value *= bigint("86400"); - break; - case Literal::SubDenomination::Week: - value *= bigint("604800"); - break; - case Literal::SubDenomination::Year: - value *= bigint("31536000"); - break; - case Literal::SubDenomination::Attov: - break; - case Literal::SubDenomination::Vite: - value *= bigint("1000000000000000000"); - break; - } + switch (_literal.subDenomination()) + { + case Literal::SubDenomination::None: + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + break; + case Literal::SubDenomination::Gwei: + value *= bigint("1000000000"); + break; + case Literal::SubDenomination::Ether: + value *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + value *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + value *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + value *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + value *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + value *= bigint("31536000"); + break; + case Literal::SubDenomination::Attov: + break; + case Literal::SubDenomination::Vite: + value *= bigint("1000000000000000000"); + } return make_tuple(true, value); } @@ -2629,7 +2630,6 @@ TypePointer TupleType::closestTemporaryType(Type const* _targetType) const FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind): m_kind(_kind), m_stateMutability(_function.stateMutability()), - m_executionBehavior(_function.executionBehavior()), // Solidity++ m_declaration(&_function) { solAssert( @@ -2664,7 +2664,6 @@ FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind): FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_kind(Kind::External), m_stateMutability(StateMutability::View), - m_executionBehavior(ExecutionBehavior::Async), // Solidity++ m_declaration(&_varDecl) { auto returnType = _varDecl.annotation().type; @@ -2748,35 +2747,11 @@ FunctionType::FunctionType(EventDefinition const& _event): ); } -// Solidity++: -FunctionType::FunctionType(MessageDefinition const& _message): - m_kind(Kind::SendMessage), - m_stateMutability(StateMutability::Payable), - m_executionBehavior(ExecutionBehavior::Async), - m_declaration(&_message) -{ - for (ASTPointer const& var: _message.parameters()) - { - m_parameterNames.push_back(var->name()); - m_parameterTypes.push_back(var->annotation().type); - } - - solAssert( - m_parameterNames.size() == m_parameterTypes.size(), - "Parameter names list must match parameter types list!" - ); - solAssert( - m_returnParameterNames.size() == m_returnParameterTypes.size(), - "Return parameter names list must match return parameter types list!" - ); -} - FunctionType::FunctionType(FunctionTypeName const& _typeName): m_parameterNames(_typeName.parameterTypes().size(), ""), m_returnParameterNames(_typeName.returnParameterTypes().size(), ""), m_kind(_typeName.visibility() == Visibility::External ? Kind::External : Kind::Internal), - m_stateMutability(_typeName.stateMutability()), - m_executionBehavior(ExecutionBehavior::Async) + m_stateMutability(_typeName.stateMutability()) { if (_typeName.isPayable()) solAssert(m_kind == Kind::External, "Internal payable function type used."); @@ -2915,7 +2890,6 @@ string FunctionType::richIdentifier() const case Kind::ABIDecode: id += "abidecode"; break; case Kind::MetaType: id += "metatype"; break; // Solidity++: - case Kind::SendMessage: id += "messagecall"; break; case Kind::BLAKE2B: id += "blake2b"; break; case Kind::PrevHash: id += "prevhash"; break; case Kind::Height: id += "height"; break; @@ -2932,6 +2906,8 @@ string FunctionType::richIdentifier() const id += "gas"; if (m_valueSet) id += "value"; + if (m_tokenSet) // Solidity++ + id += "tti"; if (m_saltSet) id += "salt"; if (bound()) @@ -3087,6 +3063,7 @@ bool FunctionType::nameable() const !m_arbitraryParameters && !m_gasSet && !m_valueSet && + !m_tokenSet && !m_saltSet; } @@ -3133,6 +3110,8 @@ vector> FunctionType::makeStackItems() const slots.emplace_back("gas", TypeProvider::uint256()); if (m_valueSet) slots.emplace_back("value", TypeProvider::uint256()); + if (m_tokenSet) + slots.emplace_back("tti", TypeProvider::viteTokenId()); // Solidity++ if (m_saltSet) slots.emplace_back("salt", TypeProvider::fixedBytes(32)); if (bound()) @@ -3173,7 +3152,6 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const m_kind, m_arbitraryParameters, m_stateMutability, - m_executionBehavior, m_declaration ); } @@ -3229,10 +3207,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const Kind::SetValue, false, StateMutability::Pure, - ExecutionBehavior::Sync, nullptr, m_gasSet, m_valueSet, + m_tokenSet, m_saltSet ) ); @@ -3248,10 +3226,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const Kind::SetGas, false, StateMutability::Pure, - ExecutionBehavior::Sync, nullptr, m_gasSet, m_valueSet, + m_tokenSet, m_saltSet ) ); @@ -3278,7 +3256,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const TypePointer FunctionType::encodingType() const { - if (m_gasSet || m_valueSet) + if (m_gasSet || m_valueSet || m_tokenSet) return nullptr; // Only external functions can be encoded, internal functions cannot leave code boundaries. if (m_kind == Kind::External) @@ -3297,7 +3275,7 @@ TypeResult FunctionType::interfaceType(bool /*_inLibrary*/) const TypePointer FunctionType::mobileType() const { - if (m_valueSet || m_gasSet || m_saltSet || m_bound) + if (m_valueSet || m_tokenSet || m_gasSet || m_saltSet || m_bound) return nullptr; // return function without parameter names @@ -3309,10 +3287,10 @@ TypePointer FunctionType::mobileType() const m_kind, m_arbitraryParameters, m_stateMutability, - m_executionBehavior, m_declaration, m_gasSet, m_valueSet, + m_tokenSet, m_bound, m_saltSet ); @@ -3400,16 +3378,12 @@ bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) con return false; //@todo this is ugly, but cannot be prevented right now - if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet || m_saltSet != _other.m_saltSet) + if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet || m_tokenSet != _other.m_tokenSet || m_saltSet != _other.m_saltSet) return false; if (bound() != _other.bound()) return false; - // Solidity++: - if(executionBehavior() != _other.executionBehavior()) - return false; - solAssert(!bound() || *selfType() == *_other.selfType(), ""); return true; @@ -3434,23 +3408,18 @@ bool FunctionType::isBareCall() const string FunctionType::externalSignature() const { -// solAssert(m_declaration != nullptr, "External signature of function needs declaration"); -// solAssert(!m_declaration->name().empty(), "Fallback function has no signature."); - if (!m_declaration || m_declaration->name().empty()) - return "_()"; - - switch (kind()) + solAssert(m_declaration != nullptr, "External signature of function needs declaration"); + solAssert(!m_declaration->name().empty(), "Fallback function has no signature."); + switch (kind()) { case Kind::Internal: case Kind::External: case Kind::DelegateCall: case Kind::Event: - case Kind::SendMessage: // Solidity++ case Kind::Declaration: break; default: - return "_" + richIdentifier(); -// solAssert(false, "Invalid function type for requesting external signature."); + solAssert(false, "Invalid function type for requesting external signature."); } // "inLibrary" is only relevant if this is not an event. @@ -3514,7 +3483,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) return pointers; } -TypePointer FunctionType::copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt) const +TypePointer FunctionType::copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt, bool _setToken) const { solAssert(m_kind != Kind::Declaration, ""); return TypeProvider::function( @@ -3525,10 +3494,10 @@ TypePointer FunctionType::copyAndSetCallOptions(bool _setGas, bool _setValue, bo m_kind, m_arbitraryParameters, m_stateMutability, - m_executionBehavior, m_declaration, m_gasSet || _setGas, m_valueSet || _setValue, + m_tokenSet || _setToken, m_saltSet || _setSalt, m_bound ); @@ -3541,6 +3510,7 @@ FunctionTypePointer FunctionType::asBoundFunction() const solAssert(fun && fun->libraryFunction(), ""); solAssert(!m_gasSet, ""); solAssert(!m_valueSet, ""); + solAssert(!m_tokenSet, ""); solAssert(!m_saltSet, ""); return TypeProvider::function( m_parameterTypes, @@ -3550,10 +3520,10 @@ FunctionTypePointer FunctionType::asBoundFunction() const m_kind, m_arbitraryParameters, m_stateMutability, - m_executionBehavior, m_declaration, m_gasSet, m_valueSet, + m_tokenSet, m_saltSet, true ); @@ -3595,10 +3565,10 @@ FunctionTypePointer FunctionType::asExternallyCallableFunction(bool _inLibrary) kind, m_arbitraryParameters, m_stateMutability, - m_executionBehavior, m_declaration, m_gasSet, m_valueSet, + m_tokenSet, m_saltSet, m_bound ); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 351f898..dd34ea2 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1140,7 +1140,7 @@ class FunctionType: public Type enum class Kind { Internal, ///< stack-call using plain JUMP - External, ///< external call using CALL + External, ///< external call using CALL or SYNCCALL DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage BareCall, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash @@ -1179,7 +1179,6 @@ class FunctionType: public Type /// Cannot be called. Declaration, /// Solidity++: - SendMessage, BLAKE2B, PrevHash, Height, @@ -1198,9 +1197,6 @@ class FunctionType: public Type /// Creates the function type of an event. explicit FunctionType(EventDefinition const& _event); - /// Solidity++: Creates the function type of a message. - explicit FunctionType(MessageDefinition const& _message); - /// Creates the type of a function type name. explicit FunctionType(FunctionTypeName const& _typeName); /// Function type constructor to be used for a plain type (not derived from a declaration). @@ -1231,10 +1227,10 @@ class FunctionType: public Type Kind _kind = Kind::Internal, bool _arbitraryParameters = false, StateMutability _stateMutability = StateMutability::NonPayable, - ExecutionBehavior _executionBehavior = ExecutionBehavior::Sync, // Solidity++ Declaration const* _declaration = nullptr, bool _gasSet = false, bool _valueSet = false, + bool _tokenSet = false, // Solidity++ bool _saltSet = false, bool _bound = false ): @@ -1244,10 +1240,10 @@ class FunctionType: public Type m_returnParameterNames(std::move(_returnParameterNames)), m_kind(_kind), m_stateMutability(_stateMutability), - m_executionBehavior(_executionBehavior), // Solidity++ m_arbitraryParameters(_arbitraryParameters), m_gasSet(_gasSet), m_valueSet(_valueSet), + m_tokenSet(_tokenSet), // Solidity++ m_bound(_bound), m_declaration(_declaration), m_saltSet(_saltSet) @@ -1330,7 +1326,6 @@ class FunctionType: public Type bool isBareCall() const; Kind const& kind() const { return m_kind; } StateMutability stateMutability() const { return m_stateMutability; } - ExecutionBehavior executionBehavior() const { return m_executionBehavior; } // Solidity++ /// @returns the external signature of this function type given the function name std::string externalSignature() const; /// @returns the external identifier of this function (the hash of the signature). @@ -1348,11 +1343,6 @@ class FunctionType: public Type /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; bool isPayable() const { return m_stateMutability == StateMutability::Payable; } - - /// @returns true if this function is asynchronous. - /// Solidity++ only. - bool isAsync() const { return m_executionBehavior == ExecutionBehavior::Async; } - /// @return A shared pointer of StructuredDocumentation. /// Can contain a nullptr in which case indicates absence of documentation. ASTPointer documentation() const; @@ -1382,12 +1372,13 @@ class FunctionType: public Type bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } + bool tokenSet() const { return m_tokenSet; } // Solidity++ bool saltSet() const { return m_saltSet; } bool bound() const { return m_bound; } /// @returns a copy of this type, where gas or value are set manually. This will never set one /// of the parameters to false. - TypePointer copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt) const; + TypePointer copyAndSetCallOptions(bool _setGas, bool _setValue, bool _setSalt, bool _setToken = false) const; // Solidity++ /// @returns a copy of this function type with the `bound` flag set to true. /// Should only be called on library functions. @@ -1411,11 +1402,11 @@ class FunctionType: public Type std::vector m_returnParameterNames; Kind const m_kind; StateMutability m_stateMutability = StateMutability::NonPayable; - ExecutionBehavior m_executionBehavior = ExecutionBehavior::Sync; // Solidity++ /// true if the function takes an arbitrary number of arguments of arbitrary types bool const m_arbitraryParameters = false; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack + bool const m_tokenSet = false; ///< Solidity++: true iff the Vite token Id to be sent on the stack /// true iff the function is called as arg1.fun(arg2, ..., argn). /// This is achieved through the "using for" directive. bool const m_bound = false; diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index c076add..0745a89 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -71,14 +71,7 @@ void Compiler::compileViteContract( debug("Compiling constructor"); m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); - // Solidity++: @todo enable optimising for awaitable code - debug("Optimising... disabled !!!"); - // m_context.optimise(m_optimiserSettings); - - // Compile offchain functions - debug("Compiling offchain functions"); - ContractCompiler offchainCompiler(nullptr, m_offchainContext, m_optimiserSettings, m_verbose); - offchainCompiler.compileOffchain(_contract, _otherCompilers); + m_context.optimise(m_optimiserSettings); solAssert(m_context.appendYulUtilityFunctionsRan(), "appendYulUtilityFunctions() was not called in compiler context."); solAssert(m_runtimeContext.appendYulUtilityFunctionsRan(), "appendYulUtilityFunctions() was not called in runtime compiler context."); diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 10984a4..4da5dc2 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -25,7 +25,6 @@ class Compiler m_optimiserSettings(std::move(_optimiserSettings)), m_runtimeContext(_evmVersion, _revertStrings, nullptr, _verbose), m_context(_evmVersion, _revertStrings, &m_runtimeContext, _verbose), - m_offchainContext(_evmVersion, _revertStrings), m_verbose(_verbose) { } @@ -47,19 +46,11 @@ class Compiler /// @returns Runtime assembly. evmasm::Assembly const& runtimeAssembly() const { return m_context.assembly().sub(m_runtimeSub); } - /// @returns Offchain assembly. - /// Solidity++: get offchain assembly - evmasm::Assembly const& offchainAssembly() const { return m_offchainContext.assembly(); } - /// @returns Entire assembly as a shared pointer to non-const. std::shared_ptr assemblyPtr() const { return m_context.assemblyPtr(); } /// @returns Runtime assembly as a shared pointer. std::shared_ptr runtimeAssemblyPtr() const; - /// @returns Offchain assembly as a shared pointer to non-const. - /// Solidity++: get offchain assembly pointer - std::shared_ptr offchainAssemblyPtr() const { return m_offchainContext.assemblyPtr(); } - std::string generatedYulUtilityCode() const { return m_context.generatedYulUtilityCode(); } std::string runtimeGeneratedYulUtilityCode() const { return m_runtimeContext.generatedYulUtilityCode(); } @@ -75,11 +66,8 @@ class Compiler CompilerContext m_runtimeContext; size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present. CompilerContext m_context; - // Solidity++: offchain compiler context - CompilerContext m_offchainContext; // Solidity++: bool m_verbose = false; - }; } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 307d41b..09209fc 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1353,7 +1353,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) errinfo_sourceLocation(_variable.location()) << util::errinfo_comment("Stack too deep, try removing local variables.") ); - m_context.appendDebugInfo("CompilerUtils::moveToStackVariable(" + _variable.toString() + ")"); + m_context.appendDebugInfo("CompilerUtils::moveToStackVariable(" + _variable.name() + ")"); for (unsigned i = 0; i < size; ++i) m_context << swapInstruction(stackPosition - size + 1) << Instruction::POP; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 3ec6995..adb3b0b 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -111,34 +111,6 @@ void ContractCompiler::compileContract( debug("Compiled."); } -// Solidity++: compile offchain functions -void ContractCompiler::compileOffchain( - ContractDefinition const& _contract, - map> const& _otherCompilers -) -{ - debug("Compiling offchain functions..."); - CompilerContext::LocationSetter locationSetter(m_context, _contract); - - if (_contract.isLibrary()) - // Check whether this is a call (true) or a delegatecall (false). - // This has to be the first code in the contract. - appendDelegatecallCheck(); - - initializeContext(_contract, _otherCompilers); - // This generates the dispatch function for externally visible functions - // and adds the function to the compilation queue. Additionally internal functions, - // which are referenced directly or indirectly will be added. - debug("Append offchain function selector"); - appendFunctionSelector(_contract, true); - - // Solidity++: We have to include copies of functions in both onchain and offchain context - debug("Append missing functions"); - appendMissingFunctions(); - m_context.appendYulUtilityFunctions(m_optimiserSettings); - debug("Offchain functions compiled."); -} - size_t ContractCompiler::compileConstructor( ContractDefinition const& _contract, std::map> const& _otherCompilers @@ -440,9 +412,9 @@ bool hasPayableFunctions(ContractDefinition const& _contract) } -void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract, bool _isOffchain) +void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract) { - map, FunctionTypePointer> interfaceFunctions = _isOffchain ? _contract.offchainFunctions() : _contract.interfaceFunctions(); + map, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions(); map, evmasm::AssemblyItem const> callDataUnpackerEntryPoints; if (_contract.isLibrary()) @@ -450,7 +422,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac solAssert(m_context.stackHeight() == 1, "CALL / DELEGATECALL flag expected."); } - if (!_isOffchain && !_contract.isLibrary()) // Solidity++: Libraries don't have function selectors + if (!_contract.isLibrary()) // Solidity++: Libraries don't have function selectors { debug("Append function selector " + m_functionSelectorTag.toAssemblyText(m_context.assembly())); m_context << m_functionSelectorTag; @@ -708,7 +680,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) m_context.appendDebugInfo("Add parameters"); for (ASTPointer const& variable: _function.parameters()) { - debug(" - Add parameter: " + variable->toString()); + debug(" - Add parameter: " + variable->name()); m_context.addVariable(*variable, parametersSize); parametersSize -= variable->annotation().type->sizeOnStack(); } @@ -1387,18 +1359,6 @@ bool ContractCompiler::visit(EmitStatement const& _emit) return false; } -// Solidity++: -bool ContractCompiler::visit(SendStatement const& _send) -{ - CompilerContext::LocationSetter locationSetter(m_context, _send); - StackHeightChecker checker(m_context, "SendStatement"); - compileExpression(_send.address()); - CompilerUtils(m_context).convertType(*_send.address().annotation().type, IntegerType(168), true); - compileExpression(_send.expression()); - checker.check(); - return false; -} - bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); @@ -1501,7 +1461,7 @@ void ContractCompiler::appendMissingFunctions() void ContractCompiler::appendModifierOrFunctionCode() { solAssert(m_currentFunction, ""); - auto debugInfo = "ContractCompiler::appendModifierOrFunctionCode() for " + m_currentFunction->externalSignature(); + auto debugInfo = "ContractCompiler::appendModifierOrFunctionCode() for " + m_currentFunction->name(); debug(debugInfo); m_context.appendDebugInfo(debugInfo); unsigned stackSurplus = 0; @@ -1563,11 +1523,11 @@ void ContractCompiler::appendModifierOrFunctionCode() bool coderV2Outside = m_context.useABICoderV2(); m_context.setUseABICoderV2(*codeBlock->sourceUnit().annotation().useABICoderV2); - string desc = "return tag of " + toString(m_currentFunction->externalSignature()); + string desc = "return tag of " + toString(m_currentFunction->name()); m_returnTags.emplace_back(m_context.newTag(desc), m_context.stackHeight()); // Compile function body - m_context.appendDebugInfo("start of code block of " + m_currentFunction->externalSignature()); + m_context.appendDebugInfo("start of code block of " + m_currentFunction->name()); codeBlock->accept(*this); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 39d16a6..6d19b14 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -44,12 +44,6 @@ class ContractCompiler: private ASTConstVisitor ContractDefinition const& _contract, std::map> const& _otherCompilers ); - - /// Solidity++: compile offchain functions - void compileOffchain( - ContractDefinition const& _contract, - std::map> const& _otherCompilers - ); /// Compiles the constructor part of the contract. /// @returns the identifier of the runtime sub-assembly. @@ -93,7 +87,7 @@ class ContractCompiler: private ASTConstVisitor evmasm::AssemblyItem const& _notFoundTag, size_t _runs ); - void appendFunctionSelector(ContractDefinition const& _contract, bool _isOffchain = false); + void appendFunctionSelector(ContractDefinition const& _contract); void appendCallValueCheck(); void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); @@ -115,7 +109,6 @@ class ContractCompiler: private ASTConstVisitor bool visit(Return const& _return) override; bool visit(Throw const& _throw) override; bool visit(EmitStatement const& _emit) override; - bool visit(SendStatement const& _send) override; // Solidity++ bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; bool visit(ExpressionStatement const& _expressionStatement) override; bool visit(PlaceholderStatement const&) override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 0e3a7dc..3953f23 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -643,14 +643,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) [[fallthrough]]; case FunctionType::Kind::External: case FunctionType::Kind::DelegateCall: - case FunctionType::Kind::SendMessage: // Solidity++ _functionCall.expression().accept(*this); uint32_t callbackSelector; - if(_functionCall.annotation().awaitId && function.executionBehavior() == ExecutionBehavior::Async) + if(!_functionCall.annotation().async) { - auto awaitId = _functionCall.annotation().awaitId; - callbackSelector = selectorFromAwaitId(awaitId); - debug("Callback signature is " + toHex(callbackSelector, HexPrefix::Add)); + callbackSelector = selectorFromAwaitId( _functionCall.id()); + debug("Callback selector is " + toHex(callbackSelector, HexPrefix::Add)); } else callbackSelector = 0; @@ -785,9 +783,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) FunctionType::Kind::BareCall, false, StateMutability::NonPayable, - ExecutionBehavior::Async, nullptr, true, + true, true ), {}, @@ -1354,8 +1352,8 @@ bool ExpressionCompiler::visit(FunctionCallOptions const& _functionCallOptions) _functionCallOptions.expression().accept(*this); // Desired Stack: [salt], [gas], [value] - // Solidity++: desired stack: [tokenId], [amount] - enum Option { Salt, Gas, Value, TokenId, Amount }; + // Solidity++: desired stack: [tokenId], [value] + enum Option { Salt, Gas, TokenId, Value }; // Note: the order matters vector