mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Remove parser error recovery mode
This commit is contained in:
parent
c96db51013
commit
9adbced98e
@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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&)
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -173,16 +173,13 @@ 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(),
|
m_errorReporter.fatalParserError(
|
||||||
// so we don't need to report anything here.
|
5333_error,
|
||||||
if (!m_parserErrorRecovery)
|
_location,
|
||||||
m_errorReporter.fatalParserError(
|
"Source file requires different compiler version (current compiler is " +
|
||||||
5333_error,
|
std::string(VersionString) + ") - note that nightly builds are considered to be "
|
||||||
_location,
|
"strictly less than the released version"
|
||||||
"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"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
catch (SemVerError const& matchError)
|
catch (SemVerError const& matchError)
|
||||||
{
|
{
|
||||||
@ -364,78 +361,62 @@ 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();
|
||||||
{
|
contractKind = parseContractKind();
|
||||||
documentation = parseStructuredDocumentation();
|
std::tie(name, nameLocation) = expectIdentifierWithLocation();
|
||||||
contractKind = parseContractKind();
|
if (m_scanner->currentToken() == Token::Is)
|
||||||
tie(name, nameLocation) = expectIdentifierWithLocation();
|
do
|
||||||
if (m_scanner->currentToken() == Token::Is)
|
|
||||||
do
|
|
||||||
{
|
|
||||||
advance();
|
|
||||||
baseContracts.push_back(parseInheritanceSpecifier());
|
|
||||||
}
|
|
||||||
while (m_scanner->currentToken() == Token::Comma);
|
|
||||||
expectToken(Token::LBrace);
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
Token currentTokenValue = m_scanner->currentToken();
|
advance();
|
||||||
if (currentTokenValue == Token::RBrace)
|
baseContracts.push_back(parseInheritanceSpecifier());
|
||||||
break;
|
|
||||||
else if (
|
|
||||||
(currentTokenValue == Token::Function && m_scanner->peekNextToken() != Token::LParen) ||
|
|
||||||
currentTokenValue == Token::Constructor ||
|
|
||||||
currentTokenValue == Token::Receive ||
|
|
||||||
currentTokenValue == Token::Fallback
|
|
||||||
)
|
|
||||||
subNodes.push_back(parseFunctionDefinition());
|
|
||||||
else if (currentTokenValue == Token::Struct)
|
|
||||||
subNodes.push_back(parseStructDefinition());
|
|
||||||
else if (currentTokenValue == Token::Enum)
|
|
||||||
subNodes.push_back(parseEnumDefinition());
|
|
||||||
else if (currentTokenValue == Token::Type)
|
|
||||||
subNodes.push_back(parseUserDefinedValueTypeDefinition());
|
|
||||||
else if (
|
|
||||||
// Workaround because `error` is not a keyword.
|
|
||||||
currentTokenValue == Token::Identifier &&
|
|
||||||
currentLiteral() == "error" &&
|
|
||||||
m_scanner->peekNextToken() == Token::Identifier &&
|
|
||||||
m_scanner->peekNextNextToken() == Token::LParen
|
|
||||||
)
|
|
||||||
subNodes.push_back(parseErrorDefinition());
|
|
||||||
else if (variableDeclarationStart())
|
|
||||||
{
|
|
||||||
VarDeclParserOptions options;
|
|
||||||
options.kind = VarDeclKind::State;
|
|
||||||
options.allowInitialValue = true;
|
|
||||||
subNodes.push_back(parseVariableDeclaration(options));
|
|
||||||
expectToken(Token::Semicolon);
|
|
||||||
}
|
|
||||||
else if (currentTokenValue == Token::Modifier)
|
|
||||||
subNodes.push_back(parseModifierDefinition());
|
|
||||||
else if (currentTokenValue == Token::Event)
|
|
||||||
subNodes.push_back(parseEventDefinition());
|
|
||||||
else if (currentTokenValue == Token::Using)
|
|
||||||
subNodes.push_back(parseUsingDirective());
|
|
||||||
else
|
|
||||||
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
|
|
||||||
}
|
}
|
||||||
}
|
while (m_scanner->currentToken() == Token::Comma);
|
||||||
catch (FatalError const&)
|
expectToken(Token::LBrace);
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
if (
|
Token currentTokenValue = m_scanner->currentToken();
|
||||||
!m_errorReporter.hasErrors() ||
|
if (currentTokenValue == Token::RBrace)
|
||||||
!m_parserErrorRecovery ||
|
break;
|
||||||
m_errorReporter.hasExcessiveErrors()
|
else if (
|
||||||
|
(currentTokenValue == Token::Function && m_scanner->peekNextToken() != Token::LParen) ||
|
||||||
|
currentTokenValue == Token::Constructor ||
|
||||||
|
currentTokenValue == Token::Receive ||
|
||||||
|
currentTokenValue == Token::Fallback
|
||||||
)
|
)
|
||||||
BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */
|
subNodes.push_back(parseFunctionDefinition());
|
||||||
m_inParserRecovery = true;
|
else if (currentTokenValue == Token::Struct)
|
||||||
|
subNodes.push_back(parseStructDefinition());
|
||||||
|
else if (currentTokenValue == Token::Enum)
|
||||||
|
subNodes.push_back(parseEnumDefinition());
|
||||||
|
else if (currentTokenValue == Token::Type)
|
||||||
|
subNodes.push_back(parseUserDefinedValueTypeDefinition());
|
||||||
|
else if (
|
||||||
|
// Workaround because `error` is not a keyword.
|
||||||
|
currentTokenValue == Token::Identifier &&
|
||||||
|
currentLiteral() == "error" &&
|
||||||
|
m_scanner->peekNextToken() == Token::Identifier &&
|
||||||
|
m_scanner->peekNextNextToken() == Token::LParen
|
||||||
|
)
|
||||||
|
subNodes.push_back(parseErrorDefinition());
|
||||||
|
else if (variableDeclarationStart())
|
||||||
|
{
|
||||||
|
VarDeclParserOptions options;
|
||||||
|
options.kind = VarDeclKind::State;
|
||||||
|
options.allowInitialValue = true;
|
||||||
|
subNodes.push_back(parseVariableDeclaration(options));
|
||||||
|
expectToken(Token::Semicolon);
|
||||||
|
}
|
||||||
|
else if (currentTokenValue == Token::Modifier)
|
||||||
|
subNodes.push_back(parseModifierDefinition());
|
||||||
|
else if (currentTokenValue == Token::Event)
|
||||||
|
subNodes.push_back(parseEventDefinition());
|
||||||
|
else if (currentTokenValue == Token::Using)
|
||||||
|
subNodes.push_back(parseUsingDirective());
|
||||||
|
else
|
||||||
|
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
|
||||||
}
|
}
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
if (m_inParserRecovery)
|
expectToken(Token::RBrace);
|
||||||
expectTokenOrConsumeUntil(Token::RBrace, "ContractDefinition");
|
|
||||||
else
|
|
||||||
expectToken(Token::RBrace);
|
|
||||||
return nodeFactory.createNode<ContractDefinition>(
|
return nodeFactory.createNode<ContractDefinition>(
|
||||||
name,
|
name,
|
||||||
nameLocation,
|
nameLocation,
|
||||||
@ -1295,26 +1276,10 @@ 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)
|
||||||
{
|
statements.push_back(parseStatement(true));
|
||||||
while (m_scanner->currentToken() != Token::RBrace)
|
nodeFactory.markEndPosition();
|
||||||
statements.push_back(parseStatement(true));
|
expectToken(Token::RBrace);
|
||||||
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);
|
|
||||||
return nodeFactory.createNode<Block>(_docString, unchecked, statements);
|
return nodeFactory.createNode<Block>(_docString, unchecked, statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1323,86 +1288,70 @@ 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() != "")
|
||||||
|
docString = std::make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
||||||
|
switch (m_scanner->currentToken())
|
||||||
{
|
{
|
||||||
if (m_scanner->currentCommentLiteral() != "")
|
case Token::If:
|
||||||
docString = std::make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
return parseIfStatement(docString);
|
||||||
switch (m_scanner->currentToken())
|
case Token::While:
|
||||||
{
|
return parseWhileStatement(docString);
|
||||||
case Token::If:
|
case Token::Do:
|
||||||
return parseIfStatement(docString);
|
return parseDoWhileStatement(docString);
|
||||||
case Token::While:
|
case Token::For:
|
||||||
return parseWhileStatement(docString);
|
return parseForStatement(docString);
|
||||||
case Token::Do:
|
case Token::Unchecked:
|
||||||
return parseDoWhileStatement(docString);
|
case Token::LBrace:
|
||||||
case Token::For:
|
return parseBlock(_allowUnchecked, docString);
|
||||||
return parseForStatement(docString);
|
case Token::Continue:
|
||||||
case Token::Unchecked:
|
statement = ASTNodeFactory(*this).createNode<Continue>(docString);
|
||||||
case Token::LBrace:
|
advance();
|
||||||
return parseBlock(_allowUnchecked, docString);
|
break;
|
||||||
case Token::Continue:
|
case Token::Break:
|
||||||
statement = ASTNodeFactory(*this).createNode<Continue>(docString);
|
statement = ASTNodeFactory(*this).createNode<Break>(docString);
|
||||||
advance();
|
advance();
|
||||||
break;
|
break;
|
||||||
case Token::Break:
|
case Token::Return:
|
||||||
statement = ASTNodeFactory(*this).createNode<Break>(docString);
|
{
|
||||||
advance();
|
ASTNodeFactory nodeFactory(*this);
|
||||||
break;
|
ASTPointer<Expression> expression;
|
||||||
case Token::Return:
|
if (advance() != Token::Semicolon)
|
||||||
{
|
|
||||||
ASTNodeFactory nodeFactory(*this);
|
|
||||||
ASTPointer<Expression> expression;
|
|
||||||
if (advance() != Token::Semicolon)
|
|
||||||
{
|
|
||||||
expression = parseExpression();
|
|
||||||
nodeFactory.setEndPositionFromNode(expression);
|
|
||||||
}
|
|
||||||
statement = nodeFactory.createNode<Return>(docString, expression);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Token::Throw:
|
|
||||||
{
|
|
||||||
statement = ASTNodeFactory(*this).createNode<Throw>(docString);
|
|
||||||
advance();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Token::Try:
|
|
||||||
return parseTryStatement(docString);
|
|
||||||
case Token::Assembly:
|
|
||||||
return parseInlineAssembly(docString);
|
|
||||||
case Token::Emit:
|
|
||||||
statement = parseEmitStatement(docString);
|
|
||||||
break;
|
|
||||||
case Token::Identifier:
|
|
||||||
if (m_scanner->currentLiteral() == "revert" && m_scanner->peekNextToken() == Token::Identifier)
|
|
||||||
statement = parseRevertStatement(docString);
|
|
||||||
else if (m_insideModifier && m_scanner->currentLiteral() == "_")
|
|
||||||
{
|
{
|
||||||
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
|
expression = parseExpression();
|
||||||
advance();
|
nodeFactory.setEndPositionFromNode(expression);
|
||||||
}
|
}
|
||||||
else
|
statement = nodeFactory.createNode<Return>(docString, expression);
|
||||||
statement = parseSimpleStatement(docString);
|
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
statement = parseSimpleStatement(docString);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
case Token::Throw:
|
||||||
{
|
{
|
||||||
if (
|
statement = ASTNodeFactory(*this).createNode<Throw>(docString);
|
||||||
!m_errorReporter.hasErrors() ||
|
advance();
|
||||||
!m_parserErrorRecovery ||
|
break;
|
||||||
m_errorReporter.hasExcessiveErrors()
|
|
||||||
)
|
|
||||||
BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */
|
|
||||||
m_inParserRecovery = true;
|
|
||||||
}
|
}
|
||||||
if (m_inParserRecovery)
|
case Token::Try:
|
||||||
expectTokenOrConsumeUntil(Token::Semicolon, "Statement");
|
return parseTryStatement(docString);
|
||||||
else
|
case Token::Assembly:
|
||||||
expectToken(Token::Semicolon);
|
return parseInlineAssembly(docString);
|
||||||
|
case Token::Emit:
|
||||||
|
statement = parseEmitStatement(docString);
|
||||||
|
break;
|
||||||
|
case Token::Identifier:
|
||||||
|
if (m_scanner->currentLiteral() == "revert" && m_scanner->peekNextToken() == Token::Identifier)
|
||||||
|
statement = parseRevertStatement(docString);
|
||||||
|
else if (m_insideModifier && m_scanner->currentLiteral() == "_")
|
||||||
|
{
|
||||||
|
statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString);
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
statement = parseSimpleStatement(docString);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statement = parseSimpleStatement(docString);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expectToken(Token::Semicolon);
|
||||||
return statement;
|
return statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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},
|
||||||
|
@ -1 +0,0 @@
|
|||||||
--error-recovery --ast-compact-json --pretty-json --hashes
|
|
@ -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.
|
|
||||||
| ^
|
|
@ -1 +0,0 @@
|
|||||||
1
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
--error-recovery
|
|
@ -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
|
|
||||||
| ^
|
|
@ -1 +0,0 @@
|
|||||||
1
|
|
@ -1,3 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
|
||||||
pragma 0.5.11;
|
|
||||||
c
|
|
@ -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"] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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": {}
|
|
||||||
}
|
|
@ -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()));
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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*/)
|
||||||
{
|
{
|
||||||
|
@ -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"(
|
||||||
|
@ -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() :
|
||||||
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 ';'.
|
|
@ -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 '}'.
|
|
@ -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 '}'
|
|
@ -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.
|
|
@ -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 ';'.
|
|
@ -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 ';'.
|
|
@ -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.
|
|
@ -1,3 +0,0 @@
|
|||||||
pragma solidity ^99.99.0;
|
|
||||||
// ----
|
|
||||||
// SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is ....
|
|
@ -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
|
|
@ -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 '}'
|
|
@ -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"?
|
|
@ -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:
|
||||||
|
@ -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"}},
|
||||||
|
Loading…
Reference in New Issue
Block a user