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