diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 80b7b6591..c901e7a2a 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -5,6 +5,9 @@ EXCLUDE_FILES=( "libsolutil/picosha2.h" "test/libsolutil/UTF8.cpp" + "test/libsolidity/syntaxTests/license/license_cr_endings.sol" + "test/libsolidity/syntaxTests/license/license_crlf_endings.sol" + "test/libsolidity/syntaxTests/license/license_whitespace_trailing.sol" ) EXCLUDE_FILES_JOINED=$(printf "%s\|" "${EXCLUDE_FILES[@]}") EXCLUDE_FILES_JOINED=${EXCLUDE_FILES_JOINED%??} diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index b90d11d09..a82199b59 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -36,6 +36,7 @@ namespace solidity::frontend::test namespace { + map requireParsedCBORMetadata(bytes const& _bytecode) { bytes cborMetadata = solidity::test::onlyMetadata(_bytecode); @@ -44,6 +45,30 @@ map requireParsedCBORMetadata(bytes const& _bytecode) BOOST_REQUIRE(tmp); return *tmp; } + +optional compileAndCheckLicenseMetadata(string const& _contractName, char const* _sourceCode) +{ + CompilerStack compilerStack; + compilerStack.setSources({{"A.sol", std::string(_sourceCode)}}); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + + std::string const& serialisedMetadata = compilerStack.metadata(_contractName); + BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); + Json::Value metadata; + BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + + BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); + BOOST_REQUIRE(metadata["sources"].isMember("A.sol")); + + if (metadata["sources"]["A.sol"].isMember("license")) + { + BOOST_REQUIRE(metadata["sources"]["A.sol"]["license"].isString()); + return metadata["sources"]["A.sol"]["license"].asString(); + } + else + return nullopt; +} + } BOOST_AUTO_TEST_SUITE(Metadata) @@ -334,6 +359,197 @@ BOOST_AUTO_TEST_CASE(metadata_revert_strings) BOOST_CHECK_EQUAL(metadata["settings"]["debug"]["revertStrings"], "strip"); } +BOOST_AUTO_TEST_CASE(metadata_license_missing) +{ + char const* sourceCode = R"( + pragma solidity >=0.0; + contract C { + } + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); +} + +BOOST_AUTO_TEST_CASE(metadata_license_gpl3) +{ + // Can't use a raw string here due to the stylechecker. + char const* sourceCode = + "// NOTE: we also add trailing whitespace after the license, to see it is trimmed.\n" + "// SPDX-License-Identifier: GPL-3.0 \n" + "pragma solidity >=0.0;\n" + "contract C {\n" + "}\n"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_whitespace_before_spdx) +{ + char const* sourceCode = R"( + // SPDX-License-Identifier: GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_whitespace_after_colon) +{ + char const* sourceCode = R"( + // SPDX-License-Identifier: GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_gpl3_or_apache2) +{ + char const* sourceCode = R"( + // SPDX-License-Identifier: GPL-3.0 OR Apache-2.0 + pragma solidity >=0.0; + contract C { + } + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0 OR Apache-2.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_ignored_unicode) +{ + char const* sourceCode = R"( + // SPDX-License-Identifier: ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ + pragma solidity >=0.0; + contract C { + } + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); +} + +BOOST_AUTO_TEST_CASE(metadata_license_ignored_stray_unicode) +{ + char const* sourceCode = R"( + // SPDX-License-Identifier: GPL-3.0 ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ + pragma solidity >=0.0; + contract C { + } + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_bidi_marks) +{ + char const* sourceCode = + "// \xE2\x80\xAE""0.3-LPG :reifitnedI-esneciL-XDPS\xE2\x80\xAC\n" + "// NOTE: The text above is reversed using Unicode directional marks. In raw form it would look like this:\n" + "// 0.3-LPG :reifitnedI-esneciL-XDPS\n" + "contract C {}\n"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); +} + +BOOST_AUTO_TEST_CASE(metadata_license_bottom) +{ + char const* sourceCode = R"( + contract C {} + // SPDX-License-Identifier: GPL-3.0 + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_cr_endings) +{ + char const* sourceCode = + "// SPDX-License-Identifier: GPL-3.0\r" + "contract C {}\r"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_crlf_endings) +{ + char const* sourceCode = + "// SPDX-License-Identifier: GPL-3.0\r\n" + "contract C {}\r\n"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_in_string) +{ + char const* sourceCode = R"( + contract C { + bytes license = "// SPDX-License-Identifier: GPL-3.0"; + } + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); +} + +BOOST_AUTO_TEST_CASE(metadata_license_in_contract) +{ + char const* sourceCode = R"( + contract C { + // SPDX-License-Identifier: GPL-3.0 + } + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); +} + +BOOST_AUTO_TEST_CASE(metadata_license_missing_colon) +{ + char const* sourceCode = R"( + // SPDX-License-Identifier GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); +} + +BOOST_AUTO_TEST_CASE(metadata_license_multiline) +{ + char const* sourceCode = R"( + /* SPDX-License-Identifier: GPL-3.0 */ + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_natspec) +{ + char const* sourceCode = R"( + /// SPDX-License-Identifier: GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_natspec_multiline) +{ + char const* sourceCode = R"( + /** SPDX-License-Identifier: GPL-3.0 */ + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace) +{ + char const* sourceCode = R"( + //SPDX-License-Identifier:GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace_multiline) +{ + char const* sourceCode = R"( + /*SPDX-License-Identifier:GPL-3.0*/ + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + +BOOST_AUTO_TEST_CASE(metadata_license_nonempty_line) +{ + char const* sourceCode = R"( + pragma solidity >= 0.0; // SPDX-License-Identifier: GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 92b246b34..009b3613b 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -51,13 +51,23 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix return conclude(_stream, _linePrefix, _formatted); } +string SyntaxTest::addPreamble(string const& _sourceCode) +{ + // Silence compiler version warning + string preamble = "pragma solidity >=0.0;\n"; + // NOTE: this check is intentionally loose to match weird cases. + // We can manually adjust a test case where this causes problem. + if (_sourceCode.find("SPDX-License-Identifier:") == string::npos) + preamble += "// SPDX-License-Identifier: GPL-3.0\n"; + return preamble + _sourceCode; +} + void SyntaxTest::setupCompiler() { - string const preamble = "pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n"; compiler().reset(); auto sourcesWithPragma = m_sources; for (auto& source: sourcesWithPragma) - source.second = preamble + source.second; + source.second = addPreamble(source.second); compiler().setSources(sourcesWithPragma); compiler().setEVMVersion(m_evmVersion); compiler().setParserErrorRecovery(m_parserErrorRecovery); @@ -103,20 +113,30 @@ void SyntaxTest::parseAndAnalyze() void SyntaxTest::filterObtainedErrors() { - string const preamble = "pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n"; for (auto const& currentError: filterErrors(compiler().errors(), true)) { int locationStart = -1, locationEnd = -1; string sourceName; if (auto location = boost::get_error_info(*currentError)) { + solAssert(location->source, ""); + sourceName = location->source->name(); + + solAssert(m_sources.count(sourceName) == 1, ""); + int preambleSize = static_cast(location->source->source().size()) - static_cast(m_sources[sourceName].size()); + solAssert(preambleSize >= 0, ""); + // ignore the version & license pragma inserted by the testing tool when calculating locations. - if (location->start >= static_cast(preamble.size())) - locationStart = location->start - static_cast(preamble.size()); - if (location->end >= static_cast(preamble.size())) - locationEnd = location->end - static_cast(preamble.size()); - if (location->source) - sourceName = location->source->name(); + if (location->start != -1) + { + solAssert(location->start >= preambleSize, ""); + locationStart = location->start - preambleSize; + } + if (location->end != -1) + { + solAssert(location->end >= preambleSize, ""); + locationEnd = location->end - preambleSize; + } } m_errorList.emplace_back(SyntaxTestError{ currentError->typeName(), diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 0df95f7a6..597fa0e1b 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -50,6 +50,9 @@ public: TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; protected: + /// Returns @param _sourceCode prefixed with the version pragma and the SPDX license identifier. + static std::string addPreamble(std::string const& _sourceCode); + void setupCompiler(); void parseAndAnalyze() override; virtual void filterObtainedErrors(); diff --git a/test/libsolidity/syntaxTests/imports/multiple_non_existent_file_names.sol b/test/libsolidity/syntaxTests/imports/multiple_non_existent_file_names.sol new file mode 100644 index 000000000..4172955ab --- /dev/null +++ b/test/libsolidity/syntaxTests/imports/multiple_non_existent_file_names.sol @@ -0,0 +1,10 @@ +import "~~~some-long-unlikely-file-name-12"; +import "~~~some-long-unlikely-file-name-456"; +import "~~~some-long-unlikely-file-name-7890"; +// This test is here to verify that the license/pragma preamble added by the test suite does not +// affect source locations in error messages. Positions in messages below should start at 0 and +// there should be no gaps between the ranges. +// ---- +// ParserError 6275: (0-44): Source "~~~some-long-unlikely-file-name-12" not found: File not supplied initially. +// ParserError 6275: (45-90): Source "~~~some-long-unlikely-file-name-456" not found: File not supplied initially. +// ParserError 6275: (91-137): Source "~~~some-long-unlikely-file-name-7890" not found: File not supplied initially. diff --git a/test/libsolidity/syntaxTests/license/license_bidi_marks.sol b/test/libsolidity/syntaxTests/license/license_bidi_marks.sol new file mode 100644 index 000000000..12c88861d --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_bidi_marks.sol @@ -0,0 +1,5 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +// ‮0.3-LPG :reifitnedI-esneciL-XDPS‬ +// NOTE: The text above is reversed using Unicode directional marks. In raw form it would look like this: +// 0.3-LPG :reifitnedI-esneciL-XDPS +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_bottom.sol b/test/libsolidity/syntaxTests/license/license_bottom.sol new file mode 100644 index 000000000..6a7e5016e --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_bottom.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +contract C {} +// SPDX-License-Identifier: GPL-3.0 diff --git a/test/libsolidity/syntaxTests/license/license_cr_endings.sol b/test/libsolidity/syntaxTests/license/license_cr_endings.sol new file mode 100644 index 000000000..a844d80a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_cr_endings.sol @@ -0,0 +1 @@ +// This test is actually useless, as the test suite adds the automatic preamble. // SPDX-License-Identifier: GPL-3.0 contract C {} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/license/license_crlf_endings.sol b/test/libsolidity/syntaxTests/license/license_crlf_endings.sol new file mode 100644 index 000000000..8e83125ee --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_crlf_endings.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +// SPDX-License-Identifier: GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol b/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol new file mode 100644 index 000000000..325e78cfe --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol @@ -0,0 +1,3 @@ +// This is parsed as GPL-3.0: +// SPDX-License-Identifier: GPL-3.0 ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_in_contract.sol b/test/libsolidity/syntaxTests/license/license_in_contract.sol new file mode 100644 index 000000000..8318fbd80 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_in_contract.sol @@ -0,0 +1,5 @@ +contract C { +// SPDX-License-Identifier: GPL-3.0 +} +// ---- +// Warning 1878: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_in_import.sol b/test/libsolidity/syntaxTests/license/license_in_import.sol new file mode 100644 index 000000000..e31d49aa7 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_in_import.sol @@ -0,0 +1,5 @@ +import "// SPDX-License-Identifier: GPL-3.0"; +contract C {} +// ---- +// Warning 1878: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +// ParserError 6275: (0-45): Source "// SPDX-License-Identifier: GPL-3.0" not found: File not supplied initially. diff --git a/test/libsolidity/syntaxTests/license/license_in_string.sol b/test/libsolidity/syntaxTests/license/license_in_string.sol new file mode 100644 index 000000000..5db1c6f66 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_in_string.sol @@ -0,0 +1,5 @@ +contract C { + bytes license = "// SPDX-License-Identifier: GPL-3.0"; +} +// ---- +// Warning 1878: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_missing.sol b/test/libsolidity/syntaxTests/license/license_missing.sol index 2dde0d209..8b463f625 100644 --- a/test/libsolidity/syntaxTests/license/license_missing.sol +++ b/test/libsolidity/syntaxTests/license/license_missing.sol @@ -1 +1,2 @@ +// This test is actually useless, as the test suite adds the automatic preamble. contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_missing_colon.sol b/test/libsolidity/syntaxTests/license/license_missing_colon.sol new file mode 100644 index 000000000..0212093a1 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_missing_colon.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +// SPDX-License-Identifier GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_multiline.sol b/test/libsolidity/syntaxTests/license/license_multiline.sol new file mode 100644 index 000000000..026c47a83 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_multiline.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +/* SPDX-License-Identifier: GPL-3.0 */ +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_natspec.sol b/test/libsolidity/syntaxTests/license/license_natspec.sol new file mode 100644 index 000000000..2717b0ca0 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_natspec.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +/// SPDX-License-Identifier: GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_natspec_multiline.sol b/test/libsolidity/syntaxTests/license/license_natspec_multiline.sol new file mode 100644 index 000000000..77b0daf93 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_natspec_multiline.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +/** SPDX-License-Identifier: GPL-3.0 */ +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_no_whitespace.sol b/test/libsolidity/syntaxTests/license/license_no_whitespace.sol new file mode 100644 index 000000000..d69215ee4 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_no_whitespace.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +//SPDX-License-Identifier:GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_no_whitespace_multiline.sol b/test/libsolidity/syntaxTests/license/license_no_whitespace_multiline.sol new file mode 100644 index 000000000..782ad5d5b --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_no_whitespace_multiline.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +/*SPDX-License-Identifier:GPL-3.0*/ +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_nonempty_line.sol b/test/libsolidity/syntaxTests/license/license_nonempty_line.sol new file mode 100644 index 000000000..43659d595 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_nonempty_line.sol @@ -0,0 +1,3 @@ +// This test is actually useless, as the test suite adds the automatic preamble. +pragma solidity >= 0.0; // SPDX-License-Identifier: GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_unicode.sol b/test/libsolidity/syntaxTests/license/license_unicode.sol new file mode 100644 index 000000000..801c6e576 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_unicode.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ +contract C {} +// ---- +// Warning 1878: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_whitespace_after_colon.sol b/test/libsolidity/syntaxTests/license/license_whitespace_after_colon.sol new file mode 100644 index 000000000..8489873ab --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_whitespace_after_colon.sol @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_whitespace_before_spdx.sol b/test/libsolidity/syntaxTests/license/license_whitespace_before_spdx.sol new file mode 100644 index 000000000..a83285383 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_whitespace_before_spdx.sol @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: GPL-3.0 +contract C {} diff --git a/test/libsolidity/syntaxTests/license/license_whitespace_trailing.sol b/test/libsolidity/syntaxTests/license/license_whitespace_trailing.sol new file mode 100644 index 000000000..24903ce48 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_whitespace_trailing.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 +// NOTE: Trailing space at the end of the line above is intentional. +contract C {}