aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp38
-rw-r--r--libsolidity/analysis/DeclarationContainer.h13
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp106
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h29
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp10
-rw-r--r--libsolidity/analysis/TypeChecker.cpp1
-rw-r--r--libsolidity/ast/AST.cpp20
-rw-r--r--libsolidity/ast/AST.h82
-rw-r--r--libsolidity/ast/ASTAnnotations.h16
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp2
-rw-r--r--libsolidity/ast/ASTPrinter.cpp2
-rw-r--r--libsolidity/ast/Types.cpp20
-rw-r--r--libsolidity/ast/Types.h32
-rw-r--r--libsolidity/interface/CompilerStack.cpp38
-rw-r--r--libsolidity/interface/CompilerStack.h2
-rw-r--r--libsolidity/parsing/Parser.cpp109
16 files changed, 397 insertions, 123 deletions
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index 7339ad5d..ac56562e 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -28,15 +28,19 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-Declaration const* DeclarationContainer::conflictingDeclaration(Declaration const& _declaration) const
+Declaration const* DeclarationContainer::conflictingDeclaration(
+ Declaration const& _declaration,
+ ASTString const* _name
+) const
{
- ASTString const& declarationName(_declaration.name());
- solAssert(!declarationName.empty(), "");
+ if (!_name)
+ _name = &_declaration.name();
+ solAssert(!_name->empty(), "");
vector<Declaration const*> declarations;
- if (m_declarations.count(declarationName))
- declarations += m_declarations.at(declarationName);
- if (m_invisibleDeclarations.count(declarationName))
- declarations += m_invisibleDeclarations.at(declarationName);
+ if (m_declarations.count(*_name))
+ declarations += m_declarations.at(*_name);
+ if (m_invisibleDeclarations.count(*_name))
+ declarations += m_invisibleDeclarations.at(*_name);
if (dynamic_cast<FunctionDefinition const*>(&_declaration))
{
@@ -51,25 +55,31 @@ Declaration const* DeclarationContainer::conflictingDeclaration(Declaration cons
return nullptr;
}
-bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update)
+bool DeclarationContainer::registerDeclaration(
+ Declaration const& _declaration,
+ ASTString const* _name,
+ bool _invisible,
+ bool _update
+)
{
- ASTString const& declarationName(_declaration.name());
- if (declarationName.empty())
+ if (!_name)
+ _name = &_declaration.name();
+ if (_name->empty())
return true;
if (_update)
{
solAssert(!dynamic_cast<FunctionDefinition const*>(&_declaration), "Attempt to update function definition.");
- m_declarations.erase(declarationName);
- m_invisibleDeclarations.erase(declarationName);
+ m_declarations.erase(*_name);
+ m_invisibleDeclarations.erase(*_name);
}
else if (conflictingDeclaration(_declaration))
return false;
if (_invisible)
- m_invisibleDeclarations[declarationName].push_back(&_declaration);
+ m_invisibleDeclarations[*_name].push_back(&_declaration);
else
- m_declarations[declarationName].push_back(&_declaration);
+ m_declarations[*_name].push_back(&_declaration);
return true;
}
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 064724d1..5862f7a5 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -41,23 +41,24 @@ class DeclarationContainer
{
public:
explicit DeclarationContainer(
- Declaration const* _enclosingDeclaration = nullptr,
+ ASTNode const* _enclosingNode = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr
):
- m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
+ m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared or the name is empty.
+ /// @param _name the name to register, if nullptr the intrinsic name of @a _declaration is used.
/// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
/// @param _update if true, replaces a potential declaration that is already present
/// @returns false if the name was already declared.
- bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false);
+ bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
- Declaration const* enclosingDeclaration() const { return m_enclosingDeclaration; }
+ ASTNode const* enclosingNode() const { return m_enclosingNode; }
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
/// @returns whether declaration is valid, and if not also returns previous declaration.
- Declaration const* conflictingDeclaration(Declaration const& _declaration) const;
+ Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
private:
- Declaration const* m_enclosingDeclaration;
+ ASTNode const* m_enclosingNode;
DeclarationContainer const* m_enclosingContainer;
std::map<ASTString, std::vector<Declaration const*>> m_declarations;
std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 612989e1..92347bfc 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -38,16 +38,24 @@ NameAndTypeResolver::NameAndTypeResolver(
) :
m_errors(_errors)
{
+ if (!m_scopes[nullptr])
+ m_scopes[nullptr].reset(new DeclarationContainer());
for (Declaration const* declaration: _globals)
- m_scopes[nullptr].registerDeclaration(*declaration);
+ m_scopes[nullptr]->registerDeclaration(*declaration);
}
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
{
+ if (!m_scopes[&_sourceUnit])
+ // By importing, it is possible that the container already exists.
+ m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
+ m_currentScope = m_scopes[&_sourceUnit].get();
+
// The helper registers all declarations in m_scopes as a side-effect of its construction.
try
{
DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
+ _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
}
catch (FatalError const&)
{
@@ -58,11 +66,43 @@ bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
return true;
}
+bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, SourceUnit const*> const& _sourceUnits)
+{
+ DeclarationContainer& target = *m_scopes.at(&_sourceUnit);
+ bool error = false;
+ for (auto const& node: _sourceUnit.nodes())
+ if (auto imp = dynamic_cast<ImportDirective const*>(node.get()))
+ {
+ string const& path = imp->annotation().absolutePath;
+ if (!_sourceUnits.count(path))
+ {
+ reportDeclarationError( node->location(),
+ "Import \"" +
+ path +
+ "\" (referenced as \"" +
+ imp->path() +
+ "\") not found."
+ );
+ error = true;
+ }
+ else if (imp->name().empty())
+ {
+ auto scope = m_scopes.find(_sourceUnits.at(path));
+ solAssert(scope != end(m_scopes), "");
+ for (auto const& nameAndDeclaration: scope->second->declarations())
+ for (auto const& declaration: nameAndDeclaration.second)
+ target.registerDeclaration(*declaration, &nameAndDeclaration.first);
+ }
+ }
+ return !error;
+}
+
bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{
try
{
- m_currentScope = &m_scopes[nullptr];
+ m_currentScope = m_scopes[_contract.scope()].get();
+ solAssert(!!m_currentScope, "");
ReferencesResolver resolver(m_errors, *this, nullptr);
bool success = true;
@@ -70,12 +110,12 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
if (!resolver.resolve(*baseContract))
success = false;
- m_currentScope = &m_scopes[&_contract];
+ m_currentScope = m_scopes[&_contract].get();
if (success)
{
linearizeBaseContracts(_contract);
- std::vector<ContractDefinition const*> properBases(
+ vector<ContractDefinition const*> properBases(
++_contract.annotation().linearizedBaseContracts.begin(),
_contract.annotation().linearizedBaseContracts.end()
);
@@ -87,7 +127,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
// these can contain code, only resolve parameters for now
for (ASTPointer<ASTNode> const& node: _contract.subNodes())
{
- m_currentScope = &m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract];
+ m_currentScope = m_scopes[m_scopes.count(node.get()) ? node.get() : &_contract].get();
if (!resolver.resolve(*node))
success = false;
}
@@ -95,12 +135,12 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
if (!success)
return false;
- m_currentScope = &m_scopes[&_contract];
+ m_currentScope = m_scopes[&_contract].get();
// now resolve references inside the code
for (ModifierDefinition const* modifier: _contract.functionModifiers())
{
- m_currentScope = &m_scopes[modifier];
+ m_currentScope = m_scopes[modifier].get();
ReferencesResolver resolver(m_errors, *this, nullptr, true);
if (!resolver.resolve(*modifier))
success = false;
@@ -108,7 +148,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
for (FunctionDefinition const* function: _contract.definedFunctions())
{
- m_currentScope = &m_scopes[function];
+ m_currentScope = m_scopes[function].get();
if (!ReferencesResolver(
m_errors,
*this,
@@ -133,7 +173,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
{
try
{
- m_scopes[nullptr].registerDeclaration(_declaration, false, true);
+ m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true);
solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
}
catch (FatalError const&)
@@ -145,12 +185,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
return true;
}
-vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
+vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
{
auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes))
return vector<Declaration const*>({});
- return iterator->second.resolveName(_name, false);
+ return iterator->second->resolveName(_name, false);
}
vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
@@ -166,7 +206,7 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c
{
if (!m_scopes.count(candidates.front()))
return nullptr;
- candidates = m_scopes.at(candidates.front()).resolveName(_path[i], false);
+ candidates = m_scopes.at(candidates.front())->resolveName(_path[i], false);
}
if (candidates.size() == 1)
return candidates.front();
@@ -210,7 +250,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
{
auto iterator = m_scopes.find(&_base);
solAssert(iterator != end(m_scopes), "");
- for (auto const& nameAndDeclaration: iterator->second.declarations())
+ for (auto const& nameAndDeclaration: iterator->second->declarations())
for (auto const& declaration: nameAndDeclaration.second)
// Import if it was declared in the base, is not the constructor and is visible in derived classes
if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts())
@@ -342,17 +382,29 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
}
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
- map<ASTNode const*, DeclarationContainer>& _scopes,
+ map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
ErrorList& _errors
):
m_scopes(_scopes),
- m_currentScope(nullptr),
+ m_currentScope(&_astRoot),
m_errors(_errors)
{
+ solAssert(!!m_scopes.at(m_currentScope), "");
_astRoot.accept(*this);
}
+bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
+{
+ SourceUnit const* importee = _import.annotation().sourceUnit;
+ solAssert(!!importee, "");
+ if (!m_scopes[importee])
+ m_scopes[importee].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get()));
+ m_scopes[&_import] = m_scopes[importee];
+ registerDeclaration(_import, false);
+ return true;
+}
+
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
{
registerDeclaration(_contract, true);
@@ -450,9 +502,10 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&)
void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
{
- map<ASTNode const*, DeclarationContainer>::iterator iter;
+ map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
bool newlyAdded;
- tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope]));
+ shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
+ tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
solAssert(newlyAdded, "Unable to add new scope.");
m_currentScope = &_declaration;
}
@@ -460,16 +513,16 @@ void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declara
void DeclarationRegistrationHelper::closeCurrentScope()
{
solAssert(m_currentScope, "Closed non-existing scope.");
- m_currentScope = m_scopes[m_currentScope].enclosingDeclaration();
+ m_currentScope = m_scopes[m_currentScope]->enclosingNode();
}
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
{
- if (!m_scopes[m_currentScope].registerDeclaration(_declaration, !_declaration.isVisibleInContract()))
+ if (!m_scopes[m_currentScope]->registerDeclaration(_declaration, nullptr, !_declaration.isVisibleInContract()))
{
SourceLocation firstDeclarationLocation;
SourceLocation secondDeclarationLocation;
- Declaration const* conflictingDeclaration = m_scopes[m_currentScope].conflictingDeclaration(_declaration);
+ Declaration const* conflictingDeclaration = m_scopes[m_currentScope]->conflictingDeclaration(_declaration);
solAssert(conflictingDeclaration, "");
if (_declaration.location().start < conflictingDeclaration->location().start)
@@ -500,14 +553,17 @@ string DeclarationRegistrationHelper::currentCanonicalName() const
{
string ret;
for (
- Declaration const* scope = m_currentScope;
+ ASTNode const* scope = m_currentScope;
scope != nullptr;
- scope = m_scopes[scope].enclosingDeclaration()
+ scope = m_scopes[scope]->enclosingNode()
)
{
- if (!ret.empty())
- ret = "." + ret;
- ret = scope->name() + ret;
+ if (auto decl = dynamic_cast<Declaration const*>(scope))
+ {
+ if (!ret.empty())
+ ret = "." + ret;
+ ret = decl->name() + ret;
+ }
}
return ret;
}
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 1547a274..89b9818b 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -46,6 +46,8 @@ public:
/// Registers all declarations found in the source unit.
/// @returns false in case of error.
bool registerDeclarations(SourceUnit& _sourceUnit);
+ /// Applies the effect of import directives.
+ bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
/// Resolves all names and types referenced from the given contract.
/// @returns false in case of error.
bool resolveNamesAndTypes(ContractDefinition& _contract);
@@ -55,9 +57,9 @@ public:
bool updateDeclaration(Declaration const& _declaration);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
- /// the global scope is used (i.e. the one containing only the contract).
+ /// the global scope is used (i.e. the one containing only the pre-defined global variables).
/// @returns a pointer to the declaration on success or nullptr on failure.
- std::vector<Declaration const*> resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
+ std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const;
/// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase.
@@ -88,11 +90,6 @@ private:
template <class _T>
static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
- /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
- /// where nullptr denotes the global scope. Note that structs are not scope since they do
- /// not contain code.
- std::map<ASTNode const*, DeclarationContainer> m_scopes;
-
// creates the Declaration error and adds it in the errors list
void reportDeclarationError(
SourceLocation _sourceLoction,
@@ -110,6 +107,13 @@ private:
// creates the Declaration error and adds it in the errors list and throws FatalError
void reportFatalTypeError(Error const& _e);
+
+ /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
+ /// where nullptr denotes the global scope. Note that structs are not scope since they do
+ /// not contain code.
+ /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes;
+
DeclarationContainer* m_currentScope = nullptr;
ErrorList& m_errors;
};
@@ -121,9 +125,14 @@ private:
class DeclarationRegistrationHelper: private ASTVisitor
{
public:
- DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot, ErrorList& _errors);
+ DeclarationRegistrationHelper(
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
+ ASTNode& _astRoot,
+ ErrorList& _errors
+ );
private:
+ bool visit(ImportDirective& _declaration) override;
bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct) override;
@@ -159,8 +168,8 @@ private:
// creates the Declaration error and adds it in the errors list and throws FatalError
void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
- std::map<ASTNode const*, DeclarationContainer>& m_scopes;
- Declaration const* m_currentScope = nullptr;
+ std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
+ ASTNode const* m_currentScope = nullptr;
VariableScope* m_currentFunction = nullptr;
ErrorList& m_errors;
};
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 2fe53e8b..ca002f58 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -139,7 +139,9 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
bool isPointer = true;
if (_variable.isExternalCallableParameter())
{
- auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
+ auto const& contract = dynamic_cast<ContractDefinition const&>(
+ *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ );
if (contract.isLibrary())
{
if (varLoc == Location::Memory)
@@ -162,9 +164,11 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
else
typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
- else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
+ else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
{
- auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
+ auto const& contract = dynamic_cast<ContractDefinition const&>(
+ *dynamic_cast<Declaration const&>(*_variable.scope()).scope()
+ );
// force locations of public or external function (return) parameters to memory
if (varLoc == Location::Storage && !contract.isLibrary())
fatalTypeError(_variable.location(),
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 01de5eb0..9718bf75 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -780,6 +780,7 @@ bool TypeChecker::visit(Assignment const& _assignment)
bool TypeChecker::visit(TupleExpression const& _tuple)
{
vector<ASTPointer<Expression>> const& components = _tuple.components();
+ solAssert(!_tuple.isInlineArray(), "Tuple type not properly declared");
TypePointers types;
if (_tuple.annotation().lValueRequested)
{
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 6006d441..b5affa8e 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -56,6 +56,26 @@ Error ASTNode::createTypeError(string const& _description) const
return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description);
}
+SourceUnitAnnotation& SourceUnit::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new SourceUnitAnnotation();
+ return static_cast<SourceUnitAnnotation&>(*m_annotation);
+}
+
+ImportAnnotation& ImportDirective::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new ImportAnnotation();
+ return static_cast<ImportAnnotation&>(*m_annotation);
+}
+
+TypePointer ImportDirective::type() const
+{
+ solAssert(!!annotation().sourceUnit, "");
+ return make_shared<ModuleType>(*annotation().sourceUnit);
+}
+
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
{
auto exportedFunctionList = interfaceFunctionList();
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 1217d945..84e9e706 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -120,6 +120,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
+ virtual SourceUnitAnnotation& annotation() const override;
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
@@ -128,27 +129,7 @@ private:
};
/**
- * Import directive for referencing other files / source objects.
- * Example: import "abc.sol"
- * Source objects are identified by a string which can be a file name but does not have to be.
- */
-class ImportDirective: public ASTNode
-{
-public:
- ImportDirective(SourceLocation const& _location, ASTPointer<ASTString> const& _identifier):
- ASTNode(_location), m_identifier(_identifier) {}
-
- virtual void accept(ASTVisitor& _visitor) override;
- virtual void accept(ASTConstVisitor& _visitor) const override;
-
- ASTString const& identifier() const { return *m_identifier; }
-
-private:
- ASTPointer<ASTString> m_identifier;
-};
-
-/**
- * Abstract AST class for a declaration (contract, function, struct, variable).
+ * Abstract AST class for a declaration (contract, function, struct, variable, import directive).
*/
class Declaration: public ASTNode
{
@@ -172,8 +153,8 @@ public:
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
- Declaration const* scope() const { return m_scope; }
- void setScope(Declaration const* _scope) { m_scope = _scope; }
+ ASTNode const* scope() const { return m_scope; }
+ void setScope(ASTNode const* _scope) { m_scope = _scope; }
virtual bool isLValue() const { return false; }
virtual bool isPartOfExternalInterface() const { return false; }
@@ -190,7 +171,46 @@ protected:
private:
ASTPointer<ASTString> m_name;
Visibility m_visibility;
- Declaration const* m_scope;
+ ASTNode const* m_scope;
+};
+
+/**
+ * Import directive for referencing other files / source objects.
+ * Example: import "abc.sol" // imports all symbols of "abc.sol" into current scope
+ * Source objects are identified by a string which can be a file name but does not have to be.
+ * Other ways to use it:
+ * import "abc" as x; // creates symbol "x" that contains all symbols in "abc"
+ * import * as x from "abc"; // same as above
+ * import {a as b, c} from "abc"; // creates new symbols "b" and "c" referencing "a" and "c" in "abc", respectively.
+ */
+class ImportDirective: public Declaration
+{
+public:
+ ImportDirective(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _path,
+ ASTPointer<ASTString> const& _unitAlias,
+ std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>>&& _symbolAliases
+ ):
+ Declaration(_location, _unitAlias),
+ m_path(_path),
+ m_symbolAliases(_symbolAliases)
+ { }
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ASTString const& path() const { return *m_path; }
+ virtual ImportAnnotation& annotation() const override;
+
+ virtual TypePointer type() const override;
+
+private:
+ ASTPointer<ASTString> m_path;
+ /// The aliases for the specific symbols to import. If non-empty import the specific symbols.
+ /// If the second component is empty, import the identifier unchanged.
+ /// If both m_unitAlias and m_symbolAlias are empty, import all symbols into the current scope.
+ std::vector<std::pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> m_symbolAliases;
};
/**
@@ -1126,9 +1146,10 @@ private:
ASTPointer<Expression> m_rightHandSide;
};
+
/**
- * Tuple or just parenthesized expression.
- * Examples: (1, 2), (x,), (x), ()
+ * Tuple, parenthesized expression, or bracketed expression.
+ * Examples: (1, 2), (x,), (x), (), [1, 2],
* Individual components might be empty shared pointers (as in the second example).
* The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple
* Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple.
@@ -1138,16 +1159,21 @@ class TupleExpression: public Expression
public:
TupleExpression(
SourceLocation const& _location,
- std::vector<ASTPointer<Expression>> const& _components
+ std::vector<ASTPointer<Expression>> const& _components,
+ bool _isArray
):
- Expression(_location), m_components(_components) {}
+ Expression(_location),
+ m_components(_components),
+ m_isArray(_isArray) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<Expression>> const& components() const { return m_components; }
+ bool isInlineArray() const { return m_isArray; }
private:
std::vector<ASTPointer<Expression>> m_components;
+ bool m_isArray;
};
/**
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 4e0187cf..235338bb 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -54,6 +54,22 @@ struct DocumentedAnnotation
std::multimap<std::string, DocTag> docTags;
};
+struct SourceUnitAnnotation: ASTAnnotation
+{
+ /// The "absolute" (in the compiler sense) path of this source unit.
+ std::string path;
+ /// The exported symbols (all global symbols).
+ std::map<ASTString, std::vector<Declaration const*>> exportedSymbols;
+};
+
+struct ImportAnnotation: ASTAnnotation
+{
+ /// The absolute path of the source unit to import.
+ std::string absolutePath;
+ /// The actual source unit.
+ SourceUnit const* sourceUnit = nullptr;
+};
+
struct TypeDeclarationAnnotation: ASTAnnotation
{
/// The name of this type, prefixed by proper namespaces if globally accessible.
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index f208c3c9..377fa7e6 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -91,7 +91,7 @@ Json::Value const& ASTJsonConverter::json()
bool ASTJsonConverter::visit(ImportDirective const& _node)
{
- addJsonNode("Import", { make_pair("file", _node.identifier())});
+ addJsonNode("Import", { make_pair("file", _node.path())});
return true;
}
diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
index 9253e0bf..b2ce1c26 100644
--- a/libsolidity/ast/ASTPrinter.cpp
+++ b/libsolidity/ast/ASTPrinter.cpp
@@ -49,7 +49,7 @@ void ASTPrinter::print(ostream& _stream)
bool ASTPrinter::visit(ImportDirective const& _node)
{
- writeLine("ImportDirective \"" + _node.identifier() + "\"");
+ writeLine("ImportDirective \"" + _node.path() + "\"");
printSourcePart(_node);
return goDeeper();
}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 2dc7fb84..49347022 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -1925,9 +1925,25 @@ string ModifierType::toString(bool _short) const
return name + ")";
}
-MagicType::MagicType(MagicType::Kind _kind):
- m_kind(_kind)
+bool ModuleType::operator==(Type const& _other) const
{
+ if (_other.category() != category())
+ return false;
+ return &m_sourceUnit == &dynamic_cast<ModuleType const&>(_other).m_sourceUnit;
+}
+
+MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
+{
+ MemberList::MemberMap symbols;
+ for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
+ for (Declaration const* symbol: symbolName.second)
+ symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
+ return symbols;
+}
+
+string ModuleType::toString(bool) const
+{
+ return string("module \"") + m_sourceUnit.annotation().path + string("\"");
}
bool MagicType::operator==(Type const& _other) const
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 3ebcb2b2..723d633b 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -134,7 +134,7 @@ public:
{
Integer, IntegerConstant, StringLiteral, Bool, Real, Array,
FixedBytes, Contract, Struct, Function, Enum, Tuple,
- Mapping, TypeType, Modifier, Magic
+ Mapping, TypeType, Modifier, Magic, Module
};
/// @{
@@ -969,6 +969,34 @@ private:
};
+
+/**
+ * Special type for imported modules. These mainly give access to their scope via members.
+ */
+class ModuleType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Module; }
+
+ explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
+
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ {
+ return TypePointer();
+ }
+
+ virtual bool operator==(Type const& _other) const override;
+ virtual bool canBeStored() const override { return false; }
+ virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual unsigned sizeOnStack() const override { return 0; }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
+
+ virtual std::string toString(bool _short) const override;
+
+private:
+ SourceUnit const& m_sourceUnit;
+};
+
/**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
* (it always references a global singleton by name).
@@ -979,7 +1007,7 @@ public:
enum class Kind { Block, Message, Transaction };
virtual Category category() const override { return Category::Magic; }
- explicit MagicType(Kind _kind);
+ explicit MagicType(Kind _kind): m_kind(_kind) {}
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
{
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 18eec0a2..a6f6f224 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -22,6 +22,7 @@
*/
#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
@@ -103,12 +104,16 @@ bool CompilerStack::parse()
m_errors.clear();
m_parseSuccessful = false;
+ map<string, SourceUnit const*> sourceUnitsByName;
for (auto& sourcePair: m_sources)
{
sourcePair.second.scanner->reset();
sourcePair.second.ast = Parser(m_errors).parse(sourcePair.second.scanner);
if (!sourcePair.second.ast)
solAssert(!Error::containsOnlyWarnings(m_errors), "Parser returned null but did not report error.");
+ else
+ sourcePair.second.ast->annotation().path = sourcePair.first;
+ sourceUnitsByName[sourcePair.first] = sourcePair.second.ast.get();
}
if (!Error::containsOnlyWarnings(m_errors))
// errors while parsing. sould stop before type checking
@@ -129,6 +134,10 @@ bool CompilerStack::parse()
return false;
for (Source const* source: m_sourceOrder)
+ if (!resolver.performImports(*source->ast, sourceUnitsByName))
+ return false;
+
+ for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
@@ -361,7 +370,7 @@ void CompilerStack::resolveImports()
vector<Source const*> sourceOrder;
set<Source const*> sourcesSeen;
- function<void(Source const*)> toposort = [&](Source const* _source)
+ function<void(string const&, Source const*)> toposort = [&](string const& _sourceName, Source const* _source)
{
if (sourcesSeen.count(_source))
return;
@@ -369,26 +378,45 @@ void CompilerStack::resolveImports()
for (ASTPointer<ASTNode> const& node: _source->ast->nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{
- string const& id = import->identifier();
- if (!m_sources.count(id))
+ string path = absolutePath(import->path(), _sourceName);
+ import->annotation().absolutePath = path;
+ if (!m_sources.count(path))
BOOST_THROW_EXCEPTION(
Error(Error::Type::ParserError)
<< errinfo_sourceLocation(import->location())
<< errinfo_comment("Source not found.")
);
+ import->annotation().sourceUnit = m_sources.at(path).ast.get();
- toposort(&m_sources[id]);
+ toposort(path, &m_sources[path]);
}
sourceOrder.push_back(_source);
};
for (auto const& sourcePair: m_sources)
if (!sourcePair.second.isLibrary)
- toposort(&sourcePair.second);
+ toposort(sourcePair.first, &sourcePair.second);
swap(m_sourceOrder, sourceOrder);
}
+string CompilerStack::absolutePath(string const& _path, string const& _reference) const
+{
+ // Anything that does not start with `.` is an absolute path.
+ if (_path.empty() || _path.front() != '.')
+ return _path;
+ using path = boost::filesystem::path;
+ path p(_path);
+ path result(_reference);
+ result.remove_filename();
+ for (path::iterator it = p.begin(); it != p.end(); ++it)
+ if (*it == "..")
+ result = result.parent_path();
+ else if (*it != ".")
+ result /= *it;
+ return result.string();
+}
+
void CompilerStack::compileContract(
bool _optimize,
unsigned _runs,
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 0473d58b..3e6dc456 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -199,6 +199,8 @@ private:
};
void resolveImports();
+ /// @returns the absolute path corresponding to @a _path relative to @a _reference.
+ std::string absolutePath(std::string const& _path, std::string const& _reference) const;
/// Compile a single contract and put the result in @a _compiledContracts.
void compileContract(
bool _optimize,
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 994b031a..d53f825f 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -83,7 +83,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
nodes.push_back(parseContractDefinition(token == Token::Library));
break;
default:
- fatalParserError(std::string("Expected import directive or contract definition."));
+ fatalParserError(string("Expected import directive or contract definition."));
}
}
return nodeFactory.createNode<SourceUnit>(nodes);
@@ -113,14 +113,65 @@ int Parser::endPosition() const
ASTPointer<ImportDirective> Parser::parseImportDirective()
{
+ // import "abc" [as x];
+ // import * as x from "abc";
+ // import {a as b, c} from "abc";
ASTNodeFactory nodeFactory(*this);
expectToken(Token::Import);
- if (m_scanner->currentToken() != Token::StringLiteral)
- fatalParserError(std::string("Expected string literal (URL)."));
- ASTPointer<ASTString> url = getLiteralAndAdvance();
+ ASTPointer<ASTString> path;
+ ASTPointer<ASTString> unitAlias = make_shared<string>();
+ vector<pair<ASTPointer<Identifier>, ASTPointer<ASTString>>> symbolAliases;
+
+ if (m_scanner->currentToken() == Token::StringLiteral)
+ {
+ path = getLiteralAndAdvance();
+ if (m_scanner->currentToken() == Token::As)
+ {
+ m_scanner->next();
+ unitAlias = expectIdentifierToken();
+ }
+ }
+ else
+ {
+ if (m_scanner->currentToken() == Token::LBrace)
+ {
+ m_scanner->next();
+ while (true)
+ {
+ ASTPointer<Identifier> id = parseIdentifier();
+ ASTPointer<ASTString> alias;
+ if (m_scanner->currentToken() == Token::As)
+ {
+ expectToken(Token::As);
+ alias = expectIdentifierToken();
+ }
+ symbolAliases.push_back(move(make_pair(move(id), move(alias))));
+ if (m_scanner->currentToken() != Token::Comma)
+ break;
+ m_scanner->next();
+ }
+ expectToken(Token::RBrace);
+ }
+ else if (m_scanner->currentToken() == Token::Mul)
+ {
+ m_scanner->next();
+ expectToken(Token::As);
+ unitAlias = expectIdentifierToken();
+ }
+ else
+ fatalParserError("Expected string literal (path), \"*\" or alias list.");
+ // "from" is not a keyword but parsed as an identifier because of backwards
+ // compatibility and because it is a really common word.
+ if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
+ fatalParserError("Expected \"from\".");
+ m_scanner->next();
+ if (m_scanner->currentToken() != Token::StringLiteral)
+ fatalParserError("Expected import path.");
+ path = getLiteralAndAdvance();
+ }
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
- return nodeFactory.createNode<ImportDirective>(url);
+ return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
}
ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
@@ -171,7 +222,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
else if (currentTokenValue == Token::Using)
subNodes.push_back(parseUsingDirective());
else
- fatalParserError(std::string("Function, variable, struct or modifier declaration expected."));
+ fatalParserError(string("Function, variable, struct or modifier declaration expected."));
}
nodeFactory.markEndPosition();
expectToken(Token::RBrace);
@@ -250,7 +301,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
else if (Token::isVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
- fatalParserError(std::string("Multiple visibility specifiers."));
+ fatalParserError(string("Multiple visibility specifiers."));
visibility = parseVisibilitySpecifier(token);
}
else
@@ -327,7 +378,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
break;
expectToken(Token::Comma);
if (m_scanner->currentToken() != Token::Identifier)
- fatalParserError(std::string("Expected Identifier after ','"));
+ fatalParserError(string("Expected Identifier after ','"));
}
nodeFactory.markEndPosition();
@@ -363,7 +414,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
if (visibility != Declaration::Visibility::Default)
- fatalParserError(std::string("Visibility already specified."));
+ fatalParserError(string("Visibility already specified."));
visibility = parseVisibilitySpecifier(token);
}
else
@@ -375,9 +426,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Default)
- fatalParserError(std::string("Location already specified."));
+ fatalParserError(string("Location already specified."));
if (!type)
- fatalParserError(std::string("Location specifier needs explicit type name."));
+ fatalParserError(string("Location specifier needs explicit type name."));
location = (
token == Token::Memory ?
VariableDeclaration::Location::Memory :
@@ -532,7 +583,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Var)
{
if (!_allowVar)
- fatalParserError(std::string("Expected explicit type name."));
+ fatalParserError(string("Expected explicit type name."));
m_scanner->next();
}
else if (token == Token::Mapping)
@@ -551,7 +602,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
type = nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
}
else
- fatalParserError(std::string("Expected type name"));
+ fatalParserError(string("Expected type name"));
if (type)
// Parse "[...]" postfixes for arrays.
@@ -574,7 +625,7 @@ ASTPointer<Mapping> Parser::parseMapping()
expectToken(Token::Mapping);
expectToken(Token::LParen);
if (!Token::isElementaryTypeName(m_scanner->currentToken()))
- fatalParserError(std::string("Expected elementary type name for mapping key type"));
+ fatalParserError(string("Expected elementary type name for mapping key type"));
ASTPointer<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->currentToken());
m_scanner->next();
@@ -1036,27 +1087,35 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance());
break;
case Token::LParen:
+ case Token::LBrack:
{
- // Tuple or parenthesized expression.
- // Special cases: () is empty tuple type, (x) is not a real tuple, (x,) is one-dimensional tuple
+ // Tuple/parenthesized expression or inline array/bracketed expression.
+ // Special cases: ()/[] is empty tuple/array type, (x) is not a real tuple,
+ // (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples.
m_scanner->next();
vector<ASTPointer<Expression>> components;
- if (m_scanner->currentToken() != Token::RParen)
+ Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack);
+ bool isArray = (token == Token::LBrack);
+
+ if (m_scanner->currentToken() != oppositeToken)
while (true)
{
- if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != Token::RParen)
+ if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
components.push_back(parseExpression());
+ else if (isArray)
+ parserError("Expected expression (inline array elements cannot be omitted).");
else
components.push_back(ASTPointer<Expression>());
- if (m_scanner->currentToken() == Token::RParen)
+ if (m_scanner->currentToken() == oppositeToken)
break;
else if (m_scanner->currentToken() == Token::Comma)
m_scanner->next();
}
nodeFactory.markEndPosition();
- expectToken(Token::RParen);
- return nodeFactory.createNode<TupleExpression>(components);
+ expectToken(oppositeToken);
+ return nodeFactory.createNode<TupleExpression>(components, isArray);
}
+
default:
if (Token::isElementaryTypeName(token))
{
@@ -1065,7 +1124,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next();
}
else
- fatalParserError(std::string("Expected primary expression."));
+ fatalParserError(string("Expected primary expression."));
break;
}
return expression;
@@ -1214,8 +1273,7 @@ Token::Value Parser::expectAssignmentOperator()
Token::Value op = m_scanner->currentToken();
if (!Token::isAssignmentOp(op))
fatalParserError(
- std::string("Expected assignment operator ") +
- string(" got '") +
+ string("Expected assignment operator, got '") +
string(Token::name(m_scanner->currentToken())) +
string("'")
);
@@ -1227,8 +1285,7 @@ ASTPointer<ASTString> Parser::expectIdentifierToken()
{
if (m_scanner->currentToken() != Token::Identifier)
fatalParserError(
- std::string("Expected identifier ") +
- string(" got '") +
+ string("Expected identifier, got '") +
string(Token::name(m_scanner->currentToken())) +
string("'")
);