Merge pull request #14329 from ethereum/remove-ast-output-in-error-recovery-mode

Remove AST output in error recovery mode
This commit is contained in:
Daniel 2023-08-21 18:38:57 +02:00 committed by GitHub
commit 664baa9e6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 185 additions and 398 deletions

View File

@ -87,15 +87,19 @@ public:
static void listAccept(std::vector<T> const& _list, ASTVisitor& _visitor) static void listAccept(std::vector<T> const& _list, ASTVisitor& _visitor)
{ {
for (T const& element: _list) for (T const& element: _list)
if (element) {
element->accept(_visitor); solAssert(element);
element->accept(_visitor);
}
} }
template <class T> template <class T>
static void listAccept(std::vector<T> const& _list, ASTConstVisitor& _visitor) static void listAccept(std::vector<T> const& _list, ASTConstVisitor& _visitor)
{ {
for (T const& element: _list) for (T const& element: _list)
if (element) {
element->accept(_visitor); solAssert(element);
element->accept(_visitor);
}
} }
/// @returns a copy of the vector containing only the nodes which derive from T. /// @returns a copy of the vector containing only the nodes which derive from T.

View File

@ -560,7 +560,7 @@ bool ASTJsonExporter::visit(EventDefinition const& _node)
std::make_pair("parameters", toJson(_node.parameterList())), std::make_pair("parameters", toJson(_node.parameterList())),
std::make_pair("anonymous", _node.isAnonymous()) std::make_pair("anonymous", _node.isAnonymous())
}; };
if (m_stackState >= CompilerStack::State::AnalysisPerformed) if (m_stackState >= CompilerStack::State::AnalysisSuccessful)
_attributes.emplace_back( _attributes.emplace_back(
std::make_pair( std::make_pair(
"eventSelector", "eventSelector",
@ -579,7 +579,7 @@ bool ASTJsonExporter::visit(ErrorDefinition const& _node)
std::make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), std::make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
std::make_pair("parameters", toJson(_node.parameterList())) std::make_pair("parameters", toJson(_node.parameterList()))
}; };
if (m_stackState >= CompilerStack::State::AnalysisPerformed) if (m_stackState >= CompilerStack::State::AnalysisSuccessful)
_attributes.emplace_back(std::make_pair("errorSelector", _node.functionType(true)->externalIdentifierHex())); _attributes.emplace_back(std::make_pair("errorSelector", _node.functionType(true)->externalIdentifierHex()));
setJsonNode(_node, "ErrorDefinition", std::move(_attributes)); setJsonNode(_node, "ErrorDefinition", std::move(_attributes));

View File

@ -307,7 +307,6 @@ void CompilerStack::addSMTLib2Response(h256 const& _hash, std::string const& _re
void CompilerStack::reset(bool _keepSettings) void CompilerStack::reset(bool _keepSettings)
{ {
m_stackState = Empty; m_stackState = Empty;
m_hasError = false;
m_sources.clear(); m_sources.clear();
m_smtlib2Responses.clear(); m_smtlib2Responses.clear();
m_unhandledSMTLib2Queries.clear(); m_unhandledSMTLib2Queries.clear();
@ -403,16 +402,12 @@ bool CompilerStack::parse()
} }
} }
if (m_stopAfter <= Parsed)
m_stackState = Parsed;
else
m_stackState = ParsedAndImported;
if (Error::containsErrors(m_errorReporter.errors())) if (Error::containsErrors(m_errorReporter.errors()))
m_hasError = true; return false;
m_stackState = (m_stopAfter <= Parsed ? Parsed : ParsedAndImported);
storeContractDefinitions(); storeContractDefinitions();
return true;
return !m_hasError;
} }
void CompilerStack::importASTs(std::map<std::string, Json::Value> const& _sources) void CompilerStack::importASTs(std::map<std::string, Json::Value> const& _sources)
@ -440,8 +435,8 @@ void CompilerStack::importASTs(std::map<std::string, Json::Value> const& _source
bool CompilerStack::analyze() bool CompilerStack::analyze()
{ {
if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed) if (m_stackState != ParsedAndImported)
solThrow(CompilerError, "Must call analyze only after parsing was performed."); solThrow(CompilerError, "Must call analyze only after parsing was successful.");
if (!resolveImports()) if (!resolveImports())
return false; return false;
@ -628,11 +623,11 @@ bool CompilerStack::analyze()
noErrors = false; noErrors = false;
} }
m_stackState = AnalysisPerformed;
if (!noErrors) if (!noErrors)
m_hasError = true; return false;
return !m_hasError; m_stackState = AnalysisSuccessful;
return true;
} }
bool CompilerStack::parseAndAnalyze(State _stopAfter) bool CompilerStack::parseAndAnalyze(State _stopAfter)
@ -642,7 +637,7 @@ bool CompilerStack::parseAndAnalyze(State _stopAfter)
bool success = parse(); bool success = parse();
if (m_stackState >= m_stopAfter) if (m_stackState >= m_stopAfter)
return success; return success;
if (success || m_parserErrorRecovery) if (success)
success = analyze(); success = analyze();
return success; return success;
} }
@ -675,16 +670,13 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con
bool CompilerStack::compile(State _stopAfter) bool CompilerStack::compile(State _stopAfter)
{ {
m_stopAfter = _stopAfter; m_stopAfter = _stopAfter;
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
if (!parseAndAnalyze(_stopAfter)) if (!parseAndAnalyze(_stopAfter))
return false; return false;
if (m_stackState >= m_stopAfter) if (m_stackState >= m_stopAfter)
return true; return true;
if (m_hasError)
solThrow(CompilerError, "Called compile with errors.");
// Only compile contracts individually which have been requested. // Only compile contracts individually which have been requested.
std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> otherCompilers; std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> otherCompilers;
@ -763,7 +755,7 @@ std::vector<std::string> CompilerStack::contractNames() const
std::string const CompilerStack::lastContractName(std::optional<std::string> const& _sourceName) const std::string const CompilerStack::lastContractName(std::optional<std::string> const& _sourceName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Parsing was not successful."); solThrow(CompilerError, "Parsing was not successful.");
// try to find some user-supplied contract // try to find some user-supplied contract
std::string contractName; std::string contractName;
@ -866,7 +858,7 @@ std::string const* CompilerStack::runtimeSourceMapping(std::string const& _contr
std::string const CompilerStack::filesystemFriendlyName(std::string const& _contractName) const std::string const CompilerStack::filesystemFriendlyName(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "No compiled contracts found."); solThrow(CompilerError, "No compiled contracts found.");
// Look up the contract (by its fully-qualified name) // Look up the contract (by its fully-qualified name)
@ -980,7 +972,7 @@ std::map<std::string, unsigned> CompilerStack::sourceIndices() const
Json::Value const& CompilerStack::contractABI(std::string const& _contractName) const Json::Value const& CompilerStack::contractABI(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
return contractABI(contract(_contractName)); return contractABI(contract(_contractName));
@ -988,7 +980,7 @@ Json::Value const& CompilerStack::contractABI(std::string const& _contractName)
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, ""); solAssert(_contract.contract, "");
@ -998,7 +990,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
Json::Value const& CompilerStack::storageLayout(std::string const& _contractName) const Json::Value const& CompilerStack::storageLayout(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
return storageLayout(contract(_contractName)); return storageLayout(contract(_contractName));
@ -1006,7 +998,7 @@ Json::Value const& CompilerStack::storageLayout(std::string const& _contractName
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, ""); solAssert(_contract.contract, "");
@ -1016,7 +1008,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
Json::Value const& CompilerStack::natspecUser(std::string const& _contractName) const Json::Value const& CompilerStack::natspecUser(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
return natspecUser(contract(_contractName)); return natspecUser(contract(_contractName));
@ -1024,7 +1016,7 @@ Json::Value const& CompilerStack::natspecUser(std::string const& _contractName)
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, ""); solAssert(_contract.contract, "");
@ -1034,7 +1026,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
Json::Value const& CompilerStack::natspecDev(std::string const& _contractName) const Json::Value const& CompilerStack::natspecDev(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
return natspecDev(contract(_contractName)); return natspecDev(contract(_contractName));
@ -1042,7 +1034,7 @@ Json::Value const& CompilerStack::natspecDev(std::string const& _contractName) c
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, ""); solAssert(_contract.contract, "");
@ -1052,7 +1044,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
Json::Value CompilerStack::interfaceSymbols(std::string const& _contractName) const Json::Value CompilerStack::interfaceSymbols(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
Json::Value interfaceSymbols(Json::objectValue); Json::Value interfaceSymbols(Json::objectValue);
@ -1082,7 +1074,7 @@ Json::Value CompilerStack::interfaceSymbols(std::string const& _contractName) co
bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR) const bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
return createCBORMetadata(contract(_contractName), _forIR); return createCBORMetadata(contract(_contractName), _forIR);
@ -1090,7 +1082,7 @@ bytes CompilerStack::cborMetadata(std::string const& _contractName, bool _forIR)
std::string const& CompilerStack::metadata(Contract const& _contract) const std::string const& CompilerStack::metadata(Contract const& _contract) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
solAssert(_contract.contract, ""); solAssert(_contract.contract, "");
@ -1120,7 +1112,7 @@ SourceUnit const& CompilerStack::ast(std::string const& _sourceName) const
ContractDefinition const& CompilerStack::contractDefinition(std::string const& _contractName) const ContractDefinition const& CompilerStack::contractDefinition(std::string const& _contractName) const
{ {
if (m_stackState < AnalysisPerformed) if (m_stackState < AnalysisSuccessful)
solThrow(CompilerError, "Analysis was not successful."); solThrow(CompilerError, "Analysis was not successful.");
return *contract(_contractName).contract; return *contract(_contractName).contract;
@ -1219,15 +1211,15 @@ bool CompilerStack::resolveImports()
if (sourcesSeen.count(_source)) if (sourcesSeen.count(_source))
return; return;
sourcesSeen.insert(_source); sourcesSeen.insert(_source);
if (_source->ast) solAssert(_source->ast);
for (ASTPointer<ASTNode> const& node: _source->ast->nodes()) for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{ {
std::string const& path = *import->annotation().absolutePath; std::string const& path = *import->annotation().absolutePath;
solAssert(m_sources.count(path), ""); solAssert(m_sources.count(path), "");
import->annotation().sourceUnit = m_sources[path].ast.get(); import->annotation().sourceUnit = m_sources[path].ast.get();
toposort(&m_sources[path]); toposort(&m_sources[path]);
} }
sourceOrder.push_back(_source); sourceOrder.push_back(_source);
}; };
@ -1326,8 +1318,7 @@ void CompilerStack::assembleYul(
std::shared_ptr<evmasm::Assembly> _runtimeAssembly std::shared_ptr<evmasm::Assembly> _runtimeAssembly
) )
{ {
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisSuccessful, "");
solAssert(!m_hasError, "");
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
@ -1400,9 +1391,7 @@ void CompilerStack::compileContract(
{ {
solAssert(!m_viaIR, ""); solAssert(!m_viaIR, "");
solUnimplementedAssert(!m_eofVersion.has_value(), "Experimental EOF support is only available for via-IR compilation."); solUnimplementedAssert(!m_eofVersion.has_value(), "Experimental EOF support is only available for via-IR compilation.");
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisSuccessful, "");
if (m_hasError)
solThrow(CompilerError, "Called compile with errors.");
if (_otherCompilers.count(&_contract)) if (_otherCompilers.count(&_contract))
return; return;
@ -1438,9 +1427,7 @@ void CompilerStack::compileContract(
void CompilerStack::generateIR(ContractDefinition const& _contract) void CompilerStack::generateIR(ContractDefinition const& _contract)
{ {
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisSuccessful, "");
if (m_hasError)
solThrow(CompilerError, "Called generateIR with errors.");
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
if (!compiledContract.yulIR.empty()) if (!compiledContract.yulIR.empty())
@ -1502,9 +1489,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
{ {
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisSuccessful, "");
if (m_hasError)
solThrow(CompilerError, "Called generateEVMFromIR with errors.");
if (!_contract.canBeDeployed()) if (!_contract.canBeDeployed())
return; return;
@ -1535,7 +1520,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
CompilerStack::Contract const& CompilerStack::contract(std::string const& _contractName) const CompilerStack::Contract const& CompilerStack::contract(std::string const& _contractName) const
{ {
solAssert(m_stackState >= AnalysisPerformed, ""); solAssert(m_stackState >= AnalysisSuccessful, "");
auto it = m_contracts.find(_contractName); auto it = m_contracts.find(_contractName);
if (it != m_contracts.end()) if (it != m_contracts.end())

View File

@ -86,8 +86,6 @@ class DeclarationContainer;
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible. * Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
* It holds state and can be used to either step through the compilation stages (and abort e.g. * It holds state and can be used to either step through the compilation stages (and abort e.g.
* before compilation to bytecode) or run the whole compilation in one call. * before compilation to bytecode) or run the whole compilation in one call.
* If error recovery is active, it is possible to progress through the stages even when
* there are errors. In any case, producing code is only possible without errors.
*/ */
class CompilerStack: public langutil::CharStreamProvider class CompilerStack: public langutil::CharStreamProvider
{ {
@ -101,7 +99,7 @@ public:
SourcesSet, SourcesSet,
Parsed, Parsed,
ParsedAndImported, ParsedAndImported,
AnalysisPerformed, AnalysisSuccessful,
CompilationSuccessful CompilationSuccessful
}; };
@ -137,10 +135,6 @@ public:
/// @returns the current state. /// @returns the current state.
State state() const { return m_stackState; } State state() const { return m_stackState; }
bool hasError() const { return m_hasError; }
bool compilationSuccessful() const { return m_stackState >= CompilationSuccessful; }
/// Resets the compiler to an empty state. Unless @a _keepSettings is set to true, /// Resets the compiler to an empty state. Unless @a _keepSettings is set to true,
/// all settings are reset as well. /// all settings are reset as well.
void reset(bool _keepSettings = false); void reset(bool _keepSettings = false);
@ -520,9 +514,6 @@ private:
bool m_parserErrorRecovery = false; bool m_parserErrorRecovery = false;
State m_stackState = Empty; State m_stackState = Empty;
CompilationSourceType m_compilationSourceType = CompilationSourceType::Solidity; CompilationSourceType m_compilationSourceType = CompilationSourceType::Solidity;
/// Whether or not there has been an error during processing.
/// If this is true, the stack will refuse to generate code.
bool m_hasError = false;
MetadataFormat m_metadataFormat = defaultMetadataFormat(); MetadataFormat m_metadataFormat = defaultMetadataFormat();
}; };

View File

@ -1317,21 +1317,18 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
} }
bool parsingSuccess = compilerStack.state() >= CompilerStack::State::Parsed; bool parsingSuccess = compilerStack.state() >= CompilerStack::State::Parsed;
bool analysisPerformed = compilerStack.state() >= CompilerStack::State::AnalysisPerformed; bool analysisSuccess = compilerStack.state() >= CompilerStack::State::AnalysisSuccessful;
bool compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful; bool compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful;
if (compilerStack.hasError() && !_inputsAndSettings.parserErrorRecovery)
analysisPerformed = false;
// If analysis fails, the artifacts inside CompilerStack are potentially incomplete and must not be returned. // If analysis fails, the artifacts inside CompilerStack are potentially incomplete and must not be returned.
// Note that not completing analysis due to stopAfter does not count as a failure. It's neither failure nor success. // Note that not completing analysis due to stopAfter does not count as a failure. It's neither failure nor success.
bool analysisFailed = !analysisPerformed && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed; bool analysisFailed = !analysisSuccess && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisSuccessful;
bool compilationFailed = !compilationSuccess && binariesRequested; bool compilationFailed = !compilationSuccess && binariesRequested;
/// Inconsistent state - stop here to receive error reports from users /// Inconsistent state - stop here to receive error reports from users
if ( if (
(compilationFailed || !analysisPerformed) && (compilationFailed || analysisFailed || !parsingSuccess) &&
(errors.empty() && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed) errors.empty()
) )
return formatFatalError(Error::Type::InternalCompilerError, "No error reported, but compilation failed."); return formatFatalError(Error::Type::InternalCompilerError, "No error reported, but compilation failed.");
@ -1348,7 +1345,9 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
output["sources"] = Json::objectValue; output["sources"] = Json::objectValue;
unsigned sourceIndex = 0; unsigned sourceIndex = 0;
if (parsingSuccess && !analysisFailed && (!compilerStack.hasError() || _inputsAndSettings.parserErrorRecovery)) // NOTE: A case that will pass `parsingSuccess && !analysisFailed` but not `analysisSuccess` is
// stopAfter: parsing with no parsing errors.
if (parsingSuccess && !analysisFailed)
for (std::string const& sourceName: compilerStack.sourceNames()) for (std::string const& sourceName: compilerStack.sourceNames())
{ {
Json::Value sourceResult = Json::objectValue; Json::Value sourceResult = Json::objectValue;
@ -1359,7 +1358,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
} }
Json::Value contractsOutput = Json::objectValue; Json::Value contractsOutput = Json::objectValue;
for (std::string const& contractName: analysisPerformed ? compilerStack.contractNames() : std::vector<std::string>()) for (std::string const& contractName: analysisSuccess ? compilerStack.contractNames() : std::vector<std::string>())
{ {
size_t colon = contractName.rfind(':'); size_t colon = contractName.rfind(':');
solAssert(colon != std::string::npos, ""); solAssert(colon != std::string::npos, "");

View File

@ -265,7 +265,7 @@ void LanguageServer::compile()
m_compilerStack.reset(false); m_compilerStack.reset(false);
m_compilerStack.setSources(m_fileRepository.sourceUnits()); m_compilerStack.setSources(m_fileRepository.sourceUnits());
m_compilerStack.compile(CompilerStack::State::AnalysisPerformed); m_compilerStack.compile(CompilerStack::State::AnalysisSuccessful);
} }
void LanguageServer::compileAndUpdateDiagnostics() void LanguageServer::compileAndUpdateDiagnostics()
@ -554,7 +554,7 @@ ASTNode const* LanguageServer::astNodeAtSourceLocation(std::string const& _sourc
std::tuple<ASTNode const*, int> LanguageServer::astNodeAndOffsetAtSourceLocation(std::string const& _sourceUnitName, LineColumn const& _filePos) std::tuple<ASTNode const*, int> LanguageServer::astNodeAndOffsetAtSourceLocation(std::string const& _sourceUnitName, LineColumn const& _filePos)
{ {
if (m_compilerStack.state() < CompilerStack::AnalysisPerformed) if (m_compilerStack.state() < CompilerStack::AnalysisSuccessful)
return {nullptr, -1}; return {nullptr, -1};
if (!m_fileRepository.sourceUnits().count(_sourceUnitName)) if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
return {nullptr, -1}; return {nullptr, -1};

View File

@ -41,7 +41,7 @@ REPO_ROOT="$(dirname "$0")"/..
cd "$REPO_ROOT" || exit 1 cd "$REPO_ROOT" || exit 1
WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" |
grep -v "test/libsolidity/ASTJSON\|test/libsolidity/ASTRecoveryTests\|test/compilationTests/zeppelin/LICENSE\|${EXCLUDE_FILES_JOINED}" || true grep -v "test/libsolidity/ASTJSON\|test/compilationTests/zeppelin/LICENSE\|${EXCLUDE_FILES_JOINED}" || true
) )
if [[ "$WHITESPACE" != "" ]] if [[ "$WHITESPACE" != "" ]]

View File

@ -805,7 +805,7 @@ void CommandLineInterface::compile()
formatter.printErrorInformation(*error); formatter.printErrorInformation(*error);
} }
if (!successful && !m_options.input.errorRecovery) if (!successful)
solThrow(CommandLineExecutionError, ""); solThrow(CommandLineExecutionError, "");
} }
catch (CompilerError const& _exception) catch (CompilerError const& _exception)
@ -845,52 +845,56 @@ void CommandLineInterface::handleCombinedJSON()
output[g_strVersion] = frontend::VersionString; output[g_strVersion] = frontend::VersionString;
vector<string> contracts = m_compiler->contractNames(); vector<string> contracts = m_compiler->contractNames();
// NOTE: The state checks here are more strict that in Standard JSON. There we allow
// requesting certain outputs even if compilation fails as long as analysis went ok.
bool compilationSuccess = m_compiler->state() >= CompilerStack::State::CompilationSuccessful;
if (!contracts.empty()) if (!contracts.empty())
output[g_strContracts] = Json::Value(Json::objectValue); output[g_strContracts] = Json::Value(Json::objectValue);
for (string const& contractName: contracts) for (string const& contractName: contracts)
{ {
Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue;
if (m_options.compiler.combinedJsonRequests->abi) if (m_options.compiler.combinedJsonRequests->abi && compilationSuccess)
contractData[g_strAbi] = m_compiler->contractABI(contractName); contractData[g_strAbi] = m_compiler->contractABI(contractName);
if (m_options.compiler.combinedJsonRequests->metadata) if (m_options.compiler.combinedJsonRequests->metadata && compilationSuccess)
contractData["metadata"] = m_compiler->metadata(contractName); contractData["metadata"] = m_compiler->metadata(contractName);
if (m_options.compiler.combinedJsonRequests->binary && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->binary && compilationSuccess)
contractData[g_strBinary] = m_compiler->object(contractName).toHex(); contractData[g_strBinary] = m_compiler->object(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->binaryRuntime && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->binaryRuntime && compilationSuccess)
contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
if (m_options.compiler.combinedJsonRequests->opcodes && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->opcodes && compilationSuccess)
contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode, m_options.output.evmVersion); contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode, m_options.output.evmVersion);
if (m_options.compiler.combinedJsonRequests->asm_ && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->asm_ && compilationSuccess)
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); contractData[g_strAsm] = m_compiler->assemblyJSON(contractName);
if (m_options.compiler.combinedJsonRequests->storageLayout && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->storageLayout && compilationSuccess)
contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName);
if (m_options.compiler.combinedJsonRequests->generatedSources && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->generatedSources && compilationSuccess)
contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false);
if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->generatedSourcesRuntime && compilationSuccess)
contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true);
if (m_options.compiler.combinedJsonRequests->srcMap && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->srcMap && compilationSuccess)
{ {
auto map = m_compiler->sourceMapping(contractName); auto map = m_compiler->sourceMapping(contractName);
contractData[g_strSrcMap] = map ? *map : ""; contractData[g_strSrcMap] = map ? *map : "";
} }
if (m_options.compiler.combinedJsonRequests->srcMapRuntime && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->srcMapRuntime && compilationSuccess)
{ {
auto map = m_compiler->runtimeSourceMapping(contractName); auto map = m_compiler->runtimeSourceMapping(contractName);
contractData[g_strSrcMapRuntime] = map ? *map : ""; contractData[g_strSrcMapRuntime] = map ? *map : "";
} }
if (m_options.compiler.combinedJsonRequests->funDebug && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->funDebug && compilationSuccess)
contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData(
m_compiler->object(contractName).functionDebugData m_compiler->object(contractName).functionDebugData
); );
if (m_options.compiler.combinedJsonRequests->funDebugRuntime && m_compiler->compilationSuccessful()) if (m_options.compiler.combinedJsonRequests->funDebugRuntime && compilationSuccess)
contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData(
m_compiler->runtimeObject(contractName).functionDebugData m_compiler->runtimeObject(contractName).functionDebugData
); );
if (m_options.compiler.combinedJsonRequests->signatureHashes) if (m_options.compiler.combinedJsonRequests->signatureHashes && compilationSuccess)
contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"]; contractData[g_strSignatureHashes] = m_compiler->interfaceSymbols(contractName)["methods"];
if (m_options.compiler.combinedJsonRequests->natspecDev) if (m_options.compiler.combinedJsonRequests->natspecDev && compilationSuccess)
contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName);
if (m_options.compiler.combinedJsonRequests->natspecUser) if (m_options.compiler.combinedJsonRequests->natspecUser && compilationSuccess)
contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName);
} }
@ -1164,57 +1168,56 @@ void CommandLineInterface::outputCompilationResults()
// do we need AST output? // do we need AST output?
handleAst(); handleAst();
if ( CompilerOutputs astOutputSelection;
!m_compiler->compilationSuccessful() && astOutputSelection.astCompactJson = true;
m_options.output.stopAfter == CompilerStack::State::CompilationSuccessful if (m_options.compiler.outputs != CompilerOutputs() && m_options.compiler.outputs != astOutputSelection)
)
{ {
serr() << endl << "Compilation halted after AST generation due to errors." << endl; // Currently AST is the only output allowed with --stop-after parsing. For all of the others
return; // we can safely assume that full compilation was performed and successful.
} solAssert(m_options.output.stopAfter >= CompilerStack::State::CompilationSuccessful);
vector<string> contracts = m_compiler->contractNames(); for (string const& contract: m_compiler->contractNames())
for (string const& contract: contracts)
{
if (needsHumanTargetedStdout(m_options))
sout() << endl << "======= " << contract << " =======" << endl;
// do we need EVM assembly?
if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
{ {
string ret; if (needsHumanTargetedStdout(m_options))
if (m_options.compiler.outputs.asmJson) sout() << endl << "======= " << contract << " =======" << endl;
ret = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(contract)), m_options.formatting.json);
else
ret = m_compiler->assemblyString(contract, m_fileReader.sourceUnits());
if (!m_options.output.dir.empty()) // do we need EVM assembly?
createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret); if (m_options.compiler.outputs.asm_ || m_options.compiler.outputs.asmJson)
else {
sout() << "EVM assembly:" << endl << ret << endl; string ret;
} if (m_options.compiler.outputs.asmJson)
ret = util::jsonPrint(removeNullMembers(m_compiler->assemblyJSON(contract)), m_options.formatting.json);
else
ret = m_compiler->assemblyString(contract, m_fileReader.sourceUnits());
if (m_options.compiler.estimateGas) if (!m_options.output.dir.empty())
handleGasEstimation(contract); createFile(m_compiler->filesystemFriendlyName(contract) + (m_options.compiler.outputs.asmJson ? "_evm.json" : ".evm"), ret);
else
sout() << "EVM assembly:" << endl << ret << endl;
}
handleBytecode(contract); if (m_options.compiler.estimateGas)
handleIR(contract); handleGasEstimation(contract);
handleIRAst(contract);
handleIROptimized(contract); handleBytecode(contract);
handleIROptimizedAst(contract); handleIR(contract);
handleSignatureHashes(contract); handleIRAst(contract);
handleMetadata(contract); handleIROptimized(contract);
handleABI(contract); handleIROptimizedAst(contract);
handleStorageLayout(contract); handleSignatureHashes(contract);
handleNatspec(true, contract); handleMetadata(contract);
handleNatspec(false, contract); handleABI(contract);
} // end of contracts iteration handleStorageLayout(contract);
handleNatspec(true, contract);
handleNatspec(false, contract);
} // end of contracts iteration
}
if (!m_hasOutput) if (!m_hasOutput)
{ {
if (!m_options.output.dir.empty()) if (!m_options.output.dir.empty())
sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl; sout() << "Compiler run successful. Artifact(s) can be found in directory " << m_options.output.dir << "." << endl;
else if (contracts.empty()) else if (m_compiler->contractNames().empty())
sout() << "Compiler run successful. No contracts to compile." << endl; sout() << "Compiler run successful. No contracts to compile." << endl;
else else
sout() << "Compiler run successful. No output generated." << endl; sout() << "Compiler run successful. No output generated." << endl;

View File

@ -0,0 +1 @@
--pretty-json --json-indent 4 --stop-after parsing --combined-json abi,asm,ast,bin,bin-runtime,devdoc,function-debug,function-debug-runtime,generated-sources,generated-sources-runtime,hashes,metadata,opcodes,srcmap,srcmap-runtime,storage-layout,userdoc

View File

@ -0,0 +1,4 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity *;
contract C {}

View File

@ -0,0 +1,53 @@
{
"contracts":
{
"combined_json_stop_after_parsing/input.sol:C": {}
},
"sourceList":
[
"combined_json_stop_after_parsing/input.sol"
],
"sources":
{
"combined_json_stop_after_parsing/input.sol":
{
"AST":
{
"absolutePath": "combined_json_stop_after_parsing/input.sol",
"id": 3,
"license": "GPL-3.0",
"nodeType": "SourceUnit",
"nodes":
[
{
"id": 1,
"literals":
[
"solidity",
"*"
],
"nodeType": "PragmaDirective",
"src": "36:18:0"
},
{
"abstract": false,
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"id": 2,
"name": "C",
"nameLocation": "65:1:0",
"nodeType": "ContractDefinition",
"nodes": [],
"src": "56:13:0",
"usedErrors": [],
"usedEvents": []
}
],
"src": "36:34:0"
},
"id": 0
}
},
"version": "<VERSION REMOVED>"
}

View File

@ -9,6 +9,3 @@ Warning: Recovered in Statement at ';'.
| |
6 | balances[tx.origin] = ; // missing RHS. 6 | balances[tx.origin] = ; // missing RHS.
| ^ | ^
Compilation halted after AST generation due to errors.

View File

@ -0,0 +1 @@
1

View File

@ -6,12 +6,7 @@ contract Error1 {
balances[tx.origin] = ; // missing RHS. balances[tx.origin] = ; // missing RHS.
} }
// Without error recovery we stop due to the above error. // This function parses properly
// 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) { function five() public view returns(uint) {
return 5; return 5;
} }

View File

@ -1,188 +0,0 @@
JSON AST (compact format):
======= recovery_ast_constructor/input.sol =======
{
"absolutePath": "recovery_ast_constructor/input.sol",
"exportedSymbols":
{
"Error1":
[
18
]
},
"id": 19,
"license": "GPL-3.0",
"nodeType": "SourceUnit",
"nodes":
[
{
"id": 1,
"literals":
[
"solidity",
">=",
"0.0",
".0"
],
"nodeType": "PragmaDirective",
"src": "36:24:0"
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "Error1",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 18,
"linearizedBaseContracts":
[
18
],
"name": "Error1",
"nameLocation": "71:6:0",
"nodeType": "ContractDefinition",
"nodes":
[
{
"body":
{
"id": 8,
"nodeType": "Block",
"src": "96:49:0",
"statements":
[
null
]
},
"id": 9,
"implemented": true,
"kind": "constructor",
"modifiers": [],
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 2,
"nodeType": "ParameterList",
"parameters": [],
"src": "93:2:0"
},
"returnParameters":
{
"id": 3,
"nodeType": "ParameterList",
"parameters": [],
"src": "96:0:0"
},
"scope": 18,
"src": "82:63:0",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
},
{
"body":
{
"id": 16,
"nodeType": "Block",
"src": "440:19:0",
"statements":
[
{
"expression":
{
"hexValue": "35",
"id": 14,
"isConstant": false,
"isLValue": false,
"isPure": true,
"kind": "number",
"lValueRequested": false,
"nodeType": "Literal",
"src": "453:1:0",
"typeDescriptions":
{
"typeIdentifier": "t_rational_5_by_1",
"typeString": "int_const 5"
},
"value": "5"
},
"functionReturnParameters": 13,
"id": 15,
"nodeType": "Return",
"src": "446:8:0"
}
]
},
"functionSelector": "af11c34c",
"id": 17,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "five",
"nameLocation": "407:4:0",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 10,
"nodeType": "ParameterList",
"parameters": [],
"src": "411:2:0"
},
"returnParameters":
{
"id": 13,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 12,
"mutability": "mutable",
"name": "",
"nameLocation": "-1:-1:-1",
"nodeType": "VariableDeclaration",
"scope": 17,
"src": "434:4:0",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_uint256",
"typeString": "uint256"
},
"typeName":
{
"id": 11,
"name": "uint",
"nodeType": "ElementaryTypeName",
"src": "434:4:0",
"typeDescriptions":
{
"typeIdentifier": "t_uint256",
"typeString": "uint256"
}
},
"visibility": "internal"
}
],
"src": "433:6:0"
},
"scope": 18,
"src": "398:61:0",
"stateMutability": "view",
"virtual": false,
"visibility": "public"
}
],
"scope": 19,
"src": "62:399:0",
"usedErrors": [],
"usedEvents": []
}
],
"src": "36:426:0"
}

View File

@ -3,6 +3,3 @@ Error: Expected pragma, import directive or contract/interface/library/struct/en
| |
3 | c 3 | c
| ^ | ^
Compilation halted after AST generation due to errors.

View File

@ -0,0 +1 @@
1

View File

@ -42,61 +42,5 @@
"type": "Warning" "type": "Warning"
} }
], ],
"sources": "sources": {}
{
"A":
{
"ast":
{
"absolutePath": "A",
"exportedSymbols":
{
"Errort6":
[
3
]
},
"id": 4,
"license": "GPL-3.0",
"nodeType": "SourceUnit",
"nodes":
[
{
"id": 1,
"literals":
[
"solidity",
">=",
"0.0"
],
"nodeType": "PragmaDirective",
"src": "36:22:0"
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "Errort6",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 3,
"linearizedBaseContracts":
[
3
],
"name": "Errort6",
"nameLocation": "68:7:0",
"nodeType": "ContractDefinition",
"nodes": [],
"scope": 4,
"src": "59:35:0",
"usedErrors": [],
"usedEvents": []
}
],
"src": "36:84:0"
},
"id": 0
}
}
} }

View File

@ -59,7 +59,7 @@ string compilerStateToString(CompilerStack::State _state)
case CompilerStack::State::SourcesSet: return "SourcesSet"; case CompilerStack::State::SourcesSet: return "SourcesSet";
case CompilerStack::State::Parsed: return "Parsed"; case CompilerStack::State::Parsed: return "Parsed";
case CompilerStack::State::ParsedAndImported: return "ParsedAndImported"; case CompilerStack::State::ParsedAndImported: return "ParsedAndImported";
case CompilerStack::State::AnalysisPerformed: return "AnalysisPerformed"; case CompilerStack::State::AnalysisSuccessful: return "AnalysisSuccessful";
case CompilerStack::State::CompilationSuccessful: return "CompilationSuccessful"; case CompilerStack::State::CompilationSuccessful: return "CompilationSuccessful";
} }
soltestAssert(false, "Unexpected value of state parameter"); soltestAssert(false, "Unexpected value of state parameter");
@ -102,7 +102,7 @@ void ASTJSONTest::generateTestVariants(string const& _filename)
const std::vector<CompilerStack::State> variantCompileStates = { const std::vector<CompilerStack::State> variantCompileStates = {
CompilerStack::State::Parsed, CompilerStack::State::Parsed,
CompilerStack::State::AnalysisPerformed CompilerStack::State::AnalysisSuccessful,
}; };
for (const auto state: variantCompileStates) for (const auto state: variantCompileStates)

View File

@ -102,7 +102,7 @@ EdgeNames edgeNames(EdgeMap const& _edgeMap)
tuple<CallGraphMap, CallGraphMap> collectGraphs(CompilerStack const& _compilerStack) tuple<CallGraphMap, CallGraphMap> collectGraphs(CompilerStack const& _compilerStack)
{ {
soltestAssert(!_compilerStack.hasError(), ""); soltestAssert(_compilerStack.state() >= CompilerStack::State::AnalysisSuccessful);
tuple<CallGraphMap, CallGraphMap> graphs; tuple<CallGraphMap, CallGraphMap> graphs;