mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Produce AST even when there are parser errors
This commit is contained in:
@@ -117,7 +117,7 @@ boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string c
|
||||
|
||||
void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
if (m_stackState >= ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set remappings before parsing."));
|
||||
for (auto const& remapping: _remappings)
|
||||
solAssert(!remapping.prefix.empty(), "");
|
||||
@@ -126,14 +126,14 @@ void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
|
||||
|
||||
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
if (m_stackState >= ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set EVM version before parsing."));
|
||||
m_evmVersion = _version;
|
||||
}
|
||||
|
||||
void CompilerStack::setLibraries(std::map<std::string, h160> const& _libraries)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
if (m_stackState >= ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set libraries before parsing."));
|
||||
m_libraries = _libraries;
|
||||
}
|
||||
@@ -147,21 +147,21 @@ void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs)
|
||||
|
||||
void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
if (m_stackState >= ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing."));
|
||||
m_optimiserSettings = std::move(_settings);
|
||||
}
|
||||
|
||||
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
if (m_stackState >= ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set use literal sources before parsing."));
|
||||
m_metadataLiteralSources = _metadataLiteralSources;
|
||||
}
|
||||
|
||||
void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
if (m_stackState >= ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must add SMTLib2 responses before parsing."));
|
||||
m_smtlib2Responses[_hash] = _response;
|
||||
}
|
||||
@@ -169,6 +169,7 @@ void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _respons
|
||||
void CompilerStack::reset(bool _keepSettings)
|
||||
{
|
||||
m_stackState = Empty;
|
||||
m_hasError = false;
|
||||
m_sources.clear();
|
||||
m_smtlib2Responses.clear();
|
||||
m_unhandledSMTLib2Queries.clear();
|
||||
@@ -240,24 +241,23 @@ bool CompilerStack::parse()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Error::containsOnlyWarnings(m_errorReporter.errors()))
|
||||
{
|
||||
m_stackState = ParsingSuccessful;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
m_stackState = ParsingPerformed;
|
||||
if (!Error::containsOnlyWarnings(m_errorReporter.errors()))
|
||||
m_hasError = true;
|
||||
return !m_hasError;
|
||||
}
|
||||
|
||||
bool CompilerStack::analyze()
|
||||
{
|
||||
if (m_stackState != ParsingSuccessful || m_stackState >= AnalysisSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was successful."));
|
||||
if (m_stackState != ParsingPerformed || m_stackState >= AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed."));
|
||||
resolveImports();
|
||||
|
||||
bool noErrors = true;
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser);
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (!syntaxChecker.checkSyntax(*source->ast))
|
||||
@@ -377,25 +377,26 @@ bool CompilerStack::analyze()
|
||||
m_unhandledSMTLib2Queries += modelChecker.unhandledQueries();
|
||||
}
|
||||
}
|
||||
catch(FatalError const&)
|
||||
catch (FatalError const&)
|
||||
{
|
||||
if (m_errorReporter.errors().empty())
|
||||
throw; // Something is weird here, rather throw again.
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
if (noErrors)
|
||||
{
|
||||
m_stackState = AnalysisSuccessful;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
m_stackState = AnalysisPerformed;
|
||||
if (!noErrors)
|
||||
m_hasError = true;
|
||||
|
||||
return !m_hasError;
|
||||
}
|
||||
|
||||
bool CompilerStack::parseAndAnalyze()
|
||||
{
|
||||
return parse() && analyze();
|
||||
bool success = parse();
|
||||
if (success || m_parserErrorRecovery)
|
||||
success = analyze();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool CompilerStack::isRequestedSource(string const& _sourceName) const
|
||||
@@ -425,10 +426,13 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con
|
||||
|
||||
bool CompilerStack::compile()
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
if (!parseAndAnalyze())
|
||||
return false;
|
||||
|
||||
if (m_hasError)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
||||
|
||||
// Only compile contracts individually which have been requested.
|
||||
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
||||
for (Source const* source: m_sourceOrder)
|
||||
@@ -459,7 +463,7 @@ void CompilerStack::link()
|
||||
|
||||
vector<string> CompilerStack::contractNames() const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
vector<string> contractNames;
|
||||
for (auto const& contract: m_contracts)
|
||||
@@ -469,7 +473,7 @@ vector<string> CompilerStack::contractNames() const
|
||||
|
||||
string const CompilerStack::lastContractName() const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
// try to find some user-supplied contract
|
||||
string contractName;
|
||||
@@ -528,7 +532,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
|
||||
|
||||
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
|
||||
|
||||
// Look up the contract (by its fully-qualified name)
|
||||
@@ -635,7 +639,7 @@ map<string, unsigned> CompilerStack::sourceIndices() const
|
||||
|
||||
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return contractABI(contract(_contractName));
|
||||
@@ -643,7 +647,7 @@ Json::Value const& CompilerStack::contractABI(string const& _contractName) const
|
||||
|
||||
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
@@ -657,7 +661,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
|
||||
|
||||
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return natspecUser(contract(_contractName));
|
||||
@@ -665,7 +669,7 @@ Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
|
||||
|
||||
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
@@ -679,7 +683,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
|
||||
|
||||
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return natspecDev(contract(_contractName));
|
||||
@@ -687,7 +691,7 @@ Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
|
||||
|
||||
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
@@ -701,7 +705,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
||||
|
||||
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
Json::Value methodIdentifiers(Json::objectValue);
|
||||
@@ -712,7 +716,7 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
|
||||
|
||||
string const& CompilerStack::metadata(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return metadata(contract(_contractName));
|
||||
@@ -720,7 +724,7 @@ string const& CompilerStack::metadata(string const& _contractName) const
|
||||
|
||||
string const& CompilerStack::metadata(Contract const& _contract) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
solAssert(_contract.contract, "");
|
||||
@@ -742,7 +746,9 @@ Scanner const& CompilerStack::scanner(string const& _sourceName) const
|
||||
|
||||
SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
||||
{
|
||||
if (m_stackState < ParsingSuccessful)
|
||||
if (m_stackState < ParsingPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing not yet performed."));
|
||||
if (!source(_sourceName).ast && !m_parserErrorRecovery)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||
|
||||
return *source(_sourceName).ast;
|
||||
@@ -750,7 +756,7 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
||||
|
||||
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
|
||||
{
|
||||
if (m_stackState < AnalysisSuccessful)
|
||||
if (m_stackState < AnalysisPerformed)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||
|
||||
return *contract(_contractName).contract;
|
||||
@@ -814,7 +820,7 @@ string const& CompilerStack::Source::ipfsUrl() const
|
||||
|
||||
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
|
||||
{
|
||||
solAssert(m_stackState < ParsingSuccessful, "");
|
||||
solAssert(m_stackState < ParsingPerformed, "");
|
||||
StringMap newSources;
|
||||
for (auto const& node: _ast.nodes())
|
||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||
@@ -850,7 +856,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
|
||||
|
||||
string CompilerStack::applyRemapping(string const& _path, string const& _context)
|
||||
{
|
||||
solAssert(m_stackState < ParsingSuccessful, "");
|
||||
solAssert(m_stackState < ParsingPerformed, "");
|
||||
// Try to find the longest prefix match in all remappings that are active in the current context.
|
||||
auto isPrefixOf = [](string const& _a, string const& _b)
|
||||
{
|
||||
@@ -892,7 +898,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
|
||||
|
||||
void CompilerStack::resolveImports()
|
||||
{
|
||||
solAssert(m_stackState == ParsingSuccessful, "");
|
||||
solAssert(m_stackState == ParsingPerformed, "");
|
||||
|
||||
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
||||
vector<Source const*> sourceOrder;
|
||||
@@ -903,15 +909,16 @@ void CompilerStack::resolveImports()
|
||||
if (sourcesSeen.count(_source))
|
||||
return;
|
||||
sourcesSeen.insert(_source);
|
||||
for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
|
||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||
{
|
||||
string const& path = import->annotation().absolutePath;
|
||||
solAssert(!path.empty(), "");
|
||||
solAssert(m_sources.count(path), "");
|
||||
import->annotation().sourceUnit = m_sources[path].ast.get();
|
||||
toposort(&m_sources[path]);
|
||||
}
|
||||
if (_source->ast)
|
||||
for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
|
||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||
{
|
||||
string const& path = import->annotation().absolutePath;
|
||||
solAssert(!path.empty(), "");
|
||||
solAssert(m_sources.count(path), "");
|
||||
import->annotation().sourceUnit = m_sources[path].ast.get();
|
||||
toposort(&m_sources[path]);
|
||||
}
|
||||
sourceOrder.push_back(_source);
|
||||
};
|
||||
|
||||
@@ -938,7 +945,9 @@ void CompilerStack::compileContract(
|
||||
map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
|
||||
)
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||
if (m_hasError)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
||||
|
||||
if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed())
|
||||
return;
|
||||
@@ -990,7 +999,9 @@ void CompilerStack::compileContract(
|
||||
|
||||
void CompilerStack::generateIR(ContractDefinition const& _contract)
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||
if (m_hasError)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateIR with errors."));
|
||||
|
||||
if (!_contract.canBeDeployed())
|
||||
return;
|
||||
@@ -1008,7 +1019,10 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
|
||||
|
||||
void CompilerStack::generateEWasm(ContractDefinition const& _contract)
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||
if (m_hasError)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEWasm with errors."));
|
||||
|
||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||
solAssert(!compiledContract.yulIROptimized.empty(), "");
|
||||
if (!compiledContract.eWasm.empty())
|
||||
@@ -1038,7 +1052,7 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract)
|
||||
|
||||
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
||||
{
|
||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
||||
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||
|
||||
auto it = m_contracts.find(_contractName);
|
||||
if (it != m_contracts.end())
|
||||
|
||||
@@ -78,6 +78,8 @@ class DeclarationContainer;
|
||||
* 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.
|
||||
* 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: boost::noncopyable
|
||||
{
|
||||
@@ -85,8 +87,8 @@ public:
|
||||
enum State {
|
||||
Empty,
|
||||
SourcesSet,
|
||||
ParsingSuccessful,
|
||||
AnalysisSuccessful,
|
||||
ParsingPerformed,
|
||||
AnalysisPerformed,
|
||||
CompilationSuccessful
|
||||
};
|
||||
|
||||
@@ -110,6 +112,10 @@ public:
|
||||
/// @returns the current state.
|
||||
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,
|
||||
/// all settings are reset as well.
|
||||
void reset(bool _keepSettings = false);
|
||||
@@ -414,6 +420,9 @@ private:
|
||||
bool m_metadataLiteralSources = false;
|
||||
bool m_parserErrorRecovery = false;
|
||||
State m_stackState = Empty;
|
||||
/// 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;
|
||||
bool m_release = VersionIsRelease;
|
||||
};
|
||||
|
||||
|
||||
@@ -844,11 +844,14 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
));
|
||||
}
|
||||
|
||||
bool const analysisSuccess = compilerStack.state() >= CompilerStack::State::AnalysisSuccessful;
|
||||
bool analysisPerformed = compilerStack.state() >= CompilerStack::State::AnalysisPerformed;
|
||||
bool const compilationSuccess = compilerStack.state() == CompilerStack::State::CompilationSuccessful;
|
||||
|
||||
if (compilerStack.hasError() && !_inputsAndSettings.parserErrorRecovery)
|
||||
analysisPerformed = false;
|
||||
|
||||
/// Inconsistent state - stop here to receive error reports from users
|
||||
if (((binariesRequested && !compilationSuccess) || !analysisSuccess) && errors.empty())
|
||||
if (((binariesRequested && !compilationSuccess) || !analysisPerformed) && errors.empty())
|
||||
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
|
||||
|
||||
Json::Value output = Json::objectValue;
|
||||
@@ -864,7 +867,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
|
||||
output["sources"] = Json::objectValue;
|
||||
unsigned sourceIndex = 0;
|
||||
for (string const& sourceName: analysisSuccess ? compilerStack.sourceNames() : vector<string>())
|
||||
for (string const& sourceName: analysisPerformed ? compilerStack.sourceNames() : vector<string>())
|
||||
{
|
||||
Json::Value sourceResult = Json::objectValue;
|
||||
sourceResult["id"] = sourceIndex++;
|
||||
@@ -876,7 +879,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
||||
}
|
||||
|
||||
Json::Value contractsOutput = Json::objectValue;
|
||||
for (string const& contractName: analysisSuccess ? compilerStack.contractNames() : vector<string>())
|
||||
for (string const& contractName: analysisPerformed ? compilerStack.contractNames() : vector<string>())
|
||||
{
|
||||
size_t colon = contractName.rfind(':');
|
||||
solAssert(colon != string::npos, "");
|
||||
|
||||
Reference in New Issue
Block a user