Remove parser error recovery mode

This commit is contained in:
Kamil Śliwak 2023-08-04 22:54:48 +02:00
parent c96db51013
commit 9adbced98e
44 changed files with 142 additions and 585 deletions

View File

@ -4,6 +4,7 @@ Language Features:
Compiler Features: Compiler Features:
* Parser: Remove the experimental error recovery mode (``--error-recovery`` / ``settings.parserErrorRecovery``).
* Yul Optimizer: If ``PUSH0`` is supported, favor zero literals over storing zero values in variables. * Yul Optimizer: If ``PUSH0`` is supported, favor zero literals over storing zero values in variables.

View File

@ -74,56 +74,10 @@ void ParserBase::expectToken(Token _value, bool _advance)
{ {
Token tok = m_scanner->currentToken(); Token tok = m_scanner->currentToken();
if (tok != _value) if (tok != _value)
{ fatalParserError(
std::string const expectedToken = ParserBase::tokenName(_value); 2314_error,
if (m_parserErrorRecovery) "Expected " + ParserBase::tokenName(_value) + " but got " + tokenName(tok)
parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok)); );
else
fatalParserError(2314_error, "Expected " + expectedToken + " but got " + tokenName(tok));
// Do not advance so that recovery can sync or make use of the current token.
// This is especially useful if the expected token
// is the only one that is missing and is at the end of a construct.
// "{ ... ; }" is such an example.
// ^
_advance = false;
}
if (_advance)
advance();
}
void ParserBase::expectTokenOrConsumeUntil(Token _value, std::string const& _currentNodeName, bool _advance)
{
solAssert(m_inParserRecovery, "The function is supposed to be called during parser recovery only.");
Token tok = m_scanner->currentToken();
if (tok != _value)
{
SourceLocation errorLoc = currentLocation();
int startPosition = errorLoc.start;
while (m_scanner->currentToken() != _value && m_scanner->currentToken() != Token::EOS)
advance();
std::string const expectedToken = ParserBase::tokenName(_value);
if (m_scanner->currentToken() == Token::EOS)
{
// rollback to where the token started, and raise exception to be caught at a higher level.
m_scanner->setPosition(static_cast<size_t>(startPosition));
std::string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead.";
fatalParserError(1957_error, errorLoc, msg);
}
else
{
parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
m_inParserRecovery = false;
}
}
else
{
std::string expectedToken = ParserBase::tokenName(_value);
parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
m_inParserRecovery = false;
}
if (_advance) if (_advance)
advance(); advance();
} }

View File

@ -38,14 +38,9 @@ struct ErrorId;
class ParserBase class ParserBase
{ {
public: public:
/// Set @a _parserErrorRecovery to true for additional error explicit ParserBase(ErrorReporter& errorReporter):
/// recovery. This is experimental and intended for use m_errorReporter(errorReporter)
/// by front-end tools that need partial AST information even {}
/// when errors occur.
explicit ParserBase(ErrorReporter& errorReporter, bool _parserErrorRecovery = false): m_errorReporter(errorReporter)
{
m_parserErrorRecovery = _parserErrorRecovery;
}
virtual ~ParserBase() = default; virtual ~ParserBase() = default;
@ -70,13 +65,9 @@ protected:
///@{ ///@{
///@name Helper functions ///@name Helper functions
/// If current token value is not @a _value, throw exception otherwise advance token /// If current token value is not @a _value, throw exception otherwise advance token
// @a if _advance is true and error recovery is in effect. // if @a _advance is true
void expectToken(Token _value, bool _advance = true); void expectToken(Token _value, bool _advance = true);
/// Like expectToken but if there is an error ignores tokens until
/// the expected token or EOS is seen. If EOS is encountered, back up to the error point,
/// and throw an exception so that a higher grammar rule has an opportunity to recover.
void expectTokenOrConsumeUntil(Token _value, std::string const& _currentNodeName, bool _advance = true);
Token currentToken() const; Token currentToken() const;
Token peekNextToken() const; Token peekNextToken() const;
std::string tokenName(Token _token); std::string tokenName(Token _token);
@ -108,10 +99,6 @@ protected:
ErrorReporter& m_errorReporter; ErrorReporter& m_errorReporter;
/// Current recursion depth during parsing. /// Current recursion depth during parsing.
size_t m_recursionDepth = 0; size_t m_recursionDepth = 0;
/// True if we are in parser error recovery. Usually this means we are scanning for
/// a synchronization token like ';', or '}'. We use this to reduce cascaded error messages.
bool m_inParserRecovery = false;
bool m_parserErrorRecovery = false;
}; };
} }

