diff options
| -rw-r--r-- | Changelog.md | 1 | ||||
| -rw-r--r-- | docs/grammar.txt | 2 | ||||
| -rw-r--r-- | docs/miscellaneous.rst | 55 | ||||
| -rw-r--r-- | libdevcore/JSON.h | 4 | ||||
| -rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 4 | ||||
| -rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 5 | ||||
| -rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 2 | ||||
| -rw-r--r-- | libsolidity/codegen/LValue.cpp | 1 | ||||
| -rwxr-xr-x | scripts/isolateTests.py | 24 | ||||
| -rwxr-xr-x | scripts/isolate_tests.py | 44 | ||||
| -rw-r--r-- | solc/CommandLineInterface.cpp | 1 | ||||
| -rw-r--r-- | test/ExecutionFramework.cpp | 14 | ||||
| -rw-r--r-- | test/ExecutionFramework.h | 1 | ||||
| -rw-r--r-- | test/TestHelper.cpp | 2 | ||||
| -rw-r--r-- | test/TestHelper.h | 1 | ||||
| -rw-r--r-- | test/libsolidity/Assembly.cpp | 2 | ||||
| -rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 28 | ||||
| -rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 140 | ||||
| -rw-r--r-- | test/libsolidity/SolidityParser.cpp | 71 |
19 files changed, 318 insertions, 84 deletions
diff --git a/Changelog.md b/Changelog.md index 58cf1484..c76d10e1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: Bugfixes: * Code generator: throw if calling the identity precompile failed during memory (array) copying. * Type checker: string literals that are not valid UTF-8 cannot be converted to string type + * Code generator: any non-zero value given as a boolean argument is now converted into 1. ### 0.4.6 (2016-11-22) diff --git a/docs/grammar.txt b/docs/grammar.txt index b0c9da98..d15fbaf7 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -112,7 +112,7 @@ Ufixed = 'ufixed' | 'ufixed0x8' | 'ufixed0x16' | 'ufixed0x24' | 'ufixed0x32' | ' InlineAssemblyBlock = '{' AssemblyItem* '}' -AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyLocalBinding | AssemblyAssignment | Identifier | NumberLiteral | StringLiteral | HexLiteral +AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyLocalBinding | AssemblyAssignment | NumberLiteral | StringLiteral | HexLiteral AssemblyLocalBinding = 'let' Identifier ':=' FunctionalAssemblyExpression AssemblyAssignment = Identifier ':=' FunctionalAssemblyExpression | '=:' Identifier FunctionalAssemblyExpression = Identifier '(' AssemblyItem? ( ',' AssemblyItem )* ')' diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index e479d5f6..81e39a26 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -56,6 +56,8 @@ So for the following contract snippet:: The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``. +.. index: memory layout + **************** Layout in Memory **************** @@ -72,7 +74,8 @@ Solidity always places new objects at the free memory pointer and memory is neve .. warning:: There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifecycle, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to be zeroed out. -.. index: memory layout + +.. index: calldata layout ******************* Layout of Call Data @@ -85,6 +88,56 @@ specification ABI specification requires arguments to be padded to multiples of 32 bytes. The internal function calls use a different convention. + +.. index: variable cleanup + +********************************* +Internals - Cleaning Up Variables +********************************* + +When a value is shorter than 256-bit, in some cases the remaining bits +must be cleaned. +The Solidity compiler is designed to clean such remaining bits before any operations +that might be adversely affected by the potential garbage in the remaining bits. +For example, before writing a value to the memory, the remaining bits need +to be cleared because the memory contents can be used for computing +hashes or sent as the data of a message call. Similarly, before +storing a value in the storage, the remaining bits need to be cleaned +because otherwise the garbled value can be observed. + +On the other hand, we do not clean the bits if the immediately +following operation is not affected. For instance, since any non-zero +value is considered ``true`` by ``JUMPI`` instruction, we do not clean +the boolean values before they are used as the condition for +``JUMPI``. + +In addition to the design principle above, the Solidity compiler +cleans input data when it is loaded onto the stack. + +Different types have different rules for cleaning up invalid values: + ++---------------+---------------+-------------------+ +|Type |Valid Values |Invalid Values Mean| ++===============+===============+===================+ +|enum of n |0 until n - 1 |exception | +|members | | | ++---------------+---------------+-------------------+ +|bool |0 or 1 |1 | ++---------------+---------------+-------------------+ +|signed integers|sign-extended |currently silently | +| |word |wraps; in the | +| | |future exceptions | +| | |will be thrown | +| | | | +| | | | ++---------------+---------------+-------------------+ +|unsigned |higher bits |currently silently | +|integers |zeroed |wraps; in the | +| | |future exceptions | +| | |will be thrown | ++---------------+---------------+-------------------+ + + ***************** Esoteric Features ***************** diff --git a/libdevcore/JSON.h b/libdevcore/JSON.h index 0d6e0d2e..9f7d9a03 100644 --- a/libdevcore/JSON.h +++ b/libdevcore/JSON.h @@ -27,13 +27,13 @@ namespace dev { -/// Serialise the JSON object (@a _input) with identation +/// Serialise the JSON object (@a _input) with indentation inline std::string jsonPrettyPrint(Json::Value const& _input) { return Json::StyledWriter().write(_input); } -/// Serialise theJ SON object (@a _input) without identation +/// Serialise the JSON object (@a _input) without indentation inline std::string jsonCompactPrint(Json::Value const& _input) { Json::FastWriter writer; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index f6b06be6..493707b9 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -318,7 +318,7 @@ bool ASTJsonConverter::visit(Throw const& _node) bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) { - addJsonNode(_node, "VariableDefinitionStatement", {}, true); + addJsonNode(_node, "VariableDeclarationStatement", {}, true); return true; } @@ -407,7 +407,7 @@ bool ASTJsonConverter::visit(Identifier const& _node) bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) { - addJsonNode(_node, "ElementaryTypenameExpression", { + addJsonNode(_node, "ElementaryTypeNameExpression", { make_pair("value", _node.typeName().toString()), make_pair("type", type(_node)) }); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index a8299626..7c159ff7 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -926,6 +926,8 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda if (leftAligned) m_context << shiftFactor << Instruction::MUL; } + if (_fromCalldata) + convertType(_type, _type, true); return numBytes; } @@ -940,7 +942,7 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack) m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND; } -unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const +unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) { unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries); bool leftAligned = _type.category() == Type::Category::FixedBytes; @@ -949,6 +951,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBou else { solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested."); + convertType(_type, _type, true); if (numBytes != 32 && !leftAligned && !_padToWordBoundaries) // shift the value accordingly before storing m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL; diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 4baf48ff..0a5d8e1c 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -185,7 +185,7 @@ private: void cleanHigherOrderBits(IntegerType const& _typeOnStack); /// Prepares the given type for storing in memory by shifting it if necessary. - unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const; + unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries); /// Loads type from memory assuming memory offset is on stack top. unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 23fe2d4e..b9e141d8 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -223,7 +223,6 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc { solAssert(m_dataType->storageBytes() <= 32, "Invalid storage bytes size."); solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size."); - if (m_dataType->storageBytes() == 32) { solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size."); diff --git a/scripts/isolateTests.py b/scripts/isolateTests.py deleted file mode 100755 index fed779d3..00000000 --- a/scripts/isolateTests.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/python -# -# This script reads C++ source files and writes all -# multi-line strings into individual files. -# This can be used to extract the Solidity test cases -# into files for e.g. fuzz testing as -# scripts/isolateTests.py tests/libsolidity/SolidityEndToEndTest.cpp - -import sys -lines = sys.stdin.read().split('\n') -inside = False -tests = [] -for l in lines: - if inside: - if l.strip().endswith(')";'): - inside = False - else: - tests[-1] += l + '\n' - else: - if l.strip().endswith('R"('): - inside = True - tests += [''] -for i in range(len(tests)): - open('test%d.sol' % i, 'w').write(tests[i]) diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py new file mode 100755 index 00000000..91900aa6 --- /dev/null +++ b/scripts/isolate_tests.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# +# This script reads C++ source files and writes all +# multi-line strings into individual files. +# This can be used to extract the Solidity test cases +# into files for e.g. fuzz testing as +# scripts/isolate_tests.py test/libsolidity/* + +import sys + + +def extract_cases(path): + lines = open(path).read().splitlines() + + inside = False + tests = [] + + for l in lines: + if inside: + if l.strip().endswith(')";'): + inside = False + else: + tests[-1] += l + '\n' + else: + if l.strip().endswith('R"('): + inside = True + tests += [''] + + return tests + + +def write_cases(tests, start=0): + for i, test in enumerate(tests, start=start): + open('test%d.sol' % i, 'w').write(test) + + +if __name__ == '__main__': + files = sys.argv[1:] + + i = 0 + for path in files: + cases = extract_cases(path) + write_cases(cases, start=i) + i += len(cases) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 6e59099a..c83432ca 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -325,7 +325,6 @@ void CommandLineInterface::handleFormal() void CommandLineInterface::readInputFilesAndConfigureRemappings() { - vector<string> inputFiles; bool addStdin = false; if (!m_args.count("input-file")) addStdin = true; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 0c6e0cff..9e3ecac3 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -46,6 +46,7 @@ string getIPCSocketPath() ExecutionFramework::ExecutionFramework() : m_rpc(RPCSession::instance(getIPCSocketPath())), m_optimize(dev::test::Options::get().optimize), + m_showMessages(dev::test::Options::get().showMessages), m_sender(m_rpc.account(0)) { m_rpc.test_rewindToBlock(0); @@ -53,6 +54,16 @@ ExecutionFramework::ExecutionFramework() : void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value) { + if (m_showMessages) + { + if (_isCreation) + cout << "CREATE " << m_sender.hex() << ":" << endl; + else + cout << "CALL " << m_sender.hex() << " -> " << m_contractAddress.hex() << ":" << endl; + if (_value > 0) + cout << " value: " << _value << endl; + cout << " in: " << toHex(_data) << endl; + } RPCSession::TransactionData d; d.data = "0x" + toHex(_data); d.from = "0x" + toString(m_sender); @@ -79,6 +90,9 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 m_output = fromHex(code, WhenError::Throw); } + if (m_showMessages) + cout << " out: " << toHex(m_output) << endl; + m_gasUsed = u256(receipt.gasUsed); m_logs.clear(); for (auto const& log: receipt.logEntries) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index f47f2743..733fd56d 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -281,6 +281,7 @@ protected: unsigned m_optimizeRuns = 200; bool m_optimize = false; + bool m_showMessages = false; Address m_sender; Address m_contractAddress; u256 const m_gasPrice = 100 * szabo; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index d670ebff..0c0857c9 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -41,6 +41,8 @@ Options::Options() } else if (string(suite.argv[i]) == "--optimize") optimize = true; + else if (string(suite.argv[i]) == "--show-messages") + showMessages = true; if (ipcPath.empty()) if (auto path = getenv("ETH_TEST_IPC")) diff --git a/test/TestHelper.h b/test/TestHelper.h index afe4a68f..8f05eead 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -106,6 +106,7 @@ namespace test struct Options: boost::noncopyable { std::string ipcPath; + bool showMessages = false; bool optimize = false; static Options const& get(); diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index bdbe7dba..155dd5c9 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr<string const> n = make_shared<string>(""); AssemblyItems items = compileContract(sourceCode); vector<SourceLocation> locations = - vector<SourceLocation>(16, SourceLocation(2, 75, n)) + + vector<SourceLocation>(18, SourceLocation(2, 75, n)) + vector<SourceLocation>(27, SourceLocation(20, 72, n)) + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>(2, SourceLocation(58, 67, n)) + diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index aa1eb20a..2df6e9f2 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4591,6 +4591,34 @@ BOOST_AUTO_TEST_CASE(super_overload) BOOST_CHECK(callContractFunction("h()") == encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(bool_conversion) +{ + char const* sourceCode = R"( + contract C { + function f(bool _b) returns(uint) { + if (_b) + return 1; + else + return 0; + } + function g(bool _in) returns (bool _out) { + _out = _in; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(bool)", 0) == encodeArgs(0)); + BOOST_CHECK(callContractFunction("f(bool)", 1) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(bool)", 2) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(bool)", 3) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(bool)", 255) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("g(bool)", 0) == encodeArgs(0)); + BOOST_CHECK(callContractFunction("g(bool)", 1) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("g(bool)", 2) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("g(bool)", 3) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("g(bool)", 255) == encodeArgs(1)); +} + BOOST_AUTO_TEST_CASE(packed_storage_signed) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index cf0d41dd..11c38114 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -46,7 +46,7 @@ namespace { pair<ASTPointer<SourceUnit>, std::shared_ptr<Error const>> -parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, bool _insertVersionPragma = true) +parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, bool _insertVersionPragma = true, bool _allowMultipleErrors = false) { // Silence compiler version warning string source = _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source; @@ -91,6 +91,8 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, BOOST_CHECK(success || !errors.empty()); } + if (errors.size() > 1 && !_allowMultipleErrors) + BOOST_FAIL("Multiple errors found"); for (auto const& currentError: errors) { if ( @@ -131,9 +133,9 @@ bool success(string const& _source) return !parseAnalyseAndReturnError(_source).second; } -Error expectError(std::string const& _source, bool _warning = false) +Error expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false) { - auto sourceAndError = parseAnalyseAndReturnError(_source, _warning); + auto sourceAndError = parseAnalyseAndReturnError(_source, _warning, true, _allowMultiple); BOOST_REQUIRE(!!sourceAndError.second); BOOST_REQUIRE(!!sourceAndError.first); return *sourceAndError.second; @@ -161,23 +163,28 @@ static FunctionTypePointer retrieveFunctionBySignature( } -#define CHECK_ERROR_OR_WARNING(text, typ, substring, warning) \ +#define CHECK_ERROR_OR_WARNING(text, typ, substring, warning, allowMulti) \ do \ { \ - Error err = expectError((text), (warning)); \ + Error err = expectError((text), (warning), (allowMulti)); \ BOOST_CHECK(err.type() == (Error::Type::typ)); \ - BOOST_CHECK(searchErrorMessage(err, substring)); \ + BOOST_CHECK(searchErrorMessage(err, (substring))); \ } while(0) // [checkError(text, type, substring)] asserts that the compilation down to typechecking // emits an error of type [type] and with a message containing [substring]. #define CHECK_ERROR(text, type, substring) \ -CHECK_ERROR_OR_WARNING(text, type, substring, false) +CHECK_ERROR_OR_WARNING(text, type, substring, false, false) + +// [checkError(text, type, substring)] asserts that the compilation down to typechecking +// emits an error of type [type] and with a message containing [substring]. +#define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \ +CHECK_ERROR_OR_WARNING(text, type, substring, false, true) // [checkWarning(text, type, substring)] asserts that the compilation down to typechecking // emits a warning of type [type] and with a message containing [substring]. #define CHECK_WARNING(text, substring) \ -CHECK_ERROR_OR_WARNING(text, Warning, substring, true) +CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false) // [checkSuccess(text)] asserts that the compilation down to typechecking succeeds. #define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0) @@ -839,7 +846,7 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance) contract A is B { } contract B is A { } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR_ALLOW_MULTI(text, TypeError, ""); } BOOST_AUTO_TEST_CASE(legal_override_direct) @@ -1283,7 +1290,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_twice) function() { x = 3; } } )"; - CHECK_ERROR(text, DeclarationError, ""); + CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, ""); } BOOST_AUTO_TEST_CASE(fallback_function_inheritance) @@ -1665,16 +1672,28 @@ BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay) +BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_256) { char const* text = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } function test() { a = ActionChoices.GoStraight; - b = ActionChoices.Sit; } uint256 a; + } + )"; + CHECK_ERROR(text, TypeError, ""); +} + +BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64) +{ + char const* text = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + function test() { + b = ActionChoices.Sit; + } uint64 b; } )"; @@ -2038,7 +2057,8 @@ BOOST_AUTO_TEST_CASE(complex_const_variable) //for now constant specifier is valid only for uint bytesXX and enums char const* text = R"( contract Foo { - mapping(uint => bool) constant mapVar; + mapping(uint => bool) x; + mapping(uint => bool) constant mapVar = x; } )"; CHECK_ERROR(text, TypeError, ""); @@ -2143,12 +2163,12 @@ BOOST_AUTO_TEST_CASE(multiple_constructors) BOOST_AUTO_TEST_CASE(equal_overload) { char const* sourceCode = R"( - contract test { + contract C { function test(uint a) returns (uint b) { } function test(uint a) external {} } )"; - CHECK_ERROR(sourceCode, DeclarationError, ""); + CHECK_ERROR_ALLOW_MULTI(sourceCode, DeclarationError, ""); } BOOST_AUTO_TEST_CASE(uninitialized_var) @@ -3299,7 +3319,7 @@ BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) } } )"; - CHECK_ERROR(text, TypeError, ""); + CHECK_ERROR_ALLOW_MULTI(text, TypeError, ""); } BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct) @@ -3313,8 +3333,8 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct) uint x; } function f() { - s1 x; - s2 y; + s1 memory x; + s2 memory y; true ? x : y; } } @@ -3795,7 +3815,7 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) char const* text = R"( contract test { function f() { - fixed a = 3.2; + fixed a = 3.25; bytes32 c = a; } } @@ -3885,14 +3905,47 @@ BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(rational_as_exponent_value) +BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_decimal) { char const* text = R"( contract test { function f() { fixed g = 2 ** -2.2; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_as_exponent_value_pos_decimal) +{ + char const* text = R"( + contract test { + function f() { ufixed b = 3 ** 2.5; + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_as_exponent_half) +{ + char const* text = R"( + contract test { + function f() { ufixed24x24 b = 2 ** (1/2); + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_quarter) +{ + char const* text = R"( + contract test { + function f() { fixed40x40 c = 42 ** (-1/4); } } @@ -3900,15 +3953,48 @@ BOOST_AUTO_TEST_CASE(rational_as_exponent_value) BOOST_CHECK(!success(text)); } -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents) +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); + } + } + )"; + BOOST_CHECK(!success(text)); +} + +BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_neg) +{ + char const* text = R"( + contract test { + function f() { fixed c = 42 ** fixed(-1/4); - fixed d = 16 ** fixed(-0.33); + } + } + )"; + 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); } } )"; @@ -3958,7 +4044,7 @@ BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) char const* text = R"( contract test { function f() { - fixed a = ~3.56; + fixed a = ~3.5; } } )"; @@ -3970,7 +4056,7 @@ BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) char const* text = R"( contract test { function f() { - fixed a = 1.56 | 3; + fixed a = 1.5 | 3; } } )"; @@ -3982,7 +4068,7 @@ BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) char const* text = R"( contract test { function f() { - fixed a = 1.56 ^ 3; + fixed a = 1.75 ^ 3; } } )"; @@ -3994,7 +4080,7 @@ BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) char const* text = R"( contract test { function f() { - fixed a = 1.56 & 3; + fixed a = 1.75 & 3; } } )"; @@ -4651,6 +4737,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage) function f() { assembly { x := 2 + pop } } } @@ -4666,6 +4753,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) modifier m { assembly { x := 2 + pop } _; } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 5e3c69d2..a3bfab75 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -26,6 +26,7 @@ #include <libsolidity/parsing/Parser.h> #include <libsolidity/interface/Exceptions.h> #include "../TestHelper.h" +#include "ErrorCheck.h" using namespace std; @@ -71,6 +72,22 @@ bool successParse(std::string const& _source) return true; } +Error getError(std::string const& _source) +{ + ErrorList errors; + try + { + parseText(_source, errors); + } + catch (FatalError const& /*_exception*/) + { + // no-op + } + Error const* error = Error::containsErrorOfType(errors, Error::Type::ParserError); + BOOST_REQUIRE(error); + return *error; +} + void checkFunctionNatspec( FunctionDefinition const* _function, std::string const& _expectedDoc @@ -83,6 +100,14 @@ void checkFunctionNatspec( } +#define CHECK_PARSE_ERROR(source, substring) \ +do \ +{\ + Error err = getError((source)); \ + BOOST_CHECK(searchErrorMessage(err, (substring))); \ +}\ +while(0) + BOOST_AUTO_TEST_SUITE(SolidityParser) @@ -103,7 +128,7 @@ BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) uint256 ; } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(empty_function) @@ -159,7 +184,7 @@ BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args) function b() returns (uint r) { r = a({: 1, : 2, : 3}); } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(missing_argument_in_named_args) @@ -170,7 +195,7 @@ BOOST_AUTO_TEST_CASE(missing_argument_in_named_args) function b() returns (uint r) { r = a({a: , b: , c: }); } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected primary expression"); } BOOST_AUTO_TEST_CASE(two_exact_functions) @@ -463,7 +488,7 @@ BOOST_AUTO_TEST_CASE(variable_definition_in_function_parameter) function fun(var a) {} } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected explicit type name"); } BOOST_AUTO_TEST_CASE(variable_definition_in_mapping) @@ -475,7 +500,7 @@ BOOST_AUTO_TEST_CASE(variable_definition_in_mapping) } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected elementary type name for mapping key type"); } BOOST_AUTO_TEST_CASE(variable_definition_in_function_return) @@ -487,7 +512,7 @@ BOOST_AUTO_TEST_CASE(variable_definition_in_function_return) } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected explicit type name"); } BOOST_AUTO_TEST_CASE(operator_expression) @@ -777,14 +802,14 @@ BOOST_AUTO_TEST_CASE(modifier_without_semicolon) modifier mod { if (msg.sender == 0) _ } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected token Semicolon got"); } BOOST_AUTO_TEST_CASE(modifier_arguments) { char const* text = R"( contract c { - modifier mod(uint a) { if (msg.sender == a) _; } + modifier mod(address a) { if (msg.sender == a) _; } } )"; BOOST_CHECK(successParse(text)); @@ -861,7 +886,7 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) contract c { uint private internal a; })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Visibility already specified"); } BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) @@ -916,7 +941,7 @@ BOOST_AUTO_TEST_CASE(empty_enum_declaration) contract c { enum foo { } })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "enum with no members is not allowed"); } BOOST_AUTO_TEST_CASE(malformed_enum_declaration) @@ -925,7 +950,7 @@ BOOST_AUTO_TEST_CASE(malformed_enum_declaration) contract c { enum foo { WARNING,} })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected Identifier after"); } BOOST_AUTO_TEST_CASE(external_function) @@ -943,7 +968,7 @@ BOOST_AUTO_TEST_CASE(external_variable) contract c { uint external x; })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(arrays_in_storage) @@ -991,7 +1016,7 @@ BOOST_AUTO_TEST_CASE(constant_is_keyword) contract Foo { uint constant = 4; })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(var_array) @@ -1000,7 +1025,7 @@ BOOST_AUTO_TEST_CASE(var_array) contract Foo { function f() { var[] a; } })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(location_specifiers_for_params) @@ -1032,7 +1057,7 @@ BOOST_AUTO_TEST_CASE(location_specifiers_for_state) contract Foo { uint[] memory x; })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(location_specifiers_with_var) @@ -1041,7 +1066,7 @@ BOOST_AUTO_TEST_CASE(location_specifiers_with_var) contract Foo { function f() { var memory x; } })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Location specifier needs explicit type name"); } BOOST_AUTO_TEST_CASE(empty_comment) @@ -1088,7 +1113,7 @@ BOOST_AUTO_TEST_CASE(local_const_variable) return local; } })"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected token Semicolon"); } BOOST_AUTO_TEST_CASE(multi_variable_declaration) @@ -1207,7 +1232,7 @@ BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_lvalue) } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected expression"); } BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_without_lvalue) @@ -1220,7 +1245,7 @@ BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_without_lvalue) } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected expression"); } BOOST_AUTO_TEST_CASE(conditional_true_false_literal) @@ -1321,7 +1346,7 @@ BOOST_AUTO_TEST_CASE(no_double_radix_in_fixed_literal) fixed40x40 pi = 3.14.15; } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected token Semicolon"); } BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check) @@ -1333,7 +1358,7 @@ BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check) } } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected primary expression"); } BOOST_AUTO_TEST_CASE(payable_accessor) @@ -1343,7 +1368,7 @@ BOOST_AUTO_TEST_CASE(payable_accessor) uint payable x; } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected identifier"); } BOOST_AUTO_TEST_CASE(function_type_in_expression) @@ -1376,7 +1401,7 @@ BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_modifiers) function (uint, uint) modifier1() returns (uint) f1; } )"; - BOOST_CHECK(!successParse(text)); + CHECK_PARSE_ERROR(text, "Expected token LBrace"); } BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_assignment) |
