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