From d78522b08b4eb654d4e7c0bd662c39448e41ba9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 2 Sep 2021 15:17:56 +0200 Subject: [PATCH] AsmParser: Accept optional code snippets after the @src tags --- libyul/AsmParser.cpp | 18 ++++- scripts/error_codes.py | 1 + test/libyul/Parser.cpp | 172 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 2 deletions(-) diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index ca2864a5c..8cd88f411 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -135,7 +135,8 @@ void Parser::fetchSourceLocationFromComment() regex_constants::ECMAScript | regex_constants::optimize ); static regex const srcTagArgsRegex = regex( - R"~~(^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~", // index and location, e.g.: 1:234:-1 + R"~~(^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~" // index and location, e.g.: 1:234:-1 + R"~~(("(?:[^"\\]|\\.)*"?)?)~~", // optional code snippet, e.g.: "string memory s = \"abc\";..." regex_constants::ECMAScript | regex_constants::optimize ); @@ -164,9 +165,22 @@ void Parser::fetchSourceLocationFromComment() return; } - solAssert(srcTagArgsMatch.size() == 4, ""); + solAssert(srcTagArgsMatch.size() == 5, ""); position += srcTagArgsMatch.position() + srcTagArgsMatch.length(); + if (srcTagArgsMatch[4].matched && ( + !boost::algorithm::ends_with(srcTagArgsMatch[4].str(), "\"") || + boost::algorithm::ends_with(srcTagArgsMatch[4].str(), "\\\"") + )) + { + m_errorReporter.syntaxError( + 1544_error, + commentLocation, + "Invalid code snippet in source location mapping. Quote is not terminated." + ); + return; + } + optional const sourceIndex = toInt(srcTagArgsMatch[1].str()); optional const start = toInt(srcTagArgsMatch[2].str()); optional const end = toInt(srcTagArgsMatch[3].str()); diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 6f3f9ce95..214e6d3d2 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -192,6 +192,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): # white list of ids which are not covered by tests white_ids = { "9804", # Tested in test/libyul/ObjectParser.cpp. + "1544", "2674", "6367", "8387", diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 4168b23a3..a973e9e5b 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -624,6 +624,20 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_no_whitespace) CHECK_LOCATION(result->debugData->location, "", -1, -1); } +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_separated_with_single_space) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 @src 1:333:444 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->location, "source1", 333, 444); +} + BOOST_AUTO_TEST_CASE(customSourceLocations_leading_trailing_whitespace) { ErrorList errorList; @@ -656,6 +670,164 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) CHECK_LOCATION(varDecl.debugData->location, "", 10, 20); } +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"~~~( + { + /// @src 0:149:156 "new C(\"123\")" + let x := 123 + + let y := /** @src 1:96:165 "contract D {..." */ 128 + } + )~~~"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + BOOST_REQUIRE_EQUAL(result->statements.size(), 2); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = get(result->statements.at(0)); + CHECK_LOCATION(varX.debugData->location, "source0", 149, 156); + + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + VariableDeclaration const& varY = get(result->statements.at(1)); + BOOST_REQUIRE(!!varY.value); + BOOST_REQUIRE(holds_alternative(*varY.value)); + Literal const& literal128 = get(*varY.value); + CHECK_LOCATION(literal128.debugData->location, "source1", 96, 165); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_empty_snippet) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 "" + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->location, "source0", 111, 222); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_before_snippet) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222"abc" def + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 8387_error); + CHECK_LOCATION(result->debugData->location, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_after_snippet) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 "abc"def + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->location, "source0", 111, 222); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_no_whitespace) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 "abc"@src 1:333:444 "abc" + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->location, "source1", 333, 444); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_unterminated_quote) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 " abc @src 1:333:444 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 1544_error); + CHECK_LOCATION(result->debugData->location, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_with_nested_locations) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"~~~( + { + /// @src 0:149:156 "new C(\"123\") /// @src 1:3:4 " + let x := 123 + + let y := /** @src 1:96:165 "function f() internal { \"\/** @src 0:6:7 *\/\"; }" */ 128 + } + )~~~"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + BOOST_REQUIRE_EQUAL(result->statements.size(), 2); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = get(result->statements.at(0)); + CHECK_LOCATION(varX.debugData->location, "source0", 149, 156); + + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + VariableDeclaration const& varY = get(result->statements.at(1)); + BOOST_REQUIRE(!!varY.value); + BOOST_REQUIRE(holds_alternative(*varY.value)); + Literal const& literal128 = get(*varY.value); + CHECK_LOCATION(literal128.debugData->location, "source1", 96, 165); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_multiple_src_tags_on_one_line) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + " /// " + 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)~~" + R"~~( X "@src 0:10:20 "new C(\"123\") /// @src 1:4:5 "" XYZ)~~" + R"~~( @src0:20:30 "abc"@src0:2:4 @src-0:2:5@)~~" + R"~~( @some text with random @ signs @@@ @- @** 1:6:7 "src 1:8:9")~~" + "\n" + " let x := 123\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + BOOST_REQUIRE_EQUAL(result->statements.size(), 1); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = get(result->statements.at(0)); + CHECK_LOCATION(varX.debugData->location, "source1", 4, 5); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces