1 /** 2 * A `Dsymbol` representing a renamed import. 3 * 4 * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d, _dimport.d) 8 * Documentation: https://dlang.org/phobos/dmd_dimport.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dimport.d 10 */ 11 12 module dmd.dimport; 13 14 import dmd.arraytypes; 15 import dmd.astenums; 16 import dmd.declaration; 17 import dmd.dmodule; 18 import dmd.dscope; 19 import dmd.dsymbol; 20 import dmd.dsymbolsem; 21 import dmd.errors; 22 import dmd.expression; 23 import dmd.globals; 24 import dmd.identifier; 25 import dmd.mtype; 26 import dmd.visitor; 27 28 /*********************************************************** 29 */ 30 extern (C++) final class Import : Dsymbol 31 { 32 /* static import aliasId = pkg1.pkg2.id : alias1 = name1, alias2 = name2; 33 */ 34 Identifier[] packages; // array of Identifier's representing packages 35 Identifier id; // module Identifier 36 Identifier aliasId; 37 int isstatic; // !=0 if static import 38 Visibility visibility; 39 40 // Pairs of alias=name to bind into current namespace 41 Identifiers names; 42 Identifiers aliases; 43 44 Module mod; 45 Package pkg; // leftmost package/module 46 47 // corresponding AliasDeclarations for alias=name pairs 48 AliasDeclarations aliasdecls; 49 this(const ref Loc loc,Identifier[]packages,Identifier id,Identifier aliasId,int isstatic)50 extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic) 51 { 52 Identifier selectIdent() 53 { 54 // select Dsymbol identifier (bracketed) 55 if (aliasId) 56 { 57 // import [aliasId] = std.stdio; 58 return aliasId; 59 } 60 else if (packages.length > 0) 61 { 62 // import [std].stdio; 63 return packages[0]; 64 } 65 else 66 { 67 // import [id]; 68 return id; 69 } 70 } 71 72 super(loc, selectIdent()); 73 74 assert(id); 75 version (none) 76 { 77 printf("Import::Import("); 78 foreach (id; packages) 79 { 80 printf("%s.", id.toChars()); 81 } 82 printf("%s)\n", id.toChars()); 83 } 84 this.packages = packages; 85 this.id = id; 86 this.aliasId = aliasId; 87 this.isstatic = isstatic; 88 this.visibility = Visibility.Kind.private_; // default to private 89 } 90 addAlias(Identifier name,Identifier _alias)91 extern (D) void addAlias(Identifier name, Identifier _alias) 92 { 93 if (isstatic) 94 error("cannot have an import bind list"); 95 if (!aliasId) 96 this.ident = null; // make it an anonymous import 97 names.push(name); 98 aliases.push(_alias); 99 } 100 kind()101 override const(char)* kind() const 102 { 103 return isstatic ? "static import" : "import"; 104 } 105 visible()106 override Visibility visible() pure nothrow @nogc @safe 107 { 108 return visibility; 109 } 110 111 // copy only syntax trees syntaxCopy(Dsymbol s)112 override Import syntaxCopy(Dsymbol s) 113 { 114 assert(!s); 115 auto si = new Import(loc, packages, id, aliasId, isstatic); 116 si.comment = comment; 117 for (size_t i = 0; i < names.dim; i++) 118 { 119 si.addAlias(names[i], aliases[i]); 120 } 121 return si; 122 } 123 124 /******************************* 125 * Load this module. 126 * Returns: 127 * true for errors, false for success 128 */ load(Scope * sc)129 bool load(Scope* sc) 130 { 131 //printf("Import::load('%s') %p\n", toPrettyChars(), this); 132 // See if existing module 133 const errors = global.errors; 134 DsymbolTable dst = Package.resolve(packages, null, &pkg); 135 version (none) 136 { 137 if (pkg && pkg.isModule()) 138 { 139 .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()); 140 mod = pkg.isModule(); // Error recovery - treat as import of that module 141 return true; 142 } 143 } 144 Dsymbol s = dst.lookup(id); 145 if (s) 146 { 147 if (s.isModule()) 148 mod = cast(Module)s; 149 else 150 { 151 if (s.isAliasDeclaration()) 152 { 153 .error(loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), id.toChars()); 154 } 155 else if (Package p = s.isPackage()) 156 { 157 if (p.isPkgMod == PKG.unknown) 158 { 159 uint preverrors = global.errors; 160 mod = Module.load(loc, packages, id); 161 if (!mod) 162 p.isPkgMod = PKG.package_; 163 else 164 { 165 // mod is a package.d, or a normal module which conflicts with the package name. 166 if (mod.isPackageFile) 167 mod.tag = p.tag; // reuse the same package tag 168 else 169 { 170 // show error if Module.load does not 171 if (preverrors == global.errors) 172 .error(loc, "%s `%s` from file %s conflicts with %s `%s`", mod.kind(), mod.toPrettyChars(), mod.srcfile.toChars, p.kind(), p.toPrettyChars()); 173 return true; 174 } 175 } 176 } 177 else 178 { 179 mod = p.isPackageMod(); 180 } 181 if (!mod) 182 { 183 .error(loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), id.toChars()); 184 } 185 } 186 else if (pkg) 187 { 188 .error(loc, "can only import from a module, not from package `%s.%s`", pkg.toPrettyChars(), id.toChars()); 189 } 190 else 191 { 192 .error(loc, "can only import from a module, not from package `%s`", id.toChars()); 193 } 194 } 195 } 196 if (!mod) 197 { 198 // Load module 199 mod = Module.load(loc, packages, id); 200 if (mod) 201 { 202 // id may be different from mod.ident, if so then insert alias 203 dst.insert(id, mod); 204 } 205 } 206 if (mod && !mod.importedFrom) 207 mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule; 208 if (!pkg) 209 { 210 if (mod && mod.isPackageFile) 211 { 212 // one level depth package.d file (import pkg; ./pkg/package.d) 213 // it's necessary to use the wrapping Package already created 214 pkg = mod.pkg; 215 } 216 else 217 pkg = mod; 218 } 219 //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); 220 return global.errors != errors; 221 } 222 importAll(Scope * sc)223 override void importAll(Scope* sc) 224 { 225 if (mod) return; // Already done 226 load(sc); 227 if (!mod) return; // Failed 228 229 if (sc.stc & STC.static_) 230 isstatic = true; 231 mod.importAll(null); 232 mod.checkImportDeprecation(loc, sc); 233 if (sc.explicitVisibility) 234 visibility = sc.visibility; 235 if (!isstatic && !aliasId && !names.dim) 236 sc.scopesym.importScope(mod, visibility); 237 // Enable access to pkgs/mod as soon as posible, because compiler 238 // can traverse them before the import gets semantic (Issue: 21501) 239 if (!aliasId && !names.dim) 240 addPackageAccess(sc.scopesym); 241 } 242 243 /******************************* 244 * Mark the imported packages as accessible from the current 245 * scope. This access check is necessary when using FQN b/c 246 * we're using a single global package tree. 247 * https://issues.dlang.org/show_bug.cgi?id=313 248 */ addPackageAccess(ScopeDsymbol scopesym)249 extern (D) void addPackageAccess(ScopeDsymbol scopesym) 250 { 251 //printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this); 252 if (packages.length > 0) 253 { 254 // import a.b.c.d; 255 auto p = pkg; // a 256 scopesym.addAccessiblePackage(p, visibility); 257 foreach (id; packages[1 .. $]) // [b, c] 258 { 259 p = cast(Package) p.symtab.lookup(id); 260 // https://issues.dlang.org/show_bug.cgi?id=17991 261 // An import of truly empty file/package can happen 262 // https://issues.dlang.org/show_bug.cgi?id=20151 263 // Package in the path conflicts with a module name 264 if (p is null) 265 break; 266 scopesym.addAccessiblePackage(p, visibility); 267 } 268 } 269 scopesym.addAccessiblePackage(mod, visibility); // d 270 } 271 toAlias()272 override Dsymbol toAlias() 273 { 274 if (aliasId) 275 return mod; 276 return this; 277 } 278 279 /***************************** 280 * Add import to sd's symbol table. 281 */ addMember(Scope * sc,ScopeDsymbol sd)282 override void addMember(Scope* sc, ScopeDsymbol sd) 283 { 284 //printf("Import.addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd.toChars(), sc); 285 if (names.dim == 0) 286 return Dsymbol.addMember(sc, sd); 287 if (aliasId) 288 Dsymbol.addMember(sc, sd); 289 /* Instead of adding the import to sd's symbol table, 290 * add each of the alias=name pairs 291 */ 292 for (size_t i = 0; i < names.dim; i++) 293 { 294 Identifier name = names[i]; 295 Identifier _alias = aliases[i]; 296 if (!_alias) 297 _alias = name; 298 auto tname = new TypeIdentifier(loc, name); 299 auto ad = new AliasDeclaration(loc, _alias, tname); 300 ad._import = this; 301 ad.addMember(sc, sd); 302 aliasdecls.push(ad); 303 } 304 } 305 setScope(Scope * sc)306 override void setScope(Scope* sc) 307 { 308 Dsymbol.setScope(sc); 309 if (aliasdecls.dim) 310 { 311 if (!mod) 312 importAll(sc); 313 314 sc = sc.push(mod); 315 sc.visibility = visibility; 316 foreach (ad; aliasdecls) 317 ad.setScope(sc); 318 sc = sc.pop(); 319 } 320 } 321 322 override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly) 323 { 324 //printf("%s.Import.search(ident = '%s', flags = x%x)\n", toChars(), ident.toChars(), flags); 325 if (!pkg) 326 { 327 load(null); 328 mod.importAll(null); 329 mod.dsymbolSemantic(null); 330 } 331 // Forward it to the package/module 332 return pkg.search(loc, ident, flags); 333 } 334 overloadInsert(Dsymbol s)335 override bool overloadInsert(Dsymbol s) 336 { 337 /* Allow multiple imports with the same package base, but disallow 338 * alias collisions 339 * https://issues.dlang.org/show_bug.cgi?id=5412 340 */ 341 assert(ident && ident == s.ident); 342 Import imp; 343 if (!aliasId && (imp = s.isImport()) !is null && !imp.aliasId) 344 return true; 345 else 346 return false; 347 } 348 inout(Import)349 override inout(Import) isImport() inout 350 { 351 return this; 352 } 353 accept(Visitor v)354 override void accept(Visitor v) 355 { 356 v.visit(this); 357 } 358 } 359