Skip to content

Commit f07e4a2

Browse files
committed
Adds support for tuples in test file parser.
1 parent f5fa62d commit f07e4a2

File tree

3 files changed

+152
-16
lines changed

3 files changed

+152
-16
lines changed

test/libsolidity/util/TestFileParser.cpp

+64-12
Original file line numberDiff line numberDiff line change
@@ -140,20 +140,33 @@ string TestFileParser::parseFunctionSignature()
140140
signature += formatToken(SoltToken::LParen);
141141
expect(SoltToken::LParen);
142142

143-
while (!accept(SoltToken::RParen))
143+
string parameters = parseIdentifierOrTuple();
144+
145+
while (accept(SoltToken::Comma))
144146
{
145-
signature += m_scanner.currentLiteral();
146-
expect(SoltToken::Identifier);
147-
while (accept(SoltToken::Comma))
148-
{
149-
signature += m_scanner.currentLiteral();
150-
expect(SoltToken::Comma);
151-
signature += m_scanner.currentLiteral();
152-
expect(SoltToken::Identifier);
153-
}
147+
parameters += formatToken(SoltToken::Comma);
148+
expect(SoltToken::Comma);
149+
if (accept(SoltToken::RParen, true))
150+
throw Error(Error::Type::ParserError, "Invalid signature detected: " + signature);
151+
parameters += parseIdentifierOrTuple();
152+
}
153+
if (accept(SoltToken::Arrow, true))
154+
throw Error(Error::Type::ParserError, "Invalid signature detected: " + signature);
155+
156+
signature += parameters;
157+
/// The last `)` could have been consumed already by
158+
/// `parseIdentifierOrTuple` and the occurrence of another `)`
159+
/// needs to be handled specifically.
160+
if (accept(SoltToken::RParen, true))
161+
{
162+
/// In case the function signature does not declare any parameters,
163+
/// its string representation still contains a sinlge `)`, because
164+
/// that's the end of recursion in `parseIdentifierOrTuple`.
165+
/// If already appended to the parameters, do not append again.
166+
const bool isListEmpty = parameters == formatToken(SoltToken::RParen);
167+
if (!isListEmpty)
168+
signature += formatToken(SoltToken::RParen);
154169
}
155-
signature += formatToken(SoltToken::RParen);
156-
expect(SoltToken::RParen);
157170
return signature;
158171
}
159172

@@ -245,6 +258,45 @@ pair<bytes, ABIType> TestFileParser::parseABITypeLiteral()
245258
}
246259
}
247260

261+
string TestFileParser::parseIdentifierOrTuple()
262+
{
263+
string identOrTuple;
264+
if (accept(SoltToken::Identifier))
265+
{
266+
identOrTuple = m_scanner.currentLiteral();
267+
expect(SoltToken::Identifier);
268+
return identOrTuple;
269+
}
270+
/// This is the end of recursion.
271+
if (accept(SoltToken::RParen, false))
272+
return formatToken(SoltToken::RParen);
273+
274+
bool isTuple = false;
275+
if (accept(SoltToken::LParen, true))
276+
{
277+
identOrTuple += formatToken(SoltToken::LParen);
278+
isTuple = true;
279+
}
280+
281+
identOrTuple += parseIdentifierOrTuple();
282+
if (identOrTuple == "()")
283+
throw Error(Error::Type::ParserError, "Empty tuples are not supported.");
284+
285+
while (accept(SoltToken::Comma))
286+
{
287+
identOrTuple += formatToken(SoltToken::Comma);
288+
expect(SoltToken::Comma);
289+
identOrTuple += parseIdentifierOrTuple();
290+
}
291+
292+
if (isTuple)
293+
{
294+
identOrTuple += formatToken(SoltToken::RParen);
295+
expect(SoltToken::RParen);
296+
}
297+
return identOrTuple;
298+
}
299+
248300
string TestFileParser::parseComment()
249301
{
250302
string comment = m_scanner.currentLiteral();

test/libsolidity/util/TestFileParser.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ using ParameterList = std::vector<Parameter>;
133133
*/
134134
struct FunctionCallExpectations
135135
{
136-
/// Representation of the comma-separated (or empty) list of expectated result values
136+
/// Representation of the comma-separated (or empty) list of expected result values
137137
/// attached to the function call object. It is checked against the actual result of
138138
/// a function call when used in test framework.
139139
ParameterList result;
@@ -319,7 +319,12 @@ class TestFileParser
319319
/// if data type is not supported.
320320
std::pair<bytes, ABIType> parseABITypeLiteral();
321321

322-
/// Parses a comment
322+
/// Recursively parses an identifier or a tuple definition that contains identifiers
323+
/// and / or parentheses like `((uint, uint), (uint, (uint, uint)), uint)`.
324+
std::string parseIdentifierOrTuple();
325+
326+
/// Parses a comment that is defined like this:
327+
/// # A nice comment. #
323328
std::string parseComment();
324329

325330
/// Parses the current number literal.

test/libsolidity/util/TestFileParserTests.cpp

+81-2
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,61 @@ BOOST_AUTO_TEST_CASE(call_arguments)
265265
);
266266
}
267267