View File

@ -159,14 +159,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
SemVerMatchExpressionParser parser(tokens, literals); SemVerMatchExpressionParser parser(tokens, literals);
SemVerMatchExpression matchExpression = parser.parse(); SemVerMatchExpression matchExpression = parser.parse();
static SemVerVersion const currentVersion{std::string(VersionString)}; static SemVerVersion const currentVersion{std::string(VersionString)};
if (!matchExpression.matches(currentVersion)) solAssert(matchExpression.matches(currentVersion));
m_errorReporter.syntaxError(
3997_error,
_pragma.location(),
"Source file requires different compiler version (current compiler is " +
std::string(VersionString) + ") - note that nightly builds are considered to be "
"strictly less than the released version"
);
m_versionPragmaFound = true; m_versionPragmaFound = true;
} }
catch (SemVerError const&) catch (SemVerError const&)

View File

@ -352,7 +352,7 @@ bool CompilerStack::parse()
if (SemVerVersion{std::string(VersionString)}.isPrerelease()) if (SemVerVersion{std::string(VersionString)}.isPrerelease())
m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production."); m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production.");
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery}; Parser parser{m_errorReporter, m_evmVersion};
std::vector<std::string> sourcesToParse; std::vector<std::string> sourcesToParse;
for (auto const& s: m_sources) for (auto const& s: m_sources)
@ -1104,7 +1104,7 @@ SourceUnit const& CompilerStack::ast(std::string const& _sourceName) const
{ {
if (m_stackState < Parsed) if (m_stackState < Parsed)
solThrow(CompilerError, "Parsing not yet performed."); solThrow(CompilerError, "Parsing not yet performed.");
if (!source(_sourceName).ast && !m_parserErrorRecovery) if (!source(_sourceName).ast)
solThrow(CompilerError, "Parsing was not successful."); solThrow(CompilerError, "Parsing was not successful.");
return *source(_sourceName).ast; return *source(_sourceName).ast;

View File

@ -158,14 +158,6 @@ public:
/// Sets whether to strip revert strings, add additional strings or do nothing at all. /// Sets whether to strip revert strings, add additional strings or do nothing at all.
void setRevertStringBehaviour(RevertStrings _revertStrings); void setRevertStringBehaviour(RevertStrings _revertStrings);
/// Set whether or not parser error is desired.
/// When called without an argument it will revert to the default.
/// Must be set before parsing.
void setParserErrorRecovery(bool _wantErrorRecovery = false)
{
m_parserErrorRecovery = _wantErrorRecovery;
}
/// Sets the pipeline to go through the Yul IR or not. /// Sets the pipeline to go through the Yul IR or not.
/// Must be set before parsing. /// Must be set before parsing.
void setViaIR(bool _viaIR); void setViaIR(bool _viaIR);
@ -511,7 +503,6 @@ private:
bool m_metadataLiteralSources = false; bool m_metadataLiteralSources = false;
MetadataHash m_metadataHash = MetadataHash::IPFS; MetadataHash m_metadataHash = MetadataHash::IPFS;
langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default(); langutil::DebugInfoSelection m_debugInfoSelection = langutil::DebugInfoSelection::Default();
bool m_parserErrorRecovery = false;
State m_stackState = Empty; State m_stackState = Empty;
CompilationSourceType m_compilationSourceType = CompilationSourceType::Solidity; CompilationSourceType m_compilationSourceType = CompilationSourceType::Solidity;
MetadataFormat m_metadataFormat = defaultMetadataFormat(); MetadataFormat m_metadataFormat = defaultMetadataFormat();

View File

@ -423,7 +423,7 @@ std::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input) std::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
{ {
static std::set<std::string> keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; static std::set<std::string> keys{"debug", "evmVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"};
return checkKeys(_input, keys, "settings"); return checkKeys(_input, keys, "settings");
} }
@ -773,13 +773,6 @@ std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler:
ret.stopAfter = CompilerStack::State::Parsed; ret.stopAfter = CompilerStack::State::Parsed;
} }
if (settings.isMember("parserErrorRecovery"))
{
if (!settings["parserErrorRecovery"].isBool())
return formatFatalError(Error::Type::JSONError, "\"settings.parserErrorRecovery\" must be a Boolean.");
ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool();
}
if (settings.isMember("viaIR")) if (settings.isMember("viaIR"))
{ {
if (!settings["viaIR"].isBool()) if (!settings["viaIR"].isBool())
@ -1166,7 +1159,6 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second);
compilerStack.setViaIR(_inputsAndSettings.viaIR); compilerStack.setViaIR(_inputsAndSettings.viaIR);
compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); compilerStack.setEVMVersion(_inputsAndSettings.evmVersion);
compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery);
compilerStack.setRemappings(std::move(_inputsAndSettings.remappings)); compilerStack.setRemappings(std::move(_inputsAndSettings.remappings));
compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings)); compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings));
compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings); compilerStack.setRevertStringBehaviour(_inputsAndSettings.revertStrings);

