Adds line numbers to parser errors in isoltest.

This commit is contained in:
Erik Kundt 2019-07-13 12:04:22 +02:00
parent 579bdaddb9
commit 2a1473fd30
7 changed files with 60 additions and 25 deletions

View File

@ -62,16 +62,19 @@ bool TestCase::validateSettings(langutil::EVMVersion)
return true; return true;
} }
string TestCase::parseSourceAndSettings(istream& _stream) pair<string, size_t> TestCase::parseSourceAndSettingsWithLineNumbers(istream& _stream)
{ {
string source; string source;
string line; string line;
size_t lineNumber = 1;
static string const comment("// "); static string const comment("// ");
static string const settingsDelimiter("// ===="); static string const settingsDelimiter("// ====");
static string const delimiter("// ----"); static string const delimiter("// ----");
bool sourcePart = true; bool sourcePart = true;
while (getline(_stream, line)) while (getline(_stream, line))
{ {
lineNumber++;
if (boost::algorithm::starts_with(line, delimiter)) if (boost::algorithm::starts_with(line, delimiter))
break; break;
else if (boost::algorithm::starts_with(line, settingsDelimiter)) else if (boost::algorithm::starts_with(line, settingsDelimiter))
@ -92,7 +95,12 @@ string TestCase::parseSourceAndSettings(istream& _stream)
else else
throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source.")); throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."));
} }
return source; return make_pair(source, lineNumber);
}
string TestCase::parseSourceAndSettings(istream& _stream)
{
return get<0>(parseSourceAndSettingsWithLineNumbers(_stream));
} }
string TestCase::parseSimpleExpectations(std::istream& _file) string TestCase::parseSimpleExpectations(std::istream& _file)

View File

@ -89,6 +89,7 @@ public:
virtual bool validateSettings(langutil::EVMVersion /*_evmVersion*/); virtual bool validateSettings(langutil::EVMVersion /*_evmVersion*/);
protected: protected:
std::pair<std::string, std::size_t> parseSourceAndSettingsWithLineNumbers(std::istream& _file);
std::string parseSourceAndSettings(std::istream& _file); std::string parseSourceAndSettings(std::istream& _file);
static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c); static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c);

View File

@ -43,7 +43,8 @@ SemanticTest::SemanticTest(string const& _filename, string const& _ipcPath, lang
soltestAssert(file, "Cannot open test contract: \"" + _filename + "\"."); soltestAssert(file, "Cannot open test contract: \"" + _filename + "\".");
file.exceptions(ios::badbit); file.exceptions(ios::badbit);
m_source = parseSourceAndSettings(file); std::tie(m_source, m_lineOffset) = parseSourceAndSettingsWithLineNumbers(file);
if (m_settings.count("compileViaYul")) if (m_settings.count("compileViaYul"))
{ {
if (m_settings["compileViaYul"] == "also") if (m_settings["compileViaYul"] == "also")
@ -163,7 +164,7 @@ void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) con
void SemanticTest::parseExpectations(istream& _stream) void SemanticTest::parseExpectations(istream& _stream)
{ {
TestFileParser parser{_stream}; TestFileParser parser{_stream};
auto functionCalls = parser.parseFunctionCalls(); auto functionCalls = parser.parseFunctionCalls(m_lineOffset);
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests)); std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
} }

View File

@ -64,6 +64,7 @@ public:
private: private:
std::string m_source; std::string m_source;
std::size_t m_lineOffset;
std::vector<TestFunctionCall> m_tests; std::vector<TestFunctionCall> m_tests;
bool m_runWithYul = false; bool m_runWithYul = false;
bool m_runWithoutYul = true; bool m_runWithoutYul = true;

View File

@ -48,7 +48,7 @@ char TestFileParser::Scanner::peek() const noexcept
return *next; return *next;
} }
vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls() vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls(size_t _lineOffset)
{ {
vector<FunctionCall> calls; vector<FunctionCall> calls;
if (!accept(Token::EOS)) if (!accept(Token::EOS))
@ -70,32 +70,47 @@ vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls()
if (calls.empty()) if (calls.empty())
expect(Token::Newline); expect(Token::Newline);
else else
accept(Token::Newline, true); if (accept(Token::Newline, true))
m_lineNumber++;
call.signature = parseFunctionSignature(); try
if (accept(Token::Comma, true)) {
call.value = parseFunctionCallValue(); call.signature = parseFunctionSignature();
if (accept(Token::Colon, true)) if (accept(Token::Comma, true))
call.arguments = parseFunctionCallArguments(); call.value = parseFunctionCallValue();
if (accept(Token::Colon, true))
call.arguments = parseFunctionCallArguments();
if (accept(Token::Newline, true)) if (accept(Token::Newline, true))
call.displayMode = FunctionCall::DisplayMode::MultiLine; {
call.displayMode = FunctionCall::DisplayMode::MultiLine;
m_lineNumber++;
}
call.arguments.comment = parseComment(); call.arguments.comment = parseComment();
if (accept(Token::Newline, true)) if (accept(Token::Newline, true))
call.displayMode = FunctionCall::DisplayMode::MultiLine; {
call.displayMode = FunctionCall::DisplayMode::MultiLine;
m_lineNumber++;
}
expect(Token::Arrow); expect(Token::Arrow);
call.expectations = parseFunctionCallExpectations(); call.expectations = parseFunctionCallExpectations();
accept(Token::Newline, true); if (accept(Token::Newline, true))
call.expectations.comment = parseComment(); m_lineNumber++;
call.expectations.comment = parseComment();
if (call.signature == "constructor()") if (call.signature == "constructor()")
call.isConstructor = true; call.isConstructor = true;
calls.emplace_back(std::move(call)); calls.emplace_back(std::move(call));
}
catch (Error const& _e)
{
throw Error{_e.type(), "Line " + to_string(_lineOffset + m_lineNumber) + ": " + _e.what()};
}
} }
} }
} }
@ -207,7 +222,10 @@ Parameter TestFileParser::parseParameter()
{ {
Parameter parameter; Parameter parameter;
if (accept(Token::Newline, true)) if (accept(Token::Newline, true))
{
parameter.format.newline = true; parameter.format.newline = true;
m_lineNumber++;
}
bool isSigned = false; bool isSigned = false;

View File

@ -59,7 +59,9 @@ public:
/// Throws an exception if a function call cannot be parsed because of its /// Throws an exception if a function call cannot be parsed because of its
/// incorrect structure, an invalid or unsupported encoding /// incorrect structure, an invalid or unsupported encoding
/// of its arguments or expected results. /// of its arguments or expected results.
std::vector<FunctionCall> parseFunctionCalls(); /// Passes the source line offset, such that parsing errors can be enhanced
/// with a line number it occurred in.
std::vector<FunctionCall> parseFunctionCalls(std::size_t _lineOffset);
private: private:
using Token = soltest::Token; using Token = soltest::Token;
@ -179,6 +181,10 @@ private:
/// A scanner instance /// A scanner instance
Scanner m_scanner; Scanner m_scanner;
/// The current line number. Incremented when Token::Newline (//) is found and
/// used to enhance parser error messages.
size_t m_lineNumber = 0;
}; };
} }

View File

@ -44,7 +44,7 @@ vector<FunctionCall> parse(string const& _source)
{ {
istringstream stream{_source, ios_base::out}; istringstream stream{_source, ios_base::out};
TestFileParser parser{stream}; TestFileParser parser{stream};
return parser.parseFunctionCalls(); return parser.parseFunctionCalls(0);
} }
void testFunctionCall( void testFunctionCall(