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