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