From d3c459b5a99715c96733825f78d63cc57265ee3c Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 14 Dec 2015 18:01:40 +0100 Subject: [PATCH 1/2] Parse complex import directives. --- libsolidity/analysis/NameAndTypeResolver.cpp | 2 +- libsolidity/ast/AST.h | 26 ++++-- libsolidity/ast/ASTJsonConverter.cpp | 2 +- libsolidity/ast/ASTPrinter.cpp | 2 +- libsolidity/interface/CompilerStack.cpp | 2 +- libsolidity/parsing/Parser.cpp | 87 +++++++++++++++----- test/libsolidity/SolidityParser.cpp | 21 +++++ 7 files changed, 114 insertions(+), 28 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index efdb1953f..02e4d7ab3 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -78,7 +78,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapidentifier() + + imp->path() + "\") not found." ); error = true; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 1ba4f65b3..e270afd53 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -129,23 +129,39 @@ private: /** * Import directive for referencing other files / source objects. - * Example: import "abc.sol" + * 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 ASTNode { public: - ImportDirective(SourceLocation const& _location, ASTPointer const& _identifier): - ASTNode(_location), m_identifier(_identifier) {} + ImportDirective( + SourceLocation const& _location, + ASTPointer const& _path, + ASTPointer const& _unitAlias, + std::vector, ASTPointer>>&& _symbolAliases + ): + ASTNode(_location), m_path(_path), m_unitAlias(_unitAlias), m_symbolAliases(_symbolAliases) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - ASTString const& identifier() const { return *m_identifier; } + ASTString const& path() const { return *m_path; } virtual ImportAnnotation& annotation() const override; private: - ASTPointer m_identifier; + ASTPointer m_path; + /// The alias for the module itself. If present, import the whole unit under that name and + /// ignore m_symbolAlias. + ASTPointer m_unitAlias; + /// 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, ASTPointer>> m_symbolAliases; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index f208c3c94..377fa7e65 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 9253e0bf5..b2ce1c262 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/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7f0975230..3238423a3 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -376,7 +376,7 @@ void CompilerStack::resolveImports() for (ASTPointer const& node: _source->ast->nodes()) if (ImportDirective const* import = dynamic_cast(node.get())) { - string path = absolutePath(import->identifier(), _sourceName); + string path = absolutePath(import->path(), _sourceName); import->annotation().absolutePath = path; if (!m_sources.count(path)) BOOST_THROW_EXCEPTION( diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index caf38b1df..2b8569308 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -83,7 +83,7 @@ ASTPointer Parser::parse(shared_ptr 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(nodes); @@ -113,14 +113,65 @@ int Parser::endPosition() const ASTPointer 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 url = getLiteralAndAdvance(); + ASTPointer path; + ASTPointer unitAlias; + vector, ASTPointer>> 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 id = parseIdentifier(); + ASTPointer 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(url); + return nodeFactory.createNode(path, unitAlias, move(symbolAliases)); } ASTPointer Parser::parseContractDefinition(bool _isLibrary) @@ -171,7 +222,7 @@ ASTPointer 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 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 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 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 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 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 Parser::parseTypeName(bool _allowVar) type = nodeFactory.createNode(identifierPath); } else - fatalParserError(std::string("Expected type name")); + fatalParserError(string("Expected type name")); if (type) // Parse "[...]" postfixes for arrays. @@ -574,7 +625,7 @@ ASTPointer 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 keyType; keyType = ASTNodeFactory(*this).createNode(m_scanner->currentToken()); m_scanner->next(); @@ -1072,7 +1123,7 @@ ASTPointer Parser::parsePrimaryExpression() m_scanner->next(); } else - fatalParserError(std::string("Expected primary expression.")); + fatalParserError(string("Expected primary expression.")); break; } return expression; @@ -1221,8 +1272,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("'") ); @@ -1234,8 +1284,7 @@ ASTPointer 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("'") ); diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index e6c8a8d83..5c5a9f68c 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1047,6 +1047,27 @@ BOOST_AUTO_TEST_CASE(using_for) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(complex_import) +{ + char const* text = R"( + import "abc" as x; + import * as x from "abc"; + import {a as b, c as d, f} from "def"; + contract x {} + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(from_is_not_keyword) +{ + // "from" is not a keyword although it is used as a keyword in import directives. + char const* text = R"( + contract from { + } + )"; + BOOST_CHECK(successParse(text)); +} + BOOST_AUTO_TEST_CASE(inline_array_declaration) { char const* text = R"( From 603dc58040e62ef99d0a10084340dd4548a438a8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Dec 2015 15:46:03 +0100 Subject: [PATCH 2/2] Simple aliasing during import. --- libsolidity/analysis/NameAndTypeResolver.cpp | 25 +++++-- libsolidity/analysis/NameAndTypeResolver.h | 8 +- libsolidity/ast/AST.cpp | 13 ++++ libsolidity/ast/AST.h | 79 ++++++++++---------- libsolidity/ast/ASTAnnotations.h | 10 +++ libsolidity/ast/Types.cpp | 20 ++++- libsolidity/ast/Types.h | 32 +++++++- libsolidity/interface/CompilerStack.cpp | 3 + libsolidity/parsing/Parser.cpp | 2 +- test/libsolidity/Imports.cpp | 8 ++ 10 files changed, 148 insertions(+), 52 deletions(-) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 02e4d7ab3..92347bfc3 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -46,14 +46,16 @@ NameAndTypeResolver::NameAndTypeResolver( bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit) { - solAssert(!m_scopes[&_sourceUnit], ""); - m_scopes[&_sourceUnit].reset(new DeclarationContainer(nullptr, m_scopes[nullptr].get())); + 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&) { @@ -83,7 +85,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapname().empty()) { auto scope = m_scopes.find(_sourceUnits.at(path)); solAssert(scope != end(m_scopes), ""); @@ -380,7 +382,7 @@ void NameAndTypeResolver::reportFatalTypeError(Error const& _e) } DeclarationRegistrationHelper::DeclarationRegistrationHelper( - map>& _scopes, + map>& _scopes, ASTNode& _astRoot, ErrorList& _errors ): @@ -392,6 +394,17 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper( _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); @@ -489,9 +502,9 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) { - map>::iterator iter; + map>::iterator iter; bool newlyAdded; - unique_ptr container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); + shared_ptr 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; diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 3c444abad..89b9818ba 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -111,7 +111,8 @@ private: /// 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> m_scopes; + /// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope. + std::map> m_scopes; DeclarationContainer* m_currentScope = nullptr; ErrorList& m_errors; @@ -125,12 +126,13 @@ class DeclarationRegistrationHelper: private ASTVisitor { public: DeclarationRegistrationHelper( - std::map>& _scopes, + std::map>& _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; @@ -166,7 +168,7 @@ 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>& m_scopes; + std::map>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; ErrorList& m_errors; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 701202f92..b5affa8e6 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -56,6 +56,13 @@ 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(*m_annotation); +} + ImportAnnotation& ImportDirective::annotation() const { if (!m_annotation) @@ -63,6 +70,12 @@ ImportAnnotation& ImportDirective::annotation() const return static_cast(*m_annotation); } +TypePointer ImportDirective::type() const +{ + solAssert(!!annotation().sourceUnit, ""); + return make_shared(*annotation().sourceUnit); +} + map, FunctionTypePointer> ContractDefinition::interfaceFunctions() const { auto exportedFunctionList = interfaceFunctionList(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index e270afd53..84e9e7064 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> nodes() const { return m_nodes; } @@ -128,44 +129,7 @@ private: }; /** - * 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 ASTNode -{ -public: - ImportDirective( - SourceLocation const& _location, - ASTPointer const& _path, - ASTPointer const& _unitAlias, - std::vector, ASTPointer>>&& _symbolAliases - ): - ASTNode(_location), m_path(_path), m_unitAlias(_unitAlias), 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; - -private: - ASTPointer m_path; - /// The alias for the module itself. If present, import the whole unit under that name and - /// ignore m_symbolAlias. - ASTPointer m_unitAlias; - /// 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, ASTPointer>> m_symbolAliases; -}; - -/** - * 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 { @@ -210,6 +174,45 @@ private: 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 const& _path, + ASTPointer const& _unitAlias, + std::vector, ASTPointer>>&& _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 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, ASTPointer>> m_symbolAliases; +}; + /** * Abstract class that is added to each AST node that can store local variables. */ diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 0bc91c60a..235338bb8 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -54,10 +54,20 @@ struct DocumentedAnnotation std::multimap 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> 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 diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 2dc7fb84c..493470225 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(_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 3ebcb2b29..723d633b3 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 3238423a3..a6f6f224b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -111,6 +111,8 @@ bool CompilerStack::parse() 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)) @@ -384,6 +386,7 @@ void CompilerStack::resolveImports() << errinfo_sourceLocation(import->location()) << errinfo_comment("Source not found.") ); + import->annotation().sourceUnit = m_sources.at(path).ast.get(); toposort(path, &m_sources[path]); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 2b8569308..2b31c385d 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -119,7 +119,7 @@ ASTPointer Parser::parseImportDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Import); ASTPointer path; - ASTPointer unitAlias; + ASTPointer unitAlias = make_shared(); vector, ASTPointer>> symbolAliases; if (m_scanner->currentToken() == Token::StringLiteral) diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index ab8e2257f..57239cb91 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -93,6 +93,14 @@ BOOST_AUTO_TEST_CASE(relative_import_multiplex) BOOST_CHECK(c.compile()); } +BOOST_AUTO_TEST_CASE(simple_alias) +{ + CompilerStack c; + c.addSource("a", "contract A {}"); + c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B { function() { x.A r = x.A(20); } }"); + BOOST_CHECK(c.compile()); +} + BOOST_AUTO_TEST_SUITE_END() }