xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/dimport.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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