diff options
37 files changed, 412 insertions, 296 deletions
diff --git a/Changelog.md b/Changelog.md index e64a8fea..36186462 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,14 +1,17 @@ ### 0.4.14 (unreleased) Features: + * C API (``jsonCompiler``): Export the ``license`` method. * Inline Assembly: Show useful error message if trying to access calldata variables. * Inline Assembly: Support variable declaration without initial value (defaults to 0). * Type Checker: Disallow value transfers to contracts without a payable fallback function. * Type Checker: Include types in explicit conversion error message. * Type Checker: Raise proper error for arrays too large for ABI encoding. + * Type checker: Warn if using ``this`` in a constructor. Bugfixes: * Type Checker: Fix invalid "specify storage keyword" warning for reference members of structs. + * Type Checker: Mark modifiers as internal. ### 0.4.13 (2017-07-06) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 76e3bfb1..b5df9fda 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -10,9 +10,10 @@ Checklist for making a release: - [ ] Wait for the CI runs on the tag itself (they should push artefacts onto the Github release page). - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems). + - [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash) - [ ] Make a release of ``solc-js``: Increment the version number, create a pull request for that, merge it after tests succeeded. - [ ] Run ``npm publish`` in the updated ``solc-js`` repository. - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. - [ ] Merge ``release`` back into ``develop``. - [ ] Announce on Twitter and Reddit. - - [ ] Lay back, wait for bug reports and repeat from step 1 :) + - [ ] Lean back, wait for bug reports and repeat from step 1 :) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index ea3b185a..4ce9d22d 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -160,10 +160,24 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA endif() if (EMSCRIPTEN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0 -O3 -s LINKABLE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s NO_EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_DYNAMIC_EXECUTION=1") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_FILESYSTEM=1 -s AGGRESSIVE_VARIABLE_ELIMINATION=1") + # Do emit a separate memory initialiser file + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0") + # Leave only exported symbols as public and agressively remove others + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden") + # Optimisation level + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + # Re-enable exception catching (optimisations above -O1 disable it) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") + # Remove any code related to exit (such as atexit) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_EXIT_RUNTIME=1") + # Remove any code related to filesystem access + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_FILESYSTEM=1") + # Remove variables even if it needs to be duplicated (can improve speed at the cost of size) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s AGGRESSIVE_VARIABLE_ELIMINATION=1") + # Allow memory growth, but disable some optimisations + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1") + # Disable eval() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1") add_definitions(-DETH_EMSCRIPTEN=1) endif() endif() diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 82c52159..2cf57427 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -316,6 +316,8 @@ For example, :: + pragma solidity ^0.4.0; + contract Test { function Test(){ b = 0x12345678901234567890123456789012; } event Event(uint indexed a, bytes32 b) diff --git a/docs/assembly.rst b/docs/assembly.rst index 37222faf..4e665b7e 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -92,7 +92,7 @@ you really know what you are doing. function sumAsm(uint[] _data) returns (uint o_sum) { for (uint i = 0; i < _data.length; ++i) { assembly { - o_sum := mload(add(add(_data, 0x20), mul(i, 0x20))) + o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20)))) } } } @@ -490,7 +490,7 @@ is performed by replacing the variable's value on the stack by the new value. .. code:: - assembly { + { let v := 0 // functional-style assignment as part of variable declaration let g := add(v, 2) sload(10) @@ -509,7 +509,7 @@ case called ``default``. .. code:: - assembly { + { let x := 0 switch calldataload(4) case 0 { @@ -538,7 +538,7 @@ The following example computes the sum of an area in memory. .. code:: - assembly { + { let x := 0 for { let i := 0 } lt(i, 0x100) { i := add(i, 0x20) } { x := add(x, mload(i)) @@ -565,7 +565,7 @@ The following example implements the power function by square-and-multiply. .. code:: - assembly { + { function power(base, exponent) -> result { switch exponent case 0 { result := 1 } diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c9769ff..11e674d1 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -968,6 +968,8 @@ Interfaces are denoted by their own keyword: :: + pragma solidity ^0.4.11; + interface Token { function transfer(address recipient, uint amount); } diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 8d7c78a2..128e6fae 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -128,7 +128,6 @@ the gas can be specified with special options ``.value()`` and ``.gas()``, respe function info() payable returns (uint ret) { return 42; } } - contract Consumer { InfoFeed feed; function setFeed(address addr) { feed = InfoFeed(addr); } @@ -231,7 +230,6 @@ creation-dependencies are not possible. } } - contract C { D d = new D(4); // will be executed as part of C's constructor diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 03ee8388..73210991 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -116,6 +116,8 @@ array in the return statement. Pretty cool, huh? Example:: + pragma solidity ^0.4.0; + contract C { function f() returns (uint8[5]) { string[4] memory adaArr = ["This", "is", "an", "array"]; @@ -192,6 +194,8 @@ should be noted that you must declare them as static memory arrays. Examples:: + pragma solidity ^0.4.0; + contract C { struct S { uint a; @@ -200,10 +204,9 @@ Examples:: S public x = S(1, 2); string name = "Ada"; - string[4] memory adaArr = ["This", "is", "an", "array"]; + string[4] adaArr = ["This", "is", "an", "array"]; } - contract D { C c = new C(); } @@ -243,6 +246,8 @@ which will be extended in the future. In addition, Arachnid has written `solidit For now, if you want to modify a string (even when you only want to know its length), you should always convert it to a ``bytes`` first:: + pragma solidity ^0.4.0; + contract C { string s; @@ -288,6 +293,8 @@ situation. If you do not want to throw, you can return a pair:: + pragma solidity ^0.4.0; + contract C { uint[] counters; @@ -302,9 +309,9 @@ If you do not want to throw, you can return a pair:: function checkCounter(uint index) { var (counter, error) = getCounter(index); if (error) { - ... + // ... } else { - ... + // ... } } } @@ -363,6 +370,8 @@ of variable it concerns: Example:: + pragma solidity ^0.4.0; + contract C { uint[] data1; uint[] data2; @@ -375,7 +384,7 @@ Example:: append(data2); } - function append(uint[] storage d) { + function append(uint[] storage d) internal { d.push(1); } } @@ -393,6 +402,9 @@ A common mistake is to declare a local variable and assume that it will be created in memory, although it will be created in storage:: /// THIS CONTRACT CONTAINS AN ERROR + + pragma solidity ^0.4.0; + contract C { uint someVariable; uint[] data; @@ -417,6 +429,8 @@ slot ``0``) is modified by ``x.push(2)``. The correct way to do this is the following:: + pragma solidity ^0.4.0; + contract C { uint someVariable; uint[] data; @@ -533,11 +547,12 @@ In the case of a ``contract A`` calling a new instance of ``contract B``, parent You will need to make sure that you have both contracts aware of each other's presence and that ``contract B`` has a ``payable`` constructor. In this example:: + pragma solidity ^0.4.0; + contract B { function B() payable {} } - contract A { address child; @@ -580,6 +595,8 @@ Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) Sure. Take care that if you cross the memory / storage boundary, independent copies will be created:: + pragma solidity ^0.4.0; + contract C { uint[20] x; @@ -588,11 +605,11 @@ independent copies will be created:: h(x); } - function g(uint[20] y) { + function g(uint[20] y) internal { y[2] = 3; } - function h(uint[20] storage y) { + function h(uint[20] storage y) internal { y[3] = 4; } } diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index e4b403f6..f9d197b7 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -197,17 +197,16 @@ for the two input parameters and two returned values. pragma solidity ^0.4.0; - /** @title Shape calculator.*/ - contract shapeCalculator{ - /**@dev Calculates a rectangle's surface and perimeter. - * @param w Width of the rectangle. - * @param h Height of the rectangle. - * @return s The calculated surface. - * @return p The calculated perimeter. - */ - function rectangle(uint w, uint h) returns (uint s, uint p) { - s = w * h; - p = 2 * (w + h); - } - } - + /** @title Shape calculator. */ + contract shapeCalculator { + /** @dev Calculates a rectangle's surface and perimeter. + * @param w Width of the rectangle. + * @param h Height of the rectangle. + * @return s The calculated surface. + * @return p The calculated perimeter. + */ + function rectangle(uint w, uint h) returns (uint s, uint p) { + s = w * h; + p = 2 * (w + h); + } + } diff --git a/docs/types.rst b/docs/types.rst index b9ecd083..0dc436c9 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -543,7 +543,7 @@ So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper. that you are accessing the low-level bytes of the UTF-8 representation, and not the individual characters! -It is possible to mark arrays ``public`` and have Solidity create a getter. +It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter <visibility-and-getters>`. The numeric index will become a required parameter for the getter. .. index:: ! array;allocating, new @@ -795,7 +795,7 @@ Because of this, mappings do not have a length or a concept of a key or value be Mappings are only allowed for state variables (or as storage reference types in internal functions). -It is possible to mark mappings ``public`` and have Solidity create a getter. +It is possible to mark mappings ``public`` and have Solidity create a :ref:`getter <visibility-and-getters>`. The ``_KeyType`` will become a required parameter for the getter and it will return ``_ValueType``. diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index f637dfb1..254f436f 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -203,7 +203,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (us == "INCLUDE") { if (_t.size() != 2) - error<IncorrectParameterCount>(); + error<IncorrectParameterCount>(us); string fileName = firstAsString(); if (fileName.empty()) error<InvalidName>("Empty file name provided"); @@ -215,7 +215,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (us == "SET") { if (_t.size() != 3) - error<IncorrectParameterCount>(); + error<IncorrectParameterCount>(us); int c = 0; for (auto const& i: _t) if (c++ == 2) @@ -226,7 +226,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (us == "GET") { if (_t.size() != 2) - error<IncorrectParameterCount>(); + error<IncorrectParameterCount>(us); m_asm.append((u256)varAddress(firstAsString())); m_asm.append(Instruction::MLOAD); } @@ -237,7 +237,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) string n; unsigned ii = 0; if (_t.size() != 3 && _t.size() != 4) - error<IncorrectParameterCount>(); + error<IncorrectParameterCount>(us); vector<string> args; for (auto const& i: _t) { @@ -288,7 +288,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (us == "LIT") { if (_t.size() < 3) - error<IncorrectParameterCount>(); + error<IncorrectParameterCount>(us); unsigned ii = 0; CodeFragment pos; bytes data; @@ -303,7 +303,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) { pos = CodeFragment(i, _s); if (pos.m_asm.deposit() != 1) - error<InvalidDeposit>(); + error<InvalidDeposit>(us); } else if (i.tag() != 0) { @@ -384,10 +384,10 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else code.push_back(CodeFragment(i, _s)); } - auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(); }; - auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(); }; - auto requireMaxSize = [&](unsigned s) { if (code.size() > s) error<IncorrectParameterCount>(); }; - auto requireDeposit = [&](unsigned i, int s) { if (code[i].m_asm.deposit() != s) error<InvalidDeposit>(); }; + auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); }; + auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); }; + auto requireMaxSize = [&](unsigned s) { if (code.size() > s) error<IncorrectParameterCount>(us); }; + auto requireDeposit = [&](unsigned i, int s) { if (code[i].m_asm.deposit() != s) error<InvalidDeposit>(us); }; if (_s.macros.count(make_pair(s, code.size()))) { @@ -412,11 +412,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (c_instructions.count(us)) { auto it = c_instructions.find(us); - int ea = instructionInfo(it->second).args; - if (ea >= 0) - requireSize(ea); - else - requireMinSize(-ea); + requireSize(instructionInfo(it->second).args); for (unsigned i = code.size(); i; --i) m_asm.append(code[i - 1].m_asm, 1); @@ -475,7 +471,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) m_asm.append(code[1].m_asm, minDep); m_asm << end.tag(); if (m_asm.deposit() != deposit) - error<InvalidDeposit>(); + error<InvalidDeposit>(us); } else if (us == "WHEN" || us == "UNLESS") { diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index 05376cd5..4ec11ca9 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -34,7 +34,10 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error { CompilerState cs; cs.populateStandard(); - bytes ret = CodeFragment::compile(_src, cs).assembly(cs).optimise(_opt).assemble().bytecode; + auto assembly = CodeFragment::compile(_src, cs).assembly(cs); + if (_opt) + assembly = assembly.optimise(true); + bytes ret = assembly.assemble().bytecode; for (auto i: cs.treesToKill) killBigints(i); return ret; @@ -70,7 +73,10 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v CompilerState cs; cs.populateStandard(); stringstream ret; - CodeFragment::compile(_src, cs).assembly(cs).optimise(_opt).stream(ret); + auto assembly = CodeFragment::compile(_src, cs).assembly(cs); + if (_opt) + assembly = assembly.optimise(true); + assembly.stream(ret); for (auto i: cs.treesToKill) killBigints(i); return ret.str(); diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index b1b31163..46477e1e 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -38,12 +38,14 @@ bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) bool StaticAnalyzer::visit(ContractDefinition const& _contract) { m_library = _contract.isLibrary(); + m_currentContract = &_contract; return true; } void StaticAnalyzer::endVisit(ContractDefinition const&) { m_library = false; + m_currentContract = nullptr; } bool StaticAnalyzer::visit(FunctionDefinition const& _function) @@ -54,6 +56,7 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) solAssert(!m_currentFunction, ""); solAssert(m_localVarUseCount.empty(), ""); m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); + m_constructor = _function.isConstructor(); return true; } @@ -61,6 +64,7 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) { m_currentFunction = nullptr; m_nonPayablePublic = false; + m_constructor = false; for (auto const& var: m_localVarUseCount) if (var.second == 0) m_errorReporter.warning(var.first->location(), "Unused local variable"); @@ -131,6 +135,11 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) "\"callcode\" has been deprecated in favour of \"delegatecall\"." ); + if (m_constructor && m_currentContract) + if (ContractType const* type = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type.get())) + if (type->contractDefinition() == *m_currentContract) + m_errorReporter.warning(_memberAccess.location(), "\"this\" used in constructor."); + return true; } diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index cd6913b5..21a487df 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -77,6 +77,12 @@ private: std::map<VariableDeclaration const*, int> m_localVarUseCount; FunctionDefinition const* m_currentFunction = nullptr; + + /// Flag that indicates a constructor. + bool m_constructor = false; + + /// Current contract. + ContractDefinition const* m_currentContract = nullptr; }; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index cde14ea0..81ddc754 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -704,7 +704,7 @@ public: ASTPointer<ParameterList> const& _parameters, ASTPointer<Block> const& _body ): - CallableDeclaration(_location, _name, Visibility::Default, _parameters), + CallableDeclaration(_location, _name, Visibility::Internal, _parameters), Documented(_documentation), m_body(_body) { diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 76bfb1a8..bcfccc3e 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -196,9 +196,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::UInt: return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned); case Token::Fixed: - return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Signed); + return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed); case Token::UFixed: - return make_shared<FixedPointType>(128, 128, FixedPointType::Modifier::Unsigned); + return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: @@ -352,12 +352,11 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedPoint) { FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); - if (convertTo.integerBits() < m_bits || isAddress()) + + if (isAddress()) return false; - else if (isSigned()) - return convertTo.isSigned(); else - return !convertTo.isSigned() || convertTo.integerBits() > m_bits; + return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue(); } else return false; @@ -487,22 +486,20 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons return MemberList::MemberMap(); } -FixedPointType::FixedPointType(int _integerBits, int _fractionalBits, FixedPointType::Modifier _modifier): - m_integerBits(_integerBits), m_fractionalBits(_fractionalBits), m_modifier(_modifier) +FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPointType::Modifier _modifier): + m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_modifier(_modifier) { solAssert( - m_integerBits + m_fractionalBits > 0 && - m_integerBits + m_fractionalBits <= 256 && - m_integerBits % 8 == 0 && - m_fractionalBits % 8 == 0, + 8 <= m_totalBits && m_totalBits <= 256 && m_totalBits % 8 == 0 && + 0 <= m_fractionalDigits && m_fractionalDigits <= 80, "Invalid bit number(s) for fixed type: " + - dev::toString(_integerBits) + "x" + dev::toString(_fractionalBits) - ); + dev::toString(_totalBits) + "x" + dev::toString(_fractionalDigits) + ); } string FixedPointType::identifier() const { - return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(integerBits()) + "x" + std::to_string(fractionalBits()); + return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits); } bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -510,12 +507,10 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const if (_convertTo.category() == category()) { FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); - if (convertTo.m_integerBits < m_integerBits || convertTo.m_fractionalBits < m_fractionalBits) + if (convertTo.numBits() < m_totalBits || convertTo.fractionalDigits() < m_fractionalDigits) return false; - else if (isSigned()) - return convertTo.isSigned(); else - return !convertTo.isSigned() || (convertTo.m_integerBits > m_integerBits); + return convertTo.maxIntegerValue() >= maxIntegerValue() && convertTo.minIntegerValue() <= minIntegerValue(); } return false; } @@ -549,13 +544,30 @@ bool FixedPointType::operator==(Type const& _other) const if (_other.category() != category()) return false; FixedPointType const& other = dynamic_cast<FixedPointType const&>(_other); - return other.m_integerBits == m_integerBits && other.m_fractionalBits == m_fractionalBits && other.m_modifier == m_modifier; + return other.m_totalBits == m_totalBits && other.m_fractionalDigits == m_fractionalDigits && other.m_modifier == m_modifier; } string FixedPointType::toString(bool) const { string prefix = isSigned() ? "fixed" : "ufixed"; - return prefix + dev::toString(m_integerBits) + "x" + dev::toString(m_fractionalBits); + return prefix + dev::toString(m_totalBits) + "x" + dev::toString(m_fractionalDigits); +} + +bigint FixedPointType::maxIntegerValue() const +{ + bigint maxValue = (bigint(1) << (m_totalBits - (isSigned() ? 1 : 0))) - 1; + return maxValue / pow(bigint(10), m_fractionalDigits); +} + +bigint FixedPointType::minIntegerValue() const +{ + if (isSigned()) + { + bigint minValue = -(bigint(1) << (m_totalBits - (isSigned() ? 1 : 0))); + return minValue / pow(bigint(10), m_fractionalDigits); + } + else + return bigint(0); } TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const @@ -743,13 +755,9 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedPoint) { if (auto fixed = fixedPointType()) - { - // We disallow implicit conversion if we would have to truncate (fixedPointType() - // can return a type that requires truncation). - rational value = m_value * (bigint(1) << fixed->fractionalBits()); - return value.denominator() == 1 && fixed->isImplicitlyConvertibleTo(_convertTo); - } - return false; + return fixed->isImplicitlyConvertibleTo(_convertTo); + else + return false; } else if (_convertTo.category() == Category::FixedBytes) { @@ -953,10 +961,9 @@ u256 RationalNumberType::literalValue(Literal const*) const else { auto fixed = fixedPointType(); - solAssert(!!fixed, ""); - rational shifted = m_value * (bigint(1) << fixed->fractionalBits()); - // truncate - shiftedValue = shifted.numerator() / shifted.denominator(); + solAssert(fixed, ""); + int fractionalDigits = fixed->fractionalDigits(); + shiftedValue = (m_value.numerator() / m_value.denominator()) * pow(bigint(10), fractionalDigits); } // we ignore the literal and hope that the type was correctly determined @@ -997,22 +1004,21 @@ shared_ptr<IntegerType const> RationalNumberType::integerType() const shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const { bool negative = (m_value < 0); - unsigned fractionalBits = 0; + unsigned fractionalDigits = 0; rational value = abs(m_value); // We care about the sign later. rational maxValue = negative ? rational(bigint(1) << 255, 1): rational((bigint(1) << 256) - 1, 1); - while (value * 0x100 <= maxValue && value.denominator() != 1 && fractionalBits < 256) + while (value * 10 <= maxValue && value.denominator() != 1 && fractionalDigits < 80) { - value *= 0x100; - fractionalBits += 8; + value *= 10; + fractionalDigits++; } if (value > maxValue) return shared_ptr<FixedPointType const>(); - // u256(v) is the actual value that will be put on the stack - // From here on, very similar to integerType() + // This means we round towards zero for positive and negative values. bigint v = value.numerator() / value.denominator(); if (negative) // modify value to satisfy bit requirements for negative numbers: @@ -1022,26 +1028,11 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const if (v > u256(-1)) return shared_ptr<FixedPointType const>(); - unsigned totalBits = bytesRequired(v) * 8; + unsigned totalBits = max(bytesRequired(v), 1u) * 8; solAssert(totalBits <= 256, ""); - unsigned integerBits = totalBits >= fractionalBits ? totalBits - fractionalBits : 0; - // Special case: Numbers between -1 and 0 have their sign bit in the fractional part. - if (negative && abs(m_value) < 1 && totalBits > fractionalBits) - { - fractionalBits += 8; - integerBits = 0; - } - - if (integerBits > 256 || fractionalBits > 256 || fractionalBits + integerBits > 256) - return shared_ptr<FixedPointType const>(); - if (integerBits == 0 && fractionalBits == 0) - { - integerBits = 0; - fractionalBits = 8; - } return make_shared<FixedPointType>( - integerBits, fractionalBits, + totalBits, fractionalDigits, negative ? FixedPointType::Modifier::Signed : FixedPointType::Modifier::Unsigned ); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c24cc11a..3d7dad16 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -342,7 +342,7 @@ public: }; virtual Category category() const override { return Category::FixedPoint; } - explicit FixedPointType(int _integerBits, int _fractionalBits, Modifier _modifier = Modifier::Unsigned); + explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned); virtual std::string identifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; @@ -352,8 +352,8 @@ public: virtual bool operator==(Type const& _other) const override; - virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : (m_integerBits + m_fractionalBits) / 8; } - virtual unsigned storageBytes() const override { return (m_integerBits + m_fractionalBits) / 8; } + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_totalBits / 8; } + virtual unsigned storageBytes() const override { return m_totalBits / 8; } virtual bool isValueType() const override { return true; } virtual std::string toString(bool _short) const override; @@ -361,14 +361,21 @@ public: virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } - int numBits() const { return m_integerBits + m_fractionalBits; } - int integerBits() const { return m_integerBits; } - int fractionalBits() const { return m_fractionalBits; } + /// Number of bits used for this type in total. + int numBits() const { return m_totalBits; } + /// Number of decimal digits after the radix point. + int fractionalDigits() const { return m_fractionalDigits; } bool isSigned() const { return m_modifier == Modifier::Signed; } + /// @returns the largest integer value this type con hold. Note that this is not the + /// largest value in general. + bigint maxIntegerValue() const; + /// @returns the smallest integer value this type can hold. Note hat this is not the + /// smallest value in general. + bigint minIntegerValue() const; private: - int m_integerBits; - int m_fractionalBits; + int m_totalBits; + int m_fractionalDigits; Modifier m_modifier; }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 7067ddd5..782aad9d 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -504,7 +504,7 @@ void CompilerUtils::convertType( //shift all integer bits onto the left side of the fixed type FixedPointType const& targetFixedPointType = dynamic_cast<FixedPointType const&>(_targetType); if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) - if (targetFixedPointType.integerBits() > typeOnStack->numBits()) + if (targetFixedPointType.numBits() > typeOnStack->numBits()) cleanHigherOrderBits(*typeOnStack); solUnimplemented("Not yet implemented - FixedPointType."); } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index e2507821..7a87875c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -451,18 +451,20 @@ Json::Value const& CompilerStack::natspec(Contract const& _contract, Documentati { case DocumentationType::NatspecUser: doc = &_contract.userDocumentation; + // caches the result + if (!*doc) + doc->reset(new Json::Value(Natspec::userDocumentation(*_contract.contract))); break; case DocumentationType::NatspecDev: doc = &_contract.devDocumentation; + // caches the result + if (!*doc) + doc->reset(new Json::Value(Natspec::devDocumentation(*_contract.contract))); break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } - // caches the result - if (!*doc) - doc->reset(new Json::Value(Natspec::documentation(*_contract.contract, _type))); - return *(*doc); } @@ -474,12 +476,12 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const return methodIdentifiers; } -string const& CompilerStack::onChainMetadata(string const& _contractName) const +string const& CompilerStack::metadata(string const& _contractName) const { if (m_stackState != CompilationSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); - return contract(_contractName).onChainMetadata; + return contract(_contractName).metadata; } Scanner const& CompilerStack::scanner(string const& _sourceName) const @@ -673,11 +675,11 @@ void CompilerStack::compileContract( shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); - string onChainMetadata = createOnChainMetadata(compiledContract); + string metadata = createMetadata(compiledContract); bytes cborEncodedMetadata = - // CBOR-encoding of {"bzzr0": dev::swarmHash(onChainMetadata)} + // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)} bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + - dev::swarmHash(onChainMetadata).asBytes(); + dev::swarmHash(metadata).asBytes(); solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large"); // 16-bit big endian length cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2); @@ -710,7 +712,7 @@ void CompilerStack::compileContract( BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Assembly exception for deployed bytecode")); } - compiledContract.onChainMetadata = onChainMetadata; + compiledContract.metadata = metadata; _compiledContracts[compiledContract.contract] = &compiler->assembly(); try @@ -771,7 +773,7 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } -string CompilerStack::createOnChainMetadata(Contract const& _contract) const +string CompilerStack::createMetadata(Contract const& _contract) const { Json::Value meta; meta["version"] = 1; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 03a1b806..6b8fb538 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -183,7 +183,7 @@ public: /// @returns a JSON representing a map of method identifiers (hashes) to function names. Json::Value methodIdentifiers(std::string const& _contractName) const; - std::string const& onChainMetadata(std::string const& _contractName) const; + std::string const& metadata(std::string const& _contractName) const; void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions @@ -223,7 +223,7 @@ private: eth::LinkerObject object; eth::LinkerObject runtimeObject; eth::LinkerObject cloneObject; - std::string onChainMetadata; ///< The metadata json that will be hashed into the chain. + std::string metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr<Json::Value const> abi; mutable std::unique_ptr<Json::Value const> userDocumentation; mutable std::unique_ptr<Json::Value const> devDocumentation; @@ -255,7 +255,7 @@ private: /// does not exist. ContractDefinition const& contractDefinition(std::string const& _contractName) const; - std::string createOnChainMetadata(Contract const& _contract) const; + std::string createMetadata(Contract const& _contract) const; std::string computeSourceMapping(eth::AssemblyItems const& _items) const; Json::Value const& contractABI(Contract const&) const; Json::Value const& natspec(Contract const&, DocumentationType _type) const; diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index 70486e23..7f7084ef 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -26,28 +26,11 @@ #include <libsolidity/interface/Natspec.h> #include <boost/range/irange.hpp> #include <libsolidity/ast/AST.h> -#include <libsolidity/interface/CompilerStack.h> using namespace std; using namespace dev; using namespace dev::solidity; -Json::Value Natspec::documentation( - ContractDefinition const& _contractDef, - DocumentationType _type -) -{ - switch(_type) - { - case DocumentationType::NatspecUser: - return userDocumentation(_contractDef); - case DocumentationType::NatspecDev: - return devDocumentation(_contractDef); - } - - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); -} - Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h index bec9acd2..9ac3efea 100644 --- a/libsolidity/interface/Natspec.h +++ b/libsolidity/interface/Natspec.h @@ -39,7 +39,6 @@ class ContractDefinition; class Type; using TypePointer = std::shared_ptr<Type const>; struct DocTag; -enum class DocumentationType: uint8_t; enum class DocTagType: uint8_t { @@ -61,15 +60,6 @@ enum class CommentOwner class Natspec { public: - /// Get the given type of documentation - /// @param _contractDef The contract definition - /// @param _type The type of the documentation. Can be one of the - /// types provided by @c DocumentationType - /// @return A JSON representation of provided type - static Json::Value documentation( - ContractDefinition const& _contractDef, - DocumentationType _type - ); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A JSON representation of the contract's user documentation diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index df1afdc5..82eeac3d 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -386,7 +386,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // ABI, documentation and metadata Json::Value contractData(Json::objectValue); contractData["abi"] = m_compilerStack.contractABI(contractName); - contractData["metadata"] = m_compilerStack.onChainMetadata(contractName); + contractData["metadata"] = m_compilerStack.metadata(contractName); contractData["userdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecUser); contractData["devdoc"] = m_compilerStack.natspec(contractName, DocumentationType::NatspecDev); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index b0cf364e..b98991f3 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -87,7 +87,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) nodes.push_back(parseContractDefinition(token)); break; default: - fatalParserError(string("Expected import directive or contract definition.")); + fatalParserError(string("Expected pragma, import directive or contract/interface/library definition.")); } } return nodeFactory.createNode<SourceUnit>(nodes); diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 66312f69..9cec0303 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -70,7 +70,7 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN) { solAssert( - _first + _second <= 256 && _first % 8 == 0 && _second % 8 == 0, + _first >= 8 && _first <= 256 && _first % 8 == 0 && _second <= 80, "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." ); } @@ -157,12 +157,8 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s ) { int n = parseSize(positionX + 1, _literal.end()); if ( - 0 <= m && m <= 256 && - 8 <= n && n <= 256 && - m + n > 0 && - m + n <= 256 && - m % 8 == 0 && - n % 8 == 0 + 8 <= m && m <= 256 && m % 8 == 0 && + 0 <= n && n <= 80 ) { if (keyword == Token::UFixed) return make_tuple(Token::UFixedMxN, m, n); diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index a5515d81..18e83e75 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -18,7 +18,7 @@ else() endif() if (EMSCRIPTEN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") add_executable(soljson jsonCompiler.cpp ${HEADERS}) eth_use(soljson REQUIRED Solidity::solidity) else() diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 0222ccb0..db497d84 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -292,12 +292,12 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) cout << "Function signatures: " << endl << out; } -void CommandLineInterface::handleOnChainMetadata(string const& _contract) +void CommandLineInterface::handleMetadata(string const& _contract) { if (!m_args.count(g_argMetadata)) return; - string data = m_compiler->onChainMetadata(_contract); + string data = m_compiler->metadata(_contract); if (m_args.count(g_argOutputDir)) createFile(m_compiler->filesystemFriendlyName(_contract) + "_meta.json", data); else @@ -850,7 +850,7 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strAbi)) contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName)); if (requests.count("metadata")) - contractData["metadata"] = m_compiler->onChainMetadata(contractName); + contractData["metadata"] = m_compiler->metadata(contractName); if (requests.count(g_strBinary)) contractData[g_strBinary] = m_compiler->object(contractName).toHex(); if (requests.count(g_strBinaryRuntime)) @@ -1164,7 +1164,7 @@ void CommandLineInterface::outputCompilationResults() handleBytecode(contract); handleSignatureHashes(contract); - handleOnChainMetadata(contract); + handleMetadata(contract); handleABI(contract); handleNatspec(DocumentationType::NatspecDev, contract); handleNatspec(DocumentationType::NatspecUser, contract); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index b482c20b..8a476ef5 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -64,7 +64,7 @@ private: void handleOpcode(std::string const& _contract); void handleBytecode(std::string const& _contract); void handleSignatureHashes(std::string const& _contract); - void handleOnChainMetadata(std::string const& _contract); + void handleMetadata(std::string const& _contract); void handleABI(std::string const& _contract); void handleNatspec(DocumentationType _type, std::string const& _contract); void handleGasEstimation(std::string const& _contract); diff --git a/test/contracts/LLL_ENS.cpp b/test/contracts/LLL_ENS.cpp index 3df1546d..c5fe8a82 100644 --- a/test/contracts/LLL_ENS.cpp +++ b/test/contracts/LLL_ENS.cpp @@ -345,7 +345,7 @@ protected: if (!s_compiledEns) { vector<string> errors; - s_compiledEns.reset(new bytes(compileLLL(ensCode, false, &errors))); + s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().optimize, &errors))); BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledEns, true); diff --git a/test/contracts/LLL_ERC20.cpp b/test/contracts/LLL_ERC20.cpp index 3ee1aa98..25665d64 100644 --- a/test/contracts/LLL_ERC20.cpp +++ b/test/contracts/LLL_ERC20.cpp @@ -396,7 +396,7 @@ protected: if (!s_compiledErc20) { vector<string> errors; - s_compiledErc20.reset(new bytes(compileLLL(erc20Code, false, &errors))); + s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().optimize, &errors))); BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledErc20, true); diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index 4e896fd0..9292d963 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -165,6 +165,56 @@ BOOST_AUTO_TEST_CASE(conditional_seq) BOOST_CHECK(callFallback() == toBigEndian(u256(1))); } +BOOST_AUTO_TEST_CASE(conditional_nested_else) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'input (calldataload 0x04)) + ;; Calculates width in bytes of utf-8 characters. + (return + (if (< input 0x80) 1 + (if (< input 0xE0) 2 + (if (< input 0xF0) 3 + (if (< input 0xF8) 4 + (if (< input 0xFC) 5 + 6)))))))) + + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0x00) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("test()", 0x80) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("test()", 0xe0) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("test()", 0xf0) == encodeArgs(u256(4))); + BOOST_CHECK(callContractFunction("test()", 0xf8) == encodeArgs(u256(5))); + BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); +} + +BOOST_AUTO_TEST_CASE(conditional_nested_then) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'input (calldataload 0x04)) + ;; Calculates width in bytes of utf-8 characters. + (return + (if (>= input 0x80) + (if (>= input 0xE0) + (if (>= input 0xF0) + (if (>= input 0xF8) + (if (>= input 0xFC) + 6 5) 4) 3) 2) 1)))) + + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()", 0x00) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("test()", 0x80) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("test()", 0xe0) == encodeArgs(u256(3))); + BOOST_CHECK(callContractFunction("test()", 0xf0) == encodeArgs(u256(4))); + BOOST_CHECK(callContractFunction("test()", 0xf8) == encodeArgs(u256(5))); + BOOST_CHECK(callContractFunction("test()", 0xfc) == encodeArgs(u256(6))); +} + BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index ef560b12..8b41e1db 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -47,8 +47,9 @@ public: GasMeterTestFramework() { } void compile(string const& _sourceCode) { - m_compiler.setSource("pragma solidity >= 0.0;" + _sourceCode); - ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed"); + m_compiler.reset(false); + m_compiler.addSource("", "pragma solidity >=0.0;\n" + _sourceCode); + ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(dev::test::Options::get().optimize), "Compiling contract failed"); AssemblyItems const* items = m_compiler.runtimeAssemblyItems(""); ASTNode const& sourceUnit = m_compiler.ast(); diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index aa690f0b..a6a7bc5b 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -24,6 +24,7 @@ #include <regex> #include <boost/test/unit_test.hpp> #include <libdevcore/JSON.h> +#include <libsolidity/interface/Version.h> #include "../Metadata.h" #include "../TestHelper.h" @@ -32,6 +33,8 @@ using namespace std; extern "C" { +extern char const* version(); +extern char const* license(); extern char const* compileJSONMulti(char const* _input, bool _optimize); } @@ -57,6 +60,18 @@ Json::Value compile(string const& _input) BOOST_AUTO_TEST_SUITE(JSONCompiler) +BOOST_AUTO_TEST_CASE(read_version) +{ + string output(version()); + BOOST_CHECK(output.find(VersionString) == 0); +} + +BOOST_AUTO_TEST_CASE(read_license) +{ + string output(license()); + BOOST_CHECK(output.find("GNU GENERAL PUBLIC LICENSE") != string::npos); +} + BOOST_AUTO_TEST_CASE(basic_compilation) { char const* input = R"( diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 60bb2e4e..ce1a7b18 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -43,9 +43,10 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) } )"; CompilerStack compilerStack; - BOOST_REQUIRE(compilerStack.compile(std::string(sourceCode))); + compilerStack.addSource("", std::string(sourceCode)); + ETH_TEST_REQUIRE_NO_THROW(compilerStack.compile(dev::test::Options::get().optimize), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; - std::string const& metadata = compilerStack.onChainMetadata("test"); + std::string const& metadata = compilerStack.metadata("test"); BOOST_CHECK(dev::test::isValidMetadata(metadata)); bytes hash = dev::swarmHash(metadata).asBytes(); BOOST_REQUIRE(hash.size() == 32); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index f87390e1..452a2662 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -42,7 +42,9 @@ public: void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) { - ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing contract failed"); + m_compilerStack.reset(false); + m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedInterface = m_compilerStack.contractABI(""); Json::Value expectedInterface; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index cb39101e..4b29243a 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -112,7 +112,14 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, ) { if (error && !_allowMultipleErrors) - BOOST_FAIL("Multiple errors found"); + { + string message("Multiple errors found: "); + for (auto const& e: errorReporter.errors()) + if (string const* description = boost::get_error_info<errinfo_comment>(*e)) + message += *description + ", "; + + BOOST_FAIL(message); + } if (!error) error = currentError; } @@ -2283,6 +2290,9 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 30, 0)) == *make_shared<FixedBytesType>(30)); BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared<FixedBytesType>(31)); BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared<FixedBytesType>(32)); + + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned)); } BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) @@ -4026,7 +4036,7 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_types_0x7_mxn) fixed0x7 a = .3; } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, DeclarationError, "Identifier not found"); } BOOST_AUTO_TEST_CASE(invalid_fixed_types_long_invalid_identifier) @@ -4036,7 +4046,7 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_types_long_invalid_identifier) fixed99999999999999999999999999999999999999x7 b = 9.5; } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, DeclarationError, "Identifier not found"); } BOOST_AUTO_TEST_CASE(invalid_fixed_types_7x8_mxn) @@ -4046,7 +4056,7 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_types_7x8_mxn) fixed7x8 c = 3.12345678; } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, DeclarationError, "Identifier not found"); } BOOST_AUTO_TEST_CASE(library_instances_cannot_be_used) @@ -4060,7 +4070,7 @@ BOOST_AUTO_TEST_CASE(library_instances_cannot_be_used) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "Member \"l\" not found or not visible after argument-dependent lookup in library L"); } BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) @@ -4072,7 +4082,7 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, DeclarationError, "Identifier not found"); } BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) @@ -4080,8 +4090,8 @@ BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) char const* text = R"( contract test { function f() { - uint128 a = 3; - int128 b = 4; + uint64 a = 3; + int64 b = 4; fixed c = b; ufixed d = a; c; d; @@ -4130,7 +4140,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "Type fixed128x19 is not implicitly convertible to expected type int256"); } BOOST_AUTO_TEST_CASE(rational_unary_operation) @@ -4138,10 +4148,9 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) char const* text = R"( contract test { function f() { - ufixed8x16 a = 3.25; - fixed8x16 b = -3.25; - a; - b; + ufixed16x2 a = 3.25; + fixed16x2 b = -3.25; + a; b; } } )"; @@ -4149,13 +4158,13 @@ BOOST_AUTO_TEST_CASE(rational_unary_operation) text = R"( contract test { function f() { - ufixed8x16 a = +3.25; - fixed8x16 b = -3.25; + ufixed16x2 a = +3.25; + fixed16x2 b = -3.25; a; b; } } )"; - CHECK_WARNING(text,"Use of unary + is deprecated"); + CHECK_WARNING(text, "Use of unary + is deprecated"); text = R"( contract test { function f(uint x) { @@ -4172,10 +4181,10 @@ BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) char const* text = R"( contract A { function f() { - ufixed0x8 a = 0.5; - ufixed0x56 b = 0.0000000000000006661338147750939242541790008544921875; - fixed0x8 c = -0.5; - fixed0x56 d = -0.0000000000000006661338147750939242541790008544921875; + ufixed16x2 a = 0.5; + ufixed256x52 b = 0.0000000000000006661338147750939242541790008544921875; + fixed16x2 c = -0.5; + fixed256x52 d = -0.0000000000000006661338147750939242541790008544921875; a; b; c; d; } } @@ -4188,12 +4197,12 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) char const* text = R"( contract test { function f() { - ufixed248x8 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; - ufixed0x256 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637829135688384325135165352082715782143655824815685807141335814463015972119819459298455224338812271036061391763384038070334798471324635050876128428143374549108557403087615966796875; - ufixed0x256 c = 0.0000000000015198847363997979984922685411315294875958273543939784943666052676464653042434787697605517039455161817147718251801220885263595179331845639229818863564267318422845592626219390573301877339317935702714669975697814319204326238832436501979827880859375; - fixed248x8 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5; - fixed0x256 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125; - fixed0x256 g = -0.00011788606643744342575580035176794825198893968114429702091846411734101080123092162893656820177312738451291806995868682861328125; + ufixed256x1 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; + ufixed256x77 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637; + ufixed224x78 c = 0.000000000001519884736399797998492268541131529487595827354393978494366605267646; + fixed256x1 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5; + fixed256x76 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123; + fixed256x79 g = -0.0001178860664374434257558003517679482519889396811442970209184641173410108012309; a; b; c; d; e; g; } } @@ -4201,17 +4210,30 @@ BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(zero_handling) +{ + char const* text = R"( + contract test { + function f() { + fixed16x2 a = 0; a; + ufixed32x1 b = 0; b; + } + } + )"; + CHECK_SUCCESS(text); +} + BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) { char const* text = R"( contract test { function f() { ufixed a = 11/4; - ufixed248x8 b = a; + ufixed248x8 b = a; b; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "Type ufixed128x19 is not implicitly convertible to expected type ufixed248x8"); } BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) @@ -4219,11 +4241,11 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) char const* text = R"( contract test { function f() { - ufixed0x256 a = 1/3; + ufixed256x1 a = 1/3; a; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type ufixed256x1"); } BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions) @@ -4231,10 +4253,9 @@ BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions) char const* text = R"( contract test { function f() { - ufixed0x256 a = ufixed0x256(1/3); - ufixed0x248 b = ufixed0x248(1/3); - ufixed0x8 c = ufixed0x8(1/3); - a; b; c; + ufixed256x80 a = ufixed256x80(1/3); a; + ufixed248x80 b = ufixed248x80(1/3); b; + ufixed8x1 c = ufixed8x1(1/3); c; } } )"; @@ -4246,23 +4267,35 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) char const* text = R"( contract test { function f() { - uint[3.5] a; + uint[3.5] a; a; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); } -BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_fixed_type) +BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) { char const* text = R"( contract test { function f() { - uint[fixed(3.5)] a; + uint[fixed(3.5)] a; a; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); +} + +BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) +{ + char const* text = R"( + contract test { + function f() { + uint[ufixed(3.5)] a; a; + } + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal"); } BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) @@ -4270,11 +4303,11 @@ BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) char const* text = R"( contract test { function f() { - bytes32 c = 3.2; + bytes32 c = 3.2; c; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type bytes32"); } BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) @@ -4283,18 +4316,18 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) contract test { function f() { fixed a = 3.25; - bytes32 c = a; + bytes32 c = a; c; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "fixed128x19 is not implicitly convertible to expected type bytes32"); } BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) { char const* text = R"( contract test { - mapping(ufixed8x248 => string) fixedString; + mapping(ufixed8x1 => string) fixedString; function f() { fixedString[0.5] = "Half"; } @@ -4334,7 +4367,7 @@ BOOST_AUTO_TEST_CASE(inline_array_rationals) char const* text = R"( contract test { function f() { - ufixed8x8[4] memory a = [3.5, 4.125, 2.5, 4.0]; + ufixed128x3[4] memory a = [ufixed128x3(3.5), 4.125, 2.5, 4.0]; } } )"; @@ -4351,7 +4384,7 @@ BOOST_AUTO_TEST_CASE(rational_index_access) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "rational_const 1/2 is not implicitly convertible to expected type uint256"); } BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) @@ -4359,12 +4392,12 @@ BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) char const* text = R"( contract test { function f() { - ufixed8x8 a = 3.5 * 3; - ufixed8x8 b = 4 - 2.5; - ufixed8x8 c = 11 / 4; - ufixed16x240 d = 599 + 0.21875; - ufixed8x248 e = ufixed8x248(35.245 % 12.9); - ufixed8x248 f = ufixed8x248(1.2 % 2); + ufixed64x8 a = 3.5 * 3; + ufixed64x8 b = 4 - 2.5; + ufixed64x8 c = 11 / 4; + ufixed240x5 d = 599 + 0.21875; + ufixed256x80 e = ufixed256x80(35.245 % 12.9); + ufixed256x80 f = ufixed256x80(1.2 % 2); fixed g = 2 ** -2; a; b; c; d; e; f; g; } @@ -4373,7 +4406,7 @@ BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_decimal) +BOOST_AUTO_TEST_CASE(rational_as_exponent_value_signed) { char const* text = R"( contract test { @@ -4382,10 +4415,10 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_decimal) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } -BOOST_AUTO_TEST_CASE(rational_as_exponent_value_pos_decimal) +BOOST_AUTO_TEST_CASE(rational_as_exponent_value_unsigned) { char const* text = R"( contract test { @@ -4394,7 +4427,7 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value_pos_decimal) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(rational_as_exponent_half) @@ -4402,11 +4435,11 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_half) char const* text = R"( contract test { function f() { - ufixed24x24 b = 2 ** (1/2); + 2 ** (1/2); } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_quarter) @@ -4414,11 +4447,11 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_quarter) char const* text = R"( contract test { function f() { - fixed40x40 c = 42 ** (-1/4); + 42 ** (-1/4); } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_15) @@ -4426,23 +4459,11 @@ BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_15) char const* text = R"( contract test { function f() { - ufixed a = 3 ** ufixed(1.5); - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_half) -{ - char const* text = R"( - contract test { - function f() { - ufixed b = 2 ** ufixed(1/2); + var a = 3 ** ufixed(1.5); } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_neg) @@ -4450,23 +4471,11 @@ BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_neg) char const* text = R"( contract test { function f() { - fixed c = 42 ** fixed(-1/4); - } - } - )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_neg_decimal) -{ - char const* text = R"( - contract test { - function f() { - fixed d = 16 ** fixed(-0.5); + var c = 42 ** fixed(-1/4); } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) @@ -4506,7 +4515,7 @@ BOOST_AUTO_TEST_CASE(var_handle_divided_integers) } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS(text); } BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) @@ -4514,11 +4523,11 @@ BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) char const* text = R"( contract test { function f() { - fixed a = ~3.5; + ~fixed(3.5); } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "cannot be applied"); } BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) @@ -4526,11 +4535,11 @@ BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) char const* text = R"( contract test { function f() { - fixed a = 1.5 | 3; + fixed(1.5) | 3; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) @@ -4538,11 +4547,11 @@ BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) char const* text = R"( contract test { function f() { - fixed a = 1.75 ^ 3; + fixed(1.75) ^ 3; } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) @@ -4550,25 +4559,11 @@ BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) char const* text = R"( contract test { function f() { - fixed a = 1.75 & 3; + fixed(1.75) & 3; } } )"; - BOOST_CHECK(!success(text)); -} - -BOOST_AUTO_TEST_CASE(zero_handling) -{ - char const* text = R"( - contract test { - function f() { - fixed8x8 a = 0; - ufixed8x8 b = 0; - a; b; - } - } - )"; - CHECK_SUCCESS(text); + CHECK_ERROR(text, TypeError, "not compatible with types"); } BOOST_AUTO_TEST_CASE(missing_bool_conversion) @@ -4588,7 +4583,7 @@ BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction) char const* text = R"( contract test { function f() { - ufixed a = uint128(1) + ufixed(2); + ufixed a = uint64(1) + ufixed(2); } } )"; @@ -4618,7 +4613,7 @@ BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion) } } )"; - BOOST_CHECK(!success(text)); + CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type uint256. Try converting to type ufixed256x77"); } BOOST_AUTO_TEST_CASE(unused_return_value) @@ -6359,6 +6354,34 @@ BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed from \"literal_string \"abc\"\" to \"string storage pointer\""); } +BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer) +{ + char const* text = R"( + contract C { + struct S { } + modifier m(S storage x) { + x; + _; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(using_this_in_constructor) +{ + char const* text = R"( + contract C { + function C() { + this.f(); + } + function f() { + } + } + )"; + CHECK_WARNING(text, "\"this\" used in constructor"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 2a7376b9..aa343561 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -45,7 +45,9 @@ public: bool _userDocumentation ) { - ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing failed"); + m_compilerStack.reset(false); + m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); + ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); Json::Value generatedDocumentation; if (_userDocumentation) |