1 /**
2  * A `Dsymbol` representing a renamed import.
3  *
4  * Copyright:   Copyright (C) 1999-2020 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     Identifiers* packages;  // array of Identifier's representing packages
34     Identifier id;          // module Identifier
35     Identifier aliasId;
36     int isstatic;           // !=0 if static import
37     Prot protection;
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, Identifiers* 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 && packages.dim)
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             if (packages && packages.dim)
78             {
79                 for (size_t i = 0; i < packages.dim; i++)
80                 {
81                     Identifier id = (*packages)[i];
82                     printf("%s.", id.toChars());
83                 }
84             }
85             printf("%s)\n", id.toChars());
86         }
87         this.packages = packages;
88         this.id = id;
89         this.aliasId = aliasId;
90         this.isstatic = isstatic;
91         this.protection = Prot.Kind.private_; // default to private
92     }
93 
94     extern (D) void addAlias(Identifier name, Identifier _alias)
95     {
96         if (isstatic)
97             error("cannot have an import bind list");
98         if (!aliasId)
99             this.ident = null; // make it an anonymous import
100         names.push(name);
101         aliases.push(_alias);
102     }
103 
104     override const(char)* kind() const
105     {
106         return isstatic ? "static import" : "import";
107     }
108 
109     override Prot prot() pure nothrow @nogc @safe
110     {
111         return protection;
112     }
113 
114     // copy only syntax trees
115     override Dsymbol syntaxCopy(Dsymbol s)
116     {
117         assert(!s);
118         auto si = new Import(loc, packages, id, aliasId, isstatic);
119         si.comment = comment;
120         for (size_t i = 0; i < names.dim; i++)
121         {
122             si.addAlias(names[i], aliases[i]);
123         }
124         return si;
125     }
126 
127     /*******************************
128      * Load this module.
129      * Returns:
130      *  true for errors, false for success
131      */
132     bool load(Scope* sc)
133     {
134         //printf("Import::load('%s') %p\n", toPrettyChars(), this);
135         // See if existing module
136         const errors = global.errors;
137         DsymbolTable dst = Package.resolve(packages, null, &pkg);
138         version (none)
139         {
140             if (pkg && pkg.isModule())
141             {
142                 .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());
143                 mod = pkg.isModule(); // Error recovery - treat as import of that module
144                 return true;
145             }
146         }
147         Dsymbol s = dst.lookup(id);
148         if (s)
149         {
150             if (s.isModule())
151                 mod = cast(Module)s;
152             else
153             {
154                 if (s.isAliasDeclaration())
155                 {
156                     .error(loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), id.toChars());
157                 }
158                 else if (Package p = s.isPackage())
159                 {
160                     if (p.isPkgMod == PKG.unknown)
161                     {
162                         uint preverrors = global.errors;
163                         mod = Module.load(loc, packages, id);
164                         if (!mod)
165                             p.isPkgMod = PKG.package_;
166                         else
167                         {
168                             // mod is a package.d, or a normal module which conflicts with the package name.
169                             if (mod.isPackageFile)
170                                 mod.tag = p.tag; // reuse the same package tag
171                             else
172                             {
173                                 // show error if Module.load does not
174                                 if (preverrors == global.errors)
175                                     .error(loc, "%s `%s` from file %s conflicts with %s `%s`", mod.kind(), mod.toPrettyChars(), mod.srcfile.toChars, p.kind(), p.toPrettyChars());
176                                 return true;
177                             }
178                         }
179                     }
180                     else
181                     {
182                         mod = p.isPackageMod();
183                     }
184                     if (!mod)
185                     {
186                         .error(loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), id.toChars());
187                     }
188                 }
189                 else if (pkg)
190                 {
191                     .error(loc, "can only import from a module, not from package `%s.%s`", pkg.toPrettyChars(), id.toChars());
192                 }
193                 else
194                 {
195                     .error(loc, "can only import from a module, not from package `%s`", id.toChars());
196                 }
197             }
198         }
199         if (!mod)
200         {
201             // Load module
202             mod = Module.load(loc, packages, id);
203             if (mod)
204             {
205                 // id may be different from mod.ident, if so then insert alias
206                 dst.insert(id, mod);
207             }
208         }
209         if (mod && !mod.importedFrom)
210             mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule;
211         if (!pkg)
212         {
213             if (mod && mod.isPackageFile)
214             {
215                 // one level depth package.d file (import pkg; ./pkg/package.d)
216                 // it's necessary to use the wrapping Package already created
217                 pkg = mod.pkg;
218             }
219             else
220                 pkg = mod;
221         }
222         //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg);
223         return global.errors != errors;
224     }
225 
226     override void importAll(Scope* sc)
227     {
228         if (mod) return; // Already done
229         load(sc);
230         if (!mod) return; // Failed
231 
232         if (sc.stc & STC.static_)
233             isstatic = true;
234         mod.importAll(null);
235         mod.checkImportDeprecation(loc, sc);
236         if (sc.explicitProtection)
237             protection = sc.protection;
238         if (!isstatic && !aliasId && !names.dim)
239             sc.scopesym.importScope(mod, protection);
240     }
241 
242     override Dsymbol toAlias()
243     {
244         if (aliasId)
245             return mod;
246         return this;
247     }
248 
249     /*****************************
250      * Add import to sd's symbol table.
251      */
252     override void addMember(Scope* sc, ScopeDsymbol sd)
253     {
254         //printf("Import.addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd.toChars(), sc);
255         if (names.dim == 0)
256             return Dsymbol.addMember(sc, sd);
257         if (aliasId)
258             Dsymbol.addMember(sc, sd);
259         /* Instead of adding the import to sd's symbol table,
260          * add each of the alias=name pairs
261          */
262         for (size_t i = 0; i < names.dim; i++)
263         {
264             Identifier name = names[i];
265             Identifier _alias = aliases[i];
266             if (!_alias)
267                 _alias = name;
268             auto tname = new TypeIdentifier(loc, name);
269             auto ad = new AliasDeclaration(loc, _alias, tname);
270             ad._import = this;
271             ad.addMember(sc, sd);
272             aliasdecls.push(ad);
273         }
274     }
275 
276     override void setScope(Scope* sc)
277     {
278         Dsymbol.setScope(sc);
279         if (aliasdecls.dim)
280         {
281             if (!mod)
282                 importAll(sc);
283 
284             sc = sc.push(mod);
285             sc.protection = protection;
286             foreach (ad; aliasdecls)
287                 ad.setScope(sc);
288             sc = sc.pop();
289         }
290     }
291 
292     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
293     {
294         //printf("%s.Import.search(ident = '%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
295         if (!pkg)
296         {
297             load(null);
298             mod.importAll(null);
299             mod.dsymbolSemantic(null);
300         }
301         // Forward it to the package/module
302         return pkg.search(loc, ident, flags);
303     }
304 
305     override bool overloadInsert(Dsymbol s)
306     {
307         /* Allow multiple imports with the same package base, but disallow
308          * alias collisions
309          * https://issues.dlang.org/show_bug.cgi?id=5412
310          */
311         assert(ident && ident == s.ident);
312         Import imp;
313         if (!aliasId && (imp = s.isImport()) !is null && !imp.aliasId)
314             return true;
315         else
316             return false;
317     }
318 
319     override inout(Import) isImport() inout
320     {
321         return this;
322     }
323 
324     override void accept(Visitor v)
325     {
326         v.visit(this);
327     }
328 }