Merge branch 'origin/develop' into develop_060

This commit is contained in:
chriseth 2019-10-02 16:29:36 +02:00
commit f884373142
21 changed files with 175 additions and 50 deletions

View File

@ -115,6 +115,17 @@ defaults:
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_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 - test_ubuntu1904_all: &test_ubuntu1904
docker: docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904 - image: ethereum/solidity-buildpack-deps:ubuntu1904
@ -151,6 +162,11 @@ defaults:
requires: requires:
- b_ubu - b_ubu
- workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang
<<: *workflow_trigger_on_tags
requires:
- b_ubu_clang
- workflow_ubuntu1904_release: &workflow_ubuntu1904_release - workflow_ubuntu1904_release: &workflow_ubuntu1904_release
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
@ -280,6 +296,18 @@ jobs:
pip install --user z3-solver pip install --user z3-solver
- run: *run_proofs - 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 b_ubu: &build_ubuntu1904
docker: docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904 - image: ethereum/solidity-buildpack-deps:ubuntu1904
@ -349,11 +377,11 @@ jobs:
- run: *run_build - run: *run_build
b_ubu_ossfuzz: b_ubu_ossfuzz:
<<: *build_ubuntu1904 <<: *build_ubuntu1904_clang
environment: environment:
TERM: xterm TERM: xterm
CC: /usr/bin/clang-8 CC: clang
CXX: /usr/bin/clang++-8 CXX: clang++
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake
steps: steps:
- checkout - checkout
@ -362,7 +390,7 @@ jobs:
- persist_to_workspace: *artifacts_executables_ossfuzz - persist_to_workspace: *artifacts_executables_ossfuzz
t_ubu_ossfuzz: &t_ubu_ossfuzz t_ubu_ossfuzz: &t_ubu_ossfuzz
<<: *test_ubuntu1904 <<: *test_ubuntu1904_clang
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -495,6 +523,12 @@ jobs:
t_ubu_soltest: &t_ubu_soltest t_ubu_soltest: &t_ubu_soltest
<<: *test_ubuntu1904 <<: *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_release_soltest: &t_ubu_release_soltest
<<: *t_ubu_soltest <<: *t_ubu_soltest
@ -632,6 +666,8 @@ workflows:
- b_ubu18: *workflow_trigger_on_tags - b_ubu18: *workflow_trigger_on_tags
- t_ubu_cli: *workflow_ubuntu1904 - t_ubu_cli: *workflow_ubuntu1904
- t_ubu_soltest: *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 # Ubuntu fake release build and tests
- b_ubu_release: *workflow_trigger_on_tags - b_ubu_release: *workflow_trigger_on_tags

View File

@ -28,7 +28,9 @@ Compiler Features:
Bugfixes: Bugfixes:
### 0.5.12 (unreleased) ### 0.5.13 (unreleased)
### 0.5.12 (2019-10-01)
Language Features: Language Features:
* Type Checker: Allow assignment to external function arguments except for reference types. * Type Checker: Allow assignment to external function arguments except for reference types.
@ -43,7 +45,10 @@ Compiler Features:
Bugfixes: 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. * Yul Optimizer: Fix reordering bug in connection with shifted one and mul/div-instructions in for loop conditions.

View File

@ -1,7 +1,7 @@
# Inherit default options # Inherit default options
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake") include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
# Disable Z3 and CVC4 since none of the existing fuzzers need them # Enable Z3, disable CVC4
set(USE_Z3 OFF CACHE BOOL "Disable Z3" FORCE) set(USE_Z3 ON CACHE BOOL "Enable Z3" FORCE)
set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE) set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE)
# Build fuzzing binaries # Build fuzzing binaries
set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE) set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE)

View File

