1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /**
18 * @date 2017
19 * Unit tests for parsing Yul.
20 */
21
22 #include <test/Common.h>
23
24 #include <test/libsolidity/ErrorCheck.h>
25 #include <test/libyul/Common.h>
26
27 #include <libyul/AST.h>
28 #include <libyul/AsmParser.h>
29 #include <libyul/AsmPrinter.h>
30 #include <libyul/AsmAnalysis.h>
31 #include <libyul/AsmAnalysisInfo.h>
32 #include <libyul/Dialect.h>
33 #include <liblangutil/ErrorReporter.h>
34
35 #include <boost/algorithm/string/replace.hpp>
36 #include <boost/test/unit_test.hpp>
37
38 #include <memory>
39 #include <optional>
40 #include <string>
41
42 using namespace std;
43 using namespace solidity;
44 using namespace solidity::util;
45 using namespace solidity::langutil;
46
47 BOOST_TEST_DONT_PRINT_LOG_VALUE(ErrorId)
48 BOOST_TEST_DONT_PRINT_LOG_VALUE(Error::Type)
49
50 namespace solidity::yul::test
51 {
52
53 namespace
54 {
55
parse(string const & _source,Dialect const & _dialect,ErrorReporter & errorReporter)56 shared_ptr<Block> parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter)
57 {
58 try
59 {
60 auto stream = CharStream(_source, "");
61 map<unsigned, shared_ptr<string const>> indicesToSourceNames;
62 indicesToSourceNames[0] = make_shared<string const>("source0");
63 indicesToSourceNames[1] = make_shared<string const>("source1");
64
65 auto parserResult = yul::Parser(
66 errorReporter,
67 _dialect,
68 move(indicesToSourceNames)
69 ).parse(stream);
70 if (parserResult)
71 {
72 yul::AsmAnalysisInfo analysisInfo;
73 if (yul::AsmAnalyzer(
74 analysisInfo,
75 errorReporter,
76 _dialect
77 ).analyze(*parserResult))
78 return parserResult;
79 }
80 }
81 catch (FatalError const&)
82 {
83 BOOST_FAIL("Fatal error leaked.");
84 }
85 return {};
86 }
87
parseAndReturnFirstError(string const & _source,Dialect const & _dialect,bool _allowWarningsAndInfos=true)88 std::optional<Error> parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarningsAndInfos = true)
89 {
90 ErrorList errors;
91 ErrorReporter errorReporter(errors);
92 if (!parse(_source, _dialect, errorReporter))
93 {
94 BOOST_REQUIRE(!errors.empty());
95 BOOST_CHECK_EQUAL(errors.size(), 1);
96 return *errors.front();
97 }
98 else
99 {
100 // If success is true, there might still be an error in the assembly stage.
101 if (_allowWarningsAndInfos && !Error::containsErrors(errors))
102 return {};
103 else if (!errors.empty())
104 {
105 if (!_allowWarningsAndInfos)
106 BOOST_CHECK_EQUAL(errors.size(), 1);
107 return *errors.front();
108 }
109 }
110 return {};
111 }
112
successParse(std::string const & _source,Dialect const & _dialect=Dialect::yulDeprecated (),bool _allowWarningsAndInfos=true)113 bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarningsAndInfos = true)
114 {
115 return !parseAndReturnFirstError(_source, _dialect, _allowWarningsAndInfos);
116 }
117
expectError(std::string const & _source,Dialect const & _dialect=Dialect::yulDeprecated (),bool _allowWarningsAndInfos=false)118 Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarningsAndInfos = false)
119 {
120
121 auto error = parseAndReturnFirstError(_source, _dialect, _allowWarningsAndInfos);
122 BOOST_REQUIRE(error);
123 return *error;
124 }
125
126 }
127
128 #define CHECK_ERROR_DIALECT(text, typ, substring, dialect) \
129 do \
130 { \
131 Error err = expectError((text), dialect, false); \
132 BOOST_CHECK(err.type() == (Error::Type::typ)); \
133 BOOST_CHECK(solidity::frontend::test::searchErrorMessage(err, (substring))); \
134 } while(0)
135
136 #define CHECK_ERROR(text, typ, substring) CHECK_ERROR_DIALECT(text, typ, substring, Dialect::yulDeprecated())
137
138 BOOST_AUTO_TEST_SUITE(YulParser)
139
BOOST_AUTO_TEST_CASE(builtins_analysis)140 BOOST_AUTO_TEST_CASE(builtins_analysis)
141 {
142 struct SimpleDialect: public Dialect
143 {
144 BuiltinFunction const* builtin(YulString _name) const override
145 {
146 return _name == "builtin"_yulstring ? &f : nullptr;
147 }
148 BuiltinFunction f{"builtin"_yulstring, vector<Type>(2), vector<Type>(3), {}, {}, false, {}};
149 };
150
151 SimpleDialect dialect;
152 BOOST_CHECK(successParse("{ let a, b, c := builtin(1, 2) }", dialect));
153 CHECK_ERROR_DIALECT("{ let a, b, c := builtin(1) }", TypeError, "Function \"builtin\" expects 2 arguments but got 1", dialect);
154 CHECK_ERROR_DIALECT("{ let a, b := builtin(1, 2) }", DeclarationError, "Variable count mismatch for declaration of \"a, b\": 2 variables and 3 values.", dialect);
155 }
156
BOOST_AUTO_TEST_CASE(default_types_set)157 BOOST_AUTO_TEST_CASE(default_types_set)
158 {
159 ErrorList errorList;
160 ErrorReporter reporter(errorList);
161 shared_ptr<Block> result = parse(
162 "{"
163 "let x:bool := true:bool "
164 "let z:bool := true "
165 "let y := add(1, 2) "
166 "switch y case 0 {} default {} "
167 "}",
168 EVMDialectTyped::instance(EVMVersion{}),
169 reporter
170 );
171 BOOST_REQUIRE(!!result && errorList.size() == 0);
172
173 // Use no dialect so that all types are printed.
174 // This tests that the default types are properly assigned.
175 BOOST_CHECK_EQUAL(AsmPrinter{}(*result),
176 "{\n"
177 " let x:bool := true:bool\n"
178 " let z:bool := true:bool\n"
179 " let y:u256 := add(1:u256, 2:u256)\n"
180 " switch y\n"
181 " case 0:u256 { }\n"
182 " default { }\n"
183 "}"
184 );
185
186 // Now test again with type dialect. Now the default types
187 // should be omitted.
188 BOOST_CHECK_EQUAL(AsmPrinter{EVMDialectTyped::instance(EVMVersion{})}(*result),
189 "{\n"
190 " let x:bool := true\n"
191 " let z:bool := true\n"
192 " let y := add(1, 2)\n"
193 " switch y\n"
194 " case 0 { }\n"
195 " default { }\n"
196 "}"
197 );
198 }
199
200 #define CHECK_LOCATION(_actual, _sourceName, _start, _end) \
201 do { \
202 BOOST_CHECK_EQUAL((_sourceName), ((_actual).sourceName ? *(_actual).sourceName : "")); \
203 BOOST_CHECK_EQUAL((_start), (_actual).start); \
204 BOOST_CHECK_EQUAL((_end), (_actual).end); \
205 } while (0)
206
BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block)207 BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block)
208 {
209 ErrorList errorList;
210 ErrorReporter reporter(errorList);
211 auto const sourceText =
212 "/// @src 0:234:543\n"
213 "{}\n";
214 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
215 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
216 BOOST_REQUIRE(!!result && errorList.size() == 0);
217 CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543);
218 }
219
BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children)220 BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children)
221 {
222 ErrorList errorList;
223 ErrorReporter reporter(errorList);
224 auto const sourceText =
225 "/// @src 0:234:543\n"
226 "{\n"
227 "let x:bool := true:bool\n"
228 "/// @src 0:123:432\n"
229 "let z:bool := true\n"
230 "let y := add(1, 2)\n"
231 "}\n";
232 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
233 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
234 BOOST_REQUIRE(!!result);
235 CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543);
236 BOOST_REQUIRE_EQUAL(3, result->statements.size());
237 CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 234, 543);
238 CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 123, 432);
239 // [2] is inherited source location
240 CHECK_LOCATION(originLocationOf(result->statements.at(2)), "source0", 123, 432);
241 }
242
BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources)243 BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources)
244 {
245 ErrorList errorList;
246 ErrorReporter reporter(errorList);
247 auto const sourceText =
248 "/// @src 0:234:543\n"
249 "{\n"
250 "let x:bool := true:bool\n"
251 "/// @src 1:123:432\n"
252 "let z:bool := true\n"
253 "let y := add(1, 2)\n"
254 "}\n";
255 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
256 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
257 BOOST_REQUIRE(!!result && errorList.size() == 0);
258 CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543);
259 BOOST_REQUIRE_EQUAL(3, result->statements.size());
260 CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 234, 543);
261 CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source1", 123, 432);
262 // [2] is inherited source location
263 CHECK_LOCATION(originLocationOf(result->statements.at(2)), "source1", 123, 432);
264 }
265
BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested)266 BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested)
267 {
268 ErrorList errorList;
269 ErrorReporter reporter(errorList);
270 auto const sourceText =
271 "/// @src 0:234:543\n"
272 "{\n"
273 "let y := add(1, 2)\n"
274 "/// @src 0:343:434\n"
275 "switch y case 0 {} default {}\n"
276 "}\n";
277 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
278 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
279 BOOST_REQUIRE(!!result && errorList.size() == 0);
280 CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543);
281 BOOST_REQUIRE_EQUAL(2, result->statements.size());
282 CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 343, 434);
283 }
284
BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case)285 BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case)
286 {
287 ErrorList errorList;
288 ErrorReporter reporter(errorList);
289 auto const sourceText =
290 "/// @src 0:234:543\n"
291 "{\n"
292 "let y := add(1, 2)\n"
293 "/// @src 0:343:434\n"
294 "switch y\n"
295 "/// @src 0:3141:59265\n"
296 "case 0 {\n"
297 " /// @src 0:271:828\n"
298 " let z := add(3, 4)\n"
299 "}\n"
300 "}\n";
301 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
302 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
303 BOOST_REQUIRE(!!result && errorList.size() == 0);
304 CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543);
305
306 BOOST_REQUIRE_EQUAL(2, result->statements.size());
307 BOOST_REQUIRE(holds_alternative<Switch>(result->statements.at(1)));
308 auto const& switchStmt = get<Switch>(result->statements.at(1));
309
310 CHECK_LOCATION(switchStmt.debugData->originLocation, "source0", 343, 434);
311 BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size());
312 CHECK_LOCATION(switchStmt.cases.at(0).debugData->originLocation, "source0", 3141, 59265);
313
314 auto const& caseBody = switchStmt.cases.at(0).body;
315 BOOST_REQUIRE_EQUAL(1, caseBody.statements.size());
316 CHECK_LOCATION(originLocationOf(caseBody.statements.at(0)), "source0", 271, 828);
317 }
318
BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope)319 BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope)
320 {
321 ErrorList errorList;
322 ErrorReporter reporter(errorList);
323 auto const sourceText =
324 "/// @src 0:1:100\n"
325 "{\n"
326 "{\n"
327 "/// @src 0:123:432\n"
328 "let x:bool := true:bool\n"
329 "}\n"
330 "let z:bool := true\n"
331 "let y := add(1, 2)\n"
332 "}\n";
333 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
334 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
335 BOOST_REQUIRE(!!result && errorList.size() == 0);
336
337 CHECK_LOCATION(result->debugData->originLocation, "source0", 1, 100);
338
339 BOOST_REQUIRE_EQUAL(3, result->statements.size());
340 CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 1, 100);
341
342 // First child element must be a block itself with one statement.
343 BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0)));
344 BOOST_REQUIRE_EQUAL(get<Block>(result->statements.at(0)).statements.size(), 1);
345 CHECK_LOCATION(originLocationOf(get<Block>(result->statements.at(0)).statements.at(0)), "source0", 123, 432);
346
347 // The next two elements have an inherited source location from the prior inner scope.
348 CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 123, 432);
349 CHECK_LOCATION(originLocationOf(result->statements.at(2)), "source0", 123, 432);
350 }
351
BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty)352 BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty)
353 {
354 // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child.
355 ErrorList errorList;
356 ErrorReporter reporter(errorList);
357 auto const sourceText =
358 "{\n"
359 "/// @src 0:123:432\n"
360 "let a:bool\n"
361 "/// @src 1:1:10\n"
362 "a := true:bool\n"
363 "}\n";
364 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
365 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
366 BOOST_REQUIRE(!!result && errorList.size() == 0); // should still parse
367 BOOST_REQUIRE_EQUAL(2, result->statements.size());
368 CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 123, 432);
369 CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source1", 1, 10);
370 }
371
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index)372 BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index)
373 {
374 // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child.
375 ErrorList errorList;
376 ErrorReporter reporter(errorList);
377 auto const sourceText =
378 "{\n"
379 "/// @src 1:123:432\n"
380 "let a:bool := true:bool\n"
381 "/// @src 2345:0:8\n"
382 "let b:bool := true:bool\n"
383 "\n"
384 "}\n";
385 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
386 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
387 BOOST_REQUIRE(!!result); // should still parse
388 BOOST_REQUIRE(errorList.size() == 1);
389 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
390 BOOST_TEST(errorList[0]->errorId() == 2674_error);
391 }
392
BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1)393 BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1)
394 {
395 // Tests single AST node (e.g. VariableDeclaration) with different source locations for each child.
396 ErrorList errorList;
397 ErrorReporter reporter(errorList);
398 auto const sourceText =
399 "{\n"
400 "/// @src 0:123:432\n"
401 "let x:bool \n"
402 "/// @src 0:234:2026\n"
403 ":= true:bool\n"
404 "}\n";
405 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
406 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
407 BOOST_REQUIRE(!!result && errorList.size() == 0);
408
409 BOOST_REQUIRE_EQUAL(1, result->statements.size());
410 CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 123, 432);
411 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
412 VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
413 CHECK_LOCATION(originLocationOf(*varDecl.value), "source0", 234, 2026);
414 }
415
BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2)416 BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2)
417 {
418 ErrorList errorList;
419 ErrorReporter reporter(errorList);
420 auto const sourceText = R"(
421 /// @src 0:0:5
422 {
423 let x := /// @src 1:2:3
424 add(1, /// @src 0:4:8
425 2)
426 }
427 )";
428 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
429 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
430 BOOST_REQUIRE(!!result && errorList.size() == 0);
431 BOOST_REQUIRE_EQUAL(1, result->statements.size());
432 CHECK_LOCATION(result->debugData->originLocation, "source0", 0, 5);
433
434 // `let x := add(1, `
435 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
436 VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
437 CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 0, 5);
438 BOOST_REQUIRE(!!varDecl.value);
439 BOOST_REQUIRE(holds_alternative<FunctionCall>(*varDecl.value));
440 FunctionCall const& call = get<FunctionCall>(*varDecl.value);
441 CHECK_LOCATION(call.debugData->originLocation, "source1", 2, 3);
442
443 // `2`
444 BOOST_REQUIRE_EQUAL(2, call.arguments.size());
445 CHECK_LOCATION(originLocationOf(call.arguments.at(1)), "source0", 4, 8);
446 }
447
BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3)448 BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3)
449 {
450 ErrorList errorList;
451 ErrorReporter reporter(errorList);
452 auto const sourceText = R"(
453 /// @src 1:23:45
454 { // Block
455 { // Block
456 sstore(0, 1) // FunctionCall
457 /// @src 0:420:680
458 }
459 mstore(1, 2) // FunctionCall
460 }
461 )";
462 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
463 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
464 BOOST_REQUIRE(!!result && errorList.size() == 0);
465 BOOST_REQUIRE_EQUAL(2, result->statements.size());
466 CHECK_LOCATION(result->debugData->originLocation, "source1", 23, 45);
467
468 BOOST_REQUIRE(holds_alternative<Block>(result->statements.at(0)));
469 Block const& innerBlock = get<Block>(result->statements.at(0));
470 CHECK_LOCATION(innerBlock.debugData->originLocation, "source1", 23, 45);
471
472 BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size());
473 BOOST_REQUIRE(holds_alternative<ExpressionStatement>(result->statements.at(1)));
474 ExpressionStatement const& sstoreStmt = get<ExpressionStatement>(innerBlock.statements.at(0));
475 BOOST_REQUIRE(holds_alternative<FunctionCall>(sstoreStmt.expression));
476 FunctionCall const& sstoreCall = get<FunctionCall>(sstoreStmt.expression);
477 CHECK_LOCATION(sstoreCall.debugData->originLocation, "source1", 23, 45);
478
479 BOOST_REQUIRE(holds_alternative<ExpressionStatement>(result->statements.at(1)));
480 ExpressionStatement mstoreStmt = get<ExpressionStatement>(result->statements.at(1));
481 BOOST_REQUIRE(holds_alternative<FunctionCall>(mstoreStmt.expression));
482 FunctionCall const& mstoreCall = get<FunctionCall>(mstoreStmt.expression);
483 CHECK_LOCATION(mstoreCall.debugData->originLocation, "source0", 420, 680);
484 }
485
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid)486 BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid)
487 {
488 ErrorList errorList;
489 ErrorReporter reporter(errorList);
490 auto const sourceText = R"(
491 /// @src 1:23:45
492 {
493 /// @src 0:420:680
494 /// @invalid
495 let a:bool := true
496 }
497 )";
498 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
499 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
500 BOOST_REQUIRE(!!result && errorList.size() == 0);
501 BOOST_REQUIRE_EQUAL(1, result->statements.size());
502 CHECK_LOCATION(result->debugData->originLocation, "source1", 23, 45);
503
504 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
505 VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
506 CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 420, 680);
507 }
508
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix)509 BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix)
510 {
511 ErrorList errorList;
512 ErrorReporter reporter(errorList);
513 auto const sourceText = R"(
514 /// @src 0:420:680foo
515 {}
516 )";
517 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
518 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
519 BOOST_REQUIRE(!!result);
520 BOOST_REQUIRE(errorList.size() == 1);
521 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
522 BOOST_TEST(errorList[0]->errorId() == 8387_error);
523 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
524 }
525
BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_prefix)526 BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_prefix)
527 {
528 ErrorList errorList;
529 ErrorReporter reporter(errorList);
530 auto const sourceText = R"(
531 /// abc@src 0:111:222
532 {}
533 )";
534 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
535 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
536 BOOST_REQUIRE(!!result && errorList.size() == 0);
537 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
538 }
539
BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified)540 BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified)
541 {
542 ErrorList errorList;
543 ErrorReporter reporter(errorList);
544 auto const sourceText = R"(
545 /// @src -1:-1:-1
546 {}
547 )";
548 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
549 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
550 BOOST_REQUIRE(!!result && errorList.size() == 0);
551 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
552 }
553
BOOST_AUTO_TEST_CASE(customSourceLocations_non_integer)554 BOOST_AUTO_TEST_CASE(customSourceLocations_non_integer)
555 {
556 ErrorList errorList;
557 ErrorReporter reporter(errorList);
558 auto const sourceText = R"(
559 /// @src a:b:c
560 {}
561 )";
562 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
563 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
564 BOOST_REQUIRE(!!result);
565 BOOST_REQUIRE(errorList.size() == 1);
566 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
567 BOOST_TEST(errorList[0]->errorId() == 8387_error);
568 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
569 }
570
BOOST_AUTO_TEST_CASE(customSourceLocations_bad_integer)571 BOOST_AUTO_TEST_CASE(customSourceLocations_bad_integer)
572 {
573 ErrorList errorList;
574 ErrorReporter reporter(errorList);
575 auto const sourceText = R"(
576 /// @src 111111111111111111111:222222222222222222222:333333333333333333333
577 {}
578 )";
579 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
580 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
581 BOOST_REQUIRE(!!result);
582 BOOST_REQUIRE(errorList.size() == 1);
583 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
584 BOOST_TEST(errorList[0]->errorId() == 6367_error);
585 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
586 }
587
BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match)588 BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match)
589 {
590 ErrorList errorList;
591 ErrorReporter reporter(errorList);
592 auto const sourceText = R"(
593 /// @src 0:123:432
594 {
595 /// @src 1:10:20
596 /// @src 0:30:40
597 let x:bool := true
598 }
599 )";
600 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
601 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
602 BOOST_REQUIRE(!!result && errorList.size() == 0);
603 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
604 VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
605
606 // Ensure the latest @src per documentation-comment is used (0:30:40).
607 CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 30, 40);
608 }
609
BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_no_whitespace)610 BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_no_whitespace)
611 {
612 ErrorList errorList;
613 ErrorReporter reporter(errorList);
614 auto const sourceText = R"(
615 /// @src 0:111:222@src 1:333:444
616 {}
617 )";
618 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
619 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
620 BOOST_REQUIRE(!!result);
621 BOOST_REQUIRE(errorList.size() == 1);
622 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
623 BOOST_TEST(errorList[0]->errorId() == 8387_error);
624 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
625 }
626
BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_separated_with_single_space)627 BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_separated_with_single_space)
628 {
629 ErrorList errorList;
630 ErrorReporter reporter(errorList);
631 auto const sourceText = R"(
632 /// @src 0:111:222 @src 1:333:444
633 {}
634 )";
635 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
636 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
637 BOOST_REQUIRE(!!result && errorList.size() == 0);
638 CHECK_LOCATION(result->debugData->originLocation, "source1", 333, 444);
639 }
640
BOOST_AUTO_TEST_CASE(customSourceLocations_leading_trailing_whitespace)641 BOOST_AUTO_TEST_CASE(customSourceLocations_leading_trailing_whitespace)
642 {
643 ErrorList errorList;
644 ErrorReporter reporter(errorList);
645 auto const sourceText = "/// @src 0:111:222 \n{}";
646 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
647 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
648 BOOST_REQUIRE(!!result && errorList.size() == 0);
649 CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222);
650 }
651
BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc)652 BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc)
653 {
654 ErrorList errorList;
655 ErrorReporter reporter(errorList);
656 auto const sourceText = R"(
657 /// @src 1:2:3
658 {
659 /// @src -1:10:20
660 let x:bool := true
661 }
662 )";
663 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
664 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
665 BOOST_REQUIRE(!!result && errorList.size() == 0);
666 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
667 VariableDeclaration const& varDecl = get<VariableDeclaration>(result->statements.at(0));
668
669 // -1 points to original source code, which in this case is `"source0"` (which is also
670 CHECK_LOCATION(varDecl.debugData->originLocation, "", 10, 20);
671 }
672
BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets)673 BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets)
674 {
675 ErrorList errorList;
676 ErrorReporter reporter(errorList);
677 auto const sourceText = R"~~~(
678 {
679 /// @src 0:149:156 "new C(\"123\")"
680 let x := 123
681
682 let y := /** @src 1:96:165 "contract D {..." */ 128
683 }
684 )~~~";
685 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
686 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
687 BOOST_REQUIRE(!!result && errorList.size() == 0);
688 BOOST_REQUIRE_EQUAL(result->statements.size(), 2);
689
690 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
691 VariableDeclaration const& varX = get<VariableDeclaration>(result->statements.at(0));
692 CHECK_LOCATION(varX.debugData->originLocation, "source0", 149, 156);
693
694 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(1)));
695 VariableDeclaration const& varY = get<VariableDeclaration>(result->statements.at(1));
696 BOOST_REQUIRE(!!varY.value);
697 BOOST_REQUIRE(holds_alternative<Literal>(*varY.value));
698 Literal const& literal128 = get<Literal>(*varY.value);
699 CHECK_LOCATION(literal128.debugData->originLocation, "source1", 96, 165);
700 }
701
BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_empty_snippet)702 BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_empty_snippet)
703 {
704 ErrorList errorList;
705 ErrorReporter reporter(errorList);
706 auto const sourceText = R"(
707 /// @src 0:111:222 ""
708 {}
709 )";
710 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
711 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
712 BOOST_REQUIRE(!!result && errorList.size() == 0);
713 CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222);
714 }
715
BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_before_snippet)716 BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_before_snippet)
717 {
718 ErrorList errorList;
719 ErrorReporter reporter(errorList);
720 auto const sourceText = R"(
721 /// @src 0:111:222"abc" def
722 {}
723 )";
724 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
725 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
726 BOOST_REQUIRE(!!result);
727 BOOST_REQUIRE(errorList.size() == 1);
728 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
729 BOOST_TEST(errorList[0]->errorId() == 8387_error);
730 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
731 }
732
BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_after_snippet)733 BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_after_snippet)
734 {
735 ErrorList errorList;
736 ErrorReporter reporter(errorList);
737 auto const sourceText = R"(
738 /// @src 0:111:222 "abc"def
739 {}
740 )";
741 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
742 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
743 BOOST_REQUIRE(!!result && errorList.size() == 0);
744 CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222);
745 }
746
BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_no_whitespace)747 BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_no_whitespace)
748 {
749 ErrorList errorList;
750 ErrorReporter reporter(errorList);
751 auto const sourceText = R"(
752 /// @src 0:111:222 "abc"@src 1:333:444 "abc"
753 {}
754 )";
755 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
756 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
757 BOOST_REQUIRE(!!result && errorList.size() == 0);
758 CHECK_LOCATION(result->debugData->originLocation, "source1", 333, 444);
759 }
760
BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_unterminated_quote)761 BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_unterminated_quote)
762 {
763 ErrorList errorList;
764 ErrorReporter reporter(errorList);
765 auto const sourceText = R"(
766 /// @src 0:111:222 " abc @src 1:333:444
767 {}
768 )";
769 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
770 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
771 BOOST_REQUIRE(!!result);
772 BOOST_REQUIRE(errorList.size() == 1);
773 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
774 BOOST_TEST(errorList[0]->errorId() == 1544_error);
775 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
776 }
777
BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_with_nested_locations)778 BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_with_nested_locations)
779 {
780 ErrorList errorList;
781 ErrorReporter reporter(errorList);
782 auto const sourceText = R"~~~(
783 {
784 /// @src 0:149:156 "new C(\"123\") /// @src 1:3:4 "
785 let x := 123
786
787 let y := /** @src 1:96:165 "function f() internal { \"\/** @src 0:6:7 *\/\"; }" */ 128
788 }
789 )~~~";
790 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
791 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
792 BOOST_REQUIRE(!!result && errorList.size() == 0);
793 BOOST_REQUIRE_EQUAL(result->statements.size(), 2);
794
795 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
796 VariableDeclaration const& varX = get<VariableDeclaration>(result->statements.at(0));
797 CHECK_LOCATION(varX.debugData->originLocation, "source0", 149, 156);
798
799 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(1)));
800 VariableDeclaration const& varY = get<VariableDeclaration>(result->statements.at(1));
801 BOOST_REQUIRE(!!varY.value);
802 BOOST_REQUIRE(holds_alternative<Literal>(*varY.value));
803 Literal const& literal128 = get<Literal>(*varY.value);
804 CHECK_LOCATION(literal128.debugData->originLocation, "source1", 96, 165);
805 }
806
BOOST_AUTO_TEST_CASE(astid)807 BOOST_AUTO_TEST_CASE(astid)
808 {
809 ErrorList errorList;
810 ErrorReporter reporter(errorList);
811 auto const sourceText = R"(
812 /// @src -1:-1:-1 @ast-id 7
813 {
814 /** @ast-id 2 */
815 function f(x) -> y {}
816 mstore(1, 2)
817 }
818 )";
819 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
820 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
821 BOOST_REQUIRE(!!result);
822 BOOST_CHECK(result->debugData->astID == int64_t(7));
823 auto const& funDef = get<FunctionDefinition>(result->statements.at(0));
824 BOOST_CHECK(funDef.debugData->astID == int64_t(2));
825 BOOST_CHECK(funDef.parameters.at(0).debugData->astID == nullopt);
826 BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == nullopt);
827 }
828
BOOST_AUTO_TEST_CASE(astid_reset)829 BOOST_AUTO_TEST_CASE(astid_reset)
830 {
831 ErrorList errorList;
832 ErrorReporter reporter(errorList);
833 auto const sourceText = R"(
834 /// @src -1:-1:-1 @ast-id 7 @src 1:1:1
835 {
836 /** @ast-id 2 */
837 function f(x) -> y {}
838 mstore(1, 2)
839 }
840 )";
841 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
842 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
843 BOOST_REQUIRE(!!result);
844 BOOST_CHECK(result->debugData->astID == int64_t(7));
845 auto const& funDef = get<FunctionDefinition>(result->statements.at(0));
846 BOOST_CHECK(funDef.debugData->astID == int64_t(2));
847 BOOST_CHECK(funDef.parameters.at(0).debugData->astID == nullopt);
848 BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == nullopt);
849 }
850
BOOST_AUTO_TEST_CASE(astid_multi)851 BOOST_AUTO_TEST_CASE(astid_multi)
852 {
853 ErrorList errorList;
854 ErrorReporter reporter(errorList);
855 auto const sourceText = R"(
856 /// @src -1:-1:-1 @ast-id 7 @src 1:1:1 @ast-id 8
857 {}
858 )";
859 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
860 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
861 BOOST_REQUIRE(!!result);
862 BOOST_CHECK(result->debugData->astID == int64_t(8));
863 }
864
BOOST_AUTO_TEST_CASE(astid_invalid)865 BOOST_AUTO_TEST_CASE(astid_invalid)
866 {
867 ErrorList errorList;
868 ErrorReporter reporter(errorList);
869 auto const sourceText = R"(
870 /// @src -1:-1:-1 @ast-id abc @src 1:1:1
871 {}
872 )";
873 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
874 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
875 BOOST_REQUIRE(!!result);
876 BOOST_REQUIRE(errorList.size() == 1);
877 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
878 BOOST_TEST(errorList[0]->errorId() == 1749_error);
879 CHECK_LOCATION(result->debugData->originLocation, "", -1, -1);
880 }
881
BOOST_AUTO_TEST_CASE(astid_too_large)882 BOOST_AUTO_TEST_CASE(astid_too_large)
883 {
884 ErrorList errorList;
885 ErrorReporter reporter(errorList);
886 auto const sourceText = R"(
887 /// @ast-id 9223372036854775808
888 {}
889 )";
890 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
891 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
892 BOOST_REQUIRE(!!result);
893 BOOST_REQUIRE(errorList.size() == 1);
894 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
895 BOOST_TEST(errorList[0]->errorId() == 1749_error);
896 }
897
BOOST_AUTO_TEST_CASE(astid_way_too_large)898 BOOST_AUTO_TEST_CASE(astid_way_too_large)
899 {
900 ErrorList errorList;
901 ErrorReporter reporter(errorList);
902 auto const sourceText = R"(
903 /// @ast-id 999999999999999999999999999999999999999
904 {}
905 )";
906 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
907 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
908 BOOST_REQUIRE(!!result);
909 BOOST_REQUIRE(errorList.size() == 1);
910 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
911 BOOST_TEST(errorList[0]->errorId() == 1749_error);
912 }
913
BOOST_AUTO_TEST_CASE(astid_not_fully_numeric)914 BOOST_AUTO_TEST_CASE(astid_not_fully_numeric)
915 {
916 ErrorList errorList;
917 ErrorReporter reporter(errorList);
918 auto const sourceText = R"(
919 /// @ast-id 9x
920 {}
921 )";
922 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
923 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
924 BOOST_REQUIRE(!!result);
925 BOOST_REQUIRE(errorList.size() == 1);
926 BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError);
927 BOOST_TEST(errorList[0]->errorId() == 1749_error);
928 }
929
BOOST_AUTO_TEST_CASE(customSourceLocations_multiple_src_tags_on_one_line)930 BOOST_AUTO_TEST_CASE(customSourceLocations_multiple_src_tags_on_one_line)
931 {
932 ErrorList errorList;
933 ErrorReporter reporter(errorList);
934 auto const sourceText =
935 "{\n"
936 " /// "
937 R"~~(@src 1:2:3 ""@src 1:2:4 @src-1:2:5@src 1:2:6 @src 1:2:7 "" @src 1:2:8)~~"
938 R"~~( X "@src 0:10:20 "new C(\"123\") /// @src 1:4:5 "" XYZ)~~"
939 R"~~( @src0:20:30 "abc"@src0:2:4 @src-0:2:5@)~~"
940 R"~~( @some text with random @ signs @@@ @- @** 1:6:7 "src 1:8:9")~~"
941 "\n"
942 " let x := 123\n"
943 "}\n";
944 EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{});
945 shared_ptr<Block> result = parse(sourceText, dialect, reporter);
946 BOOST_REQUIRE(!!result && errorList.size() == 0);
947 BOOST_REQUIRE_EQUAL(result->statements.size(), 1);
948
949 BOOST_REQUIRE(holds_alternative<VariableDeclaration>(result->statements.at(0)));
950 VariableDeclaration const& varX = get<VariableDeclaration>(result->statements.at(0));
951 CHECK_LOCATION(varX.debugData->originLocation, "source1", 4, 5);
952 }
953
954 BOOST_AUTO_TEST_SUITE_END()
955
956 } // end namespaces
957