diff --git a/.circleci/config.yml b/.circleci/config.yml index f8888e001..0140126e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,17 +104,6 @@ defaults: name: command line tests command: ./test/cmdlineTests.sh - - test_ubuntu1904: &test_ubuntu1904 - docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904 - steps: - - checkout - - attach_workspace: - at: build - - run: *run_soltest - - store_test_results: *store_test_results - - store_artifacts: *artifacts_test_results - - test_ubuntu1904_clang: &test_ubuntu1904_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang @@ -126,7 +115,7 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - test_ubuntu1904_all: &test_ubuntu1904 + - test_ubuntu1904: &test_ubuntu1904 docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904 steps: diff --git a/Changelog.md b/Changelog.md index 9adced5bc..77cebfb3b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -36,23 +36,29 @@ Compiler Features: * Allow revert strings to be stripped from the binary using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting. -### 0.5.13 (unreleased) +### 0.5.13 (2019-11-14) Language Features: + * Allow to obtain the address of a linked library with ``address(LibraryName)``. Compiler Features: - * Code Generator: Use SELFBALANCE for ``address(this).balance`` if using Istanbul EVM + * Code Generator: Use SELFBALANCE opcode for ``address(this).balance`` if using Istanbul EVM. + * EWasm: Experimental EWasm binary output via ``--ewasm`` and as documented in standard-json. * SMTChecker: Add break/continue support to the CHC engine. * SMTChecker: Support assignments to multi-dimensional arrays and mappings. * SMTChecker: Support inheritance and function overriding. - * EWasm: Experimental EWasm binary output. + * Standard JSON Interface: Output the storage layout of a contract when artifact ``storageLayout`` is requested. + * TypeChecker: List possible candidates when overload resolution fails. + * TypeChecker: Disallow variables of library types. Bugfixes: - * Type Checker: Disallow constructor of the same class to be used as modifier - * Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer - * Type Checker: Treat magic variables as unknown identifiers in inline assembly + * Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer. * SMTChecker: Fix internal error when accessing indices of fixed bytes. + * SMTChecker: Fix internal error when using function pointers as arguments. + * SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes. + * Type Checker: Disallow constructor of the same class to be used as modifier. + * Type Checker: Treat magic variables as unknown identifiers in inline assembly. ### 0.5.12 (2019-10-01) diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index d79aca112..402997af1 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -754,6 +754,10 @@ "bugs": [], "released": "2019-10-01" }, + "0.5.13": { + "bugs": [], + "released": "2019-11-14" + }, "0.5.2": { "bugs": [ "SignedArrayStorageCopy", diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 7421e278c..bfeebdb77 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -178,6 +178,9 @@ custom types without the overhead of external function calls: } } +It is possible to obtain the address of a library by converting +the library type to the ``address`` type, i.e. using ``address(LibraryName)``. + As the compiler cannot know where the library will be deployed at, these addresses have to be filled into the final bytecode by a linker diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 598d07ed9..24ae5557c 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -8,6 +8,8 @@ Miscellaneous Layout of State Variables in Storage ************************************ +.. _storage-inplace-encoding: + Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules: - The first item in a storage slot is stored lower-order aligned. @@ -49,6 +51,8 @@ The elements of structs and arrays are stored after each other, just as if they Mappings and Dynamic Arrays =========================== +.. _storage-hashed-encoding: + Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots. @@ -88,6 +92,267 @@ by checking if the lowest bit is set: short (not set) and long (set). .. note:: Handling invalidly encoded slots is currently not supported but may be added in the future. +JSON Output +=========== + +.. _storage-layout-top-level: + +The storage layout of a contract can be requested via the :ref:`standard JSON interface `. The output is a JSON object containing two keys, +``storage`` and ``types``. The ``storage`` object is an array where each +element has the following form: + + +.. code:: + + + { + "astId": 2, + "contract": "fileA:A", + "label": "x", + "offset": 0, + "slot": "0", + "type": "t_uint256" + } + +where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA`` +and + +- ``astId`` is the id of the AST node of the state variable's declaration +- ``contract`` is the name of the contract including its path as prefix +- ``label`` is the name of the state variable +- ``offset`` is the offset in bytes within the storage slot according to the encoding +- ``slot`` is the storage slot where the state variable resides or starts. This + number may be very large and therefore its JSON value is represented as a + string. +- ``type`` is an identifier used as key to the variable's type information (described in the following) + +The given ``type``, in this case ``t_uint256`` represents an element in +``types``, which has the form: + + +.. code:: + + { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32", + } + +where + +- ``encoding`` how the data is encoded in storage, where the possible values are: + + - ``inplace``: data is laid out contiguously in storage (see :ref:`above `). + - ``mapping``: Keccak-256 hash-based method (see :ref:`above `). + - ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above `). + - ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above `). + +- ``label`` is the canonical type name. +- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used. + +Some types have extra information besides the four above. Mappings contain +its ``key`` and ``value`` types (again referencing an entry in this mapping +of types), arrays have its ``base`` type, and structs list their ``members`` in +the same format as the top-level ``storage`` (see :ref:`above +`). + +.. note :: + The JSON output format of a contract's storage layout is still considered experimental + and is subject to change in non-breaking releases of Solidity. + +The following example shows a contract and its storage layout, containing +value and reference types, types that are encoded packed, and nested types. + + +.. code:: + + pragma solidity >=0.4.0 <0.7.0; + contract A { + struct S { + uint128 a; + uint128 b; + uint[2] staticArray; + uint[] dynArray; + } + + uint x; + uint y; + S s; + address addr; + mapping (uint => mapping (address => bool)) map; + uint[] array; + string s1; + bytes b1; + } + +.. code:: + + "storageLayout": { + "storage": [ + { + "astId": 14, + "contract": "fileA:A", + "label": "x", + "offset": 0, + "slot": "0", + "type": "t_uint256" + }, + { + "astId": 16, + "contract": "fileA:A", + "label": "y", + "offset": 0, + "slot": "1", + "type": "t_uint256" + }, + { + "astId": 18, + "contract": "fileA:A", + "label": "s", + "offset": 0, + "slot": "2", + "type": "t_struct(S)12_storage" + }, + { + "astId": 20, + "contract": "fileA:A", + "label": "addr", + "offset": 0, + "slot": "6", + "type": "t_address" + }, + { + "astId": 26, + "contract": "fileA:A", + "label": "map", + "offset": 0, + "slot": "7", + "type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))" + }, + { + "astId": 29, + "contract": "fileA:A", + "label": "array", + "offset": 0, + "slot": "8", + "type": "t_array(t_uint256)dyn_storage" + }, + { + "astId": 31, + "contract": "fileA:A", + "label": "s1", + "offset": 0, + "slot": "9", + "type": "t_string_storage" + }, + { + "astId": 33, + "contract": "fileA:A", + "label": "b1", + "offset": 0, + "slot": "10", + "type": "t_bytes_storage" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)2_storage": { + "base": "t_uint256", + "encoding": "inplace", + "label": "uint256[2]", + "numberOfBytes": "64" + }, + "t_array(t_uint256)dyn_storage": { + "base": "t_uint256", + "encoding": "dynamic_array", + "label": "uint256[]", + "numberOfBytes": "32" + }, + "t_bool": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes_storage": { + "encoding": "bytes", + "label": "bytes", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_bool)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": "t_bool" + }, + "t_mapping(t_uint256,t_mapping(t_address,t_bool))": { + "encoding": "mapping", + "key": "t_uint256", + "label": "mapping(uint256 => mapping(address => bool))", + "numberOfBytes": "32", + "value": "t_mapping(t_address,t_bool)" + }, + "t_string_storage": { + "encoding": "bytes", + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(S)12_storage": { + "encoding": "inplace", + "label": "struct A.S", + "members": [ + { + "astId": 2, + "contract": "fileA:A", + "label": "a", + "offset": 0, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 4, + "contract": "fileA:A", + "label": "b", + "offset": 16, + "slot": "0", + "type": "t_uint128" + }, + { + "astId": 8, + "contract": "fileA:A", + "label": "staticArray", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)2_storage" + }, + { + "astId": 11, + "contract": "fileA:A", + "label": "dynArray", + "offset": 0, + "slot": "3", + "type": "t_array(t_uint256)dyn_storage" + } + ], + "numberOfBytes": "128" + }, + "t_uint128": { + "encoding": "inplace", + "label": "uint128", + "numberOfBytes": "16" + }, + "t_uint256": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + .. index: memory layout **************** diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index bfed31f30..5c9ab9201 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -291,6 +291,7 @@ Input Description // metadata - Metadata // ir - Yul intermediate representation of the code before optimization // irOptimized - Intermediate representation after optimization + // storageLayout - Slots, offsets and types of the contract's state variables. // evm.assembly - New assembly format // evm.legacyAssembly - Old-style assembly format in JSON // evm.bytecode.object - Bytecode object @@ -391,6 +392,8 @@ Output Description "devdoc": {}, // Intermediate representation (string) "ir": "", + // See the Storage Layout documentation. + "storageLayout": {"storage": [...], "types": {...} }, // EVM-related outputs "evm": { // Assembly (string) diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index a0cb2ba37..a5936c947 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -112,6 +112,8 @@ set(sources interface/ReadFile.h interface/StandardCompiler.cpp interface/StandardCompiler.h + interface/StorageLayout.cpp + interface/StorageLayout.h interface/Version.cpp interface/Version.h parsing/DocStringParser.cpp diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 593ca3654..eede6eca0 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -38,10 +38,12 @@ namespace solidity NameAndTypeResolver::NameAndTypeResolver( GlobalContext& _globalContext, + langutil::EVMVersion _evmVersion, map>& _scopes, ErrorReporter& _errorReporter -) : +): m_scopes(_scopes), + m_evmVersion(_evmVersion), m_errorReporter(_errorReporter), m_globalContext(_globalContext) { @@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res { if (m_scopes.count(&_node)) setScope(&_node); - return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); + return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node); } } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 56e640413..4499071bc 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -28,6 +28,8 @@ #include #include +#include + #include #include @@ -55,6 +57,7 @@ public: /// are filled during the lifetime of this object. NameAndTypeResolver( GlobalContext& _globalContext, + langutil::EVMVersion _evmVersion, std::map>& _scopes, langutil::ErrorReporter& _errorReporter ); @@ -130,6 +133,7 @@ private: /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope. std::map>& m_scopes; + langutil::EVMVersion m_evmVersion; DeclarationContainer* m_currentScope = nullptr; langutil::ErrorReporter& m_errorReporter; GlobalContext& m_globalContext; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 2274d0b11..c7960d20c 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -350,7 +350,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) yul::AsmAnalyzer( analysisInfo, errorsIgnored, - yul::EVMDialect::strictAssemblyForEVM(EVMVersion{}), + yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), resolver ).analyze(_inlineAssembly.operations()); return false; diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 56394416a..f1e512ab0 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -52,10 +53,12 @@ public: ReferencesResolver( langutil::ErrorReporter& _errorReporter, NameAndTypeResolver& _resolver, + langutil::EVMVersion _evmVersion, bool _resolveInsideCode = false ): m_errorReporter(_errorReporter), m_resolver(_resolver), + m_evmVersion(_evmVersion), m_resolveInsideCode(_resolveInsideCode) {} @@ -99,6 +102,7 @@ private: langutil::ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; + langutil::EVMVersion m_evmVersion; /// Stack of return parameters. std::vector m_returnParameters; bool const m_resolveInsideCode; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 83da543fa..a7fa47297 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -457,6 +457,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) TypePointer varType = _variable.annotation().type; solAssert(!!varType, "Variable type not provided."); + if (auto contractType = dynamic_cast(varType)) + if (contractType->contractDefinition().isLibrary()) + m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library."); if (_variable.value()) expectType(*_variable.value(), *varType); if (_variable.isConstant()) @@ -2633,12 +2636,30 @@ bool TypeChecker::visit(Identifier const& _identifier) if (functionType->canTakeArguments(*annotation.arguments)) candidates.push_back(declaration); } - if (candidates.empty()) - m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup."); - else if (candidates.size() == 1) + if (candidates.size() == 1) annotation.referencedDeclaration = candidates.front(); else - m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup."); + { + SecondarySourceLocation ssl; + + for (Declaration const* declaration: annotation.overloadedDeclarations) + if (declaration->location().isEmpty()) + { + // 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(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup."); + else + m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup."); + } } } solAssert( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 16297378d..e679fb66c 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3411,6 +3411,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current return members; } +BoolResult TypeType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (auto const* address = dynamic_cast(&_convertTo)) + if (address->stateMutability() == StateMutability::NonPayable) + if (auto const* contractType = dynamic_cast(m_actualType)) + return contractType->contractDefinition().isLibrary(); + return isImplicitlyConvertibleTo(_convertTo); +} + ModifierType::ModifierType(ModifierDefinition const& _modifier) { TypePointers params; diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 2629b1b6c..588e8b8cf 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1317,6 +1317,7 @@ public: std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; + BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override; private: TypePointer m_actualType; }; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index a8050c695..b3da0b242 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -475,7 +475,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); - acceptAndConvert(*_functionCall.arguments().front(), *_functionCall.annotation().type); + auto const& expression = *_functionCall.arguments().front(); + auto const& targetType = *_functionCall.annotation().type; + if (auto const* typeType = dynamic_cast(expression.annotation().type)) + if (auto const* addressType = dynamic_cast(&targetType)) + { + auto const* contractType = dynamic_cast(typeType->actualType()); + solAssert( + contractType && + contractType->contractDefinition().isLibrary() && + addressType->stateMutability() == StateMutability::NonPayable, + "" + ); + m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName()); + return false; + } + acceptAndConvert(expression, targetType); return false; } diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index e89dcb5a3..dc1f611b6 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -428,15 +428,20 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) auto const& funType = dynamic_cast(calledExpr->annotation().type); solAssert(funType, ""); + auto const& functionParams = funDef->parameters(); + auto const& arguments = _funCall.arguments(); + unsigned firstParam = 0; if (funType->bound()) { auto const& boundFunction = dynamic_cast(calledExpr); solAssert(boundFunction, ""); - funArgs.push_back(expr(boundFunction->expression())); + funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type())); + firstParam = 1; } - for (auto arg: _funCall.arguments()) - funArgs.push_back(expr(*arg)); + solAssert((arguments.size() + firstParam) == functionParams.size(), ""); + for (unsigned i = 0; i < arguments.size(); ++i) + funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type())); initializeFunctionCallParameters(*funDef, funArgs); // The reason why we need to pushCallStack here instead of visit(FunctionDefinition) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index ac8bb3c4f..8405668ef 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -508,7 +508,13 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function) auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: _function.parameters() + _function.returnParameters()) - varSorts.push_back(smt::smtSort(*var->type())); + { + // SMT solvers do not support function types as arguments. + if (var->type()->category() == Type::Category::Function) + varSorts.push_back(make_shared(smt::Kind::Int)); + else + varSorts.push_back(smt::smtSort(*var->type())); + } return make_shared( m_stateSorts + varSorts, boolSort @@ -526,7 +532,13 @@ smt::SortPointer CHC::sort(ASTNode const* _node) auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: m_currentFunction->localVariables()) - varSorts.push_back(smt::smtSort(*var->type())); + { + // SMT solvers do not support function types as arguments. + if (var->type()->category() == Type::Category::Function) + varSorts.push_back(make_shared(smt::Kind::Int)); + else + varSorts.push_back(smt::smtSort(*var->type())); + } return make_shared( fSort->domain + varSorts, boolSort @@ -540,7 +552,7 @@ unique_ptr CHC::createSymbolicBlock(smt::SortPoin _name, m_context ); - m_interface->registerRelation(block->currentValue()); + m_interface->registerRelation(block->currentFunctionValue()); return block; } @@ -572,7 +584,7 @@ smt::Expression CHC::error() smt::Expression CHC::error(unsigned _idx) { - return m_errorPredicate->valueAtIndex(_idx)({}); + return m_errorPredicate->functionValueAtIndex(_idx)({}); } unique_ptr CHC::createBlock(ASTNode const* _node, string const& _prefix) @@ -589,7 +601,7 @@ void CHC::createErrorBlock() { solAssert(m_errorPredicate, ""); m_errorPredicate->increaseIndex(); - m_interface->registerRelation(m_errorPredicate->currentValue()); + m_interface->registerRelation(m_errorPredicate->currentFunctionValue()); } void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints) diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 965da2d79..a9fb9f2b7 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -154,15 +154,15 @@ private: //@{ /// Constructor predicate. /// Default constructor sets state vars to 0. - std::unique_ptr m_constructorPredicate; + std::unique_ptr m_constructorPredicate; /// Artificial Interface predicate. /// Single entry block for all functions. - std::unique_ptr m_interfacePredicate; + std::unique_ptr m_interfacePredicate; /// Artificial Error predicate. /// Single error block for all assertions. - std::unique_ptr m_errorPredicate; + std::unique_ptr m_errorPredicate; //@} /// Variables. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index f9128d39a..da4a207a0 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -136,9 +136,13 @@ void SMTEncoder::visitFunctionOrModifier() *modifierInvocation->name()->annotation().referencedDeclaration ); vector modifierArgsExpr; - if (modifierInvocation->arguments()) - for (auto arg: *modifierInvocation->arguments()) - modifierArgsExpr.push_back(expr(*arg)); + if (auto const* arguments = modifierInvocation->arguments()) + { + auto const& modifierParams = modifierDef.parameters(); + solAssert(modifierParams.size() == arguments->size(), ""); + for (unsigned i = 0; i < arguments->size(); ++i) + modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type())); + } initializeFunctionCallParameters(modifierDef, modifierArgsExpr); pushCallStack({&modifierDef, modifierInvocation.get()}); modifierDef.body().accept(*this); @@ -595,10 +599,10 @@ void SMTEncoder::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (_identifier.annotation().type->category() == Type::Category::Function) - visitFunctionIdentifier(_identifier); else if (auto decl = identifierToVariable(_identifier)) defineExpr(_identifier, currentValue(*decl)); + else if (_identifier.annotation().type->category() == Type::Category::Function) + visitFunctionIdentifier(_identifier); else if (_identifier.name() == "now") defineGlobalVariable(_identifier.name(), _identifier); else if (_identifier.name() == "this") @@ -699,7 +703,7 @@ void SMTEncoder::endVisit(Return const& _return) } } else if (returnParams.size() == 1) - m_context.addAssertion(expr(*_return.expression()) == m_context.newValue(*returnParams.front())); + m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front())); } } @@ -1353,7 +1357,7 @@ void SMTEncoder::createExpr(Expression const& _e) void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value) { createExpr(_e); - solAssert(smt::smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported"); + solAssert(_value.sort->kind != smt::Kind::Function, "Equality operator applied to type that is not fully supported"); m_context.addAssertion(expr(_e) == _value); } diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index ab23b8712..591158a25 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -19,6 +19,7 @@ #include #include +#include #include using namespace std; @@ -139,7 +140,28 @@ pair> newSymbolicVariable( else if (isBool(_type.category())) var = make_shared(type, _uniqueName, _context); else if (isFunction(_type.category())) - var = make_shared(type, _uniqueName, _context); + { + auto const& fType = dynamic_cast(type); + auto const& paramsIn = fType->parameterTypes(); + auto const& paramsOut = fType->returnParameterTypes(); + auto findFunctionParam = [&](auto&& params) { + return find_if( + begin(params), + end(params), + [&](TypePointer _paramType) { return _paramType->category() == solidity::Type::Category::Function; } + ); + }; + if ( + findFunctionParam(paramsIn) != end(paramsIn) || + findFunctionParam(paramsOut) != end(paramsOut) + ) + { + abstract = true; + var = make_shared(TypeProvider::uint256(), type, _uniqueName, _context); + } + else + var = make_shared(type, _uniqueName, _context); + } else if (isInteger(_type.category())) var = make_shared(type, type, _uniqueName, _context); else if (isFixedBytes(_type.category())) diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 3190cc6c6..3a814ec7d 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -19,7 +19,6 @@ #include #include -#include using namespace std; using namespace dev; @@ -153,16 +152,38 @@ SymbolicFunctionVariable::SymbolicFunctionVariable( solAssert(m_sort->kind == Kind::Function, ""); } -void SymbolicFunctionVariable::resetDeclaration() +Expression SymbolicFunctionVariable::currentValue(solidity::TypePointer const& _targetType) const { - m_declaration = m_context.newVariable(currentName(), m_sort); + return m_abstract.currentValue(_targetType); +} + +Expression SymbolicFunctionVariable::currentFunctionValue() const +{ + return m_declaration; +} + +Expression SymbolicFunctionVariable::valueAtIndex(int _index) const +{ + return m_abstract.valueAtIndex(_index); +} + +Expression SymbolicFunctionVariable::functionValueAtIndex(int _index) const +{ + return SymbolicVariable::valueAtIndex(_index); +} + +Expression SymbolicFunctionVariable::resetIndex() +{ + SymbolicVariable::resetIndex(); + return m_abstract.resetIndex(); } Expression SymbolicFunctionVariable::increaseIndex() { ++(*m_ssa); resetDeclaration(); - return currentValue(); + m_abstract.increaseIndex(); + return m_abstract.currentValue(); } Expression SymbolicFunctionVariable::operator()(vector _arguments) const @@ -170,6 +191,11 @@ Expression SymbolicFunctionVariable::operator()(vector _arguments) c return m_declaration(_arguments); } +void SymbolicFunctionVariable::resetDeclaration() +{ + m_declaration = m_context.newVariable(currentName(), m_sort); +} + SymbolicMappingVariable::SymbolicMappingVariable( solidity::TypePointer _type, string _uniqueName, diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index c65a26326..9ae637490 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace dev @@ -138,7 +139,12 @@ public: }; /** - * Specialization of SymbolicVariable for FunctionType + * Specialization of SymbolicVariable for FunctionType. + * Besides containing a symbolic function declaration, + * it also has an integer used as abstraction. + * By default, the abstract representation is used when + * values are requested, and the function declaration is + * used when operator() is applied over arguments. */ class SymbolicFunctionVariable: public SymbolicVariable { @@ -154,8 +160,20 @@ public: EncodingContext& _context ); - Expression increaseIndex(); - Expression operator()(std::vector _arguments) const; + Expression currentValue(solidity::TypePointer const& _targetType = TypePointer{}) const override; + + // Explicit request the function declaration. + Expression currentFunctionValue() const; + + Expression valueAtIndex(int _index) const override; + + // Explicit request the function declaration. + Expression functionValueAtIndex(int _index) const; + + Expression resetIndex() override; + Expression increaseIndex() override; + + Expression operator()(std::vector _arguments) const override; private: /// Creates a new function declaration. @@ -163,6 +181,14 @@ private: /// Stores the current function declaration. Expression m_declaration; + + /// Abstract representation. + SymbolicIntVariable m_abstract{ + TypeProvider::uint256(), + TypeProvider::uint256(), + m_uniqueName + "_abstract", + m_context + }; }; /** diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 551e55585..a6995f84c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -286,7 +287,7 @@ bool CompilerStack::analyze() noErrors = false; m_globalContext = make_shared(); - NameAndTypeResolver resolver(*m_globalContext, m_scopes, m_errorReporter); + NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter); for (Source const* source: m_sourceOrder) if (!resolver.registerDeclarations(*source->ast)) return false; @@ -684,6 +685,28 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const return *_contract.abi; } +Json::Value const& CompilerStack::storageLayout(string const& _contractName) const +{ + if (m_stackState < AnalysisPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + return storageLayout(contract(_contractName)); +} + +Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const +{ + if (m_stackState < AnalysisPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + solAssert(_contract.contract, ""); + + // caches the result + if (!_contract.storageLayout) + _contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract))); + + return *_contract.storageLayout; +} + Json::Value const& CompilerStack::natspecUser(string const& _contractName) const { if (m_stackState < AnalysisPerformed) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 35bda446e..b7137ee92 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -287,6 +287,10 @@ public: /// Prerequisite: Successful call to parse or compile. Json::Value const& contractABI(std::string const& _contractName) const; + /// @returns a JSON representing the storage layout of the contract. + /// Prerequisite: Successful call to parse or compile. + Json::Value const& storageLayout(std::string const& _contractName) const; + /// @returns a JSON representing the contract's user documentation. /// Prerequisite: Successful call to parse or compile. Json::Value const& natspecUser(std::string const& _contractName) const; @@ -334,6 +338,7 @@ private: eth::LinkerObject eWasmObject; ///< Experimental eWasm code mutable std::unique_ptr metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr abi; + mutable std::unique_ptr storageLayout; mutable std::unique_ptr userDocumentation; mutable std::unique_ptr devDocumentation; mutable std::unique_ptr sourceMapping; @@ -397,6 +402,10 @@ private: /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& contractABI(Contract const&) const; + /// @returns the storage layout of the contract as a JSON object. + /// This will generate the JSON object and store it in the Contract object if it is not present yet. + Json::Value const& storageLayout(Contract const&) const; + /// @returns the Natspec User documentation as a JSON object. /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& natspecUser(Contract const&) const; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 70c9e3430..bc77e50c5 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -927,10 +927,12 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting string file = contractName.substr(0, colon); string name = contractName.substr(colon + 1); - // ABI, documentation and metadata + // ABI, storage layout, documentation and metadata Json::Value contractData(Json::objectValue); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental)) contractData["abi"] = compilerStack.contractABI(contractName); + if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false)) + contractData["storageLayout"] = compilerStack.storageLayout(contractName); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental)) contractData["metadata"] = compilerStack.metadata(contractName); if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental)) diff --git a/libsolidity/interface/StorageLayout.cpp b/libsolidity/interface/StorageLayout.cpp new file mode 100644 index 000000000..e987d4de2 --- /dev/null +++ b/libsolidity/interface/StorageLayout.cpp @@ -0,0 +1,119 @@ +/* + 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 . +*/ + +#include + +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +Json::Value StorageLayout::generate(ContractDefinition const& _contractDef) +{ + solAssert(!m_contract, ""); + m_contract = &_contractDef; + m_types.clear(); + + auto typeType = dynamic_cast(_contractDef.type()); + solAssert(typeType, ""); + auto contractType = dynamic_cast(typeType->actualType()); + solAssert(contractType, ""); + + Json::Value variables(Json::arrayValue); + for (auto [var, slot, offset]: contractType->stateVariables()) + variables.append(generate(*var, slot, offset)); + + Json::Value layout; + layout["storage"] = move(variables); + layout["types"] = move(m_types); + return layout; +} + +Json::Value StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset) +{ + Json::Value varEntry; + TypePointer varType = _var.type(); + + varEntry["label"] = _var.name(); + varEntry["astId"] = int(_var.id()); + varEntry["contract"] = m_contract->fullyQualifiedName(); + varEntry["slot"] = _slot.str(); + varEntry["offset"] = _offset; + varEntry["type"] = typeKeyName(varType); + + generate(varType); + + return varEntry; +} + +void StorageLayout::generate(TypePointer _type) +{ + if (m_types.isMember(typeKeyName(_type))) + return; + + // Register it now to cut recursive visits. + Json::Value& typeInfo = m_types[typeKeyName(_type)]; + typeInfo["label"] = _type->toString(true); + typeInfo["numberOfBytes"] = u256(_type->storageBytes() * _type->storageSize()).str(); + + if (auto structType = dynamic_cast(_type)) + { + Json::Value members(Json::arrayValue); + auto const& structDef = structType->structDefinition(); + for (auto const& member: structDef.members()) + { + auto const& offsets = structType->storageOffsetsOfMember(member->name()); + members.append(generate(*member, offsets.first, offsets.second)); + } + typeInfo["members"] = move(members); + typeInfo["encoding"] = "inplace"; + } + else if (auto mappingType = dynamic_cast(_type)) + { + typeInfo["key"] = typeKeyName(mappingType->keyType()); + typeInfo["value"] = typeKeyName(mappingType->valueType()); + generate(mappingType->keyType()); + generate(mappingType->valueType()); + typeInfo["encoding"] = "mapping"; + } + else if (auto arrayType = dynamic_cast(_type)) + { + if (arrayType->isByteArray()) + typeInfo["encoding"] = "bytes"; + else + { + typeInfo["base"] = typeKeyName(arrayType->baseType()); + generate(arrayType->baseType()); + typeInfo["encoding"] = arrayType->isDynamicallySized() ? "dynamic_array" : "inplace"; + } + } + else + { + solAssert(_type->isValueType(), ""); + typeInfo["encoding"] = "inplace"; + } + + solAssert(typeInfo.isMember("encoding"), ""); +} + +string StorageLayout::typeKeyName(TypePointer _type) +{ + if (auto refType = dynamic_cast(_type)) + return TypeProvider::withLocationIfReference(refType->location(), _type)->richIdentifier(); + return _type->richIdentifier(); +} diff --git a/libsolidity/interface/StorageLayout.h b/libsolidity/interface/StorageLayout.h new file mode 100644 index 000000000..1f7b3c1fd --- /dev/null +++ b/libsolidity/interface/StorageLayout.h @@ -0,0 +1,58 @@ +/* + 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 . +*/ +/** + * Generates the storage layout of a contract. + */ + +#pragma once + +#include +#include + +#include + +namespace dev +{ +namespace solidity +{ + +class StorageLayout +{ +public: + /// Generates the storage layout of the contract + /// @param _contractDef The contract definition + /// @return A JSON representation of the contract's storage layout. + Json::Value generate(ContractDefinition const& _contractDef); + +private: + /// Generates the JSON information for a variable and its storage location. + Json::Value generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset); + + /// Generates the JSON information for @param _type + void generate(TypePointer _type); + + /// The key for the JSON object describing a type. + std::string typeKeyName(TypePointer _type); + + Json::Value m_types; + + /// Current analyzed contract + ContractDefinition const* m_contract = nullptr; +}; + +} +} diff --git a/test/.solhint.json b/test/.solhint.json index 0a8b2adfc..b15f97815 100644 --- a/test/.solhint.json +++ b/test/.solhint.json @@ -5,4 +5,4 @@ "compiler-fixed": "off", "no-inline-assembly": "off" } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/require_overload/err b/test/cmdlineTests/require_overload/err new file mode 100644 index 000000000..42243e7af --- /dev/null +++ b/test/cmdlineTests/require_overload/err @@ -0,0 +1,5 @@ +require_overload/input.sol:4:9: Error: No matching declaration found after argument-dependent lookup. + require(this); + ^-----^ +Candidate: function require(bool) +Candidate: function require(bool, string memory) diff --git a/test/cmdlineTests/require_overload/exit b/test/cmdlineTests/require_overload/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/require_overload/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/require_overload/input.sol b/test/cmdlineTests/require_overload/input.sol new file mode 100644 index 000000000..6d48ac24a --- /dev/null +++ b/test/cmdlineTests/require_overload/input.sol @@ -0,0 +1,6 @@ +pragma solidity >=0.0; +contract C { + function f() public pure { + require(this); + } +} diff --git a/test/cmdlineTests/storage_layout_bytes/input.json b/test/cmdlineTests/storage_layout_bytes/input.json new file mode 100644 index 000000000..fe1468c53 --- /dev/null +++ b/test/cmdlineTests/storage_layout_bytes/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { bytes s1 = \"test\"; bytes s2; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_bytes/output.json b/test/cmdlineTests/storage_layout_bytes/output.json new file mode 100644 index 000000000..d2829f360 --- /dev/null +++ b/test/cmdlineTests/storage_layout_bytes/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { bytes s1 = \"test\"; bytes s2; } +^-----------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":43,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_dyn_array/input.json b/test/cmdlineTests/storage_layout_dyn_array/input.json new file mode 100644 index 000000000..64ab3e20b --- /dev/null +++ b/test/cmdlineTests/storage_layout_dyn_array/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint[] array1; bool[] array2; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_dyn_array/output.json b/test/cmdlineTests/storage_layout_dyn_array/output.json new file mode 100644 index 000000000..51b3ac1a1 --- /dev/null +++ b/test/cmdlineTests/storage_layout_dyn_array/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint[] array1; bool[] array2; } +^------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":44,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_many/input.json b/test/cmdlineTests/storage_layout_many/input.json new file mode 100644 index 000000000..7ea41f211 --- /dev/null +++ b/test/cmdlineTests/storage_layout_many/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_many/output.json b/test/cmdlineTests/storage_layout_many/output.json new file mode 100644 index 000000000..a2b3f8d2d --- /dev/null +++ b/test/cmdlineTests/storage_layout_many/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { struct S { uint128 a; ... int[] array; string s1; bytes b1; } +^-------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":206,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_mapping/input.json b/test/cmdlineTests/storage_layout_mapping/input.json new file mode 100644 index 000000000..6292391d5 --- /dev/null +++ b/test/cmdlineTests/storage_layout_mapping/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_mapping/output.json b/test/cmdlineTests/storage_layout_mapping/output.json new file mode 100644 index 000000000..4696d46c8 --- /dev/null +++ b/test/cmdlineTests/storage_layout_mapping/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; } +^-----------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":79,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke/input.json b/test/cmdlineTests/storage_layout_smoke/input.json new file mode 100644 index 000000000..47722e759 --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_smoke/output.json b/test/cmdlineTests/storage_layout_smoke/output.json new file mode 100644 index 000000000..08191bc5d --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { } +^------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json new file mode 100644 index 000000000..b31f3dd9d --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json @@ -0,0 +1,19 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + }, + "fileB": { + "content": "contract A { uint x; uint y; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json new file mode 100644 index 000000000..761cf8334 --- /dev/null +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { } +^------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}} diff --git a/test/cmdlineTests/storage_layout_string/input.json b/test/cmdlineTests/storage_layout_string/input.json new file mode 100644 index 000000000..834617b3a --- /dev/null +++ b/test/cmdlineTests/storage_layout_string/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { string s1 = \"test\"; string s2; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_string/output.json b/test/cmdlineTests/storage_layout_string/output.json new file mode 100644 index 000000000..19066171e --- /dev/null +++ b/test/cmdlineTests/storage_layout_string/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { string s1 = \"test\"; string s2; } +^-------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":45,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct/input.json b/test/cmdlineTests/storage_layout_struct/input.json new file mode 100644 index 000000000..31ab020cf --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_struct/output.json b/test/cmdlineTests/storage_layout_struct/output.json new file mode 100644 index 000000000..a925c99c0 --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; } +^------------------------------------------------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":116,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct_packed/input.json b/test/cmdlineTests/storage_layout_struct_packed/input.json new file mode 100644 index 000000000..80c706c7b --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct_packed/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_struct_packed/output.json b/test/cmdlineTests/storage_layout_struct_packed/output.json new file mode 100644 index 000000000..fe9bd0d50 --- /dev/null +++ b/test/cmdlineTests/storage_layout_struct_packed/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; } +^------------------------------------------------------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":122,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types/input.json b/test/cmdlineTests/storage_layout_value_types/input.json new file mode 100644 index 000000000..1477dde08 --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint x; uint y; address addr; uint[2] array; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_value_types/output.json b/test/cmdlineTests/storage_layout_value_types/output.json new file mode 100644 index 000000000..cb43276ed --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint x; uint y; address addr; uint[2] array; } +^---------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":59,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types_packed/input.json b/test/cmdlineTests/storage_layout_value_types_packed/input.json new file mode 100644 index 000000000..04a7bd3bf --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types_packed/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }" + } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "storageLayout" ], + "": [ "storageLayout" ] + } + } + } +} diff --git a/test/cmdlineTests/storage_layout_value_types_packed/output.json b/test/cmdlineTests/storage_layout_value_types_packed/output.json new file mode 100644 index 000000000..cd7f866aa --- /dev/null +++ b/test/cmdlineTests/storage_layout_value_types_packed/output.json @@ -0,0 +1,4 @@ +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version! +contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; } +^-------------------------------------------------------------------------^ +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":75,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}} diff --git a/test/libsolidity/ABIJson/library_function.sol b/test/libsolidity/ABIJson/library_function.sol index 37efe24ed..5fe404e95 100644 --- a/test/libsolidity/ABIJson/library_function.sol +++ b/test/libsolidity/ABIJson/library_function.sol @@ -1,9 +1,14 @@ +contract X {} library test { struct StructType { uint a; } - function f(StructType storage b, uint[] storage c, test d) public returns (uint[] memory e, StructType storage f) { f = f; } - function f1(uint[] memory c, test d) public pure returns (uint[] memory e) { } + function f(StructType storage b, uint[] storage c, X d) public returns (uint[] memory e, StructType storage f) { f = f; } + function f1(uint[] memory c, X d) public pure returns (uint[] memory e) { } } // ---- +// :X +// [] +// +// // :test // [ // { @@ -15,9 +20,9 @@ library test { // "type": "uint256[]" // }, // { -// "internalType": "library test", +// "internalType": "contract X", // "name": "d", -// "type": "test" +// "type": "X" // } // ], // "name": "f1", diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 84988c228..bbad3718a 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -64,7 +64,7 @@ eth::AssemblyItems compileContract(std::shared_ptr _sourceCode) map> scopes; GlobalContext globalContext; - NameAndTypeResolver resolver(globalContext, scopes, errorReporter); + NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter); solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); for (ASTPointer const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index cdb4e3625..ceb1db2ba 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -78,15 +78,34 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref for (auto& test: m_tests) test.reset(); + map libraries; + + bool constructed = false; + for (auto& test: m_tests) { - if (&test == &m_tests.front()) - if (test.call().isConstructor) - deploy("", test.call().value, test.call().arguments.rawBytes()); - else - soltestAssert(deploy("", 0, bytes()), "Failed to deploy contract."); + if (constructed) + { + soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); + soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); + } + else if (test.call().isLibrary) + { + soltestAssert( + deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, + "Failed to deploy library " + test.call().signature + ); + libraries[test.call().signature] = m_contractAddress; + continue; + } else - soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call."); + { + if (test.call().isConstructor) + deploy("", test.call().value, test.call().arguments.rawBytes(), libraries); + else + soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); + constructed = true; + } if (test.call().isConstructor) { @@ -171,8 +190,8 @@ void SemanticTest::parseExpectations(istream& _stream) std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests)); } -bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments) +bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map const& _libraries) { - auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments); + auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments, _libraries); return !output.empty() && m_transactionSuccessful; } diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index f7891633d..71e03518b 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -60,7 +60,7 @@ public: /// Compiles and deploys currently held source. /// Returns true if deployment was successful, false otherwise. - bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments); + bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); private: std::string m_source; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 349072878..a0d860c61 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -118,7 +118,7 @@ bytes compileFirstExpression( ErrorReporter errorReporter(errors); GlobalContext globalContext; map> scopes; - NameAndTypeResolver resolver(globalContext, scopes, errorReporter); + NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); vector inheritanceHierarchy; diff --git a/test/libsolidity/semanticTests/libraries/library_address.sol b/test/libsolidity/semanticTests/libraries/library_address.sol new file mode 100644 index 000000000..3854a39bf --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_address.sol @@ -0,0 +1,56 @@ +library L { + function f(uint256 v) external pure returns (uint) { + return v * v; + } + function g(uint256 v) external returns (uint) { + return v * v; + } +} +contract C { + function addr() public view returns (bool) { + return address(L) == address(0); + } + function g(uint256 v) public view returns (uint256) { + return L.f(v); + } + function h(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function i(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function j(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function k(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// library: L +// addr() -> false +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 +// h(uint256): 1 -> 1 +// h(uint256): 2 -> 4 +// h(uint256): 4 -> 16 +// i(uint256): 1 -> 1 +// i(uint256): 2 -> 4 +// i(uint256): 4 -> 16 +// j(uint256): 1 -> 1 +// j(uint256): 2 -> 4 +// j(uint256): 4 -> 16 +// k(uint256): 1 -> FAILURE +// k(uint256): 2 -> FAILURE +// k(uint256): 4 -> FAILURE diff --git a/test/libsolidity/semanticTests/libraries/library_address_homestead.sol b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol new file mode 100644 index 000000000..ce7344f4a --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol @@ -0,0 +1,24 @@ +library L { + function f(uint256 a, uint256 b) external { + assert(a * a == b); + } +} +contract C { + function addr() public view returns (bool) { + return address(L) == address(0); + } + function g(uint256 a, uint256 b) public returns (bool) { + (bool success,) = address(L).delegatecall(abi.encodeWithSignature("f(uint256,uint256)", a, b)); + return success; + } +} +// ---- +// library: L +// g(uint256,uint256): 1, 1 -> true +// g(uint256,uint256): 1, 2 -> false +// g(uint256,uint256): 2, 3 -> false +// g(uint256,uint256): 2, 4 -> true +// g(uint256,uint256): 2, 5 -> false +// g(uint256,uint256): 4, 15 -> false +// g(uint256,uint256): 4, 16 -> true +// g(uint256,uint256): 4, 17 -> false diff --git a/test/libsolidity/semanticTests/libraries/stub.sol b/test/libsolidity/semanticTests/libraries/stub.sol new file mode 100644 index 000000000..8bae3df27 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/stub.sol @@ -0,0 +1,13 @@ +library L { + function f(uint256 v) external returns (uint256) { return v*v; } +} +contract C { + function g(uint256 v) external returns (uint256) { + return L.f(v); + } +} +// ---- +// library: L +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/stub_internal.sol b/test/libsolidity/semanticTests/libraries/stub_internal.sol new file mode 100644 index 000000000..075b8dbb2 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/stub_internal.sol @@ -0,0 +1,12 @@ +library L { + function f(uint256 v) internal returns (uint256) { return v*v; } +} +contract C { + function g(uint256 v) external returns (uint256) { + return L.f(v); + } +} +// ---- +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/viaYul/require.sol b/test/libsolidity/semanticTests/viaYul/require.sol index f816ace05..09e96c0f4 100644 --- a/test/libsolidity/semanticTests/viaYul/require.sol +++ b/test/libsolidity/semanticTests/viaYul/require.sol @@ -12,17 +12,33 @@ contract C { x = true; require(true); } - /* Not properly supported by test system yet function f2(bool a) public pure returns (bool x) { x = a; string memory message; + message = "fancy message!"; require(a, message); - }*/ + } + function f3(bool a) public pure returns (bool x) { + x = a; + require(a, "msg"); + } + function f4(bool a) public pure returns (bool x) { + x = a; + string memory message; + require(a, message); + } } // ==== -// compileViaYul: true +// compileViaYul: also +// EVMVersion: >=byzantium // ---- // f(bool): true -> true // f(bool): false -> FAILURE // fail() -> FAILURE // succeed() -> true +// f2(bool): true -> true +// f2(bool): false -> FAILURE, hex"08c379a0", 0x20, 14, "fancy message!" +// f3(bool): true -> true +// f3(bool): false -> FAILURE, hex"08c379a0", 0x20, 3, "msg" +// f4(bool): true -> true +// f4(bool): false -> FAILURE, hex"08c379a0", 0x20, 0 diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_address.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_address.sol new file mode 100644 index 000000000..c0b7d109d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_address.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + function f(address a, function(uint) external g) internal pure { + address b = address(g); + assert(a == b); + } +} +// ---- +// Warning: (128-138): Type conversion is not yet fully supported and might yield false positives. +// Warning: (142-156): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol new file mode 100644 index 000000000..799852592 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) external returns (uint) g, function(uint) external returns (uint) h) public { + assert(g(2) == h(2)); + } +} +// ---- +// Warning: (155-175): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol new file mode 100644 index 000000000..1657a95c6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { + assert(g(2) == h(2)); + assert(g == h); + } +} +// ---- +// Warning: (146-150): Assertion checker does not yet implement this type of function call. +// Warning: (154-158): Assertion checker does not yet implement this type of function call. +// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (139-159): Assertion violation happens here +// Warning: (163-177): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol new file mode 100644 index 000000000..b2701fdf9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract B { + function f() pure public { + g("0123456"); + } + function g(bytes7 a) pure public { + assert(a == "0123456"); + assert(a == "1234567"); + } +} +// ---- +// Warning: (162-184): Assertion violation happens here +// Warning: (136-158): Assertion violation happens here +// Warning: (162-184): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol new file mode 100644 index 000000000..41459169f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract B { + function f() mod2("0123456") pure public { } + modifier mod2(bytes7 a) { + assert(a == "0123456"); + assert(a == "1234567"); + _; + } +} +// ---- +// Warning: (152-174): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol new file mode 100644 index 000000000..09fd68ef6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function g() public pure returns (bytes32 val) { return "abc"; } + function f1() public pure returns (bytes32 val) { return g(); } + + function a() public pure { + assert(f1() == "abc"); + assert(f1() == "cde"); + } +} +// ---- +// Warning: (238-259): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol new file mode 100644 index 000000000..f2ba3ce23 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + function h() public pure returns (bytes32 val, bytes3 val2) { return ("abc", "def"); } + function g() public pure returns (bytes32 val) { return "abc"; } + function f1() public pure returns (bytes32 val) { return g(); } + function f2() public pure returns (bytes32 val, bytes3 val2) { return h(); } + + function a() public pure { + (bytes32 v1, bytes3 v2) = f2(); + assert(v1 == "abc"); + assert(v2 == "cde"); + } +} +// ---- +// Warning: (442-461): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/function_type_as_argument.sol b/test/libsolidity/smtCheckerTests/types/function_type_as_argument.sol new file mode 100644 index 000000000..0ba33e408 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_as_argument.sol @@ -0,0 +1,5 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) external g) public { + } +} diff --git a/test/libsolidity/smtCheckerTests/types/function_type_call.sol b/test/libsolidity/smtCheckerTests/types/function_type_call.sol new file mode 100644 index 000000000..90a80dd9e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_call.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function(uint) m_g; + function f(function(uint) internal g) internal { + g(2); + } + function h() public { + f(m_g); + } +} +// ---- +// Warning: (121-125): Assertion checker does not yet implement this type of function call. +// Warning: (121-125): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_members.sol b/test/libsolidity/smtCheckerTests/types/function_type_members.sol new file mode 100644 index 000000000..2296fa798 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_members.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(function(uint) external payable g) internal { + g.selector; + g.gas(2).value(3)(4); + } +} +// ---- +// Warning: (108-118): Assertion checker does not yet support this expression. +// Warning: (122-130): Assertion checker does not yet implement this type of function call. +// Warning: (122-139): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_nested.sol b/test/libsolidity/smtCheckerTests/types/function_type_nested.sol new file mode 100644 index 000000000..5eb637315 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_nested.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; +contract C { + function(uint) m_g; + function f1(function(uint) internal g1) internal { + g1(2); + } + function f2(function(function(uint) internal) internal g2) internal { + g2(m_g); + } + function h() public { + f2(f1); + } +} +// ---- +// Warning: (123-128): Assertion checker does not yet implement this type of function call. +// Warning: (152-197): Assertion checker does not yet support the type of this variable. +// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (212-219): Assertion checker does not yet implement this type of function call. +// Warning: (255-257): Internal error: Expression undefined for SMT solver. +// Warning: (255-257): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (212-219): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol b/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol new file mode 100644 index 000000000..2e974fef8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; +contract C { + function(uint) m_g; + function r() internal view returns (function(uint)) { + return m_g; + } + function f1(function(uint) internal g1) internal { + g1(2); + } + function f2(function(function(uint) internal) internal g2) internal { + g2(r()); + } + function h() public { + f2(f1); + } +} +// ---- +// Warning: (195-200): Assertion checker does not yet implement this type of function call. +// Warning: (224-269): Assertion checker does not yet support the type of this variable. +// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (287-288): Assertion checker does not yet support this global variable. +// Warning: (284-291): Assertion checker does not yet implement this type of function call. +// Warning: (327-329): Internal error: Expression undefined for SMT solver. +// Warning: (327-329): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256)) +// Warning: (287-288): Assertion checker does not yet support this global variable. +// Warning: (284-291): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol b/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol index 3e1a1cd2a..5e6334749 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/istanbul.sol @@ -1,12 +1,12 @@ contract C { - function f() pure external { + function f() pure external returns (uint id) { assembly { - pop(chainid()) + id := chainid() } } - function g() view external { + function g() view external returns (uint sb) { assembly { - pop(selfbalance()) + sb := selfbalance() } } } diff --git a/test/libsolidity/syntaxTests/inlineAssembly/istanbul_on_petersburg.sol b/test/libsolidity/syntaxTests/inlineAssembly/istanbul_on_petersburg.sol new file mode 100644 index 000000000..aa7eb1a8b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/istanbul_on_petersburg.sol @@ -0,0 +1,17 @@ +contract C { + function f() pure external returns (uint id) { + assembly { + id := chainid() + } + } + function g() view external returns (uint sb) { + assembly { + sb := selfbalance() + } + } +} +// ==== +// EVMVersion: =petersburg +// ---- +// TypeError: (101-110): The "chainid" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg"). +// TypeError: (215-228): The "selfbalance" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg"). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol index dcf11a6e3..3eebc7926 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol @@ -6,4 +6,5 @@ contract test { } } // ---- +// TypeError: (87-90): The type of a variable cannot be a library. // TypeError: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol new file mode 100644 index 000000000..c28bec419 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol @@ -0,0 +1,14 @@ +library X { } + +contract Y { + X abc; + function foo(X param) private view + { + X ofg; + ofg = abc; + } +} +// ---- +// TypeError: (29-34): The type of a variable cannot be a library. +// TypeError: (50-57): The type of a variable cannot be a library. +// TypeError: (77-82): The type of a variable cannot be a library. diff --git a/test/libsolidity/syntaxTests/types/library_to_address.sol b/test/libsolidity/syntaxTests/types/library_to_address.sol new file mode 100644 index 000000000..50ef844da --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_to_address.sol @@ -0,0 +1,8 @@ +library L { +} +contract C { + function f() public pure returns (address) { + return address(L); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/library_to_address_payable.sol b/test/libsolidity/syntaxTests/types/library_to_address_payable.sol new file mode 100644 index 000000000..36a7a8968 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/library_to_address_payable.sol @@ -0,0 +1,9 @@ +library L { +} +contract C { + function f() public pure returns (address payable) { + return address(L); + } +} +// ---- +// TypeError: (99-109): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable. diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 275b1e657..d945ee022 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -58,6 +58,7 @@ namespace test K(Boolean, "boolean", 0) \ /* special keywords */ \ K(Left, "left", 0) \ + K(Library, "library", 0) \ K(Right, "right", 0) \ K(Failure, "FAILURE", 0) \ @@ -268,6 +269,8 @@ struct FunctionCall /// Marks this function call as "short-handed", meaning /// no `->` declared. bool omitsArrow = true; + /// Marks a library deployment call. + bool isLibrary = false; }; } diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index f834a532b..77330f51c 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -75,44 +75,56 @@ vector TestFileParser::parseFunctionCalls(siz try { - tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); - if (accept(Token::Comma, true)) - call.value = parseFunctionCallValue(); - if (accept(Token::Colon, true)) - call.arguments = parseFunctionCallArguments(); - - if (accept(Token::Newline, true)) + if (accept(Token::Library, true)) { - call.displayMode = FunctionCall::DisplayMode::MultiLine; - m_lineNumber++; - } - - call.arguments.comment = parseComment(); - - if (accept(Token::Newline, true)) - { - call.displayMode = FunctionCall::DisplayMode::MultiLine; - m_lineNumber++; - } - - if (accept(Token::Arrow, true)) - { - call.omitsArrow = false; - call.expectations = parseFunctionCallExpectations(); - if (accept(Token::Newline, true)) - m_lineNumber++; + expect(Token::Colon); + call.signature = m_scanner.currentLiteral(); + expect(Token::Identifier); + call.isLibrary = true; + call.expectations.failure = false; } else { - call.expectations.failure = false; - call.displayMode = FunctionCall::DisplayMode::SingleLine; + tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); + if (accept(Token::Comma, true)) + call.value = parseFunctionCallValue(); + if (accept(Token::Colon, true)) + call.arguments = parseFunctionCallArguments(); + + if (accept(Token::Newline, true)) + { + call.displayMode = FunctionCall::DisplayMode::MultiLine; + m_lineNumber++; + } + + call.arguments.comment = parseComment(); + + if (accept(Token::Newline, true)) + { + call.displayMode = FunctionCall::DisplayMode::MultiLine; + m_lineNumber++; + } + + if (accept(Token::Arrow, true)) + { + call.omitsArrow = false; + call.expectations = parseFunctionCallExpectations(); + if (accept(Token::Newline, true)) + m_lineNumber++; + } + else + { + call.expectations.failure = false; + call.displayMode = FunctionCall::DisplayMode::SingleLine; + } + + call.expectations.comment = parseComment(); + + if (call.signature == "constructor()") + call.isConstructor = true; + } - call.expectations.comment = parseComment(); - - if (call.signature == "constructor()") - call.isConstructor = true; - calls.emplace_back(std::move(call)); } catch (Error const& _e) @@ -456,6 +468,7 @@ void TestFileParser::Scanner::scanNextToken() if (_literal == "false") return TokenDesc{Token::Boolean, _literal}; if (_literal == "ether") return TokenDesc{Token::Ether, _literal}; if (_literal == "left") return TokenDesc{Token::Left, _literal}; + if (_literal == "library") return TokenDesc{Token::Library, _literal}; if (_literal == "right") return TokenDesc{Token::Right, _literal}; if (_literal == "hex") return TokenDesc{Token::Hex, _literal}; if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal}; diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 3ef9b6b81..2323609b5 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -57,7 +57,9 @@ void testFunctionCall( u256 _value = 0, string _argumentComment = "", string _expectationComment = "", - vector _rawArguments = vector{} + vector _rawArguments = vector{}, + bool _isConstructor = false, + bool _isLibrary = false ) { BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure); @@ -79,6 +81,9 @@ void testFunctionCall( ++index; } } + + BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor); + BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary); } BOOST_AUTO_TEST_SUITE(TestFileParserTest) @@ -883,6 +888,51 @@ BOOST_AUTO_TEST_CASE(call_unexpected_character) BOOST_REQUIRE_THROW(parse(source), langutil::Error); } +BOOST_AUTO_TEST_CASE(constructor) +{ + char const* source = R"( + // constructor() + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "constructor()", + false, + {}, + {}, + 0, + "", + "", + {}, + true + ); +} + +BOOST_AUTO_TEST_CASE(library) +{ + char const* source = R"( + // library: L + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + testFunctionCall( + calls.at(0), + Mode::SingleLine, + "L", + false, + {}, + {}, + 0, + "", + "", + {}, + false, + true + ); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 1e52c1932..5fcfef08b 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -55,6 +55,12 @@ string TestFunctionCall::format( string newline = formatToken(Token::Newline); string failure = formatToken(Token::Failure); + if (m_call.isLibrary) + { + stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature; + return; + } + /// Formats the function signature. This is the same independent from the display-mode. stream << _linePrefix << newline << ws << m_call.signature; if (m_call.value > u256(0))