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