268+
BOOST_AUTO_TEST_CASE(call_arguments_tuple)
269+
{
270+
char const* source = R"(
271+
// f((uint256, bytes32), uint256) ->
272+
// f((uint8), uint8) ->
273+
)";
274+
auto const calls = parse(source);
275+
BOOST_REQUIRE_EQUAL(calls.size(), 2);
276+
testFunctionCall(calls.at(0), Mode::SingleLine, "f((uint256,bytes32),uint256)", false);
277+
testFunctionCall(calls.at(1), Mode::SingleLine, "f((uint8),uint8)", false);
278+
}
279+
280+
BOOST_AUTO_TEST_CASE(call_arguments_tuple_of_tuples)
281+
{
282+
char const* source = R"(
283+
// f(((uint256, bytes32), bytes32), uint256)
284+
// # f(S memory s, uint256 b) #
285+
// ->
286+
)";
287+
auto const calls = parse(source);
288+
BOOST_REQUIRE_EQUAL(calls.size(), 1);
289+
testFunctionCall(
290+
calls.at(0),
291+
Mode::MultiLine,
292+
"f(((uint256,bytes32),bytes32),uint256)",
293+
false,
294+
fmt::encodeArgs(),
295+
fmt::encodeArgs(),
296+
0,
297+
" f(S memory s, uint256 b) "
298+
);
299+
}
300+
301+
BOOST_AUTO_TEST_CASE(call_arguments_recursive_tuples)
302+
{
303+
char const* source = R"(
304+
// f(((((bytes, bytes, bytes), bytes), bytes), bytes), bytes) ->
305+
// f(((((bytes, bytes, (bytes)), bytes), bytes), (bytes, bytes)), (bytes, bytes)) ->
306+
)";
307+
auto const calls = parse(source);
308+
BOOST_REQUIRE_EQUAL(calls.size(), 2);
309+
testFunctionCall(
310+
calls.at(0),
311+
Mode::SingleLine,
312+
"f(((((bytes,bytes,bytes),bytes),bytes),bytes),bytes)",
313+
false
314+
);
315+
testFunctionCall(
316+
calls.at(1),
317+
Mode::SingleLine,
318+
"f(((((bytes,bytes,(bytes)),bytes),bytes),(bytes,bytes)),(bytes,bytes))",
319+
false
320+
);
321+
}
322+
268323
BOOST_AUTO_TEST_CASE(call_arguments_mismatch)
269324
{
270325
char const* source = R"(
@@ -333,7 +388,7 @@ BOOST_AUTO_TEST_CASE(call_signature)
333388
char const* source = R"(
334389
// f(uint256, uint8, string) -> FAILURE
335390
// f(invalid, xyz, foo) -> FAILURE
336-
)";
391+
)";
337392
auto const calls = parse(source);
338393
BOOST_REQUIRE_EQUAL(calls.size(), 2);
339394
testFunctionCall(calls.at(0), Mode::SingleLine, "f(uint256,uint8,string)", true);
@@ -344,7 +399,31 @@ BOOST_AUTO_TEST_CASE(call_signature_invalid)
344399
{
345400
char const* source = R"(
346401
// f(uint8,) -> FAILURE
347-
)";
402+
)";
403+
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
404+
}
405+
406+
BOOST_AUTO_TEST_CASE(call_arguments_tuple_invalid)
407+
{
408+
char const* source = R"(
409+
// f((uint8,) -> FAILURE
410+
)";
411+
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
412+
}
413+
414+
BOOST_AUTO_TEST_CASE(call_arguments_tuple_invalid_empty)
415+
{
416+
char const* source = R"(
417+
// f(uint8, ()) -> FAILURE
418+
)";
419+
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
420+
}
421+
422+
BOOST_AUTO_TEST_CASE(call_arguments_tuple_invalid_parantheses)
423+
{
424+
char const* source = R"(
425+
// f((uint8,() -> FAILURE
426+
)";
348427
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
349428
}
350429

0 commit comments

Comments
 (0)