@ -444,16 +444,16 @@ JSON
The JSON format for a contract's interface is given by an array of function and/or event descriptions. 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: A function description is a JSON object with the fields:
- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function <fallback-function>`); - ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function <fallback-function>`).
- ``name``: the name of the function; - ``name``: the name of the function.
- ``inputs``: an array of objects, each of which contains: - ``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). * ``type``: the canonical type of the parameter (more below).
* ``components``: used for tuple types (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; - ``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 <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` (function does not accept Ether) and ``payable`` (function accepts Ether); - ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``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. 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: An event description is a JSON object with fairly similar fields:
- ``type``: always ``"event"`` - ``type``: always ``"event"``
- ``name``: the name of the event; - ``name``: the name of the event.
- ``inputs``: an array of objects, each of which contains: - ``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). * ``type``: the canonical type of the parameter (more below).
* ``components``: used for tuple types (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. * ``indexed``: ``true`` if the field is part of the log's topics, ``false`` if it one of the log's data segment.

View File

@ -750,6 +750,10 @@
"bugs": [], "bugs": [],
"released": "2019-08-12" "released": "2019-08-12"
}, },
"0.5.12": {
"bugs": [],
"released": "2019-10-01"
},
"0.5.2": { "0.5.2": {
"bugs": [ "bugs": [
"SignedArrayStorageCopy", "SignedArrayStorageCopy",

View File

@ -37,3 +37,7 @@ add_library(devcore ${sources})
target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::regex Boost::system) target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::regex Boost::system)
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
add_dependencies(devcore solidity_BuildInfo.h) add_dependencies(devcore solidity_BuildInfo.h)
if(SOLC_LINK_STATIC)
target_link_libraries(devcore PUBLIC Threads::Threads)
endif()

View File

@ -30,11 +30,6 @@ inline bool isHexDigit(char c)
('A' <= c && c <= 'F'); ('A' <= c && c <= 'F');
} }
inline bool isLineTerminator(char c)
{
return c == '\n';
}
inline bool isWhiteSpace(char c) inline bool isWhiteSpace(char c)
{ {
return c == ' ' || c == '\n' || c == '\t' || c == '\r'; return c == ' ' || c == '\n' || c == '\t' || c == '\r';

View File

@ -280,6 +280,29 @@ Token Scanner::skipSingleLineComment()
return Token::Whitespace; 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() Token Scanner::scanSingleLineDocComment()
{ {
LiteralScope literal(this, LITERAL_TYPE_COMMENT); LiteralScope literal(this, LITERAL_TYPE_COMMENT);
@ -289,7 +312,7 @@ Token Scanner::scanSingleLineDocComment()
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
if (isLineTerminator(m_char)) if (tryScanEndOfLine())
{ {
// check if next line is also a documentation comment // check if next line is also a documentation comment
skipWhitespace(); skipWhitespace();
@ -303,7 +326,6 @@ Token Scanner::scanSingleLineDocComment()
} }
else else
break; // next line is not a documentation comment, we are done break; // next line is not a documentation comment, we are done
} }
else if (isUnicodeLinebreak()) else if (isUnicodeLinebreak())
// Any line terminator that is not '\n' is considered to end the // Any line terminator that is not '\n' is considered to end the
@ -343,13 +365,13 @@ Token Scanner::scanMultiLineDocComment()
bool endFound = false; bool endFound = false;
bool charsAdded = false; bool charsAdded = false;
while (isWhiteSpace(m_char) && !isLineTerminator(m_char)) while (isWhiteSpace(m_char) && !atEndOfLine())
advance(); advance();
while (!isSourcePastEndOfInput()) while (!isSourcePastEndOfInput())
{ {
//handle newlines in multline comments //handle newlines in multline comments
if (isLineTerminator(m_char)) if (atEndOfLine())
{ {
skipWhitespace(); skipWhitespace();
if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '*') if (!m_source->isPastEndOfInput(1) && m_source->get(0) == '*' && m_source->get(1) == '*')
@ -664,10 +686,12 @@ void Scanner::scanToken()
bool Scanner::scanEscape() bool Scanner::scanEscape()
{ {
char c = m_char; char c = m_char;
advance();
// Skip escaped newlines. // Skip escaped newlines.
if (isLineTerminator(c)) if (tryScanEndOfLine())
return true; return true;
advance();
switch (c) switch (c)
{ {
case '\'': // fall through case '\'': // fall through

View File

@ -219,6 +219,12 @@ private:
Token skipSingleLineComment(); Token skipSingleLineComment();
Token skipMultiLineComment(); 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(); void scanDecimalDigits();
Token scanNumber(char _charSeen = 0); Token scanNumber(char _charSeen = 0);
std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword(); std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword();

View File

@ -91,13 +91,13 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
if (!imp->symbolAliases().empty()) if (!imp->symbolAliases().empty())
for (auto const& alias: imp->symbolAliases()) 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()) if (declarations.empty())
{ {
m_errorReporter.declarationError( m_errorReporter.declarationError(
imp->location(), imp->location(),
"Declaration \"" + "Declaration \"" +
alias.first->name() + alias.symbol->name() +
"\" not found in \"" + "\" not found in \"" +
path + path +
"\" (referenced as \"" + "\" (referenced as \"" +
@ -109,7 +109,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
else else
for (Declaration const* declaration: declarations) for (Declaration const* declaration: declarations)
if (!DeclarationRegistrationHelper::registerDeclaration( if (!DeclarationRegistrationHelper::registerDeclaration(
target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter target, *declaration, alias.alias.get(), &alias.location, true, false, m_errorReporter
)) ))
error = true; error = true;
} }
@ -523,7 +523,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
{ {
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration)) if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
_errorReporter.warning( _errorReporter.warning(
_declaration.location(), *_errorLocation,
"This declaration shadows a builtin symbol." "This declaration shadows a builtin symbol."
); );
else else

View File

@ -280,22 +280,31 @@ private:
class ImportDirective: public Declaration class ImportDirective: public Declaration
{ {
public: public:
struct SymbolAlias
{
ASTPointer<Identifier> symbol;
ASTPointer<ASTString> alias;
SourceLocation location;
};
using SymbolAliasList = std::vector<SymbolAlias>;
ImportDirective( ImportDirective(
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _path, ASTPointer<ASTString> const& _path,
ASTPointer<ASTString> const& _unitAlias, ASTPointer<ASTString> const& _unitAlias,
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases SymbolAliasList _symbolAliases
): ):
Declaration(_location, _unitAlias), Declaration(_location, _unitAlias),
m_path(_path), m_path(_path),
m_symbolAliases(_symbolAliases) m_symbolAliases(move(_symbolAliases))
{ } { }
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
ASTString const& path() const { return *m_path; } ASTString const& path() const { return *m_path; }
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> const& symbolAliases() const SymbolAliasList const& symbolAliases() const
{ {
return m_symbolAliases; return m_symbolAliases;
} }
@ -306,9 +315,9 @@ public:
private: private:
ASTPointer<ASTString> m_path; ASTPointer<ASTString> m_path;
/// The aliases for the specific symbols to import. If non-empty import the specific symbols. /// 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. /// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope.
std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> m_symbolAliases; SymbolAliasList m_symbolAliases;
}; };
/** /**

View File

@ -243,9 +243,9 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
for (auto const& symbolAlias: _node.symbolAliases()) for (auto const& symbolAlias: _node.symbolAliases())
{ {
Json::Value tuple(Json::objectValue); Json::Value tuple(Json::objectValue);
solAssert(symbolAlias.first, ""); solAssert(symbolAlias.symbol, "");
tuple["foreign"] = nodeId(*symbolAlias.first); tuple["foreign"] = nodeId(*symbolAlias.symbol);
tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue; tuple["local"] = symbolAlias.alias ? Json::Value(*symbolAlias.alias) : Json::nullValue;
symbolAliases.append(tuple); symbolAliases.append(tuple);
} }
attributes.emplace_back("symbolAliases", std::move(symbolAliases)); attributes.emplace_back("symbolAliases", std::move(symbolAliases));

View File

@ -2086,6 +2086,8 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
bool StructType::isDynamicallyEncoded() const bool StructType::isDynamicallyEncoded() const
{ {
if (recursive())
return true;
solAssert(interfaceType(false).get(), ""); solAssert(interfaceType(false).get(), "");
for (auto t: memoryMemberTypes()) for (auto t: memoryMemberTypes())
{ {

View File

@ -186,7 +186,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
expectToken(Token::Import); expectToken(Token::Import);
ASTPointer<ASTString> path; ASTPointer<ASTString> path;
ASTPointer<ASTString> unitAlias = make_shared<string>(); ASTPointer<ASTString> unitAlias = make_shared<string>();
vector<pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> symbolAliases; ImportDirective::SymbolAliasList symbolAliases;
if (m_scanner->currentToken() == Token::StringLiteral) if (m_scanner->currentToken() == Token::StringLiteral)
{ {
@ -204,14 +204,16 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
m_scanner->next(); m_scanner->next();
while (true) while (true)
{ {
ASTPointer<Identifier> id = parseIdentifier();
ASTPointer<ASTString> alias; ASTPointer<ASTString> alias;
SourceLocation aliasLocation = SourceLocation{position(), endPosition(), source()};
ASTPointer<Identifier> id = parseIdentifier();
if (m_scanner->currentToken() == Token::As) if (m_scanner->currentToken() == Token::As)
{ {
expectToken(Token::As); expectToken(Token::As);
aliasLocation = SourceLocation{position(), endPosition(), source()};
alias = expectIdentifierToken(); 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) if (m_scanner->currentToken() != Token::Comma)
break; break;
m_scanner->next(); m_scanner->next();

View File

@ -568,7 +568,7 @@ BOOST_AUTO_TEST_CASE(multiline_comment_at_eos)
BOOST_AUTO_TEST_CASE(regular_line_break_in_single_line_comment) 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 ", "")); Scanner scanner(CharStream("// abc " + string(nl) + " def ", ""));
BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), ""); 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) 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 ", "")); Scanner scanner(CharStream("/// abc " + string(nl) + " def ", ""));
BOOST_CHECK_EQUAL(scanner.currentCommentLiteral(), "abc "); 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) BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_single_line_doc_comment)
{ {
for (auto const& nl: {"\v", "\f", "\xE2\x80\xA8", "\xE2\x80\xA9"}) 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) 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.currentToken(), Token::Illegal);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def"); BOOST_CHECK_EQUAL(scanner.currentLiteral(), "def");

View File

@ -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() ->

View File

@ -5,4 +5,4 @@ library A {}
==== Source: c ==== ==== Source: c ====
import {A} from "./a"; import {A} from "./b"; import {A} from "./a"; import {A} from "./b";
// ---- // ----
// DeclarationError: (c:23-45): Identifier already declared. // DeclarationError: (c:31-32): Identifier already declared.

View File

@ -3,4 +3,4 @@ contract C {}
==== Source: b ==== ==== Source: b ====
import {C as msg} from "B.sol"; 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.

View File

@ -7,5 +7,5 @@ contract C {
// ---- // ----
// Warning: (B.sol:0-15): 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.sol:16-32): This declaration shadows a builtin symbol.
// Warning: (B.sol:0-15): This declaration shadows a builtin symbol. // Warning: (b:8-11): This declaration shadows a builtin symbol.
// Warning: (B.sol:16-32): This declaration shadows a builtin symbol. // Warning: (b:13-18): This declaration shadows a builtin symbol.

View File

@ -5,4 +5,4 @@ library A {}
==== Source: c ==== ==== Source: c ====
import {A} from "./a"; import {A} from "./b"; import {A} from "./a"; import {A} from "./b";
// ---- // ----
// DeclarationError: (c:23-45): Identifier already declared. // DeclarationError: (c:31-32): Identifier already declared.

View File

@ -0,0 +1,10 @@
contract Test {
struct RecursiveStruct {
RecursiveStruct[] vals;
}
function func() private pure {
RecursiveStruct[1] memory val;
val;
}
}