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