1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _dimport.d)
9  */
10 
11 module ddmd.dimport;
12 
13 import core.stdc..string;
14 import core.stdc.stdio;
15 
16 import ddmd.arraytypes;
17 import ddmd.declaration;
18 import ddmd.dmodule;
19 import ddmd.dscope;
20 import ddmd.dsymbol;
21 import ddmd.errors;
22 import ddmd.expression;
23 import ddmd.globals;
24 import ddmd.hdrgen;
25 import ddmd.id;
26 import ddmd.identifier;
27 import ddmd.mtype;
28 import ddmd.root.outbuffer;
29 import ddmd.utils;
30 import ddmd.visitor;
31 
32 /***********************************************************
33  */
34 extern (C++) final class Import : Dsymbol
35 {
36     /* static import aliasId = pkg1.pkg2.id : alias1 = name1, alias2 = name2;
37      */
38     Identifiers* packages;  // array of Identifier's representing packages
39     Identifier id;          // module Identifier
40     Identifier aliasId;
41     int isstatic;           // !=0 if static import
42     Prot protection;
43 
44     // Pairs of alias=name to bind into current namespace
45     Identifiers names;
46     Identifiers aliases;
47 
48     Module mod;
49     Package pkg;            // leftmost package/module
50 
51     // corresponding AliasDeclarations for alias=name pairs
52     AliasDeclarations aliasdecls;
53 
54     extern (D) this(Loc loc, Identifiers* packages, Identifier id, Identifier aliasId, int isstatic)
55     {
56         super(null);
57         assert(id);
58         version (none)
59         {
60             printf("Import::Import(");
61             if (packages && packages.dim)
62             {
63                 for (size_t i = 0; i < packages.dim; i++)
64                 {
65                     Identifier id = (*packages)[i];
66                     printf("%s.", id.toChars());
67                 }
68             }
69             printf("%s)\n", id.toChars());
70         }
71         this.loc = loc;
72         this.packages = packages;
73         this.id = id;
74         this.aliasId = aliasId;
75         this.isstatic = isstatic;
76         this.protection = PROTprivate; // default to private
77         // Set symbol name (bracketed)
78         if (aliasId)
79         {
80             // import [cstdio] = std.stdio;
81             this.ident = aliasId;
82         }
83         else if (packages && packages.dim)
84         {
85             // import [std].stdio;
86             this.ident = (*packages)[0];
87         }
88         else
89         {
90             // import [foo];
91             this.ident = id;
92         }
93     }
94 
95     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 ? cast(char*)"static import" : cast(char*)"import";
108     }
109 
110     override Prot prot()
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         for (size_t i = 0; i < names.dim; i++)
121         {
122             si.addAlias(names[i], aliases[i]);
123         }
124         return si;
125     }
126 
127     void load(Scope* sc)
128     {
129         //printf("Import::load('%s') %p\n", toPrettyChars(), this);
130         // See if existing module
131         DsymbolTable dst = Package.resolve(packages, null, &pkg);
132         version (none)
133         {
134             if (pkg && pkg.isModule())
135             {
136                 .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());
137                 mod = pkg.isModule(); // Error recovery - treat as import of that module
138                 return;
139             }
140         }
141         Dsymbol s = dst.lookup(id);
142         if (s)
143         {
144             if (s.isModule())
145                 mod = cast(Module)s;
146             else
147             {
148                 if (s.isAliasDeclaration())
149                 {
150                     .error(loc, "%s %s conflicts with %s", s.kind(), s.toPrettyChars(), id.toChars());
151                 }
152                 else if (Package p = s.isPackage())
153                 {
154                     if (p.isPkgMod == PKGunknown)
155                     {
156                         mod = Module.load(loc, packages, id);
157                         if (!mod)
158                             p.isPkgMod = PKGpackage;
159                         else
160                         {
161                             // mod is a package.d, or a normal module which conflicts with the package name.
162                             assert(mod.isPackageFile == (p.isPkgMod == PKGmodule));
163                             if (mod.isPackageFile)
164                                 mod.tag = p.tag; // reuse the same package tag
165                         }
166                     }
167                     else
168                     {
169                         mod = p.isPackageMod();
170                     }
171                     if (!mod)
172                     {
173                         .error(loc, "can only import from a module, not from package %s.%s", p.toPrettyChars(), id.toChars());
174                     }
175                 }
176                 else if (pkg)
177                 {
178                     .error(loc, "can only import from a module, not from package %s.%s", pkg.toPrettyChars(), id.toChars());
179                 }
180                 else
181                 {
182                     .error(loc, "can only import from a module, not from package %s", id.toChars());
183                 }
184             }
185         }
186         if (!mod)
187         {
188             // Load module
189             mod = Module.load(loc, packages, id);
190             if (mod)
191             {
192                 // id may be different from mod.ident, if so then insert alias
193                 dst.insert(id, mod);
194             }
195         }
196         if (mod && !mod.importedFrom)
197             mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule;
198         if (!pkg)
199             pkg = mod;
200         //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg);
201     }
202 
203     override void importAll(Scope* sc)
204     {
205         if (!mod)
206         {
207             load(sc);
208             if (mod) // if successfully loaded module
209             {
210                 if (mod.md && mod.md.isdeprecated)
211                 {
212                     Expression msg = mod.md.msg;
213                     if (StringExp se = msg ? msg.toStringExp() : null)
214                         mod.deprecation(loc, "is deprecated - %s", se..string);
215                     else
216                         mod.deprecation(loc, "is deprecated");
217                 }
218                 mod.importAll(null);
219                 if (sc.explicitProtection)
220                     protection = sc.protection;
221                 if (!isstatic && !aliasId && !names.dim)
222                 {
223                     sc.scopesym.importScope(mod, protection);
224                 }
225             }
226         }
227     }
228 
229     override void semantic(Scope* sc)
230     {
231         //printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars());
232         if (_scope)
233         {
234             sc = _scope;
235             _scope = null;
236         }
237         // Load if not already done so
238         if (!mod)
239         {
240             load(sc);
241             if (mod)
242                 mod.importAll(null);
243         }
244         if (mod)
245         {
246             // Modules need a list of each imported module
247             //printf("%s imports %s\n", sc.module.toChars(), mod.toChars());
248             sc._module.aimports.push(mod);
249 
250             if (sc.explicitProtection)
251                 protection = sc.protection;
252 
253             if (!aliasId && !names.dim) // neither a selective nor a renamed import
254             {
255                 ScopeDsymbol scopesym;
256                 for (Scope* scd = sc; scd; scd = scd.enclosing)
257                 {
258                     if (!scd.scopesym)
259                         continue;
260                     scopesym = scd.scopesym;
261                     break;
262                 }
263 
264                 if (!isstatic)
265                 {
266                     scopesym.importScope(mod, protection);
267                 }
268 
269                 // Mark the imported packages as accessible from the current
270                 // scope. This access check is necessary when using FQN b/c
271                 // we're using a single global package tree. See Bugzilla 313.
272                 if (packages)
273                 {
274                     // import a.b.c.d;
275                     auto p = pkg; // a
276                     scopesym.addAccessiblePackage(p, protection);
277                     foreach (id; (*packages)[1 .. packages.dim]) // [b, c]
278                     {
279                         p = cast(Package) p.symtab.lookup(id);
280                         scopesym.addAccessiblePackage(p, protection);
281                     }
282                 }
283                 scopesym.addAccessiblePackage(mod, protection); // d
284             }
285 
286             mod.semantic(null);
287             if (mod.needmoduleinfo)
288             {
289                 //printf("module4 %s because of %s\n", sc.module.toChars(), mod.toChars());
290                 sc._module.needmoduleinfo = 1;
291             }
292 
293             sc = sc.push(mod);
294             sc.protection = protection;
295             for (size_t i = 0; i < aliasdecls.dim; i++)
296             {
297                 AliasDeclaration ad = aliasdecls[i];
298                 //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope);
299                 if (mod.search(loc, names[i]))
300                 {
301                     ad.semantic(sc);
302                     // If the import declaration is in non-root module,
303                     // analysis of the aliased symbol is deferred.
304                     // Therefore, don't see the ad.aliassym or ad.type here.
305                 }
306                 else
307                 {
308                     Dsymbol s = mod.search_correct(names[i]);
309                     if (s)
310                         mod.error(loc, "import '%s' not found, did you mean %s '%s'?", names[i].toChars(), s.kind(), s.toChars());
311                     else
312                         mod.error(loc, "import '%s' not found", names[i].toChars());
313                     ad.type = Type.terror;
314                 }
315             }
316             sc = sc.pop();
317         }
318 
319         // object self-imports itself, so skip that (Bugzilla 7547)
320         // don't list pseudo modules __entrypoint.d, __main.d (Bugzilla 11117, 11164)
321         if (global.params.moduleDeps !is null && !(id == Id.object && sc._module.ident == Id.object) &&
322             sc._module.ident != Id.entrypoint &&
323             strcmp(sc._module.ident.toChars(), "__main") != 0)
324         {
325             /* The grammar of the file is:
326              *      ImportDeclaration
327              *          ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> "
328              *      ModuleAliasIdentifier ] "\n"
329              *
330              *      BasicImportDeclaration
331              *          ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string"
332              *              " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")"
333              *
334              *      FilePath
335              *          - any string with '(', ')' and '\' escaped with the '\' character
336              */
337             OutBuffer* ob = global.params.moduleDeps;
338             Module imod = sc.instantiatingModule();
339             if (!global.params.moduleDepsFile)
340                 ob.writestring("depsImport ");
341             ob.writestring(imod.toPrettyChars());
342             ob.writestring(" (");
343             escapePath(ob, imod.srcfile.toChars());
344             ob.writestring(") : ");
345             // use protection instead of sc.protection because it couldn't be
346             // resolved yet, see the comment above
347             protectionToBuffer(ob, protection);
348             ob.writeByte(' ');
349             if (isstatic)
350             {
351                 stcToBuffer(ob, STCstatic);
352                 ob.writeByte(' ');
353             }
354             ob.writestring(": ");
355             if (packages)
356             {
357                 for (size_t i = 0; i < packages.dim; i++)
358                 {
359                     Identifier pid = (*packages)[i];
360                     ob.printf("%s.", pid.toChars());
361                 }
362             }
363             ob.writestring(id.toChars());
364             ob.writestring(" (");
365             if (mod)
366                 escapePath(ob, mod.srcfile.toChars());
367             else
368                 ob.writestring("???");
369             ob.writeByte(')');
370             for (size_t i = 0; i < names.dim; i++)
371             {
372                 if (i == 0)
373                     ob.writeByte(':');
374                 else
375                     ob.writeByte(',');
376                 Identifier name = names[i];
377                 Identifier _alias = aliases[i];
378                 if (!_alias)
379                 {
380                     ob.printf("%s", name.toChars());
381                     _alias = name;
382                 }
383                 else
384                     ob.printf("%s=%s", _alias.toChars(), name.toChars());
385             }
386             if (aliasId)
387                 ob.printf(" -> %s", aliasId.toChars());
388             ob.writenl();
389         }
390         //printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg);
391     }
392 
393     override void semantic2(Scope* sc)
394     {
395         //printf("Import::semantic2('%s')\n", toChars());
396         if (mod)
397         {
398             mod.semantic2(null);
399             if (mod.needmoduleinfo)
400             {
401                 //printf("module5 %s because of %s\n", sc.module.toChars(), mod.toChars());
402                 if (sc)
403                     sc._module.needmoduleinfo = 1;
404             }
405         }
406     }
407 
408     override Dsymbol toAlias()
409     {
410         if (aliasId)
411             return mod;
412         return this;
413     }
414 
415     /*****************************
416      * Add import to sd's symbol table.
417      */
418     override void addMember(Scope* sc, ScopeDsymbol sd)
419     {
420         //printf("Import.addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd.toChars(), sc);
421         if (names.dim == 0)
422             return Dsymbol.addMember(sc, sd);
423         if (aliasId)
424             Dsymbol.addMember(sc, sd);
425         /* Instead of adding the import to sd's symbol table,
426          * add each of the alias=name pairs
427          */
428         for (size_t i = 0; i < names.dim; i++)
429         {
430             Identifier name = names[i];
431             Identifier _alias = aliases[i];
432             if (!_alias)
433                 _alias = name;
434             auto tname = new TypeIdentifier(loc, name);
435             auto ad = new AliasDeclaration(loc, _alias, tname);
436             ad._import = this;
437             ad.addMember(sc, sd);
438             aliasdecls.push(ad);
439         }
440     }
441 
442     override void setScope(Scope* sc)
443     {
444         Dsymbol.setScope(sc);
445         if (aliasdecls.dim)
446         {
447             if (!mod)
448                 importAll(sc);
449 
450             sc = sc.push(mod);
451             sc.protection = protection;
452             foreach (ad; aliasdecls)
453                 ad.setScope(sc);
454             sc = sc.pop();
455         }
456     }
457 
458     override Dsymbol search(Loc loc, Identifier ident, int flags = SearchLocalsOnly)
459     {
460         //printf("%s.Import.search(ident = '%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
461         if (!pkg)
462         {
463             load(null);
464             mod.importAll(null);
465             mod.semantic(null);
466         }
467         // Forward it to the package/module
468         return pkg.search(loc, ident, flags);
469     }
470 
471     override bool overloadInsert(Dsymbol s)
472     {
473         /* Allow multiple imports with the same package base, but disallow
474          * alias collisions (Bugzilla 5412).
475          */
476         assert(ident && ident == s.ident);
477         Import imp;
478         if (!aliasId && (imp = s.isImport()) !is null && !imp.aliasId)
479             return true;
480         else
481             return false;
482     }
483 
484     override inout(Import) isImport() inout
485     {
486         return this;
487     }
488 
489     override void accept(Visitor v)
490     {
491         v.visit(this);
492     }
493 }