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