diff --git a/.circleci/config.yml b/.circleci/config.yml index 35509a56e..4b27bf17d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -115,6 +115,17 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results + - test_ubuntu1904_clang: &test_ubuntu1904_clang + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang + steps: + - checkout + - attach_workspace: + at: build + - run: *run_soltest + - store_test_results: *store_test_results + - store_artifacts: *artifacts_test_results + - test_ubuntu1904_all: &test_ubuntu1904 docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904 @@ -151,6 +162,11 @@ defaults: requires: - b_ubu + - workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang + <<: *workflow_trigger_on_tags + requires: + - b_ubu_clang + - workflow_ubuntu1904_release: &workflow_ubuntu1904_release <<: *workflow_trigger_on_tags requires: @@ -280,6 +296,18 @@ jobs: pip install --user z3-solver - run: *run_proofs + b_ubu_clang: &build_ubuntu1904_clang + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang + environment: + CC: clang + CXX: clang++ + steps: + - checkout + - run: *run_build + - store_artifacts: *artifacts_solc + - persist_to_workspace: *artifacts_executables + b_ubu: &build_ubuntu1904 docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904 @@ -349,11 +377,11 @@ jobs: - run: *run_build b_ubu_ossfuzz: - <<: *build_ubuntu1904 + <<: *build_ubuntu1904_clang environment: TERM: xterm - CC: /usr/bin/clang-8 - CXX: /usr/bin/clang++-8 + CC: clang + CXX: clang++ CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake steps: - checkout @@ -362,7 +390,7 @@ jobs: - persist_to_workspace: *artifacts_executables_ossfuzz t_ubu_ossfuzz: &t_ubu_ossfuzz - <<: *test_ubuntu1904 + <<: *test_ubuntu1904_clang steps: - checkout - attach_workspace: @@ -495,6 +523,12 @@ jobs: t_ubu_soltest: &t_ubu_soltest <<: *test_ubuntu1904 + t_ubu_clang_soltest: &t_ubu_clang_soltest + <<: *test_ubuntu1904_clang + environment: + EVM: constantinople + OPTIMIZE: 0 + t_ubu_release_soltest: &t_ubu_release_soltest <<: *t_ubu_soltest @@ -632,6 +666,8 @@ workflows: - b_ubu18: *workflow_trigger_on_tags - t_ubu_cli: *workflow_ubuntu1904 - t_ubu_soltest: *workflow_ubuntu1904 + - b_ubu_clang: *workflow_trigger_on_tags + - t_ubu_clang_soltest: *workflow_ubuntu1904_clang # Ubuntu fake release build and tests - b_ubu_release: *workflow_trigger_on_tags diff --git a/Changelog.md b/Changelog.md index 9cc9bf67c..82dae4b80 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,7 +28,9 @@ Compiler Features: Bugfixes: -### 0.5.12 (unreleased) +### 0.5.13 (unreleased) + +### 0.5.12 (2019-10-01) Language Features: * Type Checker: Allow assignment to external function arguments except for reference types. @@ -43,7 +45,10 @@ Compiler Features: Bugfixes: - * Fix internal error when popping a dynamic storage array of mappings. + * Code Generator: Fix internal error when popping a dynamic storage array of mappings. + * Name Resolver: Fix wrong source location when warning on shadowed aliases in import declarations. + * Scanner: Fix multi-line natspec comment parsing with triple slashes when file is encoded with CRLF instead of LF. + * Type System: Fix arrays of recursive structs. * Yul Optimizer: Fix reordering bug in connection with shifted one and mul/div-instructions in for loop conditions. diff --git a/cmake/toolchains/libfuzzer.cmake b/cmake/toolchains/libfuzzer.cmake index 8a295e05b..354e6d09b 100644 --- a/cmake/toolchains/libfuzzer.cmake +++ b/cmake/toolchains/libfuzzer.cmake @@ -1,7 +1,7 @@ # Inherit default options include("${CMAKE_CURRENT_LIST_DIR}/default.cmake") -# Disable Z3 and CVC4 since none of the existing fuzzers need them -set(USE_Z3 OFF CACHE BOOL "Disable Z3" FORCE) +# Enable Z3, disable CVC4 +set(USE_Z3 ON CACHE BOOL "Enable Z3" FORCE) set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE) # Build fuzzing binaries set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 1cd751eee..51ad5ddd2 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -444,16 +444,16 @@ JSON The JSON format for a contract's interface is given by an array of function and/or event descriptions. A function description is a JSON object with the fields: -- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function `); -- ``name``: the name of the function; +- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function `). +- ``name``: the name of the function. - ``inputs``: an array of objects, each of which contains: - * ``name``: the name of the parameter; + * ``name``: the name of the parameter. * ``type``: the canonical type of the parameter (more below). * ``components``: used for tuple types (more below). -- ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything; -- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state `), ``view`` (:ref:`specified to not modify the blockchain state `), ``nonpayable`` (function does not accept Ether) and ``payable`` (function accepts Ether); +- ``outputs``: an array of objects similar to ``inputs``. +- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state `), ``view`` (:ref:`specified to not modify the blockchain state `), ``nonpayable`` (function does not accept Ether) and ``payable`` (function accepts Ether). Constructor and fallback function never have ``name`` or ``outputs``. Fallback function doesn't have ``inputs`` either. @@ -463,10 +463,10 @@ Constructor and fallback function never have ``name`` or ``outputs``. Fallback f An event description is a JSON object with fairly similar fields: - ``type``: always ``"event"`` -- ``name``: the name of the event; +- ``name``: the name of the event. - ``inputs``: an array of objects, each of which contains: - * ``name``: the name of the parameter; + * ``name``: the name of the parameter. * ``type``: the canonical type of the parameter (more below). * ``components``: used for tuple types (more below). * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index cd615fbc4..d79aca112 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -750,6 +750,10 @@ "bugs": [], "released": "2019-08-12" }, + "0.5.12": { + "bugs": [], + "released": "2019-10-01" + }, "0.5.2": { "bugs": [ "SignedArrayStorageCopy", diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index 8bc3bb525..ef2af6145 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -37,3 +37,7 @@ add_library(devcore ${sources}) target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::regex Boost::system) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") add_dependencies(devcore solidity_BuildInfo.h) + +if(SOLC_LINK_STATIC) + target_link_libraries(devcore PUBLIC Threads::Threads) +endif() diff --git a/liblangutil/Common.h b/liblangutil/Common.h index ed6aa2cbc..a8d935270 100644 --- a/liblangutil/Common.h +++ b/liblangutil/Common.h @@ -30,11 +30,6 @@ inline bool isHexDigit(char c) ('A' <= c && c <= 'F'); } -inline bool isLineTerminator(char c) -{ - return c == '\n'; -} - inline bool isWhiteSpace(char c) { return c == ' ' || c == '\n' || c == '\t' || c == '\r'; diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 82b19d8cf..64a34d26d 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -280,6 +280,29 @@ Token Scanner::skipSingleLineComment() return Token::Whitespace; } +bool Scanner::atEndOfLine() const +{ + return m_char == '\n' || m_char == '\r'; +} + +bool Scanner::tryScanEndOfLine() +{ + if (m_char == '\n') + { + advance(); + return true; + } + + if (m_char == '\r') + { + if (advance() && m_char == '\n') + advance(); + return true; + } + + return false; +} + Token Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); @@ -289,7 +312,7 @@ Token Scanner::scanSingleLineDocComment() while (!isSourcePastEndOfInput()) { - if (isLineTerminator(m_char)) + if (tryScanEndOfLine()) { // check if next line is also a documentation comment skipWhitespace(); @@ -303,7 +326,6 @@ Token Scanner::scanSingleLineDocComment() } else break; // next line is not a documentation comment, we are done - } else if (isUnicodeLinebreak()) // Any line terminator that is not '\n' is considered to end the @@ -343,13 +365,13 @@ Token Scanner::scanMultiLineDocComment() bool endFound = false; bool charsAdded = false; - while (isWhiteSpace(m_char) && !isLineTerminator(m_char)) + while (isWhiteSpace(m_char) && !atEndOfLine()) advance(); while (!isSourcePastEndOfInput()) { //handle newlines in multline comments - if (isLineTerminator(m_char)) + if (atEndOfLine()) { skipWhitespace(); if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '*') @@ -664,10 +686,12 @@ void Scanner::scanToken() bool Scanner::scanEscape() { char c = m_char; - advance(); + // Skip escaped newlines. - if (isLineTerminator(c)) + if (tryScanEndOfLine()) return true; + advance(); + switch (c) { case '\'': // fall through diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 555575129..3914b85ac 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -219,6 +219,12 @@ private: Token skipSingleLineComment(); Token skipMultiLineComment(); + /// Tests if current source position is CR, LF or CRLF. + bool atEndOfLine() const; + + /// Tries to consume CR, LF or CRLF line terminators and returns success or failure. + bool tryScanEndOfLine(); + void scanDecimalDigits(); Token scanNumber(char _charSeen = 0); std::tuple scanIdentifierOrKeyword(); diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index a1a266db1..dc3cfd087 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -91,13 +91,13 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapsymbolAliases().empty()) for (auto const& alias: imp->symbolAliases()) { - auto declarations = scope->second->resolveName(alias.first->name(), false); + auto declarations = scope->second->resolveName(alias.symbol->name(), false); if (declarations.empty()) { m_errorReporter.declarationError( imp->location(), "Declaration \"" + - alias.first->name() + + alias.symbol->name() + "\" not found in \"" + path + "\" (referenced as \"" + @@ -109,7 +109,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, maplocation(), true, false, m_errorReporter + target, *declaration, alias.alias.get(), &alias.location, true, false, m_errorReporter )) error = true; } @@ -523,7 +523,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( { if (dynamic_cast(shadowedDeclaration)) _errorReporter.warning( - _declaration.location(), + *_errorLocation, "This declaration shadows a builtin symbol." ); else diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 5742cc74e..4c3415108 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -280,22 +280,31 @@ private: class ImportDirective: public Declaration { public: + struct SymbolAlias + { + ASTPointer symbol; + ASTPointer alias; + SourceLocation location; + }; + + using SymbolAliasList = std::vector; + ImportDirective( SourceLocation const& _location, ASTPointer const& _path, ASTPointer const& _unitAlias, - std::vector, ASTPointer>>&& _symbolAliases + SymbolAliasList _symbolAliases ): Declaration(_location, _unitAlias), m_path(_path), - m_symbolAliases(_symbolAliases) + m_symbolAliases(move(_symbolAliases)) { } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; ASTString const& path() const { return *m_path; } - std::vector, ASTPointer>> const& symbolAliases() const + SymbolAliasList const& symbolAliases() const { return m_symbolAliases; } @@ -306,9 +315,9 @@ public: private: ASTPointer m_path; /// The aliases for the specific symbols to import. If non-empty import the specific symbols. - /// If the second component is empty, import the identifier unchanged. + /// If the `alias` component is empty, import the identifier unchanged. /// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope. - std::vector, ASTPointer>> m_symbolAliases; + SymbolAliasList m_symbolAliases; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 0f2121b7e..82d47b06c 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -243,9 +243,9 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) for (auto const& symbolAlias: _node.symbolAliases()) { Json::Value tuple(Json::objectValue); - solAssert(symbolAlias.first, ""); - tuple["foreign"] = nodeId(*symbolAlias.first); - tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue; + solAssert(symbolAlias.symbol, ""); + tuple["foreign"] = nodeId(*symbolAlias.symbol); + tuple["local"] = symbolAlias.alias ? Json::Value(*symbolAlias.alias) : Json::nullValue; symbolAliases.append(tuple); } attributes.emplace_back("symbolAliases", std::move(symbolAliases)); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index b9f9561cc..9e293ac20 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2086,6 +2086,8 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const bool StructType::isDynamicallyEncoded() const { + if (recursive()) + return true; solAssert(interfaceType(false).get(), ""); for (auto t: memoryMemberTypes()) { diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index dece4fa14..59c76d69a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -186,7 +186,7 @@ ASTPointer Parser::parseImportDirective() expectToken(Token::Import); ASTPointer path; ASTPointer unitAlias = make_shared(); - vector, ASTPointer>> symbolAliases; + ImportDirective::SymbolAliasList symbolAliases; if (m_scanner->currentToken() == Token::StringLiteral) { @@ -204,14 +204,16 @@ ASTPointer Parser::parseImportDirective() m_scanner->next(); while (true) { - ASTPointer id = parseIdentifier(); ASTPointer alias; + SourceLocation aliasLocation = SourceLocation{position(), endPosition(), source()}; + ASTPointer id = parseIdentifier(); if (m_scanner->currentToken() == Token::As) { expectToken(Token::As); + aliasLocation = SourceLocation{position(), endPosition(), source()}; alias = expectIdentifierToken(); } - symbolAliases.emplace_back(move(id), move(alias)); + symbolAliases.emplace_back(ImportDirective::SymbolAlias{move(id), move(alias), aliasLocation}); if (m_scanner->currentToken() != Token::Comma) break; m_scanner->next(); diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index debd30477..7e7b58469 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -568,7 +568,7 @@ BOOST_AUTO_TEST_CASE(multiline_comment_at_eos) BOOST_AUTO_TEST_CASE(regular_line_break_in_single_line_comment) { - for (auto const& nl: {"\r", "\n"}) + for (auto const& nl: {"\r", "\n", "\r\n"}) { Scanner scanner(CharStream("// abc " + string(nl) + " def ", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); @@ -595,7 +595,7 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_comment) BOOST_AUTO_TEST_CASE(regular_line_breaks_in_single_line_doc_comment) { - for (auto const& nl: {"\r", "\n"}) + for (auto const& nl: {"\r", "\n", "\r\n"}) { Scanner scanner(CharStream("/// abc " + string(nl) + " def ", "")); BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); @@ -605,6 +605,22 @@ BOOST_AUTO_TEST_CASE(regular_line_breaks_in_single_line_doc_comment) } } +BOOST_AUTO_TEST_CASE(regular_line_breaks_in_multiline_doc_comment) +{ + // Test CR, LF, CRLF as line valid terminators for code comments. + // Any accepted non-LF is being canonicalized to LF. + for (auto const& nl : {"\r"s, "\n"s, "\r\n"s}) + { + Scanner scanner{CharStream{"/// Hello" + nl + "/// World" + nl + "ident", ""}}; + auto const& lit = scanner.currentCommentLiteral(); + BOOST_CHECK_EQUAL(lit, "Hello\n World"); + BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "Hello\n World"); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "ident"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + } +} + BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_doc_comment) { for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) @@ -622,9 +638,9 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_doc_comment) BOOST_AUTO_TEST_CASE(regular_line_breaks_in_strings) { - for (auto const& nl: {"\n", "\r"}) + for (auto const& nl: {"\r"s, "\n"s, "\r\n"s}) { - Scanner scanner(CharStream("\"abc " + string(nl) + " def\"", "")); + Scanner scanner(CharStream("\"abc " + nl + " def\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); diff --git a/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol b/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol new file mode 100644 index 000000000..16ee9d59b --- /dev/null +++ b/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol @@ -0,0 +1,12 @@ +contract Test { + struct RecursiveStruct { + RecursiveStruct[] vals; + } + + function func() public pure { + RecursiveStruct[1] memory val = [ RecursiveStruct(new RecursiveStruct[](42)) ]; + assert(val[0].vals.length == 42); + } +} +// ----- +// func() -> diff --git a/test/libsolidity/syntaxTests/imports/library_name_clash.sol b/test/libsolidity/syntaxTests/imports/library_name_clash.sol index cd0dbd240..19f3697d5 100644 --- a/test/libsolidity/syntaxTests/imports/library_name_clash.sol +++ b/test/libsolidity/syntaxTests/imports/library_name_clash.sol @@ -5,4 +5,4 @@ library A {} ==== Source: c ==== import {A} from "./a"; import {A} from "./b"; // ---- -// DeclarationError: (c:23-45): Identifier already declared. +// DeclarationError: (c:31-32): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_alias.sol b/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_alias.sol index 8ed652a71..65a606e0c 100644 --- a/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_alias.sol +++ b/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_alias.sol @@ -3,4 +3,4 @@ contract C {} ==== Source: b ==== import {C as msg} from "B.sol"; // ---- -// Warning: (B.sol:0-13): This declaration shadows a builtin symbol. +// Warning: (b:13-16): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_multiple_imports.sol b/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_multiple_imports.sol index 848e15546..3d82041af 100644 --- a/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_multiple_imports.sol +++ b/test/libsolidity/syntaxTests/imports/shadowing_builtins_with_multiple_imports.sol @@ -7,5 +7,5 @@ contract C { // ---- // Warning: (B.sol:0-15): This declaration shadows a builtin symbol. // Warning: (B.sol:16-32): This declaration shadows a builtin symbol. -// Warning: (B.sol:0-15): This declaration shadows a builtin symbol. -// Warning: (B.sol:16-32): This declaration shadows a builtin symbol. +// Warning: (b:8-11): This declaration shadows a builtin symbol. +// Warning: (b:13-18): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/imports/shadowing_via_import.sol b/test/libsolidity/syntaxTests/imports/shadowing_via_import.sol index cd0dbd240..19f3697d5 100644 --- a/test/libsolidity/syntaxTests/imports/shadowing_via_import.sol +++ b/test/libsolidity/syntaxTests/imports/shadowing_via_import.sol @@ -5,4 +5,4 @@ library A {} ==== Source: c ==== import {A} from "./a"; import {A} from "./b"; // ---- -// DeclarationError: (c:23-45): Identifier already declared. +// DeclarationError: (c:31-32): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/structs/recursion/static_array_of_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/static_array_of_recursive_structs.sol new file mode 100644 index 000000000..4268317b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/static_array_of_recursive_structs.sol @@ -0,0 +1,10 @@ +contract Test { + struct RecursiveStruct { + RecursiveStruct[] vals; + } + + function func() private pure { + RecursiveStruct[1] memory val; + val; + } +}