aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/interface/CompilerStack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/interface/CompilerStack.cpp')
-rw-r--r--libsolidity/interface/CompilerStack.cpp84
1 files changed, 56 insertions, 28 deletions
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 441c7897..610caea1 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -27,8 +27,8 @@
#include <libsolidity/interface/Version.h>
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/ast/AST.h>
-#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/analysis/GlobalContext.h>
@@ -45,10 +45,12 @@
#include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h>
-#include <libevmasm/Exceptions.h>
-
#include <libyul/YulString.h>
+#include <liblangutil/Scanner.h>
+
+#include <libevmasm/Exceptions.h>
+
#include <libdevcore/SwarmHash.h>
#include <libdevcore/JSON.h>
@@ -58,6 +60,7 @@
using namespace std;
using namespace dev;
+using namespace langutil;
using namespace dev::solidity;
boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping)
@@ -106,6 +109,8 @@ void CompilerStack::reset(bool _keepSources)
m_stackState = Empty;
m_sources.clear();
}
+ m_smtlib2Responses.clear();
+ m_unhandledSMTLib2Queries.clear();
m_libraries.clear();
m_evmVersion = EVMVersion();
m_optimize = false;
@@ -121,7 +126,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
{
bool existed = m_sources.count(_name) != 0;
reset(true);
- m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
+ m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content, _name));
m_sources[_name].isLibrary = _isLibrary;
m_stackState = SourcesSet;
return existed;
@@ -156,7 +161,7 @@ bool CompilerStack::parse()
{
string const& newPath = newSource.first;
string const& newContents = newSource.second;
- m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents), newPath);
+ m_sources[newPath].scanner = make_shared<Scanner>(CharStream(newContents, newPath));
sourcesToParse.push_back(newPath);
}
}
@@ -221,8 +226,21 @@ bool CompilerStack::analyze()
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
- // This cannot be done in the above loop, because cross-contract types couldn't be resolved.
- // A good example is `LibraryName.TypeName x;`.
+ // Next, we check inheritance, overrides, function collisions and other things at
+ // contract or function level.
+ // This also calculates whether a contract is abstract, which is needed by the
+ // type checker.
+ ContractLevelChecker contractLevelChecker(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ if (!contractLevelChecker.check(*contract))
+ noErrors = false;
+
+ // New we run full type checks that go down to the expression level. This
+ // cannot be done earlier, because we need cross-contract types and information
+ // about whether a contract is abstract for the `new` expression.
+ // This populates the `type` annotation for all expressions.
//
// Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed,
// which is only done one step later.
@@ -282,9 +300,10 @@ bool CompilerStack::analyze()
if (noErrors)
{
- SMTChecker smtChecker(m_errorReporter, m_smtQuery);
+ SMTChecker smtChecker(m_errorReporter, m_smtlib2Responses);
for (Source const* source: m_sourceOrder)
smtChecker.analyze(*source->ast, source->scanner);
+ m_unhandledSMTLib2Queries += smtChecker.unhandledQueries();
}
}
catch(FatalError const&)
@@ -329,13 +348,14 @@ bool CompilerStack::compile()
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
if (isRequestedContract(*contract))
compileContract(*contract, compiledContracts);
- this->link();
m_stackState = CompilationSuccessful;
+ this->link();
return true;
}
void CompilerStack::link()
{
+ solAssert(m_stackState >= CompilationSuccessful, "");
for (auto& contract: m_contracts)
{
contract.second.object.link(m_libraries);
@@ -353,6 +373,19 @@ vector<string> CompilerStack::contractNames() const
return contractNames;
}
+string const CompilerStack::lastContractName() const
+{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ // try to find some user-supplied contract
+ string contractName;
+ for (auto const& it: m_sources)
+ for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
+ if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
+ contractName = contract->fullyQualifiedName();
+ return contractName;
+}
+
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
{
Contract const& currentContract = contract(_contractName);
@@ -389,6 +422,9 @@ string const* CompilerStack::runtimeSourceMapping(string const& _contractName) c
std::string const CompilerStack::filesystemFriendlyName(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
+
// Look up the contract (by its fully-qualified name)
Contract const& matchContract = m_contracts.at(_contractName);
// Check to see if it could collide on name
@@ -576,14 +612,15 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati
int startColumn;
int endLine;
int endColumn;
- tie(startLine, startColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.start);
- tie(endLine, endColumn) = scanner(*_sourceLocation.sourceName).translatePositionToLineColumn(_sourceLocation.end);
+ tie(startLine, startColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.start);
+ tie(endLine, endColumn) = scanner(_sourceLocation.source->name()).translatePositionToLineColumn(_sourceLocation.end);
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
}
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
{
+ solAssert(m_stackState < ParsingSuccessful, "");
StringMap newSources;
for (auto const& node: _ast.nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
@@ -617,6 +654,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
string CompilerStack::applyRemapping(string const& _path, string const& _context)
{
+ solAssert(m_stackState < ParsingSuccessful, "");
// 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)
{
@@ -658,6 +696,8 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context
void CompilerStack::resolveImports()
{
+ solAssert(m_stackState == ParsingSuccessful, "");
+
// topological sorting (depth first search) of the import graph, cutting potential cycles
vector<Source const*> sourceOrder;
set<Source const*> sourcesSeen;
@@ -702,6 +742,8 @@ void CompilerStack::compileContract(
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
)
{
+ solAssert(m_stackState >= AnalysisSuccessful, "");
+
if (
_compiledContracts.count(&_contract) ||
!_contract.annotation().unimplementedFunctions.empty() ||
@@ -757,23 +799,9 @@ void CompilerStack::compileContract(
_compiledContracts[compiledContract.contract] = &compiler->assembly();
}
-string const CompilerStack::lastContractName() const
-{
- if (m_contracts.empty())
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
- // try to find some user-supplied contract
- string contractName;
- for (auto const& it: m_sources)
- for (ASTPointer<ASTNode> const& node: it.second.ast->nodes())
- if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
- contractName = contract->fullyQualifiedName();
- return contractName;
-}
-
CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const
{
- if (m_contracts.empty())
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found."));
+ solAssert(m_stackState >= AnalysisSuccessful, "");
auto it = m_contracts.find(_contractName);
if (it != m_contracts.end())
@@ -908,8 +936,8 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con
SourceLocation const& location = item.location();
int length = location.start != -1 && location.end != -1 ? location.end - location.start : -1;
int sourceIndex =
- location.sourceName && sourceIndicesMap.count(*location.sourceName) ?
- sourceIndicesMap.at(*location.sourceName) :
+ location.source && sourceIndicesMap.count(location.source->name()) ?
+ sourceIndicesMap.at(location.source->name()) :
-1;
char jump = '-';
if (item.getJumpType() == eth::AssemblyItem::JumpType::IntoFunction)