| /** |
| * A `Dsymbol` representing a renamed import. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d, _dimport.d) |
| * Documentation: https://dlang.org/phobos/dmd_dimport.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dimport.d |
| */ |
| |
| module dmd.dimport; |
| |
| import dmd.arraytypes; |
| import dmd.astenums; |
| import dmd.declaration; |
| import dmd.dmodule; |
| import dmd.dscope; |
| import dmd.dsymbol; |
| import dmd.dsymbolsem; |
| import dmd.errors; |
| import dmd.expression; |
| import dmd.globals; |
| import dmd.identifier; |
| import dmd.mtype; |
| import dmd.visitor; |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class Import : Dsymbol |
| { |
| /* static import aliasId = pkg1.pkg2.id : alias1 = name1, alias2 = name2; |
| */ |
| Identifier[] packages; // array of Identifier's representing packages |
| Identifier id; // module Identifier |
| Identifier aliasId; |
| int isstatic; // !=0 if static import |
| Visibility visibility; |
| |
| // Pairs of alias=name to bind into current namespace |
| Identifiers names; |
| Identifiers aliases; |
| |
| Module mod; |
| Package pkg; // leftmost package/module |
| |
| // corresponding AliasDeclarations for alias=name pairs |
| AliasDeclarations aliasdecls; |
| |
| extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic) |
| { |
| Identifier selectIdent() |
| { |
| // select Dsymbol identifier (bracketed) |
| if (aliasId) |
| { |
| // import [aliasId] = std.stdio; |
| return aliasId; |
| } |
| else if (packages.length > 0) |
| { |
| // import [std].stdio; |
| return packages[0]; |
| } |
| else |
| { |
| // import [id]; |
| return id; |
| } |
| } |
| |
| super(loc, selectIdent()); |
| |
| assert(id); |
| version (none) |
| { |
| printf("Import::Import("); |
| foreach (id; packages) |
| { |
| printf("%s.", id.toChars()); |
| } |
| printf("%s)\n", id.toChars()); |
| } |
| this.packages = packages; |
| this.id = id; |
| this.aliasId = aliasId; |
| this.isstatic = isstatic; |
| this.visibility = Visibility.Kind.private_; // default to private |
| } |
| |
| extern (D) void addAlias(Identifier name, Identifier _alias) |
| { |
| if (isstatic) |
| error("cannot have an import bind list"); |
| if (!aliasId) |
| this.ident = null; // make it an anonymous import |
| names.push(name); |
| aliases.push(_alias); |
| } |
| |
| override const(char)* kind() const |
| { |
| return isstatic ? "static import" : "import"; |
| } |
| |
| override Visibility visible() pure nothrow @nogc @safe |
| { |
| return visibility; |
| } |
| |
| // copy only syntax trees |
| override Import syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| auto si = new Import(loc, packages, id, aliasId, isstatic); |
| si.comment = comment; |
| for (size_t i = 0; i < names.dim; i++) |
| { |
| si.addAlias(names[i], aliases[i]); |
| } |
| return si; |
| } |
| |
| /******************************* |
| * Load this module. |
| * Returns: |
| * true for errors, false for success |
| */ |
| bool load(Scope* sc) |
| { |
| //printf("Import::load('%s') %p\n", toPrettyChars(), this); |
| // See if existing module |
| const errors = global.errors; |
| DsymbolTable dst = Package.resolve(packages, null, &pkg); |
| version (none) |
| { |
| if (pkg && pkg.isModule()) |
| { |
| .error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars()); |
| mod = pkg.isModule(); // Error recovery - treat as import of that module |
| return true; |
| } |
| } |
| Dsymbol s = dst.lookup(id); |
| if (s) |
| { |
| if (s.isModule()) |
| mod = cast(Module)s; |
| else |
| { |
| if (s.isAliasDeclaration()) |
| { |
| .error(loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), id.toChars()); |
| } |
| else if (Package p = s.isPackage()) |
| { |
| if (p.isPkgMod == PKG.unknown) |
| { |
| uint preverrors = global.errors; |
| mod = Module.load(loc, packages, id); |
| if (!mod) |
| p.isPkgMod = PKG.package_; |
| else |
| { |
| // mod is a package.d, or a normal module which conflicts with the package name. |
| if (mod.isPackageFile) |
| mod.tag = p.tag; // reuse the same package tag |
| else |
| { |
| // show error if Module.load does not |
| if (preverrors == global.errors) |
| .error(loc, "%s `%s` from file %s conflicts with %s `%s`", mod.kind(), mod.toPrettyChars(), mod.srcfile.toChars, p.kind(), p.toPrettyChars()); |
| return true; |
| } |
| } |
| } |
| else |
| { |
| mod = p.isPackageMod(); |
| } |
| if (!mod) |
| { |
| .error(loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), id.toChars()); |
| } |
| } |
| else if (pkg) |
| { |
| .error(loc, "can only import from a module, not from package `%s.%s`", pkg.toPrettyChars(), id.toChars()); |
| } |
| else |
| { |
| .error(loc, "can only import from a module, not from package `%s`", id.toChars()); |
| } |
| } |
| } |
| if (!mod) |
| { |
| // Load module |
| mod = Module.load(loc, packages, id); |
| if (mod) |
| { |
| // id may be different from mod.ident, if so then insert alias |
| dst.insert(id, mod); |
| } |
| } |
| if (mod && !mod.importedFrom) |
| mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule; |
| if (!pkg) |
| { |
| if (mod && mod.isPackageFile) |
| { |
| // one level depth package.d file (import pkg; ./pkg/package.d) |
| // it's necessary to use the wrapping Package already created |
| pkg = mod.pkg; |
| } |
| else |
| pkg = mod; |
| } |
| //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); |
| return global.errors != errors; |
| } |
| |
| override void importAll(Scope* sc) |
| { |
| if (mod) return; // Already done |
| |
| /* |
| * https://issues.dlang.org/show_bug.cgi?id=15525 |
| * |
| * Loading the import has failed, |
| * most likely because of parsing errors. |
| * Therefore we cannot trust the resulting AST. |
| */ |
| if (load(sc)) return; |
| |
| if (!mod) return; // Failed |
| |
| if (sc.stc & STC.static_) |
| isstatic = true; |
| mod.importAll(null); |
| mod.checkImportDeprecation(loc, sc); |
| if (sc.explicitVisibility) |
| visibility = sc.visibility; |
| if (!isstatic && !aliasId && !names.dim) |
| sc.scopesym.importScope(mod, visibility); |
| // Enable access to pkgs/mod as soon as posible, because compiler |
| // can traverse them before the import gets semantic (Issue: 21501) |
| if (!aliasId && !names.dim) |
| addPackageAccess(sc.scopesym); |
| } |
| |
| /******************************* |
| * Mark the imported packages as accessible from the current |
| * scope. This access check is necessary when using FQN b/c |
| * we're using a single global package tree. |
| * https://issues.dlang.org/show_bug.cgi?id=313 |
| */ |
| extern (D) void addPackageAccess(ScopeDsymbol scopesym) |
| { |
| //printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this); |
| if (packages.length > 0) |
| { |
| // import a.b.c.d; |
| auto p = pkg; // a |
| scopesym.addAccessiblePackage(p, visibility); |
| foreach (id; packages[1 .. $]) // [b, c] |
| { |
| auto sym = p.symtab.lookup(id); |
| // https://issues.dlang.org/show_bug.cgi?id=17991 |
| // An import of truly empty file/package can happen |
| // https://issues.dlang.org/show_bug.cgi?id=20151 |
| // Package in the path conflicts with a module name |
| if (sym is null) |
| break; |
| // https://issues.dlang.org/show_bug.cgi?id=23327 |
| // Package conflicts with symbol of the same name |
| p = sym.isPackage(); |
| if (p is null) |
| break; |
| scopesym.addAccessiblePackage(p, visibility); |
| } |
| } |
| scopesym.addAccessiblePackage(mod, visibility); // d |
| } |
| |
| override Dsymbol toAlias() |
| { |
| if (aliasId) |
| return mod; |
| return this; |
| } |
| |
| /***************************** |
| * Add import to sd's symbol table. |
| */ |
| override void addMember(Scope* sc, ScopeDsymbol sd) |
| { |
| //printf("Import.addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd.toChars(), sc); |
| if (names.dim == 0) |
| return Dsymbol.addMember(sc, sd); |
| if (aliasId) |
| Dsymbol.addMember(sc, sd); |
| /* Instead of adding the import to sd's symbol table, |
| * add each of the alias=name pairs |
| */ |
| for (size_t i = 0; i < names.dim; i++) |
| { |
| Identifier name = names[i]; |
| Identifier _alias = aliases[i]; |
| if (!_alias) |
| _alias = name; |
| auto tname = new TypeIdentifier(loc, name); |
| auto ad = new AliasDeclaration(loc, _alias, tname); |
| ad._import = this; |
| ad.addMember(sc, sd); |
| aliasdecls.push(ad); |
| } |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| Dsymbol.setScope(sc); |
| if (aliasdecls.dim) |
| { |
| if (!mod) |
| importAll(sc); |
| |
| sc = sc.push(mod); |
| sc.visibility = visibility; |
| foreach (ad; aliasdecls) |
| ad.setScope(sc); |
| sc = sc.pop(); |
| } |
| } |
| |
| override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) |
| { |
| //printf("%s.Import.search(ident = '%s', flags = x%x)\n", toChars(), ident.toChars(), flags); |
| if (!pkg) |
| { |
| load(null); |
| mod.importAll(null); |
| mod.dsymbolSemantic(null); |
| } |
| // Forward it to the package/module |
| return pkg.search(loc, ident, flags); |
| } |
| |
| override bool overloadInsert(Dsymbol s) |
| { |
| /* Allow multiple imports with the same package base, but disallow |
| * alias collisions |
| * https://issues.dlang.org/show_bug.cgi?id=5412 |
| */ |
| assert(ident && ident == s.ident); |
| Import imp; |
| if (!aliasId && (imp = s.isImport()) !is null && !imp.aliasId) |
| return true; |
| else |
| return false; |
| } |
| |
| override inout(Import) isImport() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |