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