1 /** 2 * This file is part of DCD, a development tool for the D programming language. 3 * Copyright (C) 2014 Brian Schott 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 module dsymbol.conversion.first; 20 21 import containers.unrolledlist; 22 import dparse.ast; 23 import dparse.formatter; 24 import dparse.lexer; 25 import dsymbol.builtin.names; 26 import dsymbol.builtin.symbols; 27 import dsymbol.cache_entry; 28 import dsymbol.import_; 29 import dsymbol.modulecache; 30 import dsymbol.scope_; 31 import dsymbol.semantic; 32 import dsymbol.string_interning; 33 import dsymbol.symbol; 34 import dsymbol.type_lookup; 35 import std.algorithm.iteration : map; 36 import stdx.allocator; 37 import stdx.allocator.gc_allocator : GCAllocator; 38 import std.experimental.logger; 39 import std.typecons : Rebindable; 40 41 /** 42 * First Pass handles the following: 43 * $(UL 44 * $(LI symbol name) 45 * $(LI symbol location) 46 * $(LI alias this locations) 47 * $(LI base class names) 48 * $(LI protection level) 49 * $(LI symbol kind) 50 * $(LI function call tip) 51 * $(LI symbol file path) 52 * ) 53 */ 54 final class FirstPass : ASTVisitor 55 { 56 alias SymbolAllocator = GCAllocator; // NOTE using First`Pass.symbolAllocator` instead fails when analyzing Phobos master 57 alias ScopeAllocator = GCAllocator; // NOTE using `Mallocator` instead fails when analyzing Phobos master 58 59 /** 60 * Params: 61 * mod = the module to visit 62 * symbolFile = path to the file being converted 63 * symbolAllocator = allocator used for the auto-complete symbols 64 * semanticAllocator = allocator used for semantic symbols 65 * includeParameterSymbols = include parameter symbols as children of 66 * function decalarations and constructors 67 */ 68 this(const Module mod, istring symbolFile, IAllocator symbolAllocator, 69 IAllocator semanticAllocator, bool includeParameterSymbols, 70 ModuleCache* cache, CacheEntry* entry = null) 71 in 72 { 73 assert(mod); 74 assert(symbolAllocator); 75 assert(semanticAllocator); 76 assert(cache); 77 } 78 do 79 { 80 this.mod = mod; 81 this.symbolFile = symbolFile; 82 this.symbolAllocator = symbolAllocator; 83 this.semanticAllocator = semanticAllocator; 84 this.includeParameterSymbols = includeParameterSymbols; 85 this.entry = entry; 86 this.cache = cache; 87 } 88 89 /** 90 * Runs the against the AST and produces symbols. 91 */ 92 void run() 93 { 94 visit(mod); 95 } 96 97 override void visit(const Unittest u) 98 { 99 // Create a dummy symbol because we don't want unit test symbols leaking 100 // into the symbol they're declared in. 101 pushSymbol(UNITTEST_SYMBOL_NAME, 102 CompletionKind.dummy, istring(null)); 103 scope(exit) popSymbol(); 104 u.accept(this); 105 } 106 107 override void visit(const Constructor con) 108 { 109 visitConstructor(con.location, con.parameters, con.templateParameters, con.functionBody, con.comment); 110 } 111 112 override void visit(const SharedStaticConstructor con) 113 { 114 visitConstructor(con.location, null, null, con.functionBody, con.comment); 115 } 116 117 override void visit(const StaticConstructor con) 118 { 119 visitConstructor(con.location, null, null, con.functionBody, con.comment); 120 } 121 122 override void visit(const Destructor des) 123 { 124 visitDestructor(des.index, des.functionBody, des.comment); 125 } 126 127 override void visit(const SharedStaticDestructor des) 128 { 129 visitDestructor(des.location, des.functionBody, des.comment); 130 } 131 132 override void visit(const StaticDestructor des) 133 { 134 visitDestructor(des.location, des.functionBody, des.comment); 135 } 136 137 override void visit(const FunctionDeclaration dec) 138 { 139 assert(dec); 140 pushSymbol(dec.name.text, CompletionKind.functionName, symbolFile, 141 dec.name.index, dec.returnType); 142 scope (exit) popSymbol(); 143 currentSymbol.acSymbol.protection = protection.current; 144 currentSymbol.acSymbol.doc = makeDocumentation(dec.comment); 145 146 istring lastComment = this.lastComment; 147 this.lastComment = istring.init; 148 scope(exit) this.lastComment = lastComment; 149 150 if (dec.functionBody !is null) 151 { 152 pushFunctionScope(dec.functionBody, semanticAllocator, 153 dec.name.index + dec.name.text.length); 154 scope (exit) popScope(); 155 processParameters(currentSymbol, dec.returnType, 156 currentSymbol.acSymbol.name, dec.parameters, dec.templateParameters); 157 dec.functionBody.accept(this); 158 } 159 else 160 { 161 immutable ips = includeParameterSymbols; 162 includeParameterSymbols = false; 163 processParameters(currentSymbol, dec.returnType, 164 currentSymbol.acSymbol.name, dec.parameters, dec.templateParameters); 165 includeParameterSymbols = ips; 166 } 167 } 168 169 override void visit(const FunctionLiteralExpression exp) 170 { 171 assert(exp); 172 173 auto fbody = exp.specifiedFunctionBody; 174 if (fbody is null) 175 return; 176 auto block = fbody.blockStatement; 177 if (block is null) 178 return; 179 180 pushSymbol(FUNCTION_LITERAL_SYMBOL_NAME, CompletionKind.dummy, symbolFile, 181 block.startLocation, null); 182 scope(exit) popSymbol(); 183 184 pushScope(block.startLocation, block.endLocation); 185 scope (exit) popScope(); 186 processParameters(currentSymbol, exp.returnType, 187 FUNCTION_LITERAL_SYMBOL_NAME, exp.parameters, null); 188 block.accept(this); 189 } 190 191 override void visit(const ClassDeclaration dec) 192 { 193 visitAggregateDeclaration(dec, CompletionKind.className); 194 } 195 196 override void visit(const TemplateDeclaration dec) 197 { 198 visitAggregateDeclaration(dec, CompletionKind.templateName); 199 } 200 201 override void visit(const InterfaceDeclaration dec) 202 { 203 visitAggregateDeclaration(dec, CompletionKind.interfaceName); 204 } 205 206 override void visit(const UnionDeclaration dec) 207 { 208 visitAggregateDeclaration(dec, CompletionKind.unionName); 209 } 210 211 override void visit(const StructDeclaration dec) 212 { 213 visitAggregateDeclaration(dec, CompletionKind.structName); 214 } 215 216 override void visit(const NewAnonClassExpression nace) 217 { 218 // its base classes would be added as "inherit" breadcrumbs in the current symbol 219 skipBaseClassesOfNewAnon = true; 220 nace.accept(this); 221 skipBaseClassesOfNewAnon = false; 222 } 223 224 override void visit(const BaseClass bc) 225 { 226 if (skipBaseClassesOfNewAnon) 227 return; 228 if (bc.type2.typeIdentifierPart is null || 229 bc.type2.typeIdentifierPart.identifierOrTemplateInstance is null) 230 return; 231 auto lookup = TypeLookupsAllocator.instance.make!TypeLookup(TypeLookupKind.inherit); 232 writeIotcTo(bc.type2.typeIdentifierPart, lookup.breadcrumbs); 233 currentSymbol.typeLookups.insert(lookup); 234 235 // create an alias to the BaseClass to allow completions 236 // of the form : `instance.BaseClass.`, which is 237 // mostly used to bypass the most derived overrides. 238 const idt = lookup.breadcrumbs.back; 239 if (!idt.length) 240 return; 241 SemanticSymbol* symbol = allocateSemanticSymbol(idt, 242 CompletionKind.aliasName, symbolFile, currentScope.endLocation); 243 Type t = TypeLookupsAllocator.instance.make!Type; 244 t.type2 = cast() bc.type2; 245 addTypeToLookups(symbol.typeLookups, t); 246 symbol.parent = currentSymbol; 247 currentSymbol.addChild(symbol, true); 248 symbol.acSymbol.protection = protection.current; 249 } 250 251 override void visit(const VariableDeclaration dec) 252 { 253 assert (currentSymbol); 254 foreach (declarator; dec.declarators) 255 { 256 SemanticSymbol* symbol = allocateSemanticSymbol( 257 declarator.name.text, CompletionKind.variableName, 258 symbolFile, declarator.name.index); 259 if (dec.type !is null) 260 addTypeToLookups(symbol.typeLookups, dec.type); 261 symbol.parent = currentSymbol; 262 symbol.acSymbol.protection = protection.current; 263 symbol.acSymbol.doc = makeDocumentation(declarator.comment); 264 currentSymbol.addChild(symbol, true); 265 currentScope.addSymbol(symbol.acSymbol, false); 266 267 if (currentSymbol.acSymbol.kind == CompletionKind.structName 268 || currentSymbol.acSymbol.kind == CompletionKind.unionName) 269 { 270 structFieldNames.insert(symbol.acSymbol.name); 271 // TODO: remove this cast. See the note on structFieldTypes 272 structFieldTypes.insert(cast() dec.type); 273 } 274 } 275 if (dec.autoDeclaration !is null) 276 { 277 foreach (part; dec.autoDeclaration.parts) 278 { 279 SemanticSymbol* symbol = allocateSemanticSymbol( 280 part.identifier.text, CompletionKind.variableName, 281 symbolFile, part.identifier.index); 282 symbol.parent = currentSymbol; 283 populateInitializer(symbol, part.initializer); 284 symbol.acSymbol.protection = protection.current; 285 symbol.acSymbol.doc = makeDocumentation(dec.comment); 286 currentSymbol.addChild(symbol, true); 287 currentScope.addSymbol(symbol.acSymbol, false); 288 289 if (currentSymbol.acSymbol.kind == CompletionKind.structName 290 || currentSymbol.acSymbol.kind == CompletionKind.unionName) 291 { 292 structFieldNames.insert(symbol.acSymbol.name); 293 // TODO: remove this cast. See the note on structFieldTypes 294 structFieldTypes.insert(null); 295 } 296 } 297 } 298 } 299 300 override void visit(const AliasDeclaration aliasDeclaration) 301 { 302 if (aliasDeclaration.initializers.length == 0) 303 { 304 foreach (name; aliasDeclaration.declaratorIdentifierList.identifiers) 305 { 306 SemanticSymbol* symbol = allocateSemanticSymbol( 307 name.text, CompletionKind.aliasName, symbolFile, name.index); 308 if (aliasDeclaration.type !is null) 309 addTypeToLookups(symbol.typeLookups, aliasDeclaration.type); 310 symbol.parent = currentSymbol; 311 currentSymbol.addChild(symbol, true); 312 currentScope.addSymbol(symbol.acSymbol, false); 313 symbol.acSymbol.protection = protection.current; 314 symbol.acSymbol.doc = makeDocumentation(aliasDeclaration.comment); 315 } 316 } 317 else 318 { 319 foreach (initializer; aliasDeclaration.initializers) 320 { 321 SemanticSymbol* symbol = allocateSemanticSymbol( 322 initializer.name.text, CompletionKind.aliasName, 323 symbolFile, initializer.name.index); 324 if (initializer.type !is null) 325 addTypeToLookups(symbol.typeLookups, initializer.type); 326 symbol.parent = currentSymbol; 327 currentSymbol.addChild(symbol, true); 328 currentScope.addSymbol(symbol.acSymbol, false); 329 symbol.acSymbol.protection = protection.current; 330 symbol.acSymbol.doc = makeDocumentation(aliasDeclaration.comment); 331 } 332 } 333 } 334 335 override void visit(const AliasThisDeclaration dec) 336 { 337 const k = currentSymbol.acSymbol.kind; 338 if (k != CompletionKind.structName && k != CompletionKind.className && 339 k != CompletionKind.unionName && k != CompletionKind.mixinTemplateName) 340 { 341 return; 342 } 343 currentSymbol.typeLookups.insert(TypeLookupsAllocator.instance.make!TypeLookup( 344 internString(dec.identifier.text), TypeLookupKind.aliasThis)); 345 } 346 347 override void visit(const Declaration dec) 348 { 349 if (dec.attributeDeclaration !is null 350 && isProtection(dec.attributeDeclaration.attribute.attribute.type)) 351 { 352 protection.addScope(dec.attributeDeclaration.attribute.attribute.type); 353 return; 354 } 355 IdType p; 356 foreach (const Attribute attr; dec.attributes) 357 { 358 if (isProtection(attr.attribute.type)) 359 p = attr.attribute.type; 360 } 361 if (p != tok!"") 362 { 363 protection.beginLocal(p); 364 if (dec.declarations.length > 0) 365 { 366 protection.beginScope(); 367 dec.accept(this); 368 protection.endScope(); 369 } 370 else 371 dec.accept(this); 372 protection.endLocal(); 373 } 374 else 375 dec.accept(this); 376 } 377 378 override void visit(const Module mod) 379 { 380 rootSymbol = allocateSemanticSymbol(null, CompletionKind.moduleName, 381 symbolFile); 382 currentSymbol = rootSymbol; 383 moduleScope = GCAllocator.instance.make!Scope(0, uint.max); // NOTE using `semanticAllocator` here fails as `Segmentation fault (core dumped)` 384 currentScope = moduleScope; 385 auto objectLocation = cache.resolveImportLocation("object"); 386 if (objectLocation is null) 387 warning("Could not locate object.d or object.di"); 388 else 389 { 390 auto objectImport = allocateSemanticSymbol(IMPORT_SYMBOL_NAME, 391 CompletionKind.importSymbol, objectLocation); 392 objectImport.acSymbol.skipOver = true; 393 currentSymbol.addChild(objectImport, true); 394 currentScope.addSymbol(objectImport.acSymbol, false); 395 } 396 foreach (s; builtinSymbols[]) 397 currentScope.addSymbol(s, false); 398 mod.accept(this); 399 } 400 401 override void visit(const EnumDeclaration dec) 402 { 403 assert (currentSymbol); 404 SemanticSymbol* symbol = allocateSemanticSymbol(dec.name.text, 405 CompletionKind.enumName, symbolFile, dec.name.index); 406 if (dec.type !is null) 407 addTypeToLookups(symbol.typeLookups, dec.type); 408 symbol.acSymbol.addChildren(enumSymbols[], false); 409 symbol.parent = currentSymbol; 410 currentSymbol.addChild(symbol, true); 411 currentScope.addSymbol(symbol.acSymbol, false); 412 symbol.acSymbol.doc = makeDocumentation(dec.comment); 413 414 istring lastComment = this.lastComment; 415 this.lastComment = istring.init; 416 scope(exit) this.lastComment = lastComment; 417 418 currentSymbol = symbol; 419 420 if (dec.enumBody !is null) 421 { 422 pushScope(dec.enumBody.startLocation, dec.enumBody.endLocation); 423 dec.enumBody.accept(this); 424 popScope(); 425 } 426 427 currentSymbol = currentSymbol.parent; 428 } 429 430 mixin visitEnumMember!EnumMember; 431 mixin visitEnumMember!AnonymousEnumMember; 432 433 override void visit(const ModuleDeclaration moduleDeclaration) 434 { 435 const parts = moduleDeclaration.moduleName.identifiers; 436 rootSymbol.acSymbol.name = internString(parts.length ? parts[$ - 1].text : null); 437 } 438 439 override void visit(const StructBody structBody) 440 { 441 import std.algorithm : move; 442 443 pushScope(structBody.startLocation, structBody.endLocation); 444 scope (exit) popScope(); 445 protection.beginScope(); 446 scope (exit) protection.endScope(); 447 448 auto savedStructFieldNames = move(structFieldNames); 449 auto savedStructFieldTypes = move(structFieldTypes); 450 scope(exit) structFieldNames = move(savedStructFieldNames); 451 scope(exit) structFieldTypes = move(savedStructFieldTypes); 452 453 DSymbol* thisSymbol = SymbolAllocator.instance.make!DSymbol(THIS_SYMBOL_NAME, 454 CompletionKind.variableName, currentSymbol.acSymbol); 455 thisSymbol.location = currentScope.startLocation; 456 thisSymbol.symbolFile = symbolFile; 457 thisSymbol.type = currentSymbol.acSymbol; 458 thisSymbol.ownType = false; 459 currentScope.addSymbol(thisSymbol, false); 460 461 foreach (dec; structBody.declarations) 462 visit(dec); 463 464 // If no constructor is found, generate one 465 if ((currentSymbol.acSymbol.kind == CompletionKind.structName 466 || currentSymbol.acSymbol.kind == CompletionKind.unionName) 467 && currentSymbol.acSymbol.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null) 468 createConstructor(); 469 } 470 471 override void visit(const ImportDeclaration importDeclaration) 472 { 473 import std.algorithm : filter, map; 474 import std.path : buildPath; 475 import std.typecons : Tuple; 476 477 foreach (single; importDeclaration.singleImports.filter!( 478 a => a !is null && a.identifierChain !is null)) 479 { 480 immutable importPath = convertChainToImportPath(single.identifierChain); 481 istring modulePath = cache.resolveImportLocation(importPath); 482 if (modulePath is null) 483 { 484 warning("Could not resolve location of module '", importPath.data, "'"); 485 continue; 486 } 487 SemanticSymbol* importSymbol = allocateSemanticSymbol(IMPORT_SYMBOL_NAME, 488 CompletionKind.importSymbol, modulePath); 489 importSymbol.acSymbol.skipOver = protection.currentForImport != tok!"public"; 490 if (single.rename == tok!"") 491 { 492 size_t i = 0; 493 DSymbol* currentImportSymbol; 494 foreach (p; single.identifierChain.identifiers.map!(a => a.text)) 495 { 496 immutable bool first = i == 0; 497 immutable bool last = i + 1 >= single.identifierChain.identifiers.length; 498 immutable CompletionKind kind = last ? CompletionKind.moduleName 499 : CompletionKind.packageName; 500 istring ip = internString(p); 501 if (first) 502 { 503 auto s = currentScope.getSymbolsByName(ip); 504 if (s.length == 0) 505 { 506 currentImportSymbol = SymbolAllocator.instance.make!DSymbol(ip, kind); 507 currentScope.addSymbol(currentImportSymbol, true); 508 if (last) 509 { 510 currentImportSymbol.symbolFile = modulePath; 511 currentImportSymbol.type = importSymbol.acSymbol; 512 currentImportSymbol.ownType = false; 513 } 514 } 515 else 516 currentImportSymbol = s[0]; 517 } 518 else 519 { 520 auto s = currentImportSymbol.getPartsByName(ip); 521 if (s.length == 0) 522 { 523 auto sym = SymbolAllocator.instance.make!DSymbol(ip, kind); 524 currentImportSymbol.addChild(sym, true); 525 currentImportSymbol = sym; 526 if (last) 527 { 528 currentImportSymbol.symbolFile = modulePath; 529 currentImportSymbol.type = importSymbol.acSymbol; 530 currentImportSymbol.ownType = false; 531 } 532 } 533 else 534 currentImportSymbol = s[0]; 535 } 536 i++; 537 } 538 currentSymbol.addChild(importSymbol, true); 539 currentScope.addSymbol(importSymbol.acSymbol, false); 540 } 541 else 542 { 543 SemanticSymbol* renameSymbol = allocateSemanticSymbol( 544 internString(single.rename.text), CompletionKind.aliasName, 545 modulePath); 546 renameSymbol.acSymbol.skipOver = protection.currentForImport != tok!"public"; 547 renameSymbol.acSymbol.type = importSymbol.acSymbol; 548 renameSymbol.acSymbol.ownType = true; 549 renameSymbol.addChild(importSymbol, true); 550 currentSymbol.addChild(renameSymbol, true); 551 currentScope.addSymbol(renameSymbol.acSymbol, false); 552 } 553 if (entry !is null) 554 entry.dependencies.insert(modulePath); 555 } 556 if (importDeclaration.importBindings is null) return; 557 if (importDeclaration.importBindings.singleImport.identifierChain is null) return; 558 559 immutable chain = convertChainToImportPath(importDeclaration.importBindings.singleImport.identifierChain); 560 istring modulePath = cache.resolveImportLocation(chain); 561 if (modulePath is null) 562 { 563 warning("Could not resolve location of module '", chain, "'"); 564 return; 565 } 566 567 foreach (bind; importDeclaration.importBindings.importBinds) 568 { 569 TypeLookup* lookup = TypeLookupsAllocator.instance.make!TypeLookup( 570 TypeLookupKind.selectiveImport); 571 572 immutable bool isRenamed = bind.right != tok!""; 573 574 // The second phase must change this `importSymbol` kind to 575 // `aliasName` for symbol lookup to work. 576 SemanticSymbol* importSymbol = allocateSemanticSymbol( 577 isRenamed ? bind.left.text : IMPORT_SYMBOL_NAME, 578 CompletionKind.importSymbol, modulePath); 579 580 if (isRenamed) 581 { 582 lookup.breadcrumbs.insert(internString(bind.right.text)); 583 importSymbol.acSymbol.location = bind.left.index; 584 importSymbol.acSymbol.altFile = symbolFile; 585 } 586 lookup.breadcrumbs.insert(internString(bind.left.text)); 587 588 importSymbol.acSymbol.qualifier = SymbolQualifier.selectiveImport; 589 importSymbol.typeLookups.insert(lookup); 590 importSymbol.acSymbol.skipOver = protection.currentForImport != tok!"public"; 591 currentSymbol.addChild(importSymbol, true); 592 currentScope.addSymbol(importSymbol.acSymbol, false); 593 } 594 595 if (entry !is null) 596 entry.dependencies.insert(modulePath); 597 } 598 599 // Create scope for block statements 600 override void visit(const BlockStatement blockStatement) 601 { 602 if (blockStatement.declarationsAndStatements !is null) 603 { 604 pushScope(blockStatement.startLocation, blockStatement.endLocation); 605 scope(exit) popScope(); 606 visit (blockStatement.declarationsAndStatements); 607 } 608 } 609 610 // Create attribute/protection scope for conditional compilation declaration 611 // blocks. 612 override void visit(const ConditionalDeclaration conditionalDecl) 613 { 614 if (conditionalDecl.compileCondition !is null) 615 visit(conditionalDecl.compileCondition); 616 617 if (conditionalDecl.trueDeclarations.length) 618 { 619 protection.beginScope(); 620 scope (exit) protection.endScope(); 621 622 foreach (decl; conditionalDecl.trueDeclarations) 623 if (decl !is null) 624 visit (decl); 625 } 626 627 if (conditionalDecl.falseDeclarations.length) 628 { 629 protection.beginScope(); 630 scope (exit) protection.endScope(); 631 632 foreach (decl; conditionalDecl.falseDeclarations) 633 if (decl !is null) 634 visit (decl); 635 } 636 } 637 638 override void visit(const TemplateMixinExpression tme) 639 { 640 // TODO: support typeof here 641 if (tme.mixinTemplateName.symbol is null) 642 return; 643 const Symbol sym = tme.mixinTemplateName.symbol; 644 auto lookup = TypeLookupsAllocator.instance.make!TypeLookup(TypeLookupKind.mixinTemplate); 645 646 writeIotcTo(tme.mixinTemplateName.symbol.identifierOrTemplateChain, 647 lookup.breadcrumbs); 648 649 if (currentSymbol.acSymbol.kind != CompletionKind.functionName) 650 currentSymbol.typeLookups.insert(lookup); 651 652 /* If the mixin is named then do like if `mixin F f;` would be `mixin F; alias f = F;` 653 which's been empirically verified to produce the right completions for `f.`, 654 */ 655 if (tme.identifier != tok!"" && sym.identifierOrTemplateChain && 656 sym.identifierOrTemplateChain.identifiersOrTemplateInstances.length) 657 { 658 SemanticSymbol* symbol = allocateSemanticSymbol(tme.identifier.text, 659 CompletionKind.aliasName, symbolFile, tme.identifier.index); 660 Type tp = TypeLookupsAllocator.instance.make!Type; 661 tp.type2 = TypeLookupsAllocator.instance.make!Type2; 662 TypeIdentifierPart root; 663 TypeIdentifierPart current; 664 foreach(ioti; sym.identifierOrTemplateChain.identifiersOrTemplateInstances) 665 { 666 TypeIdentifierPart old = current; 667 current = TypeLookupsAllocator.instance.make!TypeIdentifierPart; 668 if (old) 669 { 670 old.typeIdentifierPart = current; 671 } 672 else 673 { 674 root = current; 675 } 676 current.identifierOrTemplateInstance = cast() ioti; 677 } 678 tp.type2.typeIdentifierPart = root; 679 addTypeToLookups(symbol.typeLookups, tp); 680 symbol.parent = currentSymbol; 681 currentSymbol.addChild(symbol, true); 682 currentScope.addSymbol(symbol.acSymbol, false); 683 symbol.acSymbol.protection = protection.current; 684 } 685 } 686 687 override void visit(const ForeachStatement feStatement) 688 { 689 if (feStatement.declarationOrStatement !is null 690 && feStatement.declarationOrStatement.statement !is null 691 && feStatement.declarationOrStatement.statement.statementNoCaseNoDefault !is null 692 && feStatement.declarationOrStatement.statement.statementNoCaseNoDefault.blockStatement !is null) 693 { 694 const BlockStatement bs = 695 feStatement.declarationOrStatement.statement.statementNoCaseNoDefault.blockStatement; 696 pushScope(feStatement.startIndex, bs.endLocation); 697 scope(exit) popScope(); 698 feExpression = feStatement.low.items[$ - 1]; 699 feStatement.accept(this); 700 feExpression = null; 701 } 702 else 703 { 704 const ubyte o1 = foreachTypeIndexOfInterest; 705 const ubyte o2 = foreachTypeIndex; 706 feStatement.accept(this); 707 foreachTypeIndexOfInterest = o1; 708 foreachTypeIndex = o2; 709 } 710 } 711 712 override void visit(const ForeachTypeList feTypeList) 713 { 714 foreachTypeIndex = 0; 715 foreachTypeIndexOfInterest = cast(ubyte)(feTypeList.items.length - 1); 716 feTypeList.accept(this); 717 } 718 719 override void visit(const ForeachType feType) 720 { 721 if (foreachTypeIndex++ == foreachTypeIndexOfInterest) 722 { 723 SemanticSymbol* symbol = allocateSemanticSymbol(feType.identifier.text, 724 CompletionKind.variableName, symbolFile, feType.identifier.index); 725 if (feType.type !is null) 726 addTypeToLookups(symbol.typeLookups, feType.type); 727 symbol.parent = currentSymbol; 728 currentSymbol.addChild(symbol, true); 729 currentScope.addSymbol(symbol.acSymbol, true); 730 if (symbol.typeLookups.empty && feExpression !is null) 731 populateInitializer(symbol, feExpression, true); 732 } 733 } 734 735 override void visit(const IfStatement ifs) 736 { 737 if (ifs.identifier != tok!"" && ifs.thenStatement) 738 { 739 pushScope(ifs.thenStatement.startLocation, ifs.thenStatement.endLocation); 740 scope(exit) popScope(); 741 742 SemanticSymbol* symbol = allocateSemanticSymbol(ifs.identifier.text, 743 CompletionKind.variableName, symbolFile, ifs.identifier.index); 744 if (ifs.type !is null) 745 addTypeToLookups(symbol.typeLookups, ifs.type); 746 symbol.parent = currentSymbol; 747 currentSymbol.addChild(symbol, true); 748 currentScope.addSymbol(symbol.acSymbol, true); 749 if (symbol.typeLookups.empty && ifs.expression !is null) 750 populateInitializer(symbol, ifs.expression, false); 751 } 752 ifs.accept(this); 753 } 754 755 override void visit(const WithStatement withStatement) 756 { 757 if (withStatement.expression !is null 758 && withStatement.declarationOrStatement !is null) 759 { 760 pushScope(withStatement.declarationOrStatement.startLocation, 761 withStatement.declarationOrStatement.endLocation); 762 scope(exit) popScope(); 763 764 pushSymbol(WITH_SYMBOL_NAME, CompletionKind.withSymbol, symbolFile, 765 currentScope.startLocation, null); 766 scope(exit) popSymbol(); 767 768 populateInitializer(currentSymbol, withStatement.expression, false); 769 withStatement.accept(this); 770 771 } 772 else 773 withStatement.accept(this); 774 } 775 776 override void visit(const ArgumentList list) 777 { 778 scope visitor = new ArgumentListVisitor(this); 779 visitor.visit(list); 780 } 781 782 alias visit = ASTVisitor.visit; 783 784 /// Module scope 785 Scope* moduleScope; 786 787 /// The module 788 SemanticSymbol* rootSymbol; 789 790 /// Allocator used for symbol allocation 791 IAllocator symbolAllocator; 792 793 /// Number of symbols allocated 794 uint symbolsAllocated; 795 796 private: 797 798 void createConstructor() 799 { 800 import std.array : appender; 801 import std.range : zip; 802 803 auto app = appender!string(); 804 app.put("this("); 805 bool first = true; 806 foreach (field; zip(structFieldTypes[], structFieldNames[])) 807 { 808 if (first) 809 first = false; 810 else 811 app.put(", "); 812 if (field[0] is null) 813 app.put("auto "); 814 else 815 { 816 app.formatNode(field[0]); 817 app.put(" "); 818 } 819 app.put(field[1].data); 820 } 821 app.put(")"); 822 SemanticSymbol* symbol = allocateSemanticSymbol(CONSTRUCTOR_SYMBOL_NAME, 823 CompletionKind.functionName, symbolFile, currentSymbol.acSymbol.location); 824 symbol.acSymbol.callTip = istring(app.data); 825 currentSymbol.addChild(symbol, true); 826 } 827 828 void pushScope(size_t startLocation, size_t endLocation) 829 { 830 assert (startLocation < uint.max); 831 assert (endLocation < uint.max || endLocation == size_t.max); 832 Scope* s = ScopeAllocator.instance.make!Scope(cast(uint) startLocation, cast(uint) endLocation); 833 s.parent = currentScope; 834 currentScope.children.insert(s); 835 currentScope = s; 836 } 837 838 void popScope() 839 { 840 currentScope = currentScope.parent; 841 } 842 843 void pushFunctionScope(const FunctionBody functionBody, 844 IAllocator semanticAllocator, size_t scopeBegin) 845 { 846 Scope* s = ScopeAllocator.instance.make!Scope(cast(uint) scopeBegin, 847 cast(uint) functionBody.endLocation); 848 s.parent = currentScope; 849 currentScope.children.insert(s); 850 currentScope = s; 851 } 852 853 void pushSymbol(string name, CompletionKind kind, istring symbolFile, 854 size_t location = 0, const Type type = null) 855 { 856 SemanticSymbol* symbol = allocateSemanticSymbol(name, kind, symbolFile, 857 location); 858 if (type !is null) 859 addTypeToLookups(symbol.typeLookups, type); 860 symbol.parent = currentSymbol; 861 currentSymbol.addChild(symbol, true); 862 currentScope.addSymbol(symbol.acSymbol, false); 863 currentSymbol = symbol; 864 } 865 866 void popSymbol() 867 { 868 currentSymbol = currentSymbol.parent; 869 } 870 871 template visitEnumMember(T) 872 { 873 override void visit(const T member) 874 { 875 pushSymbol(member.name.text, CompletionKind.enumMember, symbolFile, 876 member.name.index, member.type); 877 scope(exit) popSymbol(); 878 currentSymbol.acSymbol.doc = makeDocumentation(member.comment); 879 } 880 } 881 882 void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind) 883 { 884 if ((kind == CompletionKind.unionName || kind == CompletionKind.structName) && 885 dec.name == tok!"") 886 { 887 dec.accept(this); 888 return; 889 } 890 pushSymbol(dec.name.text, kind, symbolFile, dec.name.index); 891 scope(exit) popSymbol(); 892 893 if (kind == CompletionKind.className) 894 currentSymbol.acSymbol.addChildren(classSymbols[], false); 895 else 896 currentSymbol.acSymbol.addChildren(aggregateSymbols[], false); 897 currentSymbol.acSymbol.protection = protection.current; 898 currentSymbol.acSymbol.doc = makeDocumentation(dec.comment); 899 900 istring lastComment = this.lastComment; 901 this.lastComment = istring.init; 902 scope(exit) this.lastComment = lastComment; 903 904 immutable size_t scopeBegin = dec.name.index + dec.name.text.length; 905 static if (is (AggType == const(TemplateDeclaration))) 906 immutable size_t scopeEnd = dec.endLocation; 907 else 908 immutable size_t scopeEnd = dec.structBody is null ? scopeBegin : dec.structBody.endLocation; 909 pushScope(scopeBegin, scopeEnd); 910 scope(exit) popScope(); 911 protection.beginScope(); 912 scope (exit) protection.endScope(); 913 processTemplateParameters(currentSymbol, dec.templateParameters); 914 dec.accept(this); 915 } 916 917 void visitConstructor(size_t location, const Parameters parameters, 918 const TemplateParameters templateParameters, 919 const FunctionBody functionBody, string doc) 920 { 921 SemanticSymbol* symbol = allocateSemanticSymbol(CONSTRUCTOR_SYMBOL_NAME, 922 CompletionKind.functionName, symbolFile, location); 923 symbol.parent = currentSymbol; 924 currentSymbol.addChild(symbol, true); 925 processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters); 926 symbol.acSymbol.protection = protection.current; 927 symbol.acSymbol.doc = makeDocumentation(doc); 928 929 istring lastComment = this.lastComment; 930 this.lastComment = istring.init; 931 scope(exit) this.lastComment = lastComment; 932 933 if (functionBody !is null) 934 { 935 pushFunctionScope(functionBody, semanticAllocator, 936 location + 4); // 4 == "this".length 937 scope(exit) popScope(); 938 currentSymbol = symbol; 939 functionBody.accept(this); 940 currentSymbol = currentSymbol.parent; 941 } 942 } 943 944 void visitDestructor(size_t location, const FunctionBody functionBody, string doc) 945 { 946 SemanticSymbol* symbol = allocateSemanticSymbol(DESTRUCTOR_SYMBOL_NAME, 947 CompletionKind.functionName, symbolFile, location); 948 symbol.parent = currentSymbol; 949 currentSymbol.addChild(symbol, true); 950 symbol.acSymbol.callTip = internString("~this()"); 951 symbol.acSymbol.protection = protection.current; 952 symbol.acSymbol.doc = makeDocumentation(doc); 953 954 istring lastComment = this.lastComment; 955 this.lastComment = istring.init; 956 scope(exit) this.lastComment = lastComment; 957 958 if (functionBody !is null) 959 { 960 pushFunctionScope(functionBody, semanticAllocator, location + 4); // 4 == "this".length 961 scope(exit) popScope(); 962 currentSymbol = symbol; 963 functionBody.accept(this); 964 currentSymbol = currentSymbol.parent; 965 } 966 } 967 968 void processParameters(SemanticSymbol* symbol, const Type returnType, 969 string functionName, const Parameters parameters, 970 const TemplateParameters templateParameters) 971 { 972 processTemplateParameters(symbol, templateParameters); 973 if (includeParameterSymbols && parameters !is null) 974 { 975 foreach (const Parameter p; parameters.parameters) 976 { 977 SemanticSymbol* parameter = allocateSemanticSymbol( 978 p.name.text, CompletionKind.variableName, symbolFile, 979 p.name.index); 980 if (p.type !is null) 981 addTypeToLookups(parameter.typeLookups, p.type); 982 parameter.parent = currentSymbol; 983 currentSymbol.acSymbol.argNames.insert(parameter.acSymbol.name); 984 currentSymbol.addChild(parameter, true); 985 currentScope.addSymbol(parameter.acSymbol, false); 986 } 987 if (parameters.hasVarargs) 988 { 989 SemanticSymbol* argptr = allocateSemanticSymbol(ARGPTR_SYMBOL_NAME, 990 CompletionKind.variableName, istring(null), size_t.max); 991 addTypeToLookups(argptr.typeLookups, argptrType); 992 argptr.parent = currentSymbol; 993 currentSymbol.addChild(argptr, true); 994 currentScope.addSymbol(argptr.acSymbol, false); 995 996 SemanticSymbol* arguments = allocateSemanticSymbol( 997 ARGUMENTS_SYMBOL_NAME, CompletionKind.variableName, 998 istring(null), size_t.max); 999 addTypeToLookups(arguments.typeLookups, argumentsType); 1000 arguments.parent = currentSymbol; 1001 currentSymbol.addChild(arguments, true); 1002 currentScope.addSymbol(arguments.acSymbol, false); 1003 } 1004 } 1005 symbol.acSymbol.callTip = formatCallTip(returnType, functionName, 1006 parameters, templateParameters); 1007 } 1008 1009 void processTemplateParameters(SemanticSymbol* symbol, const TemplateParameters templateParameters) 1010 { 1011 if (includeParameterSymbols && templateParameters !is null 1012 && templateParameters.templateParameterList !is null) 1013 { 1014 foreach (const TemplateParameter p; templateParameters.templateParameterList.items) 1015 { 1016 string name; 1017 CompletionKind kind; 1018 size_t index; 1019 Rebindable!(const(Type)) type; 1020 if (p.templateAliasParameter !is null) 1021 { 1022 name = p.templateAliasParameter.identifier.text; 1023 kind = CompletionKind.aliasName; 1024 index = p.templateAliasParameter.identifier.index; 1025 } 1026 else if (p.templateTypeParameter !is null) 1027 { 1028 name = p.templateTypeParameter.identifier.text; 1029 kind = CompletionKind.aliasName; 1030 index = p.templateTypeParameter.identifier.index; 1031 // even if templates are not solved we can get the completions 1032 // for the type the template parameter implicitly converts to, 1033 // which is often useful for aggregate types. 1034 if (p.templateTypeParameter.colonType) 1035 type = p.templateTypeParameter.colonType; 1036 // otherwise just provide standard type properties 1037 else 1038 kind = CompletionKind.typeTmpParam; 1039 } 1040 else if (p.templateValueParameter !is null) 1041 { 1042 name = p.templateValueParameter.identifier.text; 1043 kind = CompletionKind.variableName; 1044 index = p.templateValueParameter.identifier.index; 1045 type = p.templateValueParameter.type; 1046 } 1047 else if (p.templateTupleParameter !is null) 1048 { 1049 name = p.templateTupleParameter.identifier.text; 1050 kind = CompletionKind.variadicTmpParam; 1051 index = p.templateTupleParameter.identifier.index; 1052 } 1053 else 1054 continue; 1055 SemanticSymbol* templateParameter = allocateSemanticSymbol(name, 1056 kind, symbolFile, index); 1057 if (type !is null) 1058 addTypeToLookups(templateParameter.typeLookups, type); 1059 1060 if (p.templateTupleParameter !is null) 1061 { 1062 TypeLookup* tl = TypeLookupsAllocator.instance.make!TypeLookup( 1063 istring(name), TypeLookupKind.varOrFunType); 1064 templateParameter.typeLookups.insert(tl); 1065 } 1066 else if (p.templateTypeParameter && kind == CompletionKind.typeTmpParam) 1067 { 1068 TypeLookup* tl = TypeLookupsAllocator.instance.make!TypeLookup( 1069 istring(name), TypeLookupKind.varOrFunType); 1070 templateParameter.typeLookups.insert(tl); 1071 } 1072 1073 templateParameter.parent = symbol; 1074 symbol.addChild(templateParameter, true); 1075 if (currentScope) 1076 currentScope.addSymbol(templateParameter.acSymbol, false); 1077 } 1078 } 1079 } 1080 1081 istring formatCallTip(const Type returnType, string name, 1082 const Parameters parameters, const TemplateParameters templateParameters) 1083 { 1084 import std.array : appender; 1085 1086 auto app = appender!string(); 1087 if (returnType !is null) 1088 { 1089 app.formatNode(returnType); 1090 app.put(' '); 1091 } 1092 app.put(name); 1093 if (templateParameters !is null) 1094 app.formatNode(templateParameters); 1095 if (parameters is null) 1096 app.put("()"); 1097 else 1098 app.formatNode(parameters); 1099 return istring(app.data); 1100 } 1101 1102 void populateInitializer(T)(SemanticSymbol* symbol, const T initializer, 1103 bool appendForeach = false) 1104 { 1105 auto lookup = TypeLookupsAllocator.instance.make!TypeLookup(TypeLookupKind.initializer); 1106 scope visitor = new InitializerVisitor(lookup, appendForeach, this); 1107 symbol.typeLookups.insert(lookup); 1108 visitor.visit(initializer); 1109 } 1110 1111 SemanticSymbol* allocateSemanticSymbol(string name, CompletionKind kind, 1112 istring symbolFile, size_t location = 0) 1113 in 1114 { 1115 assert (symbolAllocator !is null); 1116 } 1117 do 1118 { 1119 DSymbol* acSymbol = SymbolAllocator.instance.make!DSymbol(istring(name), kind); 1120 acSymbol.location = location; 1121 acSymbol.symbolFile = symbolFile; 1122 symbolsAllocated++; 1123 return SymbolAllocator.instance.make!SemanticSymbol(acSymbol); // NOTE using semanticAllocator here breaks when analysing phobos as: `Segmentation fault (core dumped)‘’ 1124 } 1125 1126 void addTypeToLookups(ref TypeLookups lookups, 1127 const Type type, TypeLookup* l = null) 1128 { 1129 auto lookup = l !is null ? l : TypeLookupsAllocator.instance.make!TypeLookup( 1130 TypeLookupKind.varOrFunType); 1131 auto t2 = type.type2; 1132 if (t2.type !is null) 1133 addTypeToLookups(lookups, t2.type, lookup); 1134 else if (t2.superOrThis is tok!"this") 1135 lookup.breadcrumbs.insert(internString("this")); 1136 else if (t2.superOrThis is tok!"super") 1137 lookup.breadcrumbs.insert(internString("super")); 1138 else if (t2.builtinType !is tok!"") 1139 lookup.breadcrumbs.insert(getBuiltinTypeName(t2.builtinType)); 1140 else if (t2.typeIdentifierPart !is null) 1141 writeIotcTo(t2.typeIdentifierPart, lookup.breadcrumbs); 1142 else 1143 { 1144 // TODO: Add support for typeof expressions 1145 // TODO: Add support for __vector 1146 // warning("typeof() and __vector are not yet supported"); 1147 } 1148 1149 foreach (suffix; type.typeSuffixes) 1150 { 1151 if (suffix.star != tok!"") 1152 continue; 1153 else if (suffix.type) 1154 lookup.breadcrumbs.insert(ASSOC_ARRAY_SYMBOL_NAME); 1155 else if (suffix.array) 1156 lookup.breadcrumbs.insert(ARRAY_SYMBOL_NAME); 1157 else if (suffix.star != tok!"") 1158 lookup.breadcrumbs.insert(POINTER_SYMBOL_NAME); 1159 else if (suffix.delegateOrFunction != tok!"") 1160 { 1161 import std.array : appender; 1162 auto app = appender!string(); 1163 formatNode(app, type); 1164 istring callTip = istring(app.data); 1165 // Insert the call tip and THEN the "function" string because 1166 // the breadcrumbs are processed in reverse order 1167 lookup.breadcrumbs.insert(callTip); 1168 lookup.breadcrumbs.insert(FUNCTION_SYMBOL_NAME); 1169 } 1170 } 1171 if (l is null) 1172 lookups.insert(lookup); 1173 } 1174 1175 DocString makeDocumentation(string documentation) 1176 { 1177 if (documentation.isDitto) 1178 return DocString(lastComment, true); 1179 else 1180 { 1181 lastComment = internString(documentation); 1182 return DocString(lastComment, false); 1183 } 1184 } 1185 1186 /// Current protection type 1187 ProtectionStack protection; 1188 1189 /// Current scope 1190 Scope* currentScope; 1191 1192 /// Current symbol 1193 SemanticSymbol* currentSymbol; 1194 1195 /// Path to the file being converted 1196 istring symbolFile; 1197 1198 /// Field types used for generating struct constructors if no constructor 1199 /// was defined 1200 // TODO: This should be `const Type`, but Rebindable and opEquals don't play 1201 // well together 1202 UnrolledList!(Type) structFieldTypes; 1203 1204 /// Field names for struct constructor generation 1205 UnrolledList!(istring) structFieldNames; 1206 1207 /// Last comment for ditto-ing 1208 istring lastComment; 1209 1210 const Module mod; 1211 1212 IAllocator semanticAllocator; 1213 1214 Rebindable!(const ExpressionNode) feExpression; 1215 1216 CacheEntry* entry; 1217 1218 ModuleCache* cache; 1219 1220 bool includeParameterSymbols; 1221 bool skipBaseClassesOfNewAnon; 1222 1223 ubyte foreachTypeIndexOfInterest; 1224 ubyte foreachTypeIndex; 1225 } 1226 1227 struct ProtectionStack 1228 { 1229 invariant 1230 { 1231 import std.algorithm.iteration : filter, joiner, map; 1232 import std.conv:to; 1233 import std.range : walkLength; 1234 1235 assert(stack.length == stack[].filter!(a => isProtection(a) 1236 || a == tok!":" || a == tok!"{").walkLength(), to!string(stack[].map!(a => str(a)).joiner(", "))); 1237 } 1238 1239 IdType currentForImport() const 1240 { 1241 return stack.empty ? tok!"default" : current(); 1242 } 1243 1244 IdType current() const 1245 { 1246 import std.algorithm.iteration : filter; 1247 import std.range : choose, only; 1248 1249 IdType retVal; 1250 foreach (t; choose(stack.empty, only(tok!"public"), stack[]).filter!( 1251 a => a != tok!"{" && a != tok!":")) 1252 retVal = cast(IdType) t; 1253 return retVal; 1254 } 1255 1256 void beginScope() 1257 { 1258 stack.insertBack(tok!"{"); 1259 } 1260 1261 void endScope() 1262 { 1263 import std.algorithm.iteration : joiner; 1264 import std.conv : to; 1265 import std.range : walkLength; 1266 1267 while (!stack.empty && stack.back == tok!":") 1268 { 1269 assert(stack.length >= 2); 1270 stack.popBack(); 1271 stack.popBack(); 1272 } 1273 assert(stack.length == stack[].walkLength()); 1274 assert(!stack.empty && stack.back == tok!"{", to!string(stack[].map!(a => str(a)).joiner(", "))); 1275 stack.popBack(); 1276 } 1277 1278 void beginLocal(const IdType t) 1279 { 1280 assert (t != tok!"", "DERP!"); 1281 stack.insertBack(t); 1282 } 1283 1284 void endLocal() 1285 { 1286 import std.algorithm.iteration : joiner; 1287 import std.conv : to; 1288 1289 assert(!stack.empty && stack.back != tok!":" && stack.back != tok!"{", 1290 to!string(stack[].map!(a => str(a)).joiner(", "))); 1291 stack.popBack(); 1292 } 1293 1294 void addScope(const IdType t) 1295 { 1296 assert(t != tok!"", "DERP!"); 1297 assert(isProtection(t)); 1298 if (!stack.empty && stack.back == tok!":") 1299 { 1300 assert(stack.length >= 2); 1301 stack.popBack(); 1302 assert(isProtection(stack.back)); 1303 stack.popBack(); 1304 } 1305 stack.insertBack(t); 1306 stack.insertBack(tok!":"); 1307 } 1308 1309 private: 1310 1311 UnrolledList!IdType stack; 1312 } 1313 1314 void formatNode(A, T)(ref A appender, const T node) 1315 { 1316 if (node is null) 1317 return; 1318 scope f = new Formatter!(A*)(&appender); 1319 f.format(node); 1320 } 1321 1322 private: 1323 1324 bool isDitto(scope const(char)[] comment) 1325 { 1326 import std.uni : icmp; 1327 1328 return comment.length == 5 && icmp(comment, "ditto") == 0; 1329 } 1330 1331 void writeIotcTo(T)(const TypeIdentifierPart tip, ref T output) nothrow 1332 { 1333 if (!tip.identifierOrTemplateInstance) 1334 return; 1335 if (tip.identifierOrTemplateInstance.identifier != tok!"") 1336 output.insert(internString(tip.identifierOrTemplateInstance.identifier.text)); 1337 else 1338 output.insert(internString(tip.identifierOrTemplateInstance.templateInstance.identifier.text)); 1339 1340 // the indexer of a TypeIdentifierPart means either that there's 1341 // a static array dimension or that a type is selected in a type list. 1342 // we can only handle the first case since dsymbol does not process templates yet. 1343 if (tip.indexer) 1344 output.insert(ARRAY_SYMBOL_NAME); 1345 1346 if (tip.typeIdentifierPart) 1347 writeIotcTo(tip.typeIdentifierPart, output); 1348 } 1349 1350 auto byIdentifier(const IdentifierOrTemplateChain iotc) nothrow 1351 { 1352 import std.algorithm : map; 1353 1354 return iotc.identifiersOrTemplateInstances.map!(a => a.identifier == tok!"" 1355 ? a.templateInstance.identifier.text 1356 : a.identifier.text); 1357 } 1358 1359 void writeIotcTo(T)(const IdentifierOrTemplateChain iotc, ref T output) nothrow 1360 { 1361 import std.algorithm : each; 1362 1363 byIdentifier(iotc).each!(a => output.insert(internString(a))); 1364 } 1365 1366 static istring convertChainToImportPath(const IdentifierChain ic) 1367 { 1368 import std.path : dirSeparator; 1369 import std.array : appender; 1370 auto app = appender!string(); 1371 foreach (i, ident; ic.identifiers) 1372 { 1373 app.put(ident.text); 1374 if (i + 1 < ic.identifiers.length) 1375 app.put(dirSeparator); 1376 } 1377 return istring(app.data); 1378 } 1379 1380 class InitializerVisitor : ASTVisitor 1381 { 1382 this (TypeLookup* lookup, bool appendForeach, FirstPass fp) 1383 { 1384 this.lookup = lookup; 1385 this.appendForeach = appendForeach; 1386 this.fp = fp; 1387 } 1388 1389 alias visit = ASTVisitor.visit; 1390 1391 override void visit(const FunctionLiteralExpression exp) 1392 { 1393 fp.visit(exp); 1394 } 1395 1396 override void visit(const IdentifierOrTemplateInstance ioti) 1397 { 1398 if (on && ioti.identifier != tok!"") 1399 lookup.breadcrumbs.insert(internString(ioti.identifier.text)); 1400 else if (on && ioti.templateInstance.identifier != tok!"") 1401 lookup.breadcrumbs.insert(internString(ioti.templateInstance.identifier.text)); 1402 ioti.accept(this); 1403 } 1404 1405 override void visit(const PrimaryExpression primary) 1406 { 1407 // Add identifiers without processing. Convert literals to strings with 1408 // the prefix '*' so that that the second pass can tell the difference 1409 // between "int.abc" and "10.abc". 1410 if (on && primary.basicType != tok!"") 1411 lookup.breadcrumbs.insert(internString(str(primary.basicType.type))); 1412 if (on) switch (primary.primary.type) 1413 { 1414 case tok!"identifier": 1415 lookup.breadcrumbs.insert(internString(primary.primary.text)); 1416 break; 1417 case tok!"doubleLiteral": 1418 lookup.breadcrumbs.insert(DOUBLE_LITERAL_SYMBOL_NAME); 1419 break; 1420 case tok!"floatLiteral": 1421 lookup.breadcrumbs.insert(FLOAT_LITERAL_SYMBOL_NAME); 1422 break; 1423 case tok!"idoubleLiteral": 1424 lookup.breadcrumbs.insert(IDOUBLE_LITERAL_SYMBOL_NAME); 1425 break; 1426 case tok!"ifloatLiteral": 1427 lookup.breadcrumbs.insert(IFLOAT_LITERAL_SYMBOL_NAME); 1428 break; 1429 case tok!"intLiteral": 1430 lookup.breadcrumbs.insert(INT_LITERAL_SYMBOL_NAME); 1431 break; 1432 case tok!"longLiteral": 1433 lookup.breadcrumbs.insert(LONG_LITERAL_SYMBOL_NAME); 1434 break; 1435 case tok!"realLiteral": 1436 lookup.breadcrumbs.insert(REAL_LITERAL_SYMBOL_NAME); 1437 break; 1438 case tok!"irealLiteral": 1439 lookup.breadcrumbs.insert(IREAL_LITERAL_SYMBOL_NAME); 1440 break; 1441 case tok!"uintLiteral": 1442 lookup.breadcrumbs.insert(UINT_LITERAL_SYMBOL_NAME); 1443 break; 1444 case tok!"ulongLiteral": 1445 lookup.breadcrumbs.insert(ULONG_LITERAL_SYMBOL_NAME); 1446 break; 1447 case tok!"characterLiteral": 1448 lookup.breadcrumbs.insert(CHAR_LITERAL_SYMBOL_NAME); 1449 break; 1450 case tok!"dstringLiteral": 1451 lookup.breadcrumbs.insert(DSTRING_LITERAL_SYMBOL_NAME); 1452 break; 1453 case tok!"stringLiteral": 1454 lookup.breadcrumbs.insert(STRING_LITERAL_SYMBOL_NAME); 1455 break; 1456 case tok!"wstringLiteral": 1457 lookup.breadcrumbs.insert(WSTRING_LITERAL_SYMBOL_NAME); 1458 break; 1459 case tok!"false": 1460 case tok!"true": 1461 lookup.breadcrumbs.insert(BOOL_VALUE_SYMBOL_NAME); 1462 break; 1463 default: 1464 break; 1465 } 1466 primary.accept(this); 1467 } 1468 1469 override void visit(const IndexExpression expr) 1470 { 1471 expr.unaryExpression.accept(this); 1472 foreach (index; expr.indexes) 1473 if (index.high is null) 1474 lookup.breadcrumbs.insert(ARRAY_SYMBOL_NAME); 1475 } 1476 1477 override void visit(const Initializer initializer) 1478 { 1479 on = true; 1480 initializer.accept(this); 1481 on = false; 1482 } 1483 1484 override void visit(const ArrayInitializer ai) 1485 { 1486 // If the array has any elements, assume all elements have the 1487 // same type as the first element. 1488 if (ai.arrayMemberInitializations.length) 1489 ai.arrayMemberInitializations[0].accept(this); 1490 else 1491 lookup.breadcrumbs.insert(VOID_SYMBOL_NAME); 1492 1493 lookup.breadcrumbs.insert(ARRAY_LITERAL_SYMBOL_NAME); 1494 } 1495 1496 override void visit(const ArrayLiteral al) 1497 { 1498 // ditto 1499 if (al.argumentList) 1500 { 1501 if (al.argumentList.items.length) 1502 al.argumentList.items[0].accept(this); 1503 else 1504 lookup.breadcrumbs.insert(VOID_SYMBOL_NAME); 1505 } 1506 lookup.breadcrumbs.insert(ARRAY_LITERAL_SYMBOL_NAME); 1507 } 1508 1509 // Skip it 1510 override void visit(const NewAnonClassExpression) {} 1511 1512 override void visit(const NewExpression ne) 1513 { 1514 if (ne.newAnonClassExpression) 1515 lowerNewAnonToNew((cast() ne)); 1516 ne.accept(this); 1517 } 1518 1519 private void lowerNewAnonToNew(NewExpression ne) 1520 { 1521 import std.format : format; 1522 1523 // here we follow DMDFE naming style 1524 __gshared size_t anonIndex; 1525 const idt = istring("__anonclass%d".format(++anonIndex)); 1526 1527 // the goal is to replace it so we null the field 1528 NewAnonClassExpression nace = ne.newAnonClassExpression; 1529 ne.newAnonClassExpression = null; 1530 1531 // Lower the AnonClass body to a standard ClassDeclaration and visit it. 1532 ClassDeclaration cd = theAllocator.make!(ClassDeclaration); 1533 cd.name = Token(tok!"identifier", idt, 1, 1, nace.structBody.startLocation - idt.length); 1534 cd.baseClassList = nace.baseClassList; 1535 cd.structBody = nace.structBody; 1536 fp.visit(cd); 1537 1538 // Change the NewAnonClassExpression to a standard NewExpression using 1539 // the ClassDeclaration created in previous step 1540 ne.type = theAllocator.make!(Type); 1541 ne.type.type2 = theAllocator.make!(Type2); 1542 ne.type.type2.typeIdentifierPart = theAllocator.make!(TypeIdentifierPart); 1543 ne.type.type2.typeIdentifierPart.identifierOrTemplateInstance = theAllocator.make!(IdentifierOrTemplateInstance); 1544 ne.type.type2.typeIdentifierPart.identifierOrTemplateInstance.identifier = cd.name; 1545 ne.arguments = nace.constructorArguments; 1546 } 1547 1548 override void visit(const ArgumentList list) 1549 { 1550 scope visitor = new ArgumentListVisitor(fp); 1551 visitor.visit(list); 1552 } 1553 1554 override void visit(const Expression expression) 1555 { 1556 on = true; 1557 expression.accept(this); 1558 if (appendForeach) 1559 lookup.breadcrumbs.insert(internString("foreach")); 1560 on = false; 1561 } 1562 1563 override void visit(const ExpressionNode expression) 1564 { 1565 on = true; 1566 expression.accept(this); 1567 if (appendForeach) 1568 lookup.breadcrumbs.insert(internString("foreach")); 1569 on = false; 1570 } 1571 1572 TypeLookup* lookup; 1573 bool on = false; 1574 const bool appendForeach; 1575 FirstPass fp; 1576 } 1577 1578 class ArgumentListVisitor : ASTVisitor 1579 { 1580 this(FirstPass fp) 1581 { 1582 assert(fp); 1583 this.fp = fp; 1584 } 1585 1586 alias visit = ASTVisitor.visit; 1587 1588 override void visit(const FunctionLiteralExpression exp) 1589 { 1590 fp.visit(exp); 1591 } 1592 1593 override void visit(const NewAnonClassExpression exp) 1594 { 1595 fp.visit(exp); 1596 } 1597 1598 private: 1599 FirstPass fp; 1600 }