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