View File

@ -72,7 +72,6 @@ private:
{ {
std::string language; std::string language;
Json::Value errors; Json::Value errors;
bool parserErrorRecovery = false;
CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful;
std::map<std::string, std::string> sources; std::map<std::string, std::string> sources;
std::map<util::h256, std::string> smtLib2Responses; std::map<util::h256, std::string> smtLib2Responses;

View File

@ -173,9 +173,6 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, std::vector<Tok
static SemVerVersion const currentVersion{std::string(VersionString)}; static SemVerVersion const currentVersion{std::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(),
// so we don't need to report anything here.
if (!m_parserErrorRecovery)
m_errorReporter.fatalParserError( m_errorReporter.fatalParserError(
5333_error, 5333_error,
_location, _location,
@ -364,11 +361,9 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
std::vector<ASTPointer<InheritanceSpecifier>> baseContracts; std::vector<ASTPointer<InheritanceSpecifier>> baseContracts;
std::vector<ASTPointer<ASTNode>> subNodes; std::vector<ASTPointer<ASTNode>> subNodes;
std::pair<ContractKind, bool> contractKind{}; std::pair<ContractKind, bool> contractKind{};
try
{
documentation = parseStructuredDocumentation(); documentation = parseStructuredDocumentation();
contractKind = parseContractKind(); contractKind = parseContractKind();
tie(name, nameLocation) = expectIdentifierWithLocation(); std::tie(name, nameLocation) = expectIdentifierWithLocation();
if (m_scanner->currentToken() == Token::Is) if (m_scanner->currentToken() == Token::Is)
do do
{ {
@ -420,21 +415,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
else else
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected."); fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
} }
}
catch (FatalError const&)
{
if (
!m_errorReporter.hasErrors() ||
!m_parserErrorRecovery ||
m_errorReporter.hasExcessiveErrors()
)
BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */
m_inParserRecovery = true;
}
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
if (m_inParserRecovery)
expectTokenOrConsumeUntil(Token::RBrace, "ContractDefinition");
else
expectToken(Token::RBrace); expectToken(Token::RBrace);
return nodeFactory.createNode<ContractDefinition>( return nodeFactory.createNode<ContractDefinition>(
name, name,
@ -1295,25 +1276,9 @@ ASTPointer<Block> Parser::parseBlock(bool _allowUnchecked, ASTPointer<ASTString>
} }
expectToken(Token::LBrace); expectToken(Token::LBrace);
std::vector<ASTPointer<Statement>> statements; std::vector<ASTPointer<Statement>> statements;
try
{
while (m_scanner->currentToken() != Token::RBrace) while (m_scanner->currentToken() != Token::RBrace)
statements.push_back(parseStatement(true)); statements.push_back(parseStatement(true));
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
}
catch (FatalError const&)
{
if (
!m_errorReporter.hasErrors() ||
!m_parserErrorRecovery ||
m_errorReporter.hasExcessiveErrors()
)
BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */
m_inParserRecovery = true;
}
if (m_inParserRecovery)
expectTokenOrConsumeUntil(Token::RBrace, "Block");
else
expectToken(Token::RBrace); expectToken(Token::RBrace);
return nodeFactory.createNode<Block>(_docString, unchecked, statements); return nodeFactory.createNode<Block>(_docString, unchecked, statements);
} }
@ -1323,8 +1288,6 @@ ASTPointer<Statement> Parser::parseStatement(bool _allowUnchecked)
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTPointer<ASTString> docString; ASTPointer<ASTString> docString;
ASTPointer<Statement> statement; ASTPointer<Statement> statement;
try
{
if (m_scanner->currentCommentLiteral() != "") if (m_scanner->currentCommentLiteral() != "")
docString = std::make_shared<ASTString>(m_scanner->currentCommentLiteral()); docString = std::make_shared<ASTString>(m_scanner->currentCommentLiteral());
switch (m_scanner->currentToken()) switch (m_scanner->currentToken())
@ -1388,20 +1351,6 @@ ASTPointer<Statement> Parser::parseStatement(bool _allowUnchecked)
statement = parseSimpleStatement(docString); statement = parseSimpleStatement(docString);
break; break;
} }
}
catch (FatalError const&)
{
if (
!m_errorReporter.hasErrors() ||
!m_parserErrorRecovery ||
m_errorReporter.hasExcessiveErrors()
)
BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */
m_inParserRecovery = true;
}
if (m_inParserRecovery)
expectTokenOrConsumeUntil(Token::Semicolon, "Statement");
else
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return statement; return statement;
} }

View File

@ -40,10 +40,9 @@ class Parser: public langutil::ParserBase
public: public:
explicit Parser( explicit Parser(
langutil::ErrorReporter& _errorReporter, langutil::ErrorReporter& _errorReporter,
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion
bool _errorRecovery = false
): ):
ParserBase(_errorReporter, _errorRecovery), ParserBase(_errorReporter),
m_evmVersion(_evmVersion) m_evmVersion(_evmVersion)
{} {}

View File

@ -171,7 +171,6 @@ def print_ids_per_file(ids, id_to_file_names, top_dir):
def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False):
test_sub_dirs = [ test_sub_dirs = [
path.join("test", "libsolidity", "errorRecoveryTests"),
path.join("test", "libsolidity", "smtCheckerTests"), path.join("test", "libsolidity", "smtCheckerTests"),
path.join("test", "libsolidity", "syntaxTests"), path.join("test", "libsolidity", "syntaxTests"),
path.join("test", "libyul", "yulSyntaxTests") path.join("test", "libyul", "yulSyntaxTests")

View File

@ -792,10 +792,7 @@ void CommandLineInterface::compile()
} }
} }
else else
{
m_compiler->setSources(m_fileReader.sourceUnits()); m_compiler->setSources(m_fileReader.sourceUnits());
m_compiler->setParserErrorRecovery(m_options.input.errorRecovery);
}
bool successful = m_compiler->compile(m_options.output.stopAfter); bool successful = m_compiler->compile(m_options.output.stopAfter);

View File

@ -43,7 +43,6 @@ static string const g_strBasePath = "base-path";
static string const g_strIncludePath = "include-path"; static string const g_strIncludePath = "include-path";
static string const g_strAssemble = "assemble"; static string const g_strAssemble = "assemble";
static string const g_strCombinedJson = "combined-json"; static string const g_strCombinedJson = "combined-json";
static string const g_strErrorRecovery = "error-recovery";
static string const g_strEVM = "evm"; static string const g_strEVM = "evm";
static string const g_strEVMVersion = "evm-version"; static string const g_strEVMVersion = "evm-version";
static string const g_strEOFVersion = "experimental-eof-version"; static string const g_strEOFVersion = "experimental-eof-version";
@ -226,7 +225,6 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
input.includePaths == _other.input.includePaths && input.includePaths == _other.input.includePaths &&
input.allowedDirectories == _other.input.allowedDirectories && input.allowedDirectories == _other.input.allowedDirectories &&
input.ignoreMissingFiles == _other.input.ignoreMissingFiles && input.ignoreMissingFiles == _other.input.ignoreMissingFiles &&
input.errorRecovery == _other.input.errorRecovery &&
output.dir == _other.output.dir && output.dir == _other.output.dir &&
output.overwriteFiles == _other.output.overwriteFiles && output.overwriteFiles == _other.output.overwriteFiles &&
output.evmVersion == _other.output.evmVersion && output.evmVersion == _other.output.evmVersion &&
@ -570,10 +568,6 @@ General Information)").c_str(),
g_strIgnoreMissingFiles.c_str(), g_strIgnoreMissingFiles.c_str(),
"Ignore missing files." "Ignore missing files."
) )
(
g_strErrorRecovery.c_str(),
"Enables additional parser error recovery."
)
; ;
desc.add(inputOptions); desc.add(inputOptions);
@ -962,7 +956,6 @@ void CommandLineParser::processArgs()
map<string, set<InputMode>> validOptionInputModeCombinations = { map<string, set<InputMode>> validOptionInputModeCombinations = {
// TODO: This should eventually contain all options. // TODO: This should eventually contain all options.
{g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
{g_strExperimentalViaIR, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strExperimentalViaIR, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
{g_strViaIR, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strViaIR, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
{g_strMetadataLiteral, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strMetadataLiteral, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
@ -1369,8 +1362,6 @@ void CommandLineParser::processArgs()
m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTargets) ||
m_args.count(g_strModelCheckerTimeout); m_args.count(g_strModelCheckerTimeout);
m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0);
if (m_options.input.mode == InputMode::Compiler)
m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0);
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport); solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport);
} }

View File

@ -174,7 +174,6 @@ struct CommandLineOptions
std::vector<boost::filesystem::path> includePaths; std::vector<boost::filesystem::path> includePaths;
FileReader::FileSystemPathSet allowedDirectories; FileReader::FileSystemPathSet allowedDirectories;
bool ignoreMissingFiles = false; bool ignoreMissingFiles = false;
bool errorRecovery = false;
} input; } input;
struct struct

View File

@ -71,7 +71,6 @@ Testsuite const g_interactiveTestsuites[] = {
{"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create},
{"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}},
{"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create},
{"Error Recovery", "libsolidity", "errorRecoveryTests", false, false, &SyntaxTest::createErrorRecovery},
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},

View File

@ -1 +0,0 @@
--error-recovery --ast-compact-json --pretty-json --hashes

View File

@ -1,11 +0,0 @@
Error: Expected primary expression.
--> recovery_ast_constructor/input.sol:6:27:
|
6 | balances[tx.origin] = ; // missing RHS.
| ^
Warning: Recovered in Statement at ';'.
--> recovery_ast_constructor/input.sol:6:27:
|
6 | balances[tx.origin] = ; // missing RHS.
| ^

View File

@ -1,13 +0,0 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0.0;
contract Error1 {
constructor() {
balances[tx.origin] = ; // missing RHS.
}
// This function parses properly
function five() public view returns(uint) {
return 5;
}
}

View File

@ -1 +0,0 @@
--error-recovery

View File

@ -1,5 +0,0 @@
Error: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition.
--> recovery_ast_empty_contract/input.sol:3:1:
|
3 | c
| ^

View File

@ -1,3 +0,0 @@
// SPDX-License-Identifier: GPL-3.0
pragma 0.5.11;
c

View File

@ -1,18 +0,0 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }"
}
},
"settings":
{
"parserErrorRecovery": true,
"outputSelection":
{
"*": { "": ["ast"] }
}
}
}

View File

@ -1,46 +0,0 @@
{
"errors":
[
{
"component": "general",
"errorCode": "3546",
"formattedMessage": "ParserError: Expected type name
--> A:2:58:
|
2 | pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }
| ^
",
"message": "Expected type name",
"severity": "error",
"sourceLocation":
{
"end": 94,
"file": "A",
"start": 93
},
"type": "ParserError"
},
{
"component": "general",
"errorCode": "3796",
"formattedMessage": "Warning: Recovered in ContractDefinition at '}'.
--> A:2:84:
|
2 | pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }
| ^
",
"message": "Recovered in ContractDefinition at '}'.",
"severity": "warning",
"sourceLocation":
{
"end": 120,
"file": "A",
"start": 119
},
"type": "Warning"
}
],
"sources": {}
}

View File

@ -47,15 +47,12 @@ AnalysisFramework::parseAnalyseAndReturnError(
string const& _source, string const& _source,
bool _reportWarnings, bool _reportWarnings,
bool _insertLicenseAndVersionPragma, bool _insertLicenseAndVersionPragma,
bool _allowMultipleErrors, bool _allowMultipleErrors
bool _allowRecoveryErrors
) )
{ {
compiler().reset(); compiler().reset();
compiler().setSources({{"", _insertLicenseAndVersionPragma ? withPreamble(_source) : _source}}); compiler().setSources({{"", _insertLicenseAndVersionPragma ? withPreamble(_source) : _source}});
compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
compiler().setParserErrorRecovery(_allowRecoveryErrors);
_allowMultipleErrors = _allowMultipleErrors || _allowRecoveryErrors;
if (!compiler().parse()) if (!compiler().parse())
{ {
BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors(compiler().errors())); BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors(compiler().errors()));

View File

@ -48,8 +48,7 @@ protected:
std::string const& _source, std::string const& _source,
bool _reportWarnings = false, bool _reportWarnings = false,
bool _insertLicenseAndVersionPragma = true, bool _insertLicenseAndVersionPragma = true,
bool _allowMultipleErrors = false, bool _allowMultipleErrors = false
bool _allowRecoveryErrors = false
); );
virtual ~AnalysisFramework() = default; virtual ~AnalysisFramework() = default;

View File

@ -39,14 +39,13 @@ namespace solidity::frontend::test
namespace namespace
{ {
ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors, bool errorRecovery = false) ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors)
{ {
ErrorReporter errorReporter(_errors); ErrorReporter errorReporter(_errors);
auto charStream = CharStream(_source, ""); auto charStream = CharStream(_source, "");
ASTPointer<SourceUnit> sourceUnit = Parser( ASTPointer<SourceUnit> sourceUnit = Parser(
errorReporter, errorReporter,
solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().evmVersion()
errorRecovery
).parse(charStream); ).parse(charStream);
if (!sourceUnit) if (!sourceUnit)
return ASTPointer<ContractDefinition>(); return ASTPointer<ContractDefinition>();
@ -78,12 +77,12 @@ bool successParse(std::string const& _source)
return true; return true;
} }
Error getError(std::string const& _source, bool errorRecovery = false) Error getError(std::string const& _source)
{ {
ErrorList errors; ErrorList errors;
try try
{ {
parseText(_source, errors, errorRecovery); parseText(_source, errors);
} }
catch (FatalError const& /*_exception*/) catch (FatalError const& /*_exception*/)
{ {

View File

@ -282,43 +282,6 @@ BOOST_AUTO_TEST_CASE(smoke_test)
BOOST_CHECK(containsAtMostWarnings(result)); BOOST_CHECK(containsAtMostWarnings(result));
} }
BOOST_AUTO_TEST_CASE(error_recovery_field)
{
auto input = R"(
{
"language": "Solidity",
"settings": {
"parserErrorRecovery": "1"
},
"sources": {
"empty": {
"content": ""
}
}
}
)";
Json::Value result = compile(input);
BOOST_CHECK(containsError(result, "JSONError", "\"settings.parserErrorRecovery\" must be a Boolean."));
input = R"(
{
"language": "Solidity",
"settings": {
"parserErrorRecovery": true
},
"sources": {
"empty": {
"content": ""
}
}
}
)";
result = compile(input);
BOOST_CHECK(containsAtMostWarnings(result));
}
BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean) BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean)
{ {
char const* input = R"( char const* input = R"(

View File

@ -38,10 +38,9 @@ using namespace solidity::frontend::test;
using namespace boost::unit_test; using namespace boost::unit_test;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion) SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): CommonSyntaxTest(_filename, _evmVersion)
{ {
m_optimiseYul = m_reader.boolSetting("optimize-yul", true); m_optimiseYul = m_reader.boolSetting("optimize-yul", true);
m_parserErrorRecovery = _parserErrorRecovery;
} }
void SyntaxTest::setupCompiler() void SyntaxTest::setupCompiler()
@ -49,7 +48,6 @@ void SyntaxTest::setupCompiler()
compiler().reset(); compiler().reset();
compiler().setSources(withPreamble(m_sources.sources)); compiler().setSources(withPreamble(m_sources.sources));
compiler().setEVMVersion(m_evmVersion); compiler().setEVMVersion(m_evmVersion);
compiler().setParserErrorRecovery(m_parserErrorRecovery);
compiler().setOptimiserSettings( compiler().setOptimiserSettings(
m_optimiseYul ? m_optimiseYul ?
OptimiserSettings::full() : OptimiserSettings::full() :

View File

@ -39,13 +39,9 @@ class SyntaxTest: public AnalysisFramework, public solidity::test::CommonSyntaxT
public: public:
static std::unique_ptr<TestCase> create(Config const& _config) static std::unique_ptr<TestCase> create(Config const& _config)
{ {
return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion, false); return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion);
} }
static std::unique_ptr<TestCase> createErrorRecovery(Config const& _config) SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion);
{
return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion, true);
}
SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery = false);
protected: protected:
virtual void setupCompiler(); virtual void setupCompiler();
@ -53,7 +49,6 @@ protected:
virtual void filterObtainedErrors(); virtual void filterObtainedErrors();
bool m_optimiseYul = true; bool m_optimiseYul = true;
bool m_parserErrorRecovery = false;
}; };
} }

View File

@ -1,20 +0,0 @@
pragma solidity >=0.0.0;
contract Error1 {
constructor() {
balances[tx.origin] = ; // missing RHS.
}
// Without error recovery we stop due to the above error.
// Error recovery however recovers at the above ';'
// There should be an AST for the above, albeit with error
// nodes.
// This function parses properly and should give AST info.
function five() public view returns(uint) {
return 5;
}
}
// ----
// ParserError 6933: (88-89): Expected primary expression.
// Warning 3347: (88-89): Recovered in Statement at ';'.

View File

@ -1,6 +0,0 @@
contract Errort6 {
using foo for ; // missing type name
}
// ----
// ParserError 3546: (36-37): Expected type name
// Warning 3796: (59-60): Recovered in ContractDefinition at '}'.

View File

@ -1,15 +0,0 @@
pragma solidity >=0.0.0;
// Example to show why deleting the token at the
// is bad when error recovery is in effect. Here, ")" is missing
// and there is a ";" instead. That causes us to
// not be able to synchronize to ';'. Advance again and
// '}' is deleted and then we can't synchronize the contract.
// There should be an an AST created this contract (with errors).
contract Error2 {
mapping (address => uint balances; // missing ) before "balances"
}
// ----
// ParserError 6635: (425-426): Expected ')' but got ';'
// ParserError 6635: (425-426): Expected identifier but got ';'
// ParserError 6635: (458-459): Expected ';' but got '}'

View File

@ -1,23 +0,0 @@
// Example which where scanning hits EOS, so we reset.
// Here we recover in the contractDefinition.
// There should be an an AST created this contract (with errors).
contract Error2 {
mapping (address => uint balances) // missing ;
}
// There is no error in this contract
contract SendCoin {
function sendCoin(address receiver, uint amount) public returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Transfer(msg.sender, receiver, amount);
return true;
}
}
// ----
// ParserError 6635: (235-236): Expected identifier but got '}'
// ParserError 6635: (276-284): Expected ';' but got 'contract'
// ParserError 9182: (276-284): Function, variable, struct or modifier declaration expected.
// Warning 3796: (572-573): Recovered in ContractDefinition at '}'.
// ParserError 7858: (574-575): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition.

View File

@ -1,11 +0,0 @@
pragma solidity >=0.0.0;
contract Error3 {
constructor() {
balances[tx.origin] = ; // missing RHS.
}
}
// ----
// ParserError 6933: (88-89): Expected primary expression.
// Warning 3347: (88-89): Recovered in Statement at ';'.

View File

@ -1,29 +0,0 @@
// An example with multiple errors.
// Most are caught by inserting an expected token.
// However some us S C Johnson recovery to
// skip over tokens.
pragma solidity >=0.0.0;
contract Error4 {
constructor() {
balances[tx.origin] = 1 2; // missing operator
}
function sendCoin(address receiver, uint amount) public returns(bool sufficient) {
if (balances[msg.sender] < amount) return false;
balances[msg.sender] -= amount // Missing ";"
balances[receiver] += amount // Another missing ";"
emit Transfer(msg.sender // truncated line
return true;
}
}
// ----
// ParserError 6635: (242-243): Expected ';' but got 'Number'
// ParserError 6635: (464-472): Expected ';' but got identifier
// ParserError 6635: (522-526): Expected ';' but got 'emit'
// ParserError 6635: (570-576): Expected ',' but got 'return'
// ParserError 6933: (570-576): Expected primary expression.
// Warning 3796: (581-582): Recovered in Statement at ';'.

View File

@ -1,10 +0,0 @@
pragma solidity >=0.0.0;
contract Error7 {
constructor() {
a =
// ----
// ParserError 6933: (76-76): Expected primary expression.
// ParserError 1957: (76-76): In Statement, ';'is expected; got end of source instead.
// ParserError 1957: (76-76): In Block, '}'is expected; got end of source instead.
// ParserError 1957: (76-76): In ContractDefinition, '}'is expected; got end of source instead.

View File

@ -1,3 +0,0 @@
pragma solidity ^99.99.0;
// ----
// SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is ....

View File

@ -1,6 +0,0 @@
pragma solidity ^99.99.0;
this is surely invalid
// ----
// ParserError 6635: (31-33): Expected identifier but got 'is'
// ParserError 6635: (34-40): Expected ';' but got identifier
// ParserError 6635: (49-49): Expected ';' but got end of source

View File

@ -1,7 +0,0 @@
pragma solidity ^99.99.0;
contract C {
uint ;
}
// ----
// ParserError 6635: (48-49): Expected identifier but got ';'
// ParserError 6635: (50-51): Expected ';' but got '}'

View File

@ -1,7 +0,0 @@
pragma solidity ^99.99.0;
contract C {
function f() {}
}
// ----
// SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is ....
// SyntaxError 4937: (43-58): No visibility specified. Did you intend to add "public"?

View File

@ -33,10 +33,6 @@ public:
{ {
return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion); return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion);
} }
static std::unique_ptr<TestCase> createErrorRecovery(Config const& _config)
{
return std::make_unique<SyntaxTest>(_config.filename, _config.evmVersion);
}
SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion);
~SyntaxTest() override {} ~SyntaxTest() override {}
protected: protected:

View File

@ -116,7 +116,6 @@ BOOST_AUTO_TEST_CASE(cli_mode_options)
"--include-path=/home/user/include", "--include-path=/home/user/include",
"--allow-paths=/tmp,/home,project,../contracts", "--allow-paths=/tmp,/home,project,../contracts",
"--ignore-missing", "--ignore-missing",
"--error-recovery",
"--output-dir=/tmp/out", "--output-dir=/tmp/out",
"--overwrite", "--overwrite",
"--evm-version=spuriousDragon", "--evm-version=spuriousDragon",
@ -177,7 +176,6 @@ BOOST_AUTO_TEST_CASE(cli_mode_options)
expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "c", "/usr/lib"}; expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "c", "/usr/lib"};
expectedOptions.input.ignoreMissingFiles = true; expectedOptions.input.ignoreMissingFiles = true;
expectedOptions.input.errorRecovery = (inputMode == InputMode::Compiler);
expectedOptions.output.dir = "/tmp/out"; expectedOptions.output.dir = "/tmp/out";
expectedOptions.output.overwriteFiles = true; expectedOptions.output.overwriteFiles = true;
expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); expectedOptions.output.evmVersion = EVMVersion::spuriousDragon();
@ -405,7 +403,6 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations)
{ {
map<string, vector<string>> invalidOptionInputModeCombinations = { map<string, vector<string>> invalidOptionInputModeCombinations = {
// TODO: This should eventually contain all options. // TODO: This should eventually contain all options.
{"--error-recovery", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}},
{"--experimental-via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, {"--experimental-via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}},
{"--via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, {"--via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}},
{"--metadata-literal", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, {"--metadata-literal", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}},