diff options
author | chriseth <c@ethdev.com> | 2015-10-13 05:02:35 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-10-15 23:38:42 +0800 |
commit | 7ebd536e79215f06f5ce7e14591aa494d06032b6 (patch) | |
tree | cca1f7597e4c6ddd674db92337424b0fd412b501 | |
parent | 7ba42f470753f9af25531017f319cf94eb26d3f2 (diff) | |
download | dexon-solidity-7ebd536e79215f06f5ce7e14591aa494d06032b6.tar.gz dexon-solidity-7ebd536e79215f06f5ce7e14591aa494d06032b6.tar.zst dexon-solidity-7ebd536e79215f06f5ce7e14591aa494d06032b6.zip |
Tuple expressions.
-rw-r--r-- | libsolidity/AST.h | 24 | ||||
-rw-r--r-- | libsolidity/ASTForward.h | 1 | ||||
-rw-r--r-- | libsolidity/ASTJsonConverter.cpp | 11 | ||||
-rw-r--r-- | libsolidity/ASTJsonConverter.h | 2 | ||||
-rw-r--r-- | libsolidity/ASTPrinter.cpp | 13 | ||||
-rw-r--r-- | libsolidity/ASTPrinter.h | 2 | ||||
-rw-r--r-- | libsolidity/ASTVisitor.h | 4 | ||||
-rw-r--r-- | libsolidity/AST_accept.h | 18 | ||||
-rw-r--r-- | libsolidity/Compiler.cpp | 1 | ||||
-rw-r--r-- | libsolidity/Parser.cpp | 20 | ||||
-rw-r--r-- | libsolidity/TypeChecker.cpp | 75 | ||||
-rw-r--r-- | libsolidity/TypeChecker.h | 1 | ||||
-rw-r--r-- | libsolidity/Types.cpp | 39 | ||||
-rw-r--r-- | libsolidity/Types.h | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 55 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 27 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 17 |
17 files changed, 294 insertions, 18 deletions
diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 9dd67cc2..fc1db3f3 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -1068,6 +1068,30 @@ private: }; /** + * Tuple or just parenthesized expression. + * Examples: (1, 2), (x,), (x), () + * Individual components might be empty shared pointers (as in the second example). + * The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple + * Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple. + */ +class TupleExpression: public Expression +{ +public: + TupleExpression( + SourceLocation const& _location, + std::vector<ASTPointer<Expression>> const& _components + ): + Expression(_location), m_components(_components) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + std::vector<ASTPointer<Expression>> const& components() const { return m_components; } + +private: + std::vector<ASTPointer<Expression>> m_components; +}; + +/** * Operation involving a unary operator, pre- or postfix. * Examples: ++i, delete x or !true */ diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h index 396cf50a..02dd054a 100644 --- a/libsolidity/ASTForward.h +++ b/libsolidity/ASTForward.h @@ -69,6 +69,7 @@ class VariableDeclarationStatement; class ExpressionStatement; class Expression; class Assignment; +class TupleExpression; class UnaryOperation; class BinaryOperation; class FunctionCall; diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp index 4c14f2b2..34012c73 100644 --- a/libsolidity/ASTJsonConverter.cpp +++ b/libsolidity/ASTJsonConverter.cpp @@ -226,6 +226,12 @@ bool ASTJsonConverter::visit(Assignment const& _node) return true; } +bool ASTJsonConverter::visit(TupleExpression const&) +{ + addJsonNode("TupleExpression",{}, true); + return true; +} + bool ASTJsonConverter::visit(UnaryOperation const& _node) { addJsonNode("UnaryOperation", @@ -396,6 +402,11 @@ void ASTJsonConverter::endVisit(Assignment const&) goUp(); } +void ASTJsonConverter::endVisit(TupleExpression const&) +{ + goUp(); +} + void ASTJsonConverter::endVisit(UnaryOperation const&) { goUp(); diff --git a/libsolidity/ASTJsonConverter.h b/libsolidity/ASTJsonConverter.h index 61f87860..a62259e2 100644 --- a/libsolidity/ASTJsonConverter.h +++ b/libsolidity/ASTJsonConverter.h @@ -68,6 +68,7 @@ public: bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Assignment const& _node) override; + bool visit(TupleExpression const& _node) override; bool visit(UnaryOperation const& _node) override; bool visit(BinaryOperation const& _node) override; bool visit(FunctionCall const& _node) override; @@ -99,6 +100,7 @@ public: void endVisit(VariableDeclarationStatement const&) override; void endVisit(ExpressionStatement const&) override; void endVisit(Assignment const&) override; + void endVisit(TupleExpression const&) override; void endVisit(UnaryOperation const&) override; void endVisit(BinaryOperation const&) override; void endVisit(FunctionCall const&) override; diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 534f7c78..cb231842 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -256,6 +256,14 @@ bool ASTPrinter::visit(Assignment const& _node) return goDeeper(); } +bool ASTPrinter::visit(TupleExpression const& _node) +{ + writeLine(string("TupleExpression")); + printType(_node); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(UnaryOperation const& _node) { writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + @@ -477,6 +485,11 @@ void ASTPrinter::endVisit(Assignment const&) m_indentation--; } +void ASTPrinter::endVisit(TupleExpression const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(UnaryOperation const&) { m_indentation--; diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h index a12ec0aa..95656436 100644 --- a/libsolidity/ASTPrinter.h +++ b/libsolidity/ASTPrinter.h @@ -76,6 +76,7 @@ public: bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Assignment const& _node) override; + bool visit(TupleExpression const& _node) override; bool visit(UnaryOperation const& _node) override; bool visit(BinaryOperation const& _node) override; bool visit(FunctionCall const& _node) override; @@ -115,6 +116,7 @@ public: void endVisit(VariableDeclarationStatement const&) override; void endVisit(ExpressionStatement const&) override; void endVisit(Assignment const&) override; + void endVisit(TupleExpression const&) override; void endVisit(UnaryOperation const&) override; void endVisit(BinaryOperation const&) override; void endVisit(FunctionCall const&) override; diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h index e665396c..3e50fb28 100644 --- a/libsolidity/ASTVisitor.h +++ b/libsolidity/ASTVisitor.h @@ -73,6 +73,7 @@ public: virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); } virtual bool visit(Assignment& _node) { return visitNode(_node); } + virtual bool visit(TupleExpression& _node) { return visitNode(_node); } virtual bool visit(UnaryOperation& _node) { return visitNode(_node); } virtual bool visit(BinaryOperation& _node) { return visitNode(_node); } virtual bool visit(FunctionCall& _node) { return visitNode(_node); } @@ -113,6 +114,7 @@ public: virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); } virtual void endVisit(Assignment& _node) { endVisitNode(_node); } + virtual void endVisit(TupleExpression& _node) { endVisitNode(_node); } virtual void endVisit(UnaryOperation& _node) { endVisitNode(_node); } virtual void endVisit(BinaryOperation& _node) { endVisitNode(_node); } virtual void endVisit(FunctionCall& _node) { endVisitNode(_node); } @@ -165,6 +167,7 @@ public: virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); } virtual bool visit(Assignment const& _node) { return visitNode(_node); } + virtual bool visit(TupleExpression const& _node) { return visitNode(_node); } virtual bool visit(UnaryOperation const& _node) { return visitNode(_node); } virtual bool visit(BinaryOperation const& _node) { return visitNode(_node); } virtual bool visit(FunctionCall const& _node) { return visitNode(_node); } @@ -205,6 +208,7 @@ public: virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Assignment const& _node) { endVisitNode(_node); } + virtual void endVisit(TupleExpression const& _node) { endVisitNode(_node); } virtual void endVisit(UnaryOperation const& _node) { endVisitNode(_node); } virtual void endVisit(BinaryOperation const& _node) { endVisitNode(_node); } virtual void endVisit(FunctionCall const& _node) { endVisitNode(_node); } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 994bfc8d..eb1f6098 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -559,6 +559,24 @@ void Assignment::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void TupleExpression::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + for (auto const& component: m_components) + if (component) + component->accept(_visitor); + _visitor.endVisit(*this); +} + +void TupleExpression::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + for (auto const& component: m_components) + if (component) + component->accept(_visitor); + _visitor.endVisit(*this); +} + void UnaryOperation::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 7c6c7831..cc1228a1 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -637,6 +637,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta for (size_t i = 0; i < assignments.size(); ++i) { size_t j = assignments.size() - i - 1; + solAssert(!!valueTypes[j], ""); VariableDeclaration const* varDecl = assignments[j]; if (!varDecl) utils.popStackElement(*valueTypes[j]); diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp index ab2d0094..1bb16b84 100644 --- a/libsolidity/Parser.cpp +++ b/libsolidity/Parser.cpp @@ -806,6 +806,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme ) { ASTNodeFactory varDeclNodeFactory(*this); + varDeclNodeFactory.markEndPosition(); ASTPointer<ASTString> name = expectIdentifierToken(); var = varDeclNodeFactory.createNode<VariableDeclaration>( ASTPointer<TypeName>(), @@ -1009,10 +1010,25 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() break; case Token::LParen: { + // Tuple or parenthesized expression. + // Special cases: () is empty tuple type, (x) is not a real tuple, (x,) is one-dimensional tuple m_scanner->next(); - ASTPointer<Expression> expression = parseExpression(); + vector<ASTPointer<Expression>> components; + if (m_scanner->currentToken() != Token::RParen) + while (true) + { + if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != Token::RParen) + components.push_back(parseExpression()); + else + components.push_back(ASTPointer<Expression>()); + if (m_scanner->currentToken() == Token::RParen) + break; + else if (m_scanner->currentToken() == Token::Comma) + m_scanner->next(); + } + nodeFactory.markEndPosition(); expectToken(Token::RParen); - return expression; + return nodeFactory.createNode<TupleExpression>(components); } default: if (Token::isElementaryTypeName(token)) diff --git a/libsolidity/TypeChecker.cpp b/libsolidity/TypeChecker.cpp index ca5b1eb7..c7b693bb 100644 --- a/libsolidity/TypeChecker.cpp +++ b/libsolidity/TypeChecker.cpp @@ -560,13 +560,31 @@ void TypeChecker::endVisit(Return const& _return) return; ParameterList const* params = _return.annotation().functionReturnParameters; if (!params) + { typeError(_return, "Return arguments not allowed."); + return; + } + TypePointers returnTypes; + for (auto const& var: params->parameters()) + returnTypes.push_back(type(*var)); + if (auto tupleType = dynamic_cast<TupleType const*>(type(*_return.expression()).get())) + { + if (tupleType->components().size() != params->parameters().size()) + typeError(_return, "Different number of arguments in return statement than in returns declaration."); + else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes))) + typeError( + *_return.expression(), + "Return argument type " + + type(*_return.expression())->toString() + + " is not implicitly convertible to expected type " + + TupleType(returnTypes).toString(false) + + "." + ); + } else if (params->parameters().size() != 1) typeError(_return, "Different number of arguments in return statement than in returns declaration."); else { - // this could later be changed such that the paramaters type is an anonymous struct type, - // but for now, we only allow one return parameter TypePointer const& expected = type(*params->parameters().front()); if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected)) typeError( @@ -590,7 +608,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) VariableDeclaration const& varDecl = *_statement.declarations().front(); if (!varDecl.annotation().type) fatalTypeError(_statement, "Assignment necessary for type detection."); - if (auto ref = dynamic_cast<ReferenceType const*>(varDecl.annotation().type.get())) + if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get())) { if (ref->dataStoredIn(DataLocation::Storage)) { @@ -610,10 +628,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) _statement.initialValue()->accept(*this); TypePointers valueTypes; - if (auto tupleType = dynamic_cast<TupleType const*>(_statement.initialValue()->annotation().type.get())) + if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue()).get())) valueTypes = tupleType->components(); else - valueTypes = TypePointers{_statement.initialValue()->annotation().type}; + valueTypes = TypePointers{type(*_statement.initialValue())}; // Determine which component is assigned to which variable. // If numbers do not match, fill up if variables begin or end empty (not both). @@ -741,6 +759,51 @@ bool TypeChecker::visit(Assignment const& _assignment) return false; } +bool TypeChecker::visit(TupleExpression const& _tuple) +{ + vector<ASTPointer<Expression>> const& components = _tuple.components(); + TypePointers types; + if (_tuple.annotation().lValueRequested) + { + for (auto const& component: components) + if (component) + { + requireLValue(*component); + types.push_back(type(*component)); + } + else + types.push_back(TypePointer()); + _tuple.annotation().type = make_shared<TupleType>(types); + // If some of the components are not LValues, the error is reported above. + _tuple.annotation().isLValue = true; + } + else + { + for (size_t i = 0; i < components.size(); ++i) + { + // Outside of an lvalue-context, the only situation where a component can be empty is (x,). + if (!components[i] && !(i == 1 && components.size() == 2)) + fatalTypeError(_tuple, "Tuple component cannot be empty."); + else if (components[i]) + { + components[i]->accept(*this); + types.push_back(type(*components[i])); + } + else + types.push_back(TypePointer()); + } + if (components.size() == 1) + _tuple.annotation().type = type(*components[0]); + else + { + if (components.size() == 2 && !components[1]) + types.pop_back(); + _tuple.annotation().type = make_shared<TupleType>(types); + } + } + return false; +} + bool TypeChecker::visit(UnaryOperation const& _operation) { // Inc, Dec, Add, Sub, Not, BitNot, Delete @@ -1236,10 +1299,10 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte void TypeChecker::requireLValue(Expression const& _expression) { + _expression.annotation().lValueRequested = true; _expression.accept(*this); if (!_expression.annotation().isLValue) typeError(_expression, "Expression has to be an lvalue."); - _expression.annotation().lValueRequested = true; } void TypeChecker::typeError(ASTNode const& _node, string const& _description) diff --git a/libsolidity/TypeChecker.h b/libsolidity/TypeChecker.h index d9cb39ae..7af5473b 100644 --- a/libsolidity/TypeChecker.h +++ b/libsolidity/TypeChecker.h @@ -90,6 +90,7 @@ private: virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; virtual bool visit(Assignment const& _assignment) override; + virtual bool visit(TupleExpression const& _tuple) override; virtual void endVisit(BinaryOperation const& _operation) override; virtual bool visit(UnaryOperation const& _operation) override; virtual bool visit(FunctionCall const& _functionCall) override; diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 51df5fbf..d7beab26 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -1256,8 +1256,8 @@ string TupleType::toString(bool _short) const return "tuple()"; string str = "tuple("; for (auto const& t: m_components) - str += t->toString(_short) + ", "; - str.resize(str.size() - 2); + str += (t ? t->toString(_short) : "") + ","; + str.pop_back(); return str + ")"; } @@ -1273,10 +1273,30 @@ unsigned TupleType::sizeOnStack() const { unsigned size = 0; for (auto const& t: m_components) - size += t->sizeOnStack(); + size += t ? t->sizeOnStack() : 0; return size; } +bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const +{ + if (auto tupleType = dynamic_cast<TupleType const*>(&_other)) + { + if (components().size() != tupleType->components().size()) + return false; + for (size_t i = 0; i < components().size(); ++i) + if ((!components()[i]) != (!tupleType->components()[i])) + return false; + else if ( + components()[i] && + !components()[i]->isImplicitlyConvertibleTo(*tupleType->components()[i]) + ) + return false; + return true; + } + else + return false; +} + FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): m_location(_isInternal ? Location::Internal : Location::External), m_isConstant(_function.isDeclaredConst()), @@ -1638,14 +1658,15 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const parameterTypes.push_back(t); } - //@todo make this more intelligent once we support destructuring assignments + // Removes dynamic types. TypePointers returnParameterTypes; vector<string> returnParameterNames; - if (!m_returnParameterTypes.empty() && m_returnParameterTypes.front()->calldataEncodedSize() > 0) - { - returnParameterTypes.push_back(m_returnParameterTypes.front()); - returnParameterNames.push_back(m_returnParameterNames.front()); - } + for (size_t i = 0; i < m_returnParameterTypes.size(); ++i) + if (m_returnParameterTypes[i]->calldataEncodedSize() > 0) + { + returnParameterTypes.push_back(m_returnParameterTypes[i]); + returnParameterNames.push_back(m_returnParameterNames[i]); + } return make_shared<FunctionType>( parameterTypes, returnParameterTypes, diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 5c4aacdc..e8d65c41 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -682,6 +682,7 @@ private: /** * Type that can hold a finite sequence of values of different types. + * In some cases, the components are empty pointers (when used as placeholders). */ class TupleType: public Type { @@ -695,6 +696,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; + virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; std::vector<TypePointer> const& components() const { return m_components; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index bee19010..1d9a403a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5677,6 +5677,61 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration) BOOST_CHECK(callContractFunction("f()", encodeArgs()) == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(tuples) +{ + char const* sourceCode = R"( + contract C { + uint[] data; + function g() internal returns (uint a, uint b, uint[] storage c) { + return (1, 2, data); + } + function h() external returns (uint a, uint b) { + return (5, 6); + } + function f() returns (uint) { + data.length = 1; + data[0] = 3; + uint a; uint b; + (a, b) = this.h(); + if (a != 1 || b != 2) return 1; + uint[] storage c; + (a, b, c) = g(); + if (a != 5 || b != 6 || c[0] != 3) return 2; + (a, b) = (b, a); + if (a != 6 || b != 5) return 3; + (a, , b, ) = (8, 9, 10, 11, 12); + if (a != 8 || b != 10) return 3; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0))); +} + +BOOST_AUTO_TEST_CASE(destructuring_assignment_wildcard) +{ + char const* sourceCode = R"( + contract C { + function f() returns (uint) { + uint a; + uint b; + uint c; + (a,) = (1,); + if (a != 1) return 1; + (,b) = (2,3,4); + if (b != 4) return 2; + (, c,) = (5,6,7); + if (c != 6) return 3; + (a, b,) = (11, 12, 13); + if (a != 11 || b != 12) return 4; + (, a, b) = (11, 12, 13); + if (a != 12 || b != 13) return 5; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0))); +} BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2558ba97..0b5a86d7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2460,6 +2460,33 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(tuples) +{ + char const* text = R"( + contract C { + function f() { + uint a = (1); + var (b,) = (1,); + var (c,d) = (1, 2 + a); + var (e,) = (1, 2, b); + } + } + )"; + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); +} + +BOOST_AUTO_TEST_CASE(tuples_empty_components) +{ + char const* text = R"( + contract C { + function f() { + (1,,2); + } + } + )"; + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); +} + BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5) { char const* text = R"( diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 35393811..9aa4a8ef 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -983,7 +983,7 @@ BOOST_AUTO_TEST_CASE(local_const_variable) BOOST_AUTO_TEST_CASE(multi_variable_declaration) { char const* text = R"( - library Lib { + contract C { function f() { var (a,b,c) = g(); var (d) = 2; @@ -1000,6 +1000,21 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(tuples) +{ + char const* text = R"( + contract C { + function f() { + uint a = (1); + var (b,) = (1,); + var (c,d) = (1, 2 + a); + var (e,) = (1, 2, b); + } + } + )"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + BOOST_AUTO_TEST_SUITE_END() } |