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:
parent
efa2648771
commit
7fd7cc1e76
@ -14,6 +14,7 @@ Compiler Features:
|
|||||||
* Standard JSON Interface: Compile only selected sources and contracts.
|
* Standard JSON Interface: Compile only selected sources and contracts.
|
||||||
* Standard JSON Interface: Provide secondary error locations (e.g. the source position of other conflicting declarations).
|
* Standard JSON Interface: Provide secondary error locations (e.g. the source position of other conflicting declarations).
|
||||||
* SMTChecker: Do not erase knowledge about storage pointers if another storage pointer is assigned.
|
* SMTChecker: Do not erase knowledge about storage pointers if another storage pointer is assigned.
|
||||||
|
* Standard JSON Interface: Provide AST even on errors if ``--error-recovery`` commandline switch or StandardCompiler `settings.parserErrorRecovery` is true.
|
||||||
* Yul Optimizer: Do not inline function if it would result in expressions being duplicated that are not cheap.
|
* Yul Optimizer: Do not inline function if it would result in expressions being duplicated that are not cheap.
|
||||||
|
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ Important Bugfixes:
|
|||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* Commandline Interface: Experimental parser error recovery via the ``--error-recovery`` commandline switch.
|
* Commandline Interface: Experimental parser error recovery via the ``--error-recovery`` commandline switch or StandardCompiler `settings.parserErrorRecovery` boolean.
|
||||||
* Optimizer: Add rule to simplify ``SUB(~0, X)`` to ``NOT(X)``.
|
* Optimizer: Add rule to simplify ``SUB(~0, X)`` to ``NOT(X)``.
|
||||||
* Yul Optimizer: Make the optimizer work for all dialects of Yul including eWasm.
|
* Yul Optimizer: Make the optimizer work for all dialects of Yul including eWasm.
|
||||||
|
|
||||||
|
@ -79,12 +79,14 @@ 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);
|
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);
|
element->accept(_visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string c
|
|||||||
|
|
||||||
void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set remappings before parsing."));
|
||||||
for (auto const& remapping: _remappings)
|
for (auto const& remapping: _remappings)
|
||||||
solAssert(!remapping.prefix.empty(), "");
|
solAssert(!remapping.prefix.empty(), "");
|
||||||
@ -126,14 +126,14 @@ void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
|
|||||||
|
|
||||||
void CompilerStack::setEVMVersion(langutil::EVMVersion _version)
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set EVM version before parsing."));
|
||||||
m_evmVersion = _version;
|
m_evmVersion = _version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::setLibraries(std::map<std::string, h160> const& _libraries)
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set libraries before parsing."));
|
||||||
m_libraries = _libraries;
|
m_libraries = _libraries;
|
||||||
}
|
}
|
||||||
@ -147,21 +147,21 @@ void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs)
|
|||||||
|
|
||||||
void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing."));
|
||||||
m_optimiserSettings = std::move(_settings);
|
m_optimiserSettings = std::move(_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set use literal sources before parsing."));
|
||||||
m_metadataLiteralSources = _metadataLiteralSources;
|
m_metadataLiteralSources = _metadataLiteralSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response)
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must add SMTLib2 responses before parsing."));
|
||||||
m_smtlib2Responses[_hash] = _response;
|
m_smtlib2Responses[_hash] = _response;
|
||||||
}
|
}
|
||||||
@ -169,6 +169,7 @@ void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _respons
|
|||||||
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();
|
||||||
@ -240,24 +241,23 @@ bool CompilerStack::parse()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Error::containsOnlyWarnings(m_errorReporter.errors()))
|
|
||||||
{
|
m_stackState = ParsingPerformed;
|
||||||
m_stackState = ParsingSuccessful;
|
if (!Error::containsOnlyWarnings(m_errorReporter.errors()))
|
||||||
return true;
|
m_hasError = true;
|
||||||
}
|
return !m_hasError;
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompilerStack::analyze()
|
bool CompilerStack::analyze()
|
||||||
{
|
{
|
||||||
if (m_stackState != ParsingSuccessful || m_stackState >= AnalysisSuccessful)
|
if (m_stackState != ParsingPerformed || m_stackState >= AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was successful."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed."));
|
||||||
resolveImports();
|
resolveImports();
|
||||||
|
|
||||||
bool noErrors = true;
|
bool noErrors = true;
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser);
|
SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser);
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
if (!syntaxChecker.checkSyntax(*source->ast))
|
if (!syntaxChecker.checkSyntax(*source->ast))
|
||||||
@ -384,18 +384,19 @@ bool CompilerStack::analyze()
|
|||||||
noErrors = false;
|
noErrors = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noErrors)
|
m_stackState = AnalysisPerformed;
|
||||||
{
|
if (!noErrors)
|
||||||
m_stackState = AnalysisSuccessful;
|
m_hasError = true;
|
||||||
return true;
|
|
||||||
}
|
return !m_hasError;
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompilerStack::parseAndAnalyze()
|
bool CompilerStack::parseAndAnalyze()
|
||||||
{
|
{
|
||||||
return parse() && analyze();
|
bool success = parse();
|
||||||
|
if (success || m_parserErrorRecovery)
|
||||||
|
success = analyze();
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompilerStack::isRequestedSource(string const& _sourceName) const
|
bool CompilerStack::isRequestedSource(string const& _sourceName) const
|
||||||
@ -425,10 +426,13 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con
|
|||||||
|
|
||||||
bool CompilerStack::compile()
|
bool CompilerStack::compile()
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisSuccessful)
|
if (m_stackState < AnalysisPerformed)
|
||||||
if (!parseAndAnalyze())
|
if (!parseAndAnalyze())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (m_hasError)
|
||||||
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors."));
|
||||||
|
|
||||||
// Only compile contracts individually which have been requested.
|
// Only compile contracts individually which have been requested.
|
||||||
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers;
|
||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
@ -459,7 +463,7 @@ void CompilerStack::link()
|
|||||||
|
|
||||||
vector<string> CompilerStack::contractNames() const
|
vector<string> CompilerStack::contractNames() const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisSuccessful)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||||
vector<string> contractNames;
|
vector<string> contractNames;
|
||||||
for (auto const& contract: m_contracts)
|
for (auto const& contract: m_contracts)
|
||||||
@ -469,7 +473,7 @@ vector<string> CompilerStack::contractNames() const
|
|||||||
|
|
||||||
string const CompilerStack::lastContractName() const
|
string const CompilerStack::lastContractName() const
|
||||||
{
|
{
|
||||||
if (m_stackState < AnalysisSuccessful)
|
if (m_stackState < AnalysisPerformed)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||||
// try to find some user-supplied contract
|
// try to find some user-supplied contract
|
||||||
string contractName;
|
string contractName;
|
||||||
@ -528,7 +532,7 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
|
|||||||
|
|
||||||
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
|
||||||
|
|
||||||
// Look up the contract (by its fully-qualified name)
|
// 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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
return contractABI(contract(_contractName));
|
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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
return natspecUser(contract(_contractName));
|
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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
return natspecDev(contract(_contractName));
|
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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
@ -701,7 +705,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
|
|||||||
|
|
||||||
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) 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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
Json::Value methodIdentifiers(Json::objectValue);
|
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
|
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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
return metadata(contract(_contractName));
|
return metadata(contract(_contractName));
|
||||||
@ -720,7 +724,7 @@ string const& CompilerStack::metadata(string const& _contractName) const
|
|||||||
|
|
||||||
string const& CompilerStack::metadata(Contract const& _contract) 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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
solAssert(_contract.contract, "");
|
solAssert(_contract.contract, "");
|
||||||
@ -742,7 +746,9 @@ Scanner const& CompilerStack::scanner(string const& _sourceName) const
|
|||||||
|
|
||||||
SourceUnit const& CompilerStack::ast(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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||||
|
|
||||||
return *source(_sourceName).ast;
|
return *source(_sourceName).ast;
|
||||||
@ -750,7 +756,7 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
|
|||||||
|
|
||||||
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) 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."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
|
||||||
|
|
||||||
return *contract(_contractName).contract;
|
return *contract(_contractName).contract;
|
||||||
@ -814,7 +820,7 @@ string const& CompilerStack::Source::ipfsUrl() const
|
|||||||
|
|
||||||
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
|
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
|
||||||
{
|
{
|
||||||
solAssert(m_stackState < ParsingSuccessful, "");
|
solAssert(m_stackState < ParsingPerformed, "");
|
||||||
StringMap newSources;
|
StringMap newSources;
|
||||||
for (auto const& node: _ast.nodes())
|
for (auto const& node: _ast.nodes())
|
||||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
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)
|
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.
|
// 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)
|
auto isPrefixOf = [](string const& _a, string const& _b)
|
||||||
{
|
{
|
||||||
@ -892,7 +898,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
|
|||||||
|
|
||||||
void CompilerStack::resolveImports()
|
void CompilerStack::resolveImports()
|
||||||
{
|
{
|
||||||
solAssert(m_stackState == ParsingSuccessful, "");
|
solAssert(m_stackState == ParsingPerformed, "");
|
||||||
|
|
||||||
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
// topological sorting (depth first search) of the import graph, cutting potential cycles
|
||||||
vector<Source const*> sourceOrder;
|
vector<Source const*> sourceOrder;
|
||||||
@ -903,6 +909,7 @@ void CompilerStack::resolveImports()
|
|||||||
if (sourcesSeen.count(_source))
|
if (sourcesSeen.count(_source))
|
||||||
return;
|
return;
|
||||||
sourcesSeen.insert(_source);
|
sourcesSeen.insert(_source);
|
||||||
|
if (_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()))
|
||||||
{
|
{
|
||||||
@ -938,7 +945,9 @@ void CompilerStack::compileContract(
|
|||||||
map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers
|
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())
|
if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed())
|
||||||
return;
|
return;
|
||||||
@ -990,7 +999,9 @@ void CompilerStack::compileContract(
|
|||||||
|
|
||||||
void CompilerStack::generateIR(ContractDefinition const& _contract)
|
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())
|
if (!_contract.canBeDeployed())
|
||||||
return;
|
return;
|
||||||
@ -1008,7 +1019,10 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
void CompilerStack::generateEWasm(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());
|
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||||
solAssert(!compiledContract.yulIROptimized.empty(), "");
|
solAssert(!compiledContract.yulIROptimized.empty(), "");
|
||||||
if (!compiledContract.eWasm.empty())
|
if (!compiledContract.eWasm.empty())
|
||||||
@ -1038,7 +1052,7 @@ void CompilerStack::generateEWasm(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
|
||||||
{
|
{
|
||||||
solAssert(m_stackState >= AnalysisSuccessful, "");
|
solAssert(m_stackState >= AnalysisPerformed, "");
|
||||||
|
|
||||||
auto it = m_contracts.find(_contractName);
|
auto it = m_contracts.find(_contractName);
|
||||||
if (it != m_contracts.end())
|
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.
|
* 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: boost::noncopyable
|
class CompilerStack: boost::noncopyable
|
||||||
{
|
{
|
||||||
@ -85,8 +87,8 @@ public:
|
|||||||
enum State {
|
enum State {
|
||||||
Empty,
|
Empty,
|
||||||
SourcesSet,
|
SourcesSet,
|
||||||
ParsingSuccessful,
|
ParsingPerformed,
|
||||||
AnalysisSuccessful,
|
AnalysisPerformed,
|
||||||
CompilationSuccessful
|
CompilationSuccessful
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,6 +112,10 @@ 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);
|
||||||
@ -414,6 +420,9 @@ private:
|
|||||||
bool m_metadataLiteralSources = false;
|
bool m_metadataLiteralSources = false;
|
||||||
bool m_parserErrorRecovery = false;
|
bool m_parserErrorRecovery = false;
|
||||||
State m_stackState = Empty;
|
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;
|
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;
|
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
|
/// 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.");
|
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
|
||||||
|
|
||||||
Json::Value output = Json::objectValue;
|
Json::Value output = Json::objectValue;
|
||||||
@ -864,7 +867,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
|
|
||||||
output["sources"] = Json::objectValue;
|
output["sources"] = Json::objectValue;
|
||||||
unsigned sourceIndex = 0;
|
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;
|
Json::Value sourceResult = Json::objectValue;
|
||||||
sourceResult["id"] = sourceIndex++;
|
sourceResult["id"] = sourceIndex++;
|
||||||
@ -876,7 +879,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
|
|||||||
}
|
}
|
||||||
|
|
||||||
Json::Value contractsOutput = Json::objectValue;
|
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(':');
|
size_t colon = contractName.rfind(':');
|
||||||
solAssert(colon != string::npos, "");
|
solAssert(colon != string::npos, "");
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
REPO_ROOT="$(dirname "$0")"/..
|
REPO_ROOT="$(dirname "$0")"/..
|
||||||
cd $REPO_ROOT
|
cd $REPO_ROOT
|
||||||
|
|
||||||
WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | grep -v "test/libsolidity/ASTJSON\|test/compilationTests/zeppelin/LICENSE")
|
WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | grep -v "test/libsolidity/ASTJSON\|test/libsolidity/ASTRecoveryTests\|test/compilationTests/zeppelin/LICENSE")
|
||||||
|
|
||||||
if [[ "$WHITESPACE" != "" ]]
|
if [[ "$WHITESPACE" != "" ]]
|
||||||
then
|
then
|
||||||
|
@ -943,8 +943,7 @@ bool CommandLineInterface::processInput()
|
|||||||
m_compiler->setSources(m_sourceCodes);
|
m_compiler->setSources(m_sourceCodes);
|
||||||
if (m_args.count(g_argLibraries))
|
if (m_args.count(g_argLibraries))
|
||||||
m_compiler->setLibraries(m_libraries);
|
m_compiler->setLibraries(m_libraries);
|
||||||
if (m_args.count(g_argErrorRecovery))
|
m_compiler->setParserErrorRecovery(m_args.count(g_argErrorRecovery));
|
||||||
m_compiler->setParserErrorRecovery(true);
|
|
||||||
m_compiler->setEVMVersion(m_evmVersion);
|
m_compiler->setEVMVersion(m_evmVersion);
|
||||||
// TODO: Perhaps we should not compile unless requested
|
// TODO: Perhaps we should not compile unless requested
|
||||||
|
|
||||||
@ -966,8 +965,13 @@ bool CommandLineInterface::processInput()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!successful)
|
if (!successful)
|
||||||
|
{
|
||||||
|
if (m_args.count(g_argErrorRecovery))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (CompilerError const& _exception)
|
catch (CompilerError const& _exception)
|
||||||
{
|
{
|
||||||
g_hasOutput = true;
|
g_hasOutput = true;
|
||||||
@ -1044,20 +1048,20 @@ void CommandLineInterface::handleCombinedJSON()
|
|||||||
contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName));
|
contractData[g_strAbi] = dev::jsonCompactPrint(m_compiler->contractABI(contractName));
|
||||||
if (requests.count("metadata"))
|
if (requests.count("metadata"))
|
||||||
contractData["metadata"] = m_compiler->metadata(contractName);
|
contractData["metadata"] = m_compiler->metadata(contractName);
|
||||||
if (requests.count(g_strBinary))
|
if (requests.count(g_strBinary) && m_compiler->compilationSuccessful())
|
||||||
contractData[g_strBinary] = m_compiler->object(contractName).toHex();
|
contractData[g_strBinary] = m_compiler->object(contractName).toHex();
|
||||||
if (requests.count(g_strBinaryRuntime))
|
if (requests.count(g_strBinaryRuntime) && m_compiler->compilationSuccessful())
|
||||||
contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
|
contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex();
|
||||||
if (requests.count(g_strOpcodes))
|
if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful())
|
||||||
contractData[g_strOpcodes] = dev::eth::disassemble(m_compiler->object(contractName).bytecode);
|
contractData[g_strOpcodes] = dev::eth::disassemble(m_compiler->object(contractName).bytecode);
|
||||||
if (requests.count(g_strAsm))
|
if (requests.count(g_strAsm) && m_compiler->compilationSuccessful())
|
||||||
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName, m_sourceCodes);
|
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName, m_sourceCodes);
|
||||||
if (requests.count(g_strSrcMap))
|
if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful())
|
||||||
{
|
{
|
||||||
auto map = m_compiler->sourceMapping(contractName);
|
auto map = m_compiler->sourceMapping(contractName);
|
||||||
contractData[g_strSrcMap] = map ? *map : "";
|
contractData[g_strSrcMap] = map ? *map : "";
|
||||||
}
|
}
|
||||||
if (requests.count(g_strSrcMapRuntime))
|
if (requests.count(g_strSrcMapRuntime) && m_compiler->compilationSuccessful())
|
||||||
{
|
{
|
||||||
auto map = m_compiler->runtimeSourceMapping(contractName);
|
auto map = m_compiler->runtimeSourceMapping(contractName);
|
||||||
contractData[g_strSrcMapRuntime] = map ? *map : "";
|
contractData[g_strSrcMapRuntime] = map ? *map : "";
|
||||||
@ -1121,6 +1125,7 @@ void CommandLineInterface::handleAst(string const& _argStr)
|
|||||||
asts.push_back(&m_compiler->ast(sourceCode.first));
|
asts.push_back(&m_compiler->ast(sourceCode.first));
|
||||||
map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts;
|
map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts;
|
||||||
for (auto const& contract: m_compiler->contractNames())
|
for (auto const& contract: m_compiler->contractNames())
|
||||||
|
if (m_compiler->compilationSuccessful())
|
||||||
if (auto const* assemblyItems = m_compiler->runtimeAssemblyItems(contract))
|
if (auto const* assemblyItems = m_compiler->runtimeAssemblyItems(contract))
|
||||||
{
|
{
|
||||||
auto ret = GasEstimator::breakToStatementLevel(
|
auto ret = GasEstimator::breakToStatementLevel(
|
||||||
@ -1397,6 +1402,12 @@ void CommandLineInterface::outputCompilationResults()
|
|||||||
handleAst(g_argAstJson);
|
handleAst(g_argAstJson);
|
||||||
handleAst(g_argAstCompactJson);
|
handleAst(g_argAstCompactJson);
|
||||||
|
|
||||||
|
if (!m_compiler->compilationSuccessful())
|
||||||
|
{
|
||||||
|
serr() << endl << "Compilation halted after AST generation due to errors." << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vector<string> contracts = m_compiler->contractNames();
|
vector<string> contracts = m_compiler->contractNames();
|
||||||
for (string const& contract: contracts)
|
for (string const& contract: contracts)
|
||||||
{
|
{
|
||||||
|
1
test/cmdlineTests/recovery_ast_constructor/args
Normal file
1
test/cmdlineTests/recovery_ast_constructor/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--error-recovery --ast --hashes
|
8
test/cmdlineTests/recovery_ast_constructor/err
Normal file
8
test/cmdlineTests/recovery_ast_constructor/err
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
recovery_ast_constructor/input.sol:5:27: Error: Expected primary expression.
|
||||||
|
balances[tx.origin] = ; // missing RHS.
|
||||||
|
^
|
||||||
|
recovery_ast_constructor/input.sol:5:27: Warning: Recovered in Statement at ';'.
|
||||||
|
balances[tx.origin] = ; // missing RHS.
|
||||||
|
^
|
||||||
|
|
||||||
|
Compilation halted after AST generation due to errors.
|
17
test/cmdlineTests/recovery_ast_constructor/input.sol
Normal file
17
test/cmdlineTests/recovery_ast_constructor/input.sol
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
pragma solidity >=0.0.0;
|
||||||
|
|
||||||
|
contract Error1 {
|
||||||
|
constructor() public {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
34
test/cmdlineTests/recovery_ast_constructor/output
Normal file
34
test/cmdlineTests/recovery_ast_constructor/output
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
Syntax trees:
|
||||||
|
|
||||||
|
|
||||||
|
======= recovery_ast_constructor/input.sol =======
|
||||||
|
PragmaDirective
|
||||||
|
Source: "pragma solidity >=0.0.0;"
|
||||||
|
ContractDefinition "Error1"
|
||||||
|
Source: "contract Error1 {\n constructor() public {\n balances[tx.origin] = ; // missing RHS.\n }\n\n // Without error recovery we stop due to the above error.\n // Error recovery however recovers at the above ';'\n // There should be an AST for the above, albeit with error\n // nodes.\n\n // This function parses properly and should give AST info.\n function five() public view returns(uint) {\n return 5;\n }\n}"
|
||||||
|
FunctionDefinition "" - public
|
||||||
|
Source: "constructor() public {\n balances[tx.origin] = ; // missing RHS.\n }"
|
||||||
|
ParameterList
|
||||||
|
Source: "()"
|
||||||
|
ParameterList
|
||||||
|
Source: ""
|
||||||
|
Block
|
||||||
|
Source: "{\n balances[tx.origin] = ; // missing RHS.\n }"
|
||||||
|
FunctionDefinition "five" - public - const
|
||||||
|
Source: "function five() public view returns(uint) {\n return 5;\n }"
|
||||||
|
ParameterList
|
||||||
|
Source: "()"
|
||||||
|
ParameterList
|
||||||
|
Source: "(uint)"
|
||||||
|
VariableDeclaration ""
|
||||||
|
Type: uint256
|
||||||
|
Source: "uint"
|
||||||
|
ElementaryTypeName uint
|
||||||
|
Source: "uint"
|
||||||
|
Block
|
||||||
|
Source: "{\n return 5;\n }"
|
||||||
|
Return
|
||||||
|
Source: "return 5"
|
||||||
|
Literal, token: [no token] value: 5
|
||||||
|
Type: int_const 5
|
||||||
|
Source: "5"
|
18
test/cmdlineTests/recovery_standard_json/input.json
Normal file
18
test/cmdlineTests/recovery_standard_json/input.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"content": "pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"parserErrorRecovery": true,
|
||||||
|
"outputSelection":
|
||||||
|
{
|
||||||
|
"*": { "": ["ast"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
test/cmdlineTests/recovery_standard_json/output.json
Normal file
7
test/cmdlineTests/recovery_standard_json/output.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{"errors":[{"component":"general","formattedMessage":"A:1:58: ParserError: Expected type name
|
||||||
|
pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }
|
||||||
|
^
|
||||||
|
","message":"Expected type name","severity":"error","sourceLocation":{"end":58,"file":"A","start":57},"type":"ParserError"},{"component":"general","formattedMessage":"A:1:84: Warning: Recovered in ContractDefinition at '}'.
|
||||||
|
pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ }
|
||||||
|
^
|
||||||
|
","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":84,"file":"A","start":83},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"0:22:0"},{"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"23:35:0"}],"src":"0:84:0"},"id":0}}}
|
Loading…
Reference in New Issue
Block a user