1 /**
2  * Defines a package and module.
3  *
4  * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules)
5  *
6  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d, _dmodule.d)
10  * Documentation:  https://dlang.org/phobos/dmd_dmodule.html
11  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmodule.d
12  */
13 
14 module dmd.dmodule;
15 
16 import core.stdc.stdio;
17 import core.stdc.stdlib;
18 import core.stdc.string;
19 import dmd.aggregate;
20 import dmd.arraytypes;
21 import dmd.astcodegen;
22 import dmd.compiler;
23 import dmd.gluelayer;
24 import dmd.dimport;
25 import dmd.dmacro;
26 import dmd.doc;
27 import dmd.dscope;
28 import dmd.dsymbol;
29 import dmd.dsymbolsem;
30 import dmd.errors;
31 import dmd.expression;
32 import dmd.expressionsem;
33 import dmd.globals;
34 import dmd.id;
35 import dmd.identifier;
36 import dmd.parse;
37 import dmd.root.file;
38 import dmd.root.filename;
39 import dmd.root.outbuffer;
40 import dmd.root.port;
41 import dmd.root.rmem;
42 import dmd.root.rootobject;
43 import dmd.root.string;
44 import dmd.semantic2;
45 import dmd.semantic3;
46 import dmd.visitor;
47 
48 version(Windows) {
49     extern (C) char* getcwd(char* buffer, size_t maxlen);
50 } else {
51     import core.sys.posix.unistd : getcwd;
52 }
53 
54 /* ===========================  ===================== */
55 /********************************************
56  * Look for the source file if it's different from filename.
57  * Look for .di, .d, directory, and along global.path.
58  * Does not open the file.
59  * Input:
60  *      filename        as supplied by the user
61  *      global.path
62  * Returns:
63  *      NULL if it's not different from filename.
64  */
65 private const(char)[] lookForSourceFile(const(char)[] filename)
66 {
67     /* Search along global.path for .di file, then .d file.
68      */
69     const sdi = FileName.forceExt(filename, global.hdr_ext);
70     if (FileName.exists(sdi) == 1)
71         return sdi;
72     scope(exit) FileName.free(sdi.ptr);
73     const sd = FileName.forceExt(filename, global.mars_ext);
74     if (FileName.exists(sd) == 1)
75         return sd;
76     scope(exit) FileName.free(sd.ptr);
77     if (FileName.exists(filename) == 2)
78     {
79         /* The filename exists and it's a directory.
80          * Therefore, the result should be: filename/package.d
81          * iff filename/package.d is a file
82          */
83         const ni = FileName.combine(filename, "package.di");
84         if (FileName.exists(ni) == 1)
85             return ni;
86         FileName.free(ni.ptr);
87         const n = FileName.combine(filename, "package.d");
88         if (FileName.exists(n) == 1)
89             return n;
90         FileName.free(n.ptr);
91     }
92     if (FileName.absolute(filename))
93         return null;
94     if (!global.path)
95         return null;
96     for (size_t i = 0; i < global.path.dim; i++)
97     {
98         const p = (*global.path)[i].toDString();
99         const(char)[] n = FileName.combine(p, sdi);
100         if (FileName.exists(n) == 1) {
101             return n;
102         }
103         FileName.free(n.ptr);
104         n = FileName.combine(p, sd);
105         if (FileName.exists(n) == 1) {
106             return n;
107         }
108         FileName.free(n.ptr);
109         const b = FileName.removeExt(filename);
110         n = FileName.combine(p, b);
111         FileName.free(b.ptr);
112         if (FileName.exists(n) == 2)
113         {
114             const n2i = FileName.combine(n, "package.di");
115             if (FileName.exists(n2i) == 1)
116                 return n2i;
117             FileName.free(n2i.ptr);
118             const n2 = FileName.combine(n, "package.d");
119             if (FileName.exists(n2) == 1) {
120                 return n2;
121             }
122             FileName.free(n2.ptr);
123         }
124         FileName.free(n.ptr);
125     }
126     return null;
127 }
128 
129 // function used to call semantic3 on a module's dependencies
130 void semantic3OnDependencies(Module m)
131 {
132     if (!m)
133         return;
134 
135     if (m.semanticRun > PASS.semantic3)
136         return;
137 
138     m.semantic3(null);
139 
140     foreach (i; 1 .. m.aimports.dim)
141         semantic3OnDependencies(m.aimports[i]);
142 }
143 
144 /**
145  * Remove generated .di files on error and exit
146  */
147 void removeHdrFilesAndFail(ref Param params, ref Modules modules)
148 {
149     if (params.doHdrGeneration)
150     {
151         foreach (m; modules)
152         {
153             if (m.isHdrFile)
154                 continue;
155             File.remove(m.hdrfile.toChars());
156         }
157     }
158 
159     fatal();
160 }
161 
162 /**
163  * Converts a chain of identifiers to the filename of the module
164  *
165  * Params:
166  *  packages = the names of the "parent" packages
167  *  ident = the name of the child package or module
168  *
169  * Returns:
170  *  the filename of the child package or module
171  */
172 private const(char)[] getFilename(Identifiers* packages, Identifier ident)
173 {
174     const(char)[] filename = ident.toString();
175 
176     if (packages == null || packages.dim == 0)
177         return filename;
178 
179     OutBuffer buf;
180     OutBuffer dotmods;
181     auto modAliases = &global.params.modFileAliasStrings;
182 
183     void checkModFileAlias(const(char)[] p)
184     {
185         /* Check and replace the contents of buf[] with
186         * an alias string from global.params.modFileAliasStrings[]
187         */
188         dotmods.writestring(p);
189         foreach_reverse (const m; *modAliases)
190         {
191             const q = strchr(m, '=');
192             assert(q);
193             if (dotmods.length == q - m && memcmp(dotmods.peekChars(), m, q - m) == 0)
194             {
195                 buf.setsize(0);
196                 auto rhs = q[1 .. strlen(q)];
197                 if (rhs.length > 0 && (rhs[$ - 1] == '/' || rhs[$ - 1] == '\\'))
198                     rhs = rhs[0 .. $ - 1]; // remove trailing separator
199                 buf.writestring(rhs);
200                 break; // last matching entry in ms[] wins
201             }
202         }
203         dotmods.writeByte('.');
204     }
205 
206     foreach (pid; *packages)
207     {
208         const p = pid.toString();
209         buf.writestring(p);
210         if (modAliases.dim)
211             checkModFileAlias(p);
212         version (Windows)
213             enum FileSeparator = '\\';
214         else
215             enum FileSeparator = '/';
216         buf.writeByte(FileSeparator);
217     }
218     buf.writestring(filename);
219     if (modAliases.dim)
220         checkModFileAlias(filename);
221     buf.writeByte(0);
222     filename = buf.extractSlice()[0 .. $ - 1];
223 
224     return filename;
225 }
226 
227 enum PKG : int
228 {
229     unknown,     // not yet determined whether it's a package.d or not
230     module_,      // already determined that's an actual package.d
231     package_,     // already determined that's an actual package
232 }
233 
234 /***********************************************************
235  */
236 extern (C++) class Package : ScopeDsymbol
237 {
238     PKG isPkgMod = PKG.unknown;
239     uint tag;        // auto incremented tag, used to mask package tree in scopes
240     Module mod;     // !=null if isPkgMod == PKG.module_
241 
242     final extern (D) this(const ref Loc loc, Identifier ident)
243     {
244         super(loc, ident);
245         __gshared uint packageTag;
246         this.tag = packageTag++;
247     }
248 
249     override const(char)* kind() const
250     {
251         return "package";
252     }
253 
254     override bool equals(const RootObject o) const
255     {
256         // custom 'equals' for bug 17441. "package a" and "module a" are not equal
257         if (this == o)
258             return true;
259         auto p = cast(Package)o;
260         return p && isModule() == p.isModule() && ident.equals(p.ident);
261     }
262 
263     /****************************************************
264      * Input:
265      *      packages[]      the pkg1.pkg2 of pkg1.pkg2.mod
266      * Returns:
267      *      the symbol table that mod should be inserted into
268      * Output:
269      *      *pparent        the rightmost package, i.e. pkg2, or NULL if no packages
270      *      *ppkg           the leftmost package, i.e. pkg1, or NULL if no packages
271      */
272     extern (D) static DsymbolTable resolve(Identifiers* packages, Dsymbol* pparent, Package* ppkg)
273     {
274         DsymbolTable dst = Module.modules;
275         Dsymbol parent = null;
276         //printf("Package::resolve()\n");
277         if (ppkg)
278             *ppkg = null;
279         if (packages)
280         {
281             for (size_t i = 0; i < packages.dim; i++)
282             {
283                 Identifier pid = (*packages)[i];
284                 Package pkg;
285                 Dsymbol p = dst.lookup(pid);
286                 if (!p)
287                 {
288                     pkg = new Package(Loc.initial, pid);
289                     dst.insert(pkg);
290                     pkg.parent = parent;
291                     pkg.symtab = new DsymbolTable();
292                 }
293                 else
294                 {
295                     pkg = p.isPackage();
296                     assert(pkg);
297                     // It might already be a module, not a package, but that needs
298                     // to be checked at a higher level, where a nice error message
299                     // can be generated.
300                     // dot net needs modules and packages with same name
301                     // But we still need a symbol table for it
302                     if (!pkg.symtab)
303                         pkg.symtab = new DsymbolTable();
304                 }
305                 parent = pkg;
306                 dst = pkg.symtab;
307                 if (ppkg && !*ppkg)
308                     *ppkg = pkg;
309                 if (pkg.isModule())
310                 {
311                     // Return the module so that a nice error message can be generated
312                     if (ppkg)
313                         *ppkg = cast(Package)p;
314                     break;
315                 }
316             }
317         }
318         if (pparent)
319             *pparent = parent;
320         return dst;
321     }
322 
323     override final inout(Package) isPackage() inout
324     {
325         return this;
326     }
327 
328     /**
329      * Checks if pkg is a sub-package of this
330      *
331      * For example, if this qualifies to 'a1.a2' and pkg - to 'a1.a2.a3',
332      * this function returns 'true'. If it is other way around or qualified
333      * package paths conflict function returns 'false'.
334      *
335      * Params:
336      *  pkg = possible subpackage
337      *
338      * Returns:
339      *  see description
340      */
341     final bool isAncestorPackageOf(const Package pkg) const
342     {
343         if (this == pkg)
344             return true;
345         if (!pkg || !pkg.parent)
346             return false;
347         return isAncestorPackageOf(pkg.parent.isPackage());
348     }
349 
350     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
351     {
352         //printf("%s Package.search('%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
353         flags &= ~SearchLocalsOnly;  // searching an import is always transitive
354         if (!isModule() && mod)
355         {
356             // Prefer full package name.
357             Dsymbol s = symtab ? symtab.lookup(ident) : null;
358             if (s)
359                 return s;
360             //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars());
361             return mod.search(loc, ident, flags);
362         }
363         return ScopeDsymbol.search(loc, ident, flags);
364     }
365 
366     override void accept(Visitor v)
367     {
368         v.visit(this);
369     }
370 
371     final Module isPackageMod()
372     {
373         if (isPkgMod == PKG.module_)
374         {
375             return mod;
376         }
377         return null;
378     }
379 
380     /**
381      * Checks for the existence of a package.d to set isPkgMod appropriately
382      * if isPkgMod == PKG.unknown
383      */
384     final void resolvePKGunknown()
385     {
386         if (isModule())
387             return;
388         if (isPkgMod != PKG.unknown)
389             return;
390 
391         Identifiers packages;
392         for (Dsymbol s = this.parent; s; s = s.parent)
393             packages.insert(0, s.ident);
394 
395         if (lookForSourceFile(getFilename(&packages, ident)))
396             Module.load(Loc(), &packages, this.ident);
397         else
398             isPkgMod = PKG.package_;
399     }
400 }
401 
402 /***********************************************************
403  */
404 extern (C++) final class Module : Package
405 {
406     extern (C++) __gshared Module rootModule;
407     extern (C++) __gshared DsymbolTable modules; // symbol table of all modules
408     extern (C++) __gshared Modules amodules;     // array of all modules
409     extern (C++) __gshared Dsymbols deferred;    // deferred Dsymbol's needing semantic() run on them
410     extern (C++) __gshared Dsymbols deferred2;   // deferred Dsymbol's needing semantic2() run on them
411     extern (C++) __gshared Dsymbols deferred3;   // deferred Dsymbol's needing semantic3() run on them
412     extern (C++) __gshared uint dprogress;       // progress resolving the deferred list
413 
414     static void _init()
415     {
416         modules = new DsymbolTable();
417     }
418 
419     /**
420      * Deinitializes the global state of the compiler.
421      *
422      * This can be used to restore the state set by `_init` to its original
423      * state.
424      */
425     static void deinitialize()
426     {
427         modules = modules.init;
428     }
429 
430     extern (C++) __gshared AggregateDeclaration moduleinfo;
431 
432     const(char)[] arg;           // original argument name
433     ModuleDeclaration* md;      // if !=null, the contents of the ModuleDeclaration declaration
434     const FileName srcfile;     // input source file
435     const FileName objfile;     // output .obj file
436     const FileName hdrfile;     // 'header' file
437     FileName docfile;           // output documentation file
438     FileBuffer* srcBuffer;      // set during load(), free'd in parse()
439     uint errors;                // if any errors in file
440     uint numlines;              // number of lines in source file
441     bool isHdrFile;             // if it is a header (.di) file
442     bool isDocFile;             // if it is a documentation input file, not D source
443     bool hasAlwaysInlines;      // contains references to functions that must be inlined
444     bool isPackageFile;         // if it is a package.d
445     Package pkg;                // if isPackageFile is true, the Package that contains this package.d
446     Strings contentImportedFiles; // array of files whose content was imported
447     int needmoduleinfo;
448     int selfimports;            // 0: don't know, 1: does not, 2: does
449 
450     /*************************************
451      * Return true if module imports itself.
452      */
453     bool selfImports()
454     {
455         //printf("Module::selfImports() %s\n", toChars());
456         if (selfimports == 0)
457         {
458             for (size_t i = 0; i < amodules.dim; i++)
459                 amodules[i].insearch = 0;
460             selfimports = imports(this) + 1;
461             for (size_t i = 0; i < amodules.dim; i++)
462                 amodules[i].insearch = 0;
463         }
464         return selfimports == 2;
465     }
466 
467     int rootimports;            // 0: don't know, 1: does not, 2: does
468 
469     /*************************************
470      * Return true if module imports root module.
471      */
472     bool rootImports()
473     {
474         //printf("Module::rootImports() %s\n", toChars());
475         if (rootimports == 0)
476         {
477             for (size_t i = 0; i < amodules.dim; i++)
478                 amodules[i].insearch = 0;
479             rootimports = 1;
480             for (size_t i = 0; i < amodules.dim; ++i)
481             {
482                 Module m = amodules[i];
483                 if (m.isRoot() && imports(m))
484                 {
485                     rootimports = 2;
486                     break;
487                 }
488             }
489             for (size_t i = 0; i < amodules.dim; i++)
490                 amodules[i].insearch = 0;
491         }
492         return rootimports == 2;
493     }
494 
495     int insearch;
496     Identifier searchCacheIdent;
497     Dsymbol searchCacheSymbol;  // cached value of search
498     int searchCacheFlags;       // cached flags
499 
500     /**
501      * A root module is one that will be compiled all the way to
502      * object code.  This field holds the root module that caused
503      * this module to be loaded.  If this module is a root module,
504      * then it will be set to `this`.  This is used to determine
505      * ownership of template instantiation.
506      */
507     Module importedFrom;
508 
509     Dsymbols* decldefs;         // top level declarations for this Module
510 
511     Modules aimports;           // all imported modules
512 
513     uint debuglevel;            // debug level
514     Identifiers* debugids;      // debug identifiers
515     Identifiers* debugidsNot;   // forward referenced debug identifiers
516 
517     uint versionlevel;          // version level
518     Identifiers* versionids;    // version identifiers
519     Identifiers* versionidsNot; // forward referenced version identifiers
520 
521     MacroTable macrotable;      // document comment macros
522     Escape* escapetable;        // document comment escapes
523 
524     size_t nameoffset;          // offset of module name from start of ModuleInfo
525     size_t namelen;             // length of module name in characters
526 
527     extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
528     {
529         super(loc, ident);
530         const(char)[] srcfilename;
531         //printf("Module::Module(filename = '%s', ident = '%s')\n", filename, ident.toChars());
532         this.arg = filename;
533         srcfilename = FileName.defaultExt(filename, global.mars_ext);
534         if (global.run_noext && global.params.run &&
535             !FileName.ext(filename) &&
536             FileName.exists(srcfilename) == 0 &&
537             FileName.exists(filename) == 1)
538         {
539             FileName.free(srcfilename.ptr);
540             srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename)
541         }
542         else if (!FileName.equalsExt(srcfilename, global.mars_ext) &&
543                  !FileName.equalsExt(srcfilename, global.hdr_ext) &&
544                  !FileName.equalsExt(srcfilename, "dd"))
545         {
546 
547             error("source file name '%.*s' must have .%.*s extension",
548                   cast(int)srcfilename.length, srcfilename.ptr,
549                   cast(int)global.mars_ext.length, global.mars_ext.ptr);
550             fatal();
551         }
552 
553         srcfile = FileName(srcfilename);
554         objfile = setOutfilename(global.params.objname, global.params.objdir, filename, global.obj_ext);
555         if (doDocComment)
556             setDocfile();
557         if (doHdrGen)
558             hdrfile = setOutfilename(global.params.hdrname, global.params.hdrdir, arg, global.hdr_ext);
559         escapetable = new Escape();
560     }
561 
562     extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
563     {
564         this(Loc.initial, filename, ident, doDocComment, doHdrGen);
565     }
566 
567     static Module create(const(char)* filename, Identifier ident, int doDocComment, int doHdrGen)
568     {
569         return create(filename.toDString, ident, doDocComment, doHdrGen);
570     }
571 
572     extern (D) static Module create(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen)
573     {
574         return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
575     }
576 
577     static Module load(Loc loc, Identifiers* packages, Identifier ident)
578     {
579         //printf("Module::load(ident = '%s')\n", ident.toChars());
580         // Build module filename by turning:
581         //  foo.bar.baz
582         // into:
583         //  foo\bar\baz
584         const(char)[] filename = getFilename(packages, ident);
585         // Look for the source file
586         if (const result = lookForSourceFile(filename))
587             filename = result; // leaks
588 
589         auto m = new Module(loc, filename, ident, 0, 0);
590 
591         if (!m.read(loc))
592             return null;
593         if (global.params.verbose)
594         {
595             OutBuffer buf;
596             if (packages)
597             {
598                 foreach (pid; *packages)
599                 {
600                     buf.writestring(pid.toString());
601                     buf.writeByte('.');
602                 }
603             }
604             buf.printf("%s\t(%s)", ident.toChars(), m.srcfile.toChars());
605             message("import    %s", buf.peekChars());
606         }
607         m = m.parse();
608 
609         // Call onImport here because if the module is going to be compiled then we
610         // need to determine it early because it affects semantic analysis. This is
611         // being done after parsing the module so the full module name can be taken
612         // from whatever was declared in the file.
613         if (!m.isRoot() && Compiler.onImport(m))
614         {
615             m.importedFrom = m;
616             assert(m.isRoot());
617         }
618         return m;
619     }
620 
621     override const(char)* kind() const
622     {
623         return "module";
624     }
625 
626     /*********************************************
627      * Combines things into output file name for .html and .di files.
628      * Input:
629      *      name    Command line name given for the file, NULL if none
630      *      dir     Command line directory given for the file, NULL if none
631      *      arg     Name of the source file
632      *      ext     File name extension to use if 'name' is NULL
633      *      global.params.preservePaths     get output path from arg
634      *      srcfile Input file - output file name must not match input file
635      */
636     extern(D) FileName setOutfilename(const(char)[] name, const(char)[] dir, const(char)[] arg, const(char)[] ext)
637     {
638         const(char)[] docfilename;
639         if (name)
640         {
641             docfilename = name;
642         }
643         else
644         {
645             const(char)[] argdoc;
646             OutBuffer buf;
647             if (arg == "__stdin.d")
648             {
649                 version (Posix)
650                     import core.sys.posix.unistd : getpid;
651                 else version (Windows)
652                     import core.sys.windows.winbase : getpid = GetCurrentProcessId;
653                 buf.printf("__stdin_%d.d", getpid());
654                 arg = buf[];
655             }
656             if (global.params.preservePaths)
657                 argdoc = arg;
658             else
659                 argdoc = FileName.name(arg);
660             // If argdoc doesn't have an absolute path, make it relative to dir
661             if (!FileName.absolute(argdoc))
662             {
663                 //FileName::ensurePathExists(dir);
664                 argdoc = FileName.combine(dir, argdoc);
665             }
666             docfilename = FileName.forceExt(argdoc, ext);
667         }
668         if (FileName.equals(docfilename, srcfile.toString()))
669         {
670             error("source file and output file have same name '%s'", srcfile.toChars());
671             fatal();
672         }
673         return FileName(docfilename);
674     }
675 
676     extern (D) void setDocfile()
677     {
678         docfile = setOutfilename(global.params.docname, global.params.docdir, arg, global.doc_ext);
679     }
680 
681     /**
682      * Loads the source buffer from the given read result into `this.srcBuffer`.
683      *
684      * Will take ownership of the buffer located inside `readResult`.
685      *
686      * Params:
687      *  loc = the location
688      *  readResult = the result of reading a file containing the source code
689      *
690      * Returns: `true` if successful
691      */
692     bool loadSourceBuffer(const ref Loc loc, ref File.ReadResult readResult)
693     {
694         //printf("Module::loadSourceBuffer('%s') file '%s'\n", toChars(), srcfile.toChars());
695         // take ownership of buffer
696         srcBuffer = new FileBuffer(readResult.extractSlice());
697         if (readResult.success)
698             return true;
699 
700         if (FileName.equals(srcfile.toString(), "object.d"))
701         {
702             .error(loc, "cannot find source code for runtime library file 'object.d'");
703             errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions.");
704             const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found";
705             errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr);
706         }
707         else
708         {
709             // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module
710             bool isPackageMod = (strcmp(toChars(), "package") != 0) && (strcmp(srcfile.name(), "package.d") == 0 || (strcmp(srcfile.name(), "package.di") == 0));
711             if (isPackageMod)
712                 .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
713             else
714                 error(loc, "is in file '%s' which cannot be read", srcfile.toChars());
715         }
716         if (!global.gag)
717         {
718             /* Print path
719              */
720             if (global.path)
721             {
722                 foreach (i, p; *global.path)
723                     fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p);
724             }
725             else
726             {
727                 fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars());
728             }
729 
730             removeHdrFilesAndFail(global.params, Module.amodules);
731         }
732         return false;
733     }
734 
735     /**
736      * Reads the file from `srcfile` and loads the source buffer.
737      *
738      * Params:
739      *  loc = the location
740      *
741      * Returns: `true` if successful
742      * See_Also: loadSourceBuffer
743      */
744     bool read(const ref Loc loc)
745     {
746         if (srcBuffer)
747             return true; // already read
748 
749         //printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
750         auto readResult = File.read(srcfile.toChars());
751 
752         return loadSourceBuffer(loc, readResult);
753     }
754 
755     /// syntactic parse
756     Module parse()
757     {
758         return parseModule!ASTCodegen();
759     }
760 
761     /// ditto
762     extern (D) Module parseModule(AST)()
763     {
764 
765 
766         enum Endian { little, big}
767         enum SourceEncoding { utf16, utf32}
768 
769         /*
770          * Convert a buffer from UTF32 to UTF8
771          * Params:
772          *    Endian = is the buffer big/little endian
773          *    buf = buffer of UTF32 data
774          * Returns:
775          *    input buffer reencoded as UTF8
776          */
777 
778         char[] UTF32ToUTF8(Endian endian)(const(char)[] buf)
779         {
780             static if (endian == Endian.little)
781                 alias readNext = Port.readlongLE;
782             else
783                 alias readNext = Port.readlongBE;
784 
785             if (buf.length & 3)
786             {
787                 error("odd length of UTF-32 char source %llu", cast(ulong) buf.length);
788                 fatal();
789             }
790 
791             const (uint)[] eBuf = cast(const(uint)[])buf;
792 
793             OutBuffer dbuf;
794             dbuf.reserve(eBuf.length);
795 
796             foreach (i; 0 .. eBuf.length)
797             {
798                 const u = readNext(&eBuf[i]);
799                 if (u & ~0x7F)
800                 {
801                     if (u > 0x10FFFF)
802                     {
803                         error("UTF-32 value %08x greater than 0x10FFFF", u);
804                         fatal();
805                     }
806                     dbuf.writeUTF8(u);
807                 }
808                 else
809                     dbuf.writeByte(u);
810             }
811             dbuf.writeByte(0); //add null terminator
812             return dbuf.extractSlice();
813         }
814 
815         /*
816          * Convert a buffer from UTF16 to UTF8
817          * Params:
818          *    Endian = is the buffer big/little endian
819          *    buf = buffer of UTF16 data
820          * Returns:
821          *    input buffer reencoded as UTF8
822          */
823 
824         char[] UTF16ToUTF8(Endian endian)(const(char)[] buf)
825         {
826             static if (endian == Endian.little)
827                 alias readNext = Port.readwordLE;
828             else
829                 alias readNext = Port.readwordBE;
830 
831             if (buf.length & 1)
832             {
833                 error("odd length of UTF-16 char source %llu", cast(ulong) buf.length);
834                 fatal();
835             }
836 
837             const (ushort)[] eBuf = cast(const(ushort)[])buf;
838 
839             OutBuffer dbuf;
840             dbuf.reserve(eBuf.length);
841 
842             //i will be incremented in the loop for high codepoints
843             foreach (ref i; 0 .. eBuf.length)
844             {
845                 uint u = readNext(&eBuf[i]);
846                 if (u & ~0x7F)
847                 {
848                     if (0xD800 <= u && u < 0xDC00)
849                     {
850                         i++;
851                         if (i >= eBuf.length)
852                         {
853                             error("surrogate UTF-16 high value %04x at end of file", u);
854                             fatal();
855                         }
856                         const u2 = readNext(&eBuf[i]);
857                         if (u2 < 0xDC00 || 0xE000 <= u2)
858                         {
859                             error("surrogate UTF-16 low value %04x out of range", u2);
860                             fatal();
861                         }
862                         u = (u - 0xD7C0) << 10;
863                         u |= (u2 - 0xDC00);
864                     }
865                     else if (u >= 0xDC00 && u <= 0xDFFF)
866                     {
867                         error("unpaired surrogate UTF-16 value %04x", u);
868                         fatal();
869                     }
870                     else if (u == 0xFFFE || u == 0xFFFF)
871                     {
872                         error("illegal UTF-16 value %04x", u);
873                         fatal();
874                     }
875                     dbuf.writeUTF8(u);
876                 }
877                 else
878                     dbuf.writeByte(u);
879             }
880             dbuf.writeByte(0); //add a terminating null byte
881             return dbuf.extractSlice();
882         }
883 
884         const(char)* srcname = srcfile.toChars();
885         //printf("Module::parse(srcname = '%s')\n", srcname);
886         isPackageFile = (strcmp(srcfile.name(), "package.d") == 0 ||
887                          strcmp(srcfile.name(), "package.di") == 0);
888         const(char)[] buf = cast(const(char)[]) srcBuffer.data;
889 
890         bool needsReencoding = true;
891         bool hasBOM = true; //assume there's a BOM
892         Endian endian;
893         SourceEncoding sourceEncoding;
894 
895         if (buf.length >= 2)
896         {
897             /* Convert all non-UTF-8 formats to UTF-8.
898              * BOM : http://www.unicode.org/faq/utf_bom.html
899              * 00 00 FE FF  UTF-32BE, big-endian
900              * FF FE 00 00  UTF-32LE, little-endian
901              * FE FF        UTF-16BE, big-endian
902              * FF FE        UTF-16LE, little-endian
903              * EF BB BF     UTF-8
904              */
905             if (buf[0] == 0xFF && buf[1] == 0xFE)
906             {
907                 endian = Endian.little;
908 
909                 sourceEncoding = buf.length >= 4 && buf[2] == 0 && buf[3] == 0
910                                  ? SourceEncoding.utf32
911                                  : SourceEncoding.utf16;
912             }
913             else if (buf[0] == 0xFE && buf[1] == 0xFF)
914             {
915                 endian = Endian.big;
916                 sourceEncoding = SourceEncoding.utf16;
917             }
918             else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0xFE && buf[3] == 0xFF)
919             {
920                 endian = Endian.big;
921                 sourceEncoding = SourceEncoding.utf32;
922             }
923             else if (buf.length >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
924             {
925                 needsReencoding = false;//utf8 with BOM
926             }
927             else
928             {
929                 /* There is no BOM. Make use of Arcane Jill's insight that
930                  * the first char of D source must be ASCII to
931                  * figure out the encoding.
932                  */
933                 hasBOM = false;
934                 if (buf.length >= 4 && buf[1] == 0 && buf[2] == 0 && buf[3] == 0)
935                 {
936                     endian = Endian.little;
937                     sourceEncoding = SourceEncoding.utf32;
938                 }
939                 else if (buf.length >= 4 && buf[0] == 0 && buf[1] == 0 && buf[2] == 0)
940                 {
941                     endian = Endian.big;
942                     sourceEncoding = SourceEncoding.utf32;
943                 }
944                 else if (buf.length >= 2 && buf[1] == 0) //try to check for UTF-16
945                 {
946                     endian = Endian.little;
947                     sourceEncoding = SourceEncoding.utf16;
948                 }
949                 else if (buf[0] == 0)
950                 {
951                     endian = Endian.big;
952                     sourceEncoding = SourceEncoding.utf16;
953                 }
954                 else {
955                     // It's UTF-8
956                     needsReencoding = false;
957                     if (buf[0] >= 0x80)
958                     {
959                         error("source file must start with BOM or ASCII character, not \\x%02X", buf[0]);
960                         fatal();
961                     }
962                 }
963             }
964             //throw away BOM
965             if (hasBOM)
966             {
967                 if (!needsReencoding) buf = buf[3..$];// utf-8 already
968                 else if (sourceEncoding == SourceEncoding.utf32) buf = buf[4..$];
969                 else buf = buf[2..$]; //utf 16
970             }
971         }
972         // Assume the buffer is from memory and has not be read from disk. Assume UTF-8.
973         else if (buf.length >= 1 && (buf[0] == '\0' || buf[0] == 0x1A))
974             needsReencoding = false;
975          //printf("%s, %d, %d, %d\n", srcfile.name.toChars(), needsReencoding, endian == Endian.little, sourceEncoding == SourceEncoding.utf16);
976         if (needsReencoding)
977         {
978             if (sourceEncoding == SourceEncoding.utf16)
979             {
980                 buf = endian == Endian.little
981                       ? UTF16ToUTF8!(Endian.little)(buf)
982                       : UTF16ToUTF8!(Endian.big)(buf);
983             }
984             else
985             {
986                 buf = endian == Endian.little
987                       ? UTF32ToUTF8!(Endian.little)(buf)
988                       : UTF32ToUTF8!(Endian.big)(buf);
989             }
990         }
991 
992         /* If it starts with the string "Ddoc", then it's a documentation
993          * source file.
994          */
995         if (buf.length>= 4 && buf[0..4] == "Ddoc")
996         {
997             comment = buf.ptr + 4;
998             isDocFile = true;
999             if (!docfile)
1000                 setDocfile();
1001             return this;
1002         }
1003         /* If it has the extension ".dd", it is also a documentation
1004          * source file. Documentation source files may begin with "Ddoc"
1005          * but do not have to if they have the .dd extension.
1006          * https://issues.dlang.org/show_bug.cgi?id=15465
1007          */
1008         if (FileName.equalsExt(arg, "dd"))
1009         {
1010             comment = buf.ptr; // the optional Ddoc, if present, is handled above.
1011             isDocFile = true;
1012             if (!docfile)
1013                 setDocfile();
1014             return this;
1015         }
1016         /* If it has the extension ".di", it is a "header" file.
1017          */
1018         if (FileName.equalsExt(arg, "di"))
1019         {
1020             isHdrFile = true;
1021         }
1022         {
1023             scope p = new Parser!AST(this, buf, cast(bool) docfile);
1024             p.nextToken();
1025             members = p.parseModule();
1026             p.reportDiagnostics();
1027             md = p.md;
1028             numlines = p.scanloc.linnum;
1029         }
1030         srcBuffer.destroy();
1031         srcBuffer = null;
1032         /* The symbol table into which the module is to be inserted.
1033          */
1034         DsymbolTable dst;
1035         if (md)
1036         {
1037             /* A ModuleDeclaration, md, was provided.
1038              * The ModuleDeclaration sets the packages this module appears in, and
1039              * the name of this module.
1040              */
1041             this.ident = md.id;
1042             Package ppack = null;
1043             dst = Package.resolve(md.packages, &this.parent, &ppack);
1044             assert(dst);
1045             Module m = ppack ? ppack.isModule() : null;
1046             if (m && (strcmp(m.srcfile.name(), "package.d") != 0 &&
1047                       strcmp(m.srcfile.name(), "package.di") != 0))
1048             {
1049                 .error(md.loc, "package name '%s' conflicts with usage as a module name in file %s", ppack.toPrettyChars(), m.srcfile.toChars());
1050             }
1051         }
1052         else
1053         {
1054             /* The name of the module is set to the source file name.
1055              * There are no packages.
1056              */
1057             dst = modules; // and so this module goes into global module symbol table
1058             /* Check to see if module name is a valid identifier
1059              */
1060             if (!Identifier.isValidIdentifier(this.ident.toChars()))
1061                 error("has non-identifier characters in filename, use module declaration instead");
1062         }
1063         // Insert module into the symbol table
1064         Dsymbol s = this;
1065         if (isPackageFile)
1066         {
1067             /* If the source tree is as follows:
1068              *     pkg/
1069              *     +- package.d
1070              *     +- common.d
1071              * the 'pkg' will be incorporated to the internal package tree in two ways:
1072              *     import pkg;
1073              * and:
1074              *     import pkg.common;
1075              *
1076              * If both are used in one compilation, 'pkg' as a module (== pkg/package.d)
1077              * and a package name 'pkg' will conflict each other.
1078              *
1079              * To avoid the conflict:
1080              * 1. If preceding package name insertion had occurred by Package::resolve,
1081              *    reuse the previous wrapping 'Package' if it exists
1082              * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here.
1083              *
1084              * Then change Package::isPkgMod to PKG.module_ and set Package::mod.
1085              *
1086              * Note that the 'wrapping Package' is the Package that contains package.d and other submodules,
1087              * the one inserted to the symbol table.
1088              */
1089             auto ps = dst.lookup(ident);
1090             Package p = ps ? ps.isPackage() : null;
1091             if (p is null)
1092             {
1093                 p = new Package(Loc.initial, ident);
1094                 p.tag = this.tag; // reuse the same package tag
1095                 p.symtab = new DsymbolTable();
1096             }
1097             this.tag = p.tag; // reuse the 'older' package tag
1098             this.pkg = p;
1099             p.parent = this.parent;
1100             p.isPkgMod = PKG.module_;
1101             p.mod = this;
1102             s = p;
1103         }
1104         if (!dst.insert(s))
1105         {
1106             /* It conflicts with a name that is already in the symbol table.
1107              * Figure out what went wrong, and issue error message.
1108              */
1109             Dsymbol prev = dst.lookup(ident);
1110             assert(prev);
1111             if (Module mprev = prev.isModule())
1112             {
1113                 if (!FileName.equals(srcname, mprev.srcfile.toChars()))
1114                     error(loc, "from file %s conflicts with another module %s from file %s", srcname, mprev.toChars(), mprev.srcfile.toChars());
1115                 else if (isRoot() && mprev.isRoot())
1116                     error(loc, "from file %s is specified twice on the command line", srcname);
1117                 else
1118                     error(loc, "from file %s must be imported with 'import %s;'", srcname, toPrettyChars());
1119                 // https://issues.dlang.org/show_bug.cgi?id=14446
1120                 // Return previously parsed module to avoid AST duplication ICE.
1121                 return mprev;
1122             }
1123             else if (Package pkg = prev.isPackage())
1124             {
1125                 // 'package.d' loaded after a previous 'Package' insertion
1126                 if (isPackageFile)
1127                     amodules.push(this); // Add to global array of all modules
1128                 else
1129                     error(md ? md.loc : loc, "from file %s conflicts with package name %s", srcname, pkg.toChars());
1130             }
1131             else
1132                 assert(global.errors);
1133         }
1134         else
1135         {
1136             // Add to global array of all modules
1137             amodules.push(this);
1138         }
1139         Compiler.onParseModule(this);
1140         return this;
1141     }
1142 
1143     override void importAll(Scope* prevsc)
1144     {
1145         //printf("+Module::importAll(this = %p, '%s'): parent = %p\n", this, toChars(), parent);
1146         if (_scope)
1147             return; // already done
1148         if (isDocFile)
1149         {
1150             error("is a Ddoc file, cannot import it");
1151             return;
1152         }
1153 
1154         /* Note that modules get their own scope, from scratch.
1155          * This is so regardless of where in the syntax a module
1156          * gets imported, it is unaffected by context.
1157          * Ignore prevsc.
1158          */
1159         Scope* sc = Scope.createGlobal(this); // create root scope
1160 
1161         if (md && md.msg)
1162             md.msg = semanticString(sc, md.msg, "deprecation message");
1163 
1164         // Add import of "object", even for the "object" module.
1165         // If it isn't there, some compiler rewrites, like
1166         //    classinst == classinst -> .object.opEquals(classinst, classinst)
1167         // would fail inside object.d.
1168         if (members.dim == 0 || (*members)[0].ident != Id.object ||
1169             (*members)[0].isImport() is null)
1170         {
1171             auto im = new Import(Loc.initial, null, Id.object, null, 0);
1172             members.shift(im);
1173         }
1174         if (!symtab)
1175         {
1176             // Add all symbols into module's symbol table
1177             symtab = new DsymbolTable();
1178             for (size_t i = 0; i < members.dim; i++)
1179             {
1180                 Dsymbol s = (*members)[i];
1181                 s.addMember(sc, sc.scopesym);
1182             }
1183         }
1184         // anything else should be run after addMember, so version/debug symbols are defined
1185         /* Set scope for the symbols so that if we forward reference
1186          * a symbol, it can possibly be resolved on the spot.
1187          * If this works out well, it can be extended to all modules
1188          * before any semantic() on any of them.
1189          */
1190         setScope(sc); // remember module scope for semantic
1191         for (size_t i = 0; i < members.dim; i++)
1192         {
1193             Dsymbol s = (*members)[i];
1194             s.setScope(sc);
1195         }
1196         for (size_t i = 0; i < members.dim; i++)
1197         {
1198             Dsymbol s = (*members)[i];
1199             s.importAll(sc);
1200         }
1201         sc = sc.pop();
1202         sc.pop(); // 2 pops because Scope::createGlobal() created 2
1203     }
1204 
1205     /**********************************
1206      * Determine if we need to generate an instance of ModuleInfo
1207      * for this Module.
1208      */
1209     int needModuleInfo()
1210     {
1211         //printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
1212         return needmoduleinfo || global.params.cov;
1213     }
1214 
1215     /*******************************************
1216      * Print deprecation warning if we're deprecated, when
1217      * this module is imported from scope sc.
1218      *
1219      * Params:
1220      *  sc = the scope into which we are imported
1221      *  loc = the location of the import statement
1222      */
1223     void checkImportDeprecation(const ref Loc loc, Scope* sc)
1224     {
1225         if (md && md.isdeprecated && !sc.isDeprecated)
1226         {
1227             Expression msg = md.msg;
1228             if (StringExp se = msg ? msg.toStringExp() : null)
1229             {
1230                 const slice = se.peekString();
1231                 deprecation(loc, "is deprecated - %.*s", cast(int)slice.length, slice.ptr);
1232             }
1233             else
1234                 deprecation(loc, "is deprecated");
1235         }
1236     }
1237 
1238     override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
1239     {
1240         /* Since modules can be circularly referenced,
1241          * need to stop infinite recursive searches.
1242          * This is done with the cache.
1243          */
1244         //printf("%s Module.search('%s', flags = x%x) insearch = %d\n", toChars(), ident.toChars(), flags, insearch);
1245         if (insearch)
1246             return null;
1247 
1248         /* Qualified module searches always search their imports,
1249          * even if SearchLocalsOnly
1250          */
1251         if (!(flags & SearchUnqualifiedModule))
1252             flags &= ~(SearchUnqualifiedModule | SearchLocalsOnly);
1253 
1254         if (searchCacheIdent == ident && searchCacheFlags == flags)
1255         {
1256             //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n",
1257             //        toChars(), ident.toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol.toChars() : "null");
1258             return searchCacheSymbol;
1259         }
1260 
1261         uint errors = global.errors;
1262 
1263         insearch = 1;
1264         Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
1265         insearch = 0;
1266 
1267         if (errors == global.errors)
1268         {
1269             // https://issues.dlang.org/show_bug.cgi?id=10752
1270             // Can cache the result only when it does not cause
1271             // access error so the side-effect should be reproduced in later search.
1272             searchCacheIdent = ident;
1273             searchCacheSymbol = s;
1274             searchCacheFlags = flags;
1275         }
1276         return s;
1277     }
1278 
1279     override bool isPackageAccessible(Package p, Prot protection, int flags = 0)
1280     {
1281         if (insearch) // don't follow import cycles
1282             return false;
1283         insearch = true;
1284         scope (exit)
1285             insearch = false;
1286         if (flags & IgnorePrivateImports)
1287             protection = Prot(Prot.Kind.public_); // only consider public imports
1288         return super.isPackageAccessible(p, protection);
1289     }
1290 
1291     override Dsymbol symtabInsert(Dsymbol s)
1292     {
1293         searchCacheIdent = null; // symbol is inserted, so invalidate cache
1294         return Package.symtabInsert(s);
1295     }
1296 
1297     void deleteObjFile()
1298     {
1299         if (global.params.obj)
1300             File.remove(objfile.toChars());
1301         if (docfile)
1302             File.remove(docfile.toChars());
1303     }
1304 
1305     /*******************************************
1306      * Can't run semantic on s now, try again later.
1307      */
1308     extern (D) static void addDeferredSemantic(Dsymbol s)
1309     {
1310         //printf("Module::addDeferredSemantic('%s')\n", s.toChars());
1311         deferred.push(s);
1312     }
1313 
1314     extern (D) static void addDeferredSemantic2(Dsymbol s)
1315     {
1316         //printf("Module::addDeferredSemantic2('%s')\n", s.toChars());
1317         deferred2.push(s);
1318     }
1319 
1320     extern (D) static void addDeferredSemantic3(Dsymbol s)
1321     {
1322         //printf("Module::addDeferredSemantic3('%s')\n", s.toChars());
1323         deferred3.push(s);
1324     }
1325 
1326     /******************************************
1327      * Run semantic() on deferred symbols.
1328      */
1329     static void runDeferredSemantic()
1330     {
1331         if (dprogress == 0)
1332             return;
1333 
1334         __gshared int nested;
1335         if (nested)
1336             return;
1337         //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim);
1338         nested++;
1339 
1340         size_t len;
1341         do
1342         {
1343             dprogress = 0;
1344             len = deferred.dim;
1345             if (!len)
1346                 break;
1347 
1348             Dsymbol* todo;
1349             Dsymbol* todoalloc = null;
1350             Dsymbol tmp;
1351             if (len == 1)
1352             {
1353                 todo = &tmp;
1354             }
1355             else
1356             {
1357                 todo = cast(Dsymbol*)Mem.check(malloc(len * Dsymbol.sizeof));
1358                 todoalloc = todo;
1359             }
1360             memcpy(todo, deferred.tdata(), len * Dsymbol.sizeof);
1361             deferred.setDim(0);
1362 
1363             for (size_t i = 0; i < len; i++)
1364             {
1365                 Dsymbol s = todo[i];
1366                 s.dsymbolSemantic(null);
1367                 //printf("deferred: %s, parent = %s\n", s.toChars(), s.parent.toChars());
1368             }
1369             //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress);
1370             if (todoalloc)
1371                 free(todoalloc);
1372         }
1373         while (deferred.dim < len || dprogress); // while making progress
1374         nested--;
1375         //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim);
1376     }
1377 
1378     static void runDeferredSemantic2()
1379     {
1380         Module.runDeferredSemantic();
1381 
1382         Dsymbols* a = &Module.deferred2;
1383         for (size_t i = 0; i < a.dim; i++)
1384         {
1385             Dsymbol s = (*a)[i];
1386             //printf("[%d] %s semantic2a\n", i, s.toPrettyChars());
1387             s.semantic2(null);
1388 
1389             if (global.errors)
1390                 break;
1391         }
1392         a.setDim(0);
1393     }
1394 
1395     static void runDeferredSemantic3()
1396     {
1397         Module.runDeferredSemantic2();
1398 
1399         Dsymbols* a = &Module.deferred3;
1400         for (size_t i = 0; i < a.dim; i++)
1401         {
1402             Dsymbol s = (*a)[i];
1403             //printf("[%d] %s semantic3a\n", i, s.toPrettyChars());
1404             s.semantic3(null);
1405 
1406             if (global.errors)
1407                 break;
1408         }
1409         a.setDim(0);
1410     }
1411 
1412     extern (D) static void clearCache()
1413     {
1414         for (size_t i = 0; i < amodules.dim; i++)
1415         {
1416             Module m = amodules[i];
1417             m.searchCacheIdent = null;
1418         }
1419     }
1420 
1421     /************************************
1422      * Recursively look at every module this module imports,
1423      * return true if it imports m.
1424      * Can be used to detect circular imports.
1425      */
1426     int imports(Module m)
1427     {
1428         //printf("%s Module::imports(%s)\n", toChars(), m.toChars());
1429         version (none)
1430         {
1431             for (size_t i = 0; i < aimports.dim; i++)
1432             {
1433                 Module mi = cast(Module)aimports.data[i];
1434                 printf("\t[%d] %s\n", i, mi.toChars());
1435             }
1436         }
1437         for (size_t i = 0; i < aimports.dim; i++)
1438         {
1439             Module mi = aimports[i];
1440             if (mi == m)
1441                 return true;
1442             if (!mi.insearch)
1443             {
1444                 mi.insearch = 1;
1445                 int r = mi.imports(m);
1446                 if (r)
1447                     return r;
1448             }
1449         }
1450         return false;
1451     }
1452 
1453     bool isRoot()
1454     {
1455         return this.importedFrom == this;
1456     }
1457 
1458     // true if the module source file is directly
1459     // listed in command line.
1460     bool isCoreModule(Identifier ident)
1461     {
1462         return this.ident == ident && parent && parent.ident == Id.core && !parent.parent;
1463     }
1464 
1465     // Back end
1466     int doppelganger; // sub-module
1467     Symbol* cov; // private uint[] __coverage;
1468     uint* covb; // bit array of valid code line numbers
1469     Symbol* sictor; // module order independent constructor
1470     Symbol* sctor; // module constructor
1471     Symbol* sdtor; // module destructor
1472     Symbol* ssharedctor; // module shared constructor
1473     Symbol* sshareddtor; // module shared destructor
1474     Symbol* stest; // module unit test
1475     Symbol* sfilename; // symbol for filename
1476 
1477     uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line]
1478 
1479     override inout(Module) isModule() inout
1480     {
1481         return this;
1482     }
1483 
1484     override void accept(Visitor v)
1485     {
1486         v.visit(this);
1487     }
1488 
1489     /***********************************************
1490      * Writes this module's fully-qualified name to buf
1491      * Params:
1492      *    buf = The buffer to write to
1493      */
1494     void fullyQualifiedName(ref OutBuffer buf)
1495     {
1496         buf.writestring(ident.toString());
1497 
1498         for (auto package_ = parent; package_ !is null; package_ = package_.parent)
1499         {
1500             buf.prependstring(".");
1501             buf.prependstring(package_.ident.toChars());
1502         }
1503     }
1504 }
1505 
1506 /***********************************************************
1507  */
1508 extern (C++) struct ModuleDeclaration
1509 {
1510     Loc loc;
1511     Identifier id;
1512     Identifiers* packages;  // array of Identifier's representing packages
1513     bool isdeprecated;      // if it is a deprecated module
1514     Expression msg;
1515 
1516     extern (D) this(const ref Loc loc, Identifiers* packages, Identifier id, Expression msg, bool isdeprecated)
1517     {
1518         this.loc = loc;
1519         this.packages = packages;
1520         this.id = id;
1521         this.msg = msg;
1522         this.isdeprecated = isdeprecated;
1523     }
1524 
1525     extern (C++) const(char)* toChars() const
1526     {
1527         OutBuffer buf;
1528         if (packages && packages.dim)
1529         {
1530             foreach (pid; *packages)
1531             {
1532                 buf.writestring(pid.toString());
1533                 buf.writeByte('.');
1534             }
1535         }
1536         buf.writestring(id.toString());
1537         return buf.extractChars();
1538     }
1539 
1540     /// Provide a human readable representation
1541     extern (D) const(char)[] toString() const
1542     {
1543         return this.toChars().toDString;
1544     }
1545 }