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