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 }