mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Improve Error Reporting of SemVer Parser
This commit is contained in:
parent
10f81425a3
commit
9e7b85ac4b
@ -15,6 +15,7 @@ Compiler Features:
|
|||||||
* Optimizer: Added optimization rule ``and(shl(X, Y), shl(X, Z)) => shl(X, and(Y, Z))``.
|
* Optimizer: Added optimization rule ``and(shl(X, Y), shl(X, Z)) => shl(X, and(Y, Z))``.
|
||||||
* SMTChecker: Support Eldarica as a Horn solver for the CHC engine when using the CLI option ``--model-checker-solvers eld``. The binary `eld` must be available in the system.
|
* SMTChecker: Support Eldarica as a Horn solver for the CHC engine when using the CLI option ``--model-checker-solvers eld``. The binary `eld` must be available in the system.
|
||||||
* SMTChecker: Make ``z3`` the default solver for the BMC and CHC engines instead of all solvers.
|
* SMTChecker: Make ``z3`` the default solver for the BMC and CHC engines instead of all solvers.
|
||||||
|
* Parser: More detailed error messages about invalid version pragmas.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -27,10 +27,12 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
|
using namespace solidity::util;
|
||||||
|
|
||||||
SemVerMatchExpressionParser::SemVerMatchExpressionParser(vector<Token> _tokens, vector<string> _literals):
|
SemVerMatchExpressionParser::SemVerMatchExpressionParser(vector<Token> _tokens, vector<string> _literals):
|
||||||
m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
|
m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
|
||||||
@ -52,7 +54,7 @@ SemVerVersion::SemVerVersion(string const& _versionString)
|
|||||||
if (level < 2)
|
if (level < 2)
|
||||||
{
|
{
|
||||||
if (i == end || *i != '.')
|
if (i == end || *i != '.')
|
||||||
BOOST_THROW_EXCEPTION(SemVerError());
|
solThrow(SemVerError, "Invalid versionString: "s + _versionString);
|
||||||
else
|
else
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@ -70,7 +72,7 @@ SemVerVersion::SemVerVersion(string const& _versionString)
|
|||||||
build = string(buildStart, i);
|
build = string(buildStart, i);
|
||||||
}
|
}
|
||||||
if (i != end)
|
if (i != end)
|
||||||
BOOST_THROW_EXCEPTION(SemVerError());
|
solThrow(SemVerError, "Invalid versionString "s + _versionString);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _version) const
|
bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _version) const
|
||||||
@ -156,12 +158,12 @@ bool SemVerMatchExpression::matches(SemVerVersion const& _version) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<SemVerMatchExpression> SemVerMatchExpressionParser::parse()
|
SemVerMatchExpression SemVerMatchExpressionParser::parse()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
if (m_tokens.empty())
|
if (m_tokens.empty())
|
||||||
return nullopt;
|
solThrow(SemVerError, "Empty version pragma.");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -171,14 +173,19 @@ optional<SemVerMatchExpression> SemVerMatchExpressionParser::parse()
|
|||||||
if (m_pos >= m_tokens.size())
|
if (m_pos >= m_tokens.size())
|
||||||
break;
|
break;
|
||||||
if (currentToken() != Token::Or)
|
if (currentToken() != Token::Or)
|
||||||
BOOST_THROW_EXCEPTION(SemVerError());
|
{
|
||||||
|
solThrow(
|
||||||
|
SemVerError,
|
||||||
|
"You can only combine version ranges using the || operator."
|
||||||
|
);
|
||||||
|
}
|
||||||
nextToken();
|
nextToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SemVerError const&)
|
catch (SemVerError const& e)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
return nullopt;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_expression;
|
return m_expression;
|
||||||
@ -265,14 +272,22 @@ unsigned SemVerMatchExpressionParser::parseVersionPart()
|
|||||||
{
|
{
|
||||||
c = currentChar();
|
c = currentChar();
|
||||||
if (v * 10 < v || v * 10 + static_cast<unsigned>(c - '0') < v * 10)
|
if (v * 10 < v || v * 10 + static_cast<unsigned>(c - '0') < v * 10)
|
||||||
BOOST_THROW_EXCEPTION(SemVerError());
|
solThrow(SemVerError, "Integer too large to be used in a version number.");
|
||||||
v = v * 10 + static_cast<unsigned>(c - '0');
|
v = v * 10 + static_cast<unsigned>(c - '0');
|
||||||
nextChar();
|
nextChar();
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
else if (c == char(-1))
|
||||||
|
solThrow(SemVerError, "Expected version number but reached end of pragma.");
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(SemVerError());
|
solThrow(
|
||||||
|
SemVerError, fmt::format(
|
||||||
|
"Expected the start of a version number but instead found character '{}'. "
|
||||||
|
"Version number is invalid or the pragma is not terminated with a semicolon.",
|
||||||
|
c
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
char SemVerMatchExpressionParser::currentChar() const
|
char SemVerMatchExpressionParser::currentChar() const
|
||||||
|
@ -25,17 +25,18 @@
|
|||||||
|
|
||||||
#include <liblangutil/Token.h>
|
#include <liblangutil/Token.h>
|
||||||
#include <libsolutil/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace solidity::langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
class SemVerError: public util::Exception
|
struct SemVerError: public util::Exception
|
||||||
{
|
{
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef major
|
#undef major
|
||||||
@ -87,8 +88,8 @@ class SemVerMatchExpressionParser
|
|||||||
public:
|
public:
|
||||||
SemVerMatchExpressionParser(std::vector<Token> _tokens, std::vector<std::string> _literals);
|
SemVerMatchExpressionParser(std::vector<Token> _tokens, std::vector<std::string> _literals);
|
||||||
|
|
||||||
/// Returns an expression if it was parseable, or nothing otherwise.
|
/// Returns an expression if it was parsable, or throws a SemVerError otherwise.
|
||||||
std::optional<SemVerMatchExpression> parse();
|
SemVerMatchExpression parse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reset();
|
void reset();
|
||||||
|
@ -152,15 +152,15 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2");
|
m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2");
|
||||||
}
|
}
|
||||||
else if (_pragma.literals()[0] == "solidity")
|
else if (_pragma.literals()[0] == "solidity")
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
|
vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end());
|
||||||
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
||||||
SemVerMatchExpressionParser parser(tokens, literals);
|
SemVerMatchExpressionParser parser(tokens, literals);
|
||||||
auto matchExpression = parser.parse();
|
SemVerMatchExpression matchExpression = parser.parse();
|
||||||
// An unparsable version pragma is an unrecoverable fatal error in the parser.
|
|
||||||
solAssert(matchExpression.has_value(), "");
|
|
||||||
static SemVerVersion const currentVersion{string(VersionString)};
|
static SemVerVersion const currentVersion{string(VersionString)};
|
||||||
if (!matchExpression->matches(currentVersion))
|
if (!matchExpression.matches(currentVersion))
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
3997_error,
|
3997_error,
|
||||||
_pragma.location(),
|
_pragma.location(),
|
||||||
@ -170,6 +170,12 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
);
|
);
|
||||||
m_versionPragmaFound = true;
|
m_versionPragmaFound = true;
|
||||||
}
|
}
|
||||||
|
catch (SemVerError const&)
|
||||||
|
{
|
||||||
|
// An unparsable version pragma is an unrecoverable fatal error in the parser.
|
||||||
|
solAssert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||||
|
|
||||||
|
@ -160,17 +160,12 @@ ASTPointer<SourceUnit> Parser::parse(CharStream& _charStream)
|
|||||||
void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> const& _tokens, vector<string> const& _literals)
|
void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> const& _tokens, vector<string> const& _literals)
|
||||||
{
|
{
|
||||||
SemVerMatchExpressionParser parser(_tokens, _literals);
|
SemVerMatchExpressionParser parser(_tokens, _literals);
|
||||||
auto matchExpression = parser.parse();
|
try
|
||||||
if (!matchExpression.has_value())
|
{
|
||||||
m_errorReporter.fatalParserError(
|
SemVerMatchExpression matchExpression = parser.parse();
|
||||||
1684_error,
|
|
||||||
_location,
|
|
||||||
"Found version pragma, but failed to parse it. "
|
|
||||||
"Please ensure there is a trailing semicolon."
|
|
||||||
);
|
|
||||||
static SemVerVersion const currentVersion{string(VersionString)};
|
static SemVerVersion const currentVersion{string(VersionString)};
|
||||||
// FIXME: only match for major version incompatibility
|
// FIXME: only match for major version incompatibility
|
||||||
if (!matchExpression->matches(currentVersion))
|
if (!matchExpression.matches(currentVersion))
|
||||||
// If m_parserErrorRecovery is true, the same message will appear from SyntaxChecker::visit(),
|
// If m_parserErrorRecovery is true, the same message will appear from SyntaxChecker::visit(),
|
||||||
// so we don't need to report anything here.
|
// so we don't need to report anything here.
|
||||||
if (!m_parserErrorRecovery)
|
if (!m_parserErrorRecovery)
|
||||||
@ -181,6 +176,15 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> c
|
|||||||
string(VersionString) + ") - note that nightly builds are considered to be "
|
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||||
"strictly less than the released version"
|
"strictly less than the released version"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
catch (SemVerError const& matchError)
|
||||||
|
{
|
||||||
|
m_errorReporter.fatalParserError(
|
||||||
|
1684_error,
|
||||||
|
_location,
|
||||||
|
"Invalid version pragma. "s + matchError.what()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<StructuredDocumentation> Parser::parseStructuredDocumentation()
|
ASTPointer<StructuredDocumentation> Parser::parseStructuredDocumentation()
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <liblangutil/Scanner.h>
|
#include <liblangutil/Scanner.h>
|
||||||
#include <liblangutil/SemVerHandler.h>
|
#include <liblangutil/SemVerHandler.h>
|
||||||
#include <test/Common.h>
|
#include <test/Common.h>
|
||||||
|
#include <test/libsolidity/util/SoltestErrors.h>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
@ -58,17 +59,41 @@ SemVerMatchExpression parseExpression(string const& _input)
|
|||||||
scanner.next();
|
scanner.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto expression = SemVerMatchExpressionParser(tokens, literals).parse();
|
try
|
||||||
BOOST_REQUIRE(expression.has_value());
|
{
|
||||||
|
auto matchExpression = SemVerMatchExpressionParser(tokens, literals).parse();
|
||||||
|
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
expression->isValid(),
|
matchExpression.isValid(),
|
||||||
"Expression \"" + _input + "\" did not parse properly."
|
"Expression \"" + _input + "\" did not parse properly."
|
||||||
);
|
);
|
||||||
return *expression;
|
|
||||||
|
return matchExpression;
|
||||||
|
}
|
||||||
|
catch (SemVerError const&)
|
||||||
|
{
|
||||||
|
// Ignored, since a test case should have a parsable version
|
||||||
|
soltestAssert(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(exception_on_invalid_version_in_semverversion_constructor)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EXCEPTION(
|
||||||
|
SemVerVersion version("1.2"),
|
||||||
|
SemVerError,
|
||||||
|
[&](auto const& _exception) { BOOST_TEST(_exception.what() == "Invalid versionString: 1.2"); return true; }
|
||||||
|
);
|
||||||
|
|
||||||
|
BOOST_CHECK_EXCEPTION(
|
||||||
|
SemVerVersion version("-1.2.0"),
|
||||||
|
SemVerError,
|
||||||
|
[&](auto const& _exception) { BOOST_TEST(_exception.what() == "Invalid versionString: -1.2.0"); return true; }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(positive_range)
|
BOOST_AUTO_TEST_CASE(positive_range)
|
||||||
{
|
{
|
||||||
// Positive range tests
|
// Positive range tests
|
||||||
@ -159,9 +184,9 @@ BOOST_AUTO_TEST_CASE(positive_range)
|
|||||||
for (auto const& t: tests)
|
for (auto const& t: tests)
|
||||||
{
|
{
|
||||||
SemVerVersion version(t.second);
|
SemVerVersion version(t.second);
|
||||||
SemVerMatchExpression expression = parseExpression(t.first);
|
SemVerMatchExpression matchExpression = parseExpression(t.first);
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
expression.matches(version),
|
matchExpression.matches(version),
|
||||||
"Version \"" + t.second + "\" did not satisfy expression \"" + t.first + "\""
|
"Version \"" + t.second + "\" did not satisfy expression \"" + t.first + "\""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -235,9 +260,9 @@ BOOST_AUTO_TEST_CASE(negative_range)
|
|||||||
for (auto const& t: tests)
|
for (auto const& t: tests)
|
||||||
{
|
{
|
||||||
SemVerVersion version(t.second);
|
SemVerVersion version(t.second);
|
||||||
SemVerMatchExpression expression = parseExpression(t.first);
|
auto matchExpression = parseExpression(t.first);
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
!expression.matches(version),
|
!matchExpression.matches(version),
|
||||||
"Version \"" + t.second + "\" did satisfy expression \"" + t.first + "\" " +
|
"Version \"" + t.second + "\" did satisfy expression \"" + t.first + "\" " +
|
||||||
"(although it should not)"
|
"(although it should not)"
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
pragma solidity ;
|
||||||
|
// ----
|
||||||
|
// ParserError 1684: (0-17): Invalid version pragma. Empty version pragma.
|
@ -1,3 +1,3 @@
|
|||||||
pragma solidity pragma;
|
pragma solidity pragma;
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-23): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-23): Invalid version pragma. Expected the start of a version number but instead found character 'p'. Version number is invalid or the pragma is not terminated with a semicolon.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
pragma solidity (8.0.0);
|
pragma solidity (8.0.0);
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-24): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-24): Invalid version pragma. Expected the start of a version number but instead found character '('. Version number is invalid or the pragma is not terminated with a semicolon.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
pragma solidity 88_;
|
pragma solidity 88_;
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-20): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-20): Invalid version pragma. Expected the start of a version number but instead found character '_'. Version number is invalid or the pragma is not terminated with a semicolon.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
pragma solidity v1.2.3;
|
pragma solidity v1.2.3;
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-23): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-23): Invalid version pragma. Expected the start of a version number but instead found character 'v'. Version number is invalid or the pragma is not terminated with a semicolon.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
pragma solidity >0.5.0<;
|
pragma solidity >0.5.0<;
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-24): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-24): Invalid version pragma. Expected version number but reached end of pragma.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
pragma solidity;
|
pragma solidity;
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-16): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-16): Invalid version pragma. Empty version pragma.
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
pragma solidity 0.4.1 - 0.6.2 0.8.17;
|
||||||
|
// ----
|
||||||
|
// ParserError 1684: (0-37): Invalid version pragma. You can only combine version ranges using the || operator.
|
@ -0,0 +1,3 @@
|
|||||||
|
pragma solidity 0.8.17 0.4.1 - 0.6.2;
|
||||||
|
// ----
|
||||||
|
// ParserError 1684: (0-37): Invalid version pragma. Expected the start of a version number but instead found character '-'. Version number is invalid or the pragma is not terminated with a semicolon.
|
@ -0,0 +1,3 @@
|
|||||||
|
pragma solidity 0.4.0 - 0.4.1 0.4.1 - 0.6.2;
|
||||||
|
// ----
|
||||||
|
// ParserError 1684: (0-44): Invalid version pragma. You can only combine version ranges using the || operator.
|
@ -1,4 +1,4 @@
|
|||||||
pragma solidity 0.4.3
|
pragma solidity 0.4.3
|
||||||
pragma abicoder v2;
|
pragma abicoder v2;
|
||||||
// ----
|
// ----
|
||||||
// ParserError 1684: (0-41): Found version pragma, but failed to parse it. Please ensure there is a trailing semicolon.
|
// ParserError 1684: (0-41): Invalid version pragma. Expected the start of a version number but instead found character 'p'. Version number is invalid or the pragma is not terminated with a semicolon.
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
pragma solidity 0.8.17 || 0.4.1 - 0.9.0;
|
||||||
|
// ----
|
@ -0,0 +1,2 @@
|
|||||||
|
pragma solidity 0.4.0 - 0.9.0 || 0.4.1 - 0.6.2;
|
||||||
|
// ----
|
@ -0,0 +1,3 @@
|
|||||||
|
pragma solidity 4294967296;
|
||||||
|
// ----
|
||||||
|
// ParserError 1684: (0-27): Invalid version pragma. Integer too large to be used in a version number.
|
Loading…
Reference in New Issue
Block a user