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 std.experimental.allocator; 37 import std.experimental.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.protection = protection.current; 141 currentSymbol.acSymbol.doc = internString(dec.comment); 142 143 if (dec.functionBody !is null) 144 { 145 pushFunctionScope(dec.functionBody, semanticAllocator, 146 dec.name.index + dec.name.text.length); 147 scope (exit) popScope(); 148 processParameters(currentSymbol, dec.returnType, 149 currentSymbol.acSymbol.name, dec.parameters, dec.templateParameters); 150 dec.functionBody.accept(this); 151 } 152 else 153 { 154 immutable ips = includeParameterSymbols; 155 includeParameterSymbols = false; 156 processParameters(currentSymbol, dec.returnType, 157 currentSymbol.acSymbol.name, dec.parameters, dec.templateParameters); 158 includeParameterSymbols = ips; 159 } 160 } 161 162 override void visit(const ClassDeclaration dec) 163 { 164 visitAggregateDeclaration(dec, CompletionKind.className); 165 } 166 167 override void visit(const TemplateDeclaration dec) 168 { 169 visitAggregateDeclaration(dec, CompletionKind.templateName); 170 } 171 172 override void visit(const InterfaceDeclaration dec) 173 { 174 visitAggregateDeclaration(dec, CompletionKind.interfaceName); 175 } 176 177 override void visit(const UnionDeclaration dec) 178 { 179 visitAggregateDeclaration(dec, CompletionKind.unionName); 180 } 181 182 override void visit(const StructDeclaration dec) 183 { 184 visitAggregateDeclaration(dec, CompletionKind.structName); 185 } 186 187 override void visit(const BaseClass bc) 188 { 189 if (bc.type2.symbol is null || bc.type2.symbol.identifierOrTemplateChain is null) 190 return; 191 auto lookup = Mallocator.instance.make!TypeLookup(TypeLookupKind.inherit); 192 writeIotcTo(bc.type2.symbol.identifierOrTemplateChain, 193 lookup.breadcrumbs); 194 currentSymbol.typeLookups.insert(lookup); 195 } 196 197 override void visit(const VariableDeclaration dec) 198 { 199 assert (currentSymbol); 200 foreach (declarator; dec.declarators) 201 { 202 SemanticSymbol* symbol = allocateSemanticSymbol( 203 declarator.name.text, CompletionKind.variableName, 204 symbolFile, declarator.name.index); 205 if (dec.type !is null) 206 addTypeToLookups(symbol.typeLookups, dec.type); 207 symbol.parent = currentSymbol; 208 symbol.protection = protection.current; 209 symbol.acSymbol.doc = internString(declarator.comment); 210 currentSymbol.addChild(symbol, true); 211 currentScope.addSymbol(symbol.acSymbol, false); 212 213 if (currentSymbol.acSymbol.kind == CompletionKind.structName) 214 { 215 structFieldNames.insert(symbol.acSymbol.name); 216 // TODO: remove this cast. See the note on structFieldTypes 217 structFieldTypes.insert(cast() dec.type); 218 } 219 } 220 if (dec.autoDeclaration !is null) 221 { 222 foreach (part; dec.autoDeclaration.parts) 223 { 224 SemanticSymbol* symbol = allocateSemanticSymbol( 225 part.identifier.text, CompletionKind.variableName, 226 symbolFile, part.identifier.index); 227 symbol.parent = currentSymbol; 228 populateInitializer(symbol, part.initializer); 229 symbol.protection = protection.current; 230 symbol.acSymbol.doc = internString(dec.comment); 231 currentSymbol.addChild(symbol, true); 232 currentScope.addSymbol(symbol.acSymbol, false); 233 234 if (currentSymbol.acSymbol.kind == CompletionKind.structName) 235 { 236 structFieldNames.insert(symbol.acSymbol.name); 237 // TODO: remove this cast. See the note on structFieldTypes 238 structFieldTypes.insert(null); 239 } 240 } 241 } 242 } 243 244 override void visit(const AliasDeclaration aliasDeclaration) 245 { 246 if (aliasDeclaration.initializers.length == 0) 247 { 248 foreach (name; aliasDeclaration.identifierList.identifiers) 249 { 250 SemanticSymbol* symbol = allocateSemanticSymbol( 251 name.text, CompletionKind.aliasName, symbolFile, name.index); 252 if (aliasDeclaration.type !is null) 253 addTypeToLookups(symbol.typeLookups, aliasDeclaration.type); 254 symbol.parent = currentSymbol; 255 currentSymbol.addChild(symbol, true); 256 currentScope.addSymbol(symbol.acSymbol, false); 257 symbol.protection = protection.current; 258 symbol.acSymbol.doc = internString(aliasDeclaration.comment); 259 } 260 } 261 else 262 { 263 foreach (initializer; aliasDeclaration.initializers) 264 { 265 SemanticSymbol* symbol = allocateSemanticSymbol( 266 initializer.name.text, CompletionKind.aliasName, 267 symbolFile, initializer.name.index); 268 if (initializer.type !is null) 269 addTypeToLookups(symbol.typeLookups, initializer.type); 270 symbol.parent = currentSymbol; 271 currentSymbol.addChild(symbol, true); 272 currentScope.addSymbol(symbol.acSymbol, false); 273 symbol.protection = protection.current; 274 symbol.acSymbol.doc = internString(aliasDeclaration.comment); 275 } 276 } 277 } 278 279 override void visit(const AliasThisDeclaration dec) 280 { 281 currentSymbol.typeLookups.insert(Mallocator.instance.make!TypeLookup( 282 internString(dec.identifier.text), TypeLookupKind.aliasThis)); 283 } 284 285 override void visit(const Declaration dec) 286 { 287 if (dec.attributeDeclaration !is null 288 && isProtection(dec.attributeDeclaration.attribute.attribute.type)) 289 { 290 protection.addScope(dec.attributeDeclaration.attribute.attribute.type); 291 return; 292 } 293 IdType p; 294 foreach (const Attribute attr; dec.attributes) 295 { 296 if (isProtection(attr.attribute.type)) 297 p = attr.attribute.type; 298 } 299 if (p != tok!"") 300 { 301 protection.beginLocal(p); 302 if (dec.declarations.length > 0) 303 { 304 protection.beginScope(); 305 dec.accept(this); 306 protection.endScope(); 307 } 308 else 309 dec.accept(this); 310 protection.endLocal(); 311 } 312 else 313 dec.accept(this); 314 } 315 316 override void visit(const Module mod) 317 { 318 rootSymbol = allocateSemanticSymbol(null, CompletionKind.moduleName, 319 symbolFile); 320 currentSymbol = rootSymbol; 321 moduleScope = semanticAllocator.make!Scope(0, uint.max); 322 currentScope = moduleScope; 323 auto objectLocation = cache.resolveImportLocation("object"); 324 if (objectLocation is null) 325 warning("Could not locate object.d or object.di"); 326 else 327 { 328 auto objectImport = allocateSemanticSymbol(IMPORT_SYMBOL_NAME, 329 CompletionKind.importSymbol, objectLocation); 330 objectImport.acSymbol.skipOver = true; 331 currentSymbol.addChild(objectImport, true); 332 currentScope.addSymbol(objectImport.acSymbol, false); 333 } 334 foreach (s; builtinSymbols[]) 335 currentScope.addSymbol(s, false); 336 mod.accept(this); 337 } 338 339 override void visit(const EnumDeclaration dec) 340 { 341 assert (currentSymbol); 342 SemanticSymbol* symbol = allocateSemanticSymbol(dec.name.text, 343 CompletionKind.enumName, symbolFile, dec.name.index); 344 if (dec.type !is null) 345 addTypeToLookups(symbol.typeLookups, dec.type); 346 symbol.acSymbol.addChildren(enumSymbols[], false); 347 symbol.parent = currentSymbol; 348 currentSymbol.addChild(symbol, true); 349 currentScope.addSymbol(symbol.acSymbol, false); 350 symbol.acSymbol.doc = internString(dec.comment); 351 currentSymbol = symbol; 352 353 if (dec.enumBody !is null) 354 { 355 pushScope(dec.enumBody.startLocation, dec.enumBody.endLocation); 356 dec.enumBody.accept(this); 357 popScope(); 358 } 359 360 currentSymbol = currentSymbol.parent; 361 } 362 363 mixin visitEnumMember!EnumMember; 364 mixin visitEnumMember!AnonymousEnumMember; 365 366 override void visit(const ModuleDeclaration moduleDeclaration) 367 { 368 rootSymbol.acSymbol.name = internString(moduleDeclaration.moduleName.identifiers[$ - 1].text); 369 } 370 371 override void visit(const StructBody structBody) 372 { 373 pushScope(structBody.startLocation, structBody.endLocation); 374 scope (exit) popScope(); 375 protection.beginScope(); 376 scope (exit) protection.endScope(); 377 378 structFieldNames.clear(); 379 structFieldTypes.clear(); 380 381 DSymbol* thisSymbol = make!DSymbol(symbolAllocator, THIS_SYMBOL_NAME, 382 CompletionKind.variableName, currentSymbol.acSymbol); 383 thisSymbol.location = currentScope.startLocation; 384 thisSymbol.symbolFile = symbolFile; 385 thisSymbol.type = currentSymbol.acSymbol; 386 thisSymbol.ownType = false; 387 currentScope.addSymbol(thisSymbol, false); 388 389 foreach (dec; structBody.declarations) 390 visit(dec); 391 392 // If no constructor is found, generate one 393 if (currentSymbol.acSymbol.kind == CompletionKind.structName 394 && currentSymbol.acSymbol.getFirstPartNamed(CONSTRUCTOR_SYMBOL_NAME) is null) 395 createConstructor(); 396 } 397 398 override void visit(const ImportDeclaration importDeclaration) 399 { 400 import std.algorithm : filter, map; 401 import std.path : buildPath; 402 import std.typecons : Tuple; 403 404 foreach (single; importDeclaration.singleImports.filter!( 405 a => a !is null && a.identifierChain !is null)) 406 { 407 immutable importPath = convertChainToImportPath(single.identifierChain); 408 istring modulePath = cache.resolveImportLocation(importPath); 409 if (modulePath is null) 410 { 411 warning("Could not resolve location of module '", importPath, "'"); 412 continue; 413 } 414 SemanticSymbol* importSymbol = allocateSemanticSymbol(IMPORT_SYMBOL_NAME, 415 CompletionKind.importSymbol, modulePath); 416 importSymbol.acSymbol.skipOver = protection.currentForImport != tok!"public"; 417 if (single.rename == tok!"") 418 { 419 size_t i = 0; 420 DSymbol* currentImportSymbol; 421 foreach (p; single.identifierChain.identifiers.map!(a => a.text)) 422 { 423 immutable bool first = i == 0; 424 immutable bool last = i + 1 >= single.identifierChain.identifiers.length; 425 immutable CompletionKind kind = last ? CompletionKind.moduleName 426 : CompletionKind.packageName; 427 istring ip = internString(p); 428 if (first) 429 { 430 auto s = currentScope.getSymbolsByName(ip); 431 if (s.length == 0) 432 { 433 currentImportSymbol = symbolAllocator.make!DSymbol(ip, kind); 434 currentScope.addSymbol(currentImportSymbol, true); 435 if (last) 436 { 437 currentImportSymbol.symbolFile = modulePath; 438 currentImportSymbol.type = importSymbol.acSymbol; 439 currentImportSymbol.ownType = false; 440 } 441 } 442 else 443 currentImportSymbol = s[0]; 444 } 445 else 446 { 447 auto s = currentImportSymbol.getPartsByName(ip); 448 if (s.length == 0) 449 { 450 auto sym = symbolAllocator.make!DSymbol(ip, kind); 451 currentImportSymbol.addChild(sym, true); 452 currentImportSymbol = sym; 453 if (last) 454 { 455 currentImportSymbol.symbolFile = modulePath; 456 currentImportSymbol.type = importSymbol.acSymbol; 457 currentImportSymbol.ownType = false; 458 } 459 } 460 else 461 currentImportSymbol = s[0]; 462 } 463 i++; 464 } 465 currentSymbol.addChild(importSymbol, true); 466 currentScope.addSymbol(importSymbol.acSymbol, false); 467 } 468 else 469 { 470 SemanticSymbol* renameSymbol = allocateSemanticSymbol( 471 internString(single.rename.text), CompletionKind.aliasName, 472 modulePath); 473 renameSymbol.acSymbol.skipOver = protection.current != tok!"public"; 474 renameSymbol.acSymbol.type = importSymbol.acSymbol; 475 renameSymbol.acSymbol.ownType = true; 476 renameSymbol.addChild(importSymbol, true); 477 currentSymbol.addChild(renameSymbol, true); 478 currentScope.addSymbol(renameSymbol.acSymbol, false); 479 } 480 if (entry !is null) 481 entry.dependencies.insert(modulePath); 482 } 483 if (importDeclaration.importBindings is null) return; 484 if (importDeclaration.importBindings.singleImport.identifierChain is null) return; 485 486 immutable chain = convertChainToImportPath(importDeclaration.importBindings.singleImport.identifierChain); 487 istring modulePath = cache.resolveImportLocation(chain); 488 if (modulePath is null) 489 { 490 warning("Could not resolve location of module '", chain, "'"); 491 return; 492 } 493 494 foreach (bind; importDeclaration.importBindings.importBinds) 495 { 496 TypeLookup* lookup = Mallocator.instance.make!TypeLookup( 497 TypeLookupKind.selectiveImport); 498 499 immutable bool isRenamed = bind.right != tok!""; 500 501 // The second phase must change this `importSymbol` kind to 502 // `aliasName` for symbol lookup to work. 503 SemanticSymbol* importSymbol = allocateSemanticSymbol( 504 isRenamed ? bind.left.text : IMPORT_SYMBOL_NAME, 505 CompletionKind.importSymbol, modulePath); 506 507 if (isRenamed) 508 { 509 lookup.breadcrumbs.insert(internString(bind.right.text)); 510 importSymbol.acSymbol.location = bind.left.index; 511 importSymbol.acSymbol.altFile = symbolFile; 512 } 513 lookup.breadcrumbs.insert(internString(bind.left.text)); 514 515 importSymbol.acSymbol.qualifier = SymbolQualifier.selectiveImport; 516 importSymbol.typeLookups.insert(lookup); 517 importSymbol.acSymbol.skipOver = protection.current != tok!"public"; 518 currentSymbol.addChild(importSymbol, true); 519 currentScope.addSymbol(importSymbol.acSymbol, false); 520 } 521 522 if (entry !is null) 523 entry.dependencies.insert(modulePath); 524 } 525 526 // Create scope for block statements 527 override void visit(const BlockStatement blockStatement) 528 { 529 if (blockStatement.declarationsAndStatements !is null) 530 { 531 pushScope(blockStatement.startLocation, blockStatement.endLocation); 532 scope(exit) popScope(); 533 visit (blockStatement.declarationsAndStatements); 534 } 535 } 536 537 override void visit(const TemplateMixinExpression tme) 538 { 539 // TODO: support typeof here 540 if (tme.mixinTemplateName.symbol is null) 541 return; 542 auto lookup = Mallocator.instance.make!TypeLookup(TypeLookupKind.mixinTemplate); 543 writeIotcTo(tme.mixinTemplateName.symbol.identifierOrTemplateChain, 544 lookup.breadcrumbs); 545 546 if (currentSymbol.acSymbol.kind != CompletionKind.functionName) 547 currentSymbol.typeLookups.insert(lookup); 548 } 549 550 override void visit(const ForeachStatement feStatement) 551 { 552 if (feStatement.declarationOrStatement !is null 553 && feStatement.declarationOrStatement.statement !is null 554 && feStatement.declarationOrStatement.statement.statementNoCaseNoDefault !is null 555 && feStatement.declarationOrStatement.statement.statementNoCaseNoDefault.blockStatement !is null) 556 { 557 const BlockStatement bs = 558 feStatement.declarationOrStatement.statement.statementNoCaseNoDefault.blockStatement; 559 pushScope(feStatement.startIndex, bs.endLocation); 560 scope(exit) popScope(); 561 feExpression = feStatement.low.items[$ - 1]; 562 feStatement.accept(this); 563 feExpression = null; 564 } 565 else 566 { 567 const ubyte o1 = foreachTypeIndexOfInterest; 568 const ubyte o2 = foreachTypeIndex; 569 feStatement.accept(this); 570 foreachTypeIndexOfInterest = o1; 571 foreachTypeIndex = o2; 572 } 573 } 574 575 override void visit(const ForeachTypeList feTypeList) 576 { 577 foreachTypeIndex = 0; 578 foreachTypeIndexOfInterest = cast(ubyte)(feTypeList.items.length - 1); 579 feTypeList.accept(this); 580 } 581 582 override void visit(const ForeachType feType) 583 { 584 if (foreachTypeIndex++ == foreachTypeIndexOfInterest) 585 { 586 SemanticSymbol* symbol = allocateSemanticSymbol(feType.identifier.text, 587 CompletionKind.variableName, symbolFile, feType.identifier.index); 588 if (feType.type !is null) 589 addTypeToLookups(symbol.typeLookups, feType.type); 590 symbol.parent = currentSymbol; 591 currentSymbol.addChild(symbol, true); 592 currentScope.addSymbol(symbol.acSymbol, true); 593 if (symbol.typeLookups.empty && feExpression !is null) 594 populateInitializer(symbol, feExpression, true); 595 } 596 } 597 598 override void visit(const WithStatement withStatement) 599 { 600 if (withStatement.expression !is null 601 && withStatement.statementNoCaseNoDefault !is null) 602 { 603 pushScope(withStatement.statementNoCaseNoDefault.startLocation, 604 withStatement.statementNoCaseNoDefault.endLocation); 605 scope(exit) popScope(); 606 607 pushSymbol(WITH_SYMBOL_NAME, CompletionKind.withSymbol, symbolFile, 608 currentScope.startLocation, null); 609 scope(exit) popSymbol(); 610 611 populateInitializer(currentSymbol, withStatement.expression, false); 612 withStatement.accept(this); 613 614 } 615 else 616 withStatement.accept(this); 617 } 618 619 alias visit = ASTVisitor.visit; 620 621 /// Module scope 622 Scope* moduleScope; 623 624 /// The module 625 SemanticSymbol* rootSymbol; 626 627 /// Allocator used for symbol allocation 628 IAllocator symbolAllocator; 629 630 /// Number of symbols allocated 631 uint symbolsAllocated; 632 633 private: 634 635 void createConstructor() 636 { 637 import std.array : appender; 638 import std.range : zip; 639 640 auto app = appender!(char[])(); 641 app.put("this("); 642 bool first = true; 643 foreach (field; zip(structFieldTypes[], structFieldNames[])) 644 { 645 if (first) 646 first = false; 647 else 648 app.put(", "); 649 if (field[0] is null) 650 app.put("auto "); 651 else 652 { 653 app.formatNode(field[0]); 654 app.put(" "); 655 } 656 app.put(field[1]); 657 } 658 app.put(")"); 659 SemanticSymbol* symbol = allocateSemanticSymbol(CONSTRUCTOR_SYMBOL_NAME, 660 CompletionKind.functionName, symbolFile, currentSymbol.acSymbol.location); 661 symbol.acSymbol.callTip = internString(cast(string) app.data); 662 currentSymbol.addChild(symbol, true); 663 } 664 665 void pushScope(size_t startLocation, size_t endLocation) 666 { 667 assert (startLocation < uint.max); 668 assert (endLocation < uint.max || endLocation == size_t.max); 669 Scope* s = semanticAllocator.make!Scope(cast(uint) startLocation, cast(uint) endLocation); 670 s.parent = currentScope; 671 currentScope.children.insert(s); 672 currentScope = s; 673 } 674 675 void popScope() 676 { 677 currentScope = currentScope.parent; 678 } 679 680 void pushFunctionScope(const FunctionBody functionBody, 681 IAllocator semanticAllocator, size_t scopeBegin) 682 { 683 import std.algorithm : max; 684 685 immutable scopeEnd = max( 686 functionBody.inStatement is null ? 0 : functionBody.inStatement.blockStatement.endLocation, 687 functionBody.outStatement is null ? 0 : functionBody.outStatement.blockStatement.endLocation, 688 functionBody.blockStatement is null ? 0 : functionBody.blockStatement.endLocation, 689 functionBody.bodyStatement is null ? 0 : functionBody.bodyStatement.blockStatement.endLocation); 690 Scope* s = semanticAllocator.make!Scope(cast(uint) scopeBegin, cast(uint) scopeEnd); 691 s.parent = currentScope; 692 currentScope.children.insert(s); 693 currentScope = s; 694 } 695 696 void pushSymbol(string name, CompletionKind kind, istring symbolFile, 697 size_t location = 0, const Type type = null) 698 { 699 SemanticSymbol* symbol = allocateSemanticSymbol(name, kind, symbolFile, 700 location); 701 if (type !is null) 702 addTypeToLookups(symbol.typeLookups, type); 703 symbol.parent = currentSymbol; 704 currentSymbol.addChild(symbol, true); 705 currentScope.addSymbol(symbol.acSymbol, false); 706 currentSymbol = symbol; 707 } 708 709 void popSymbol() 710 { 711 currentSymbol = currentSymbol.parent; 712 } 713 714 template visitEnumMember(T) 715 { 716 override void visit(const T member) 717 { 718 pushSymbol(member.name.text, CompletionKind.enumMember, symbolFile, 719 member.name.index, member.type); 720 scope(exit) popSymbol(); 721 currentSymbol.acSymbol.doc = internString(member.comment); 722 } 723 } 724 725 void visitAggregateDeclaration(AggType)(AggType dec, CompletionKind kind) 726 { 727 if (kind == CompletionKind.unionName && dec.name == tok!"") 728 { 729 dec.accept(this); 730 return; 731 } 732 pushSymbol(dec.name.text, kind, symbolFile, dec.name.index); 733 scope(exit) popSymbol(); 734 735 if (kind == CompletionKind.className) 736 currentSymbol.acSymbol.addChildren(classSymbols[], false); 737 else 738 currentSymbol.acSymbol.addChildren(aggregateSymbols[], false); 739 currentSymbol.protection = protection.current; 740 currentSymbol.acSymbol.doc = internString(dec.comment); 741 742 immutable size_t scopeBegin = dec.name.index + dec.name.text.length; 743 static if (is (AggType == const(TemplateDeclaration))) 744 immutable size_t scopeEnd = dec.endLocation; 745 else 746 immutable size_t scopeEnd = dec.structBody is null ? scopeBegin : dec.structBody.endLocation; 747 pushScope(scopeBegin, scopeEnd); 748 scope(exit) popScope(); 749 protection.beginScope(); 750 scope (exit) protection.endScope(); 751 processTemplateParameters(currentSymbol, dec.templateParameters); 752 dec.accept(this); 753 } 754 755 void visitConstructor(size_t location, const Parameters parameters, 756 const TemplateParameters templateParameters, 757 const FunctionBody functionBody, string doc) 758 { 759 SemanticSymbol* symbol = allocateSemanticSymbol(CONSTRUCTOR_SYMBOL_NAME, 760 CompletionKind.functionName, symbolFile, location); 761 symbol.parent = currentSymbol; 762 currentSymbol.addChild(symbol, true); 763 processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters); 764 symbol.protection = protection.current; 765 symbol.acSymbol.doc = internString(doc); 766 if (functionBody !is null) 767 { 768 pushFunctionScope(functionBody, semanticAllocator, 769 location + 4); // 4 == "this".length 770 scope(exit) popScope(); 771 currentSymbol = symbol; 772 functionBody.accept(this); 773 currentSymbol = currentSymbol.parent; 774 } 775 } 776 777 void visitDestructor(size_t location, const FunctionBody functionBody, string doc) 778 { 779 SemanticSymbol* symbol = allocateSemanticSymbol(DESTRUCTOR_SYMBOL_NAME, 780 CompletionKind.functionName, symbolFile, location); 781 symbol.parent = currentSymbol; 782 currentSymbol.addChild(symbol, true); 783 symbol.acSymbol.callTip = internString("~this()"); 784 symbol.protection = protection.current; 785 symbol.acSymbol.doc = internString(doc); 786 if (functionBody !is null) 787 { 788 pushFunctionScope(functionBody, semanticAllocator, location + 4); // 4 == "this".length 789 scope(exit) popScope(); 790 currentSymbol = symbol; 791 functionBody.accept(this); 792 currentSymbol = currentSymbol.parent; 793 } 794 } 795 796 void processParameters(SemanticSymbol* symbol, const Type returnType, 797 string functionName, const Parameters parameters, 798 const TemplateParameters templateParameters) 799 { 800 processTemplateParameters(symbol, templateParameters); 801 if (includeParameterSymbols && parameters !is null) 802 { 803 foreach (const Parameter p; parameters.parameters) 804 { 805 SemanticSymbol* parameter = allocateSemanticSymbol( 806 p.name.text, CompletionKind.variableName, symbolFile, 807 p.name.index); 808 if (p.type !is null) 809 addTypeToLookups(parameter.typeLookups, p.type); 810 parameter.parent = currentSymbol; 811 currentSymbol.acSymbol.argNames.insert(parameter.acSymbol.name); 812 currentSymbol.addChild(parameter, true); 813 currentScope.addSymbol(parameter.acSymbol, false); 814 } 815 if (parameters.hasVarargs) 816 { 817 SemanticSymbol* argptr = allocateSemanticSymbol(ARGPTR_SYMBOL_NAME, 818 CompletionKind.variableName, istring(null), size_t.max); 819 addTypeToLookups(argptr.typeLookups, argptrType); 820 argptr.parent = currentSymbol; 821 currentSymbol.addChild(argptr, true); 822 currentScope.addSymbol(argptr.acSymbol, false); 823 824 SemanticSymbol* arguments = allocateSemanticSymbol( 825 ARGUMENTS_SYMBOL_NAME, CompletionKind.variableName, 826 istring(null), size_t.max); 827 addTypeToLookups(arguments.typeLookups, argumentsType); 828 arguments.parent = currentSymbol; 829 currentSymbol.addChild(arguments, true); 830 currentScope.addSymbol(arguments.acSymbol, false); 831 } 832 } 833 symbol.acSymbol.callTip = formatCallTip(returnType, functionName, 834 parameters, templateParameters); 835 } 836 837 void processTemplateParameters(SemanticSymbol* symbol, const TemplateParameters templateParameters) 838 { 839 if (includeParameterSymbols && templateParameters !is null 840 && templateParameters.templateParameterList !is null) 841 { 842 foreach (const TemplateParameter p; templateParameters.templateParameterList.items) 843 { 844 string name; 845 CompletionKind kind; 846 size_t index; 847 Rebindable!(const(Type)) type; 848 if (p.templateAliasParameter !is null) 849 { 850 name = p.templateAliasParameter.identifier.text; 851 kind = CompletionKind.aliasName; 852 index = p.templateAliasParameter.identifier.index; 853 } 854 else if (p.templateTypeParameter !is null) 855 { 856 name = p.templateTypeParameter.identifier.text; 857 kind = CompletionKind.aliasName; 858 index = p.templateTypeParameter.identifier.index; 859 } 860 else if (p.templateValueParameter !is null) 861 { 862 name = p.templateValueParameter.identifier.text; 863 kind = CompletionKind.variableName; 864 index = p.templateValueParameter.identifier.index; 865 type = p.templateValueParameter.type; 866 } 867 else 868 continue; 869 SemanticSymbol* templateParameter = allocateSemanticSymbol(name, 870 kind, symbolFile, index); 871 if (type !is null) 872 addTypeToLookups(templateParameter.typeLookups, type); 873 templateParameter.parent = symbol; 874 symbol.addChild(templateParameter, true); 875 } 876 } 877 } 878 879 istring formatCallTip(const Type returnType, string name, 880 const Parameters parameters, const TemplateParameters templateParameters) 881 { 882 import std.array : appender; 883 884 auto app = appender!(char[])(); 885 if (returnType !is null) 886 { 887 app.formatNode(returnType); 888 app.put(' '); 889 } 890 app.put(name); 891 if (templateParameters !is null) 892 app.formatNode(templateParameters); 893 if (parameters is null) 894 app.put("()"); 895 else 896 app.formatNode(parameters); 897 return internString(cast(string) app.data); 898 } 899 900 void populateInitializer(T)(SemanticSymbol* symbol, const T initializer, 901 bool appendForeach = false) 902 { 903 auto lookup = Mallocator.instance.make!TypeLookup(TypeLookupKind.initializer); 904 auto visitor = scoped!InitializerVisitor(lookup, appendForeach); 905 symbol.typeLookups.insert(lookup); 906 visitor.visit(initializer); 907 } 908 909 SemanticSymbol* allocateSemanticSymbol(string name, CompletionKind kind, 910 istring symbolFile, size_t location = 0) 911 in 912 { 913 assert (symbolAllocator !is null); 914 } 915 body 916 { 917 DSymbol* acSymbol = make!DSymbol(symbolAllocator, name, kind); 918 acSymbol.location = location; 919 acSymbol.symbolFile = symbolFile; 920 symbolsAllocated++; 921 return semanticAllocator.make!SemanticSymbol(acSymbol); 922 } 923 924 void addTypeToLookups(ref UnrolledList!(TypeLookup*, Mallocator, false) lookups, 925 const Type type, TypeLookup* l = null) 926 { 927 auto lookup = l !is null ? l : Mallocator.instance.make!TypeLookup( 928 TypeLookupKind.varOrFunType); 929 auto t2 = type.type2; 930 if (t2.type !is null) 931 addTypeToLookups(lookups, t2.type, lookup); 932 else if (t2.superOrThis is tok!"this") 933 lookup.breadcrumbs.insert(internString("this")); 934 else if (t2.superOrThis is tok!"super") 935 lookup.breadcrumbs.insert(internString("super")); 936 else if (t2.builtinType !is tok!"") 937 lookup.breadcrumbs.insert(getBuiltinTypeName(t2.builtinType)); 938 else if (t2.symbol !is null) 939 writeIotcTo(t2.symbol.identifierOrTemplateChain, lookup.breadcrumbs); 940 else 941 { 942 // TODO: Add support for typeof expressions 943 // TODO: Add support for __vector 944 // warning("typeof() and __vector are not yet supported"); 945 } 946 947 foreach (suffix; type.typeSuffixes) 948 { 949 if (suffix.star != tok!"") 950 continue; 951 else if (suffix.type) 952 lookup.breadcrumbs.insert(ASSOC_ARRAY_SYMBOL_NAME); 953 else if (suffix.array) 954 lookup.breadcrumbs.insert(ARRAY_SYMBOL_NAME); 955 else if (suffix.star != tok!"") 956 lookup.breadcrumbs.insert(POINTER_SYMBOL_NAME); 957 else if (suffix.delegateOrFunction != tok!"") 958 { 959 import std.array : appender; 960 auto app = appender!(char[])(); 961 formatNode(app, type); 962 istring callTip = internString(cast(string) app.data); 963 // Insert the call tip and THEN the "function" string because 964 // the breadcrumbs are processed in reverse order 965 lookup.breadcrumbs.insert(callTip); 966 lookup.breadcrumbs.insert(FUNCTION_SYMBOL_NAME); 967 } 968 } 969 if (l is null) 970 lookups.insert(lookup); 971 } 972 973 /// Current protection type 974 ProtectionStack protection; 975 976 /// Current scope 977 Scope* currentScope; 978 979 /// Current symbol 980 SemanticSymbol* currentSymbol; 981 982 /// Path to the file being converted 983 istring symbolFile; 984 985 /// Field types used for generating struct constructors if no constructor 986 /// was defined 987 // TODO: This should be `const Type`, but Rebindable and opEquals don't play 988 // well together 989 UnrolledList!(Type) structFieldTypes; 990 991 /// Field names for struct constructor generation 992 UnrolledList!(istring) structFieldNames; 993 994 const Module mod; 995 996 IAllocator semanticAllocator; 997 998 Rebindable!(const ExpressionNode) feExpression; 999 1000 CacheEntry* entry; 1001 1002 ModuleCache* cache; 1003 1004 bool includeParameterSymbols; 1005 1006 ubyte foreachTypeIndexOfInterest; 1007 ubyte foreachTypeIndex; 1008 } 1009 1010 struct ProtectionStack 1011 { 1012 invariant 1013 { 1014 import std.algorithm.iteration : filter, joiner, map; 1015 import std.conv:to; 1016 import std.range : walkLength; 1017 1018 assert(stack.length == stack[].filter!(a => isProtection(a) 1019 || a == tok!":" || a == tok!"{").walkLength(), to!string(stack[].map!(a => str(a)).joiner(", "))); 1020 } 1021 1022 IdType currentForImport() const 1023 { 1024 return stack.empty ? tok!"default" : current(); 1025 } 1026 1027 IdType current() const 1028 { 1029 import std.algorithm.iteration : filter; 1030 import std.range : choose, only; 1031 1032 IdType retVal; 1033 foreach (t; choose(stack.empty, only(tok!"public"), stack[]).filter!( 1034 a => a != tok!"{" && a != tok!":")) 1035 retVal = cast(IdType) t; 1036 return retVal; 1037 } 1038 1039 void beginScope() 1040 { 1041 stack.insertBack(tok!"{"); 1042 } 1043 1044 void endScope() 1045 { 1046 import std.algorithm.iteration : joiner; 1047 import std.conv : to; 1048 import std.range : walkLength; 1049 1050 while (!stack.empty && stack.back == tok!":") 1051 { 1052 assert(stack.length >= 2); 1053 stack.popBack(); 1054 stack.popBack(); 1055 } 1056 assert(stack.length == stack[].walkLength()); 1057 assert(!stack.empty && stack.back == tok!"{", to!string(stack[].map!(a => str(a)).joiner(", "))); 1058 stack.popBack(); 1059 } 1060 1061 void beginLocal(const IdType t) 1062 { 1063 assert (t != tok!"", "DERP!"); 1064 stack.insertBack(t); 1065 } 1066 1067 void endLocal() 1068 { 1069 import std.algorithm.iteration : joiner; 1070 import std.conv : to; 1071 1072 assert(!stack.empty && stack.back != tok!":" && stack.back != tok!"{", 1073 to!string(stack[].map!(a => str(a)).joiner(", "))); 1074 stack.popBack(); 1075 } 1076 1077 void addScope(const IdType t) 1078 { 1079 assert(t != tok!"", "DERP!"); 1080 assert(isProtection(t)); 1081 if (!stack.empty && stack.back == tok!":") 1082 { 1083 assert(stack.length >= 2); 1084 stack.popBack(); 1085 assert(isProtection(stack.back)); 1086 stack.popBack(); 1087 } 1088 stack.insertBack(t); 1089 stack.insertBack(tok!":"); 1090 } 1091 1092 private: 1093 1094 UnrolledList!IdType stack; 1095 } 1096 1097 void formatNode(A, T)(ref A appender, const T node) 1098 { 1099 if (node is null) 1100 return; 1101 auto f = scoped!(Formatter!(A*))(&appender); 1102 f.format(node); 1103 } 1104 1105 private: 1106 1107 auto byIdentifier(const IdentifierOrTemplateChain iotc) 1108 { 1109 import std.algorithm : map; 1110 1111 return iotc.identifiersOrTemplateInstances.map!(a => a.identifier == tok!"" 1112 ? a.templateInstance.identifier.text 1113 : a.identifier.text); 1114 } 1115 1116 void writeIotcTo(T)(const IdentifierOrTemplateChain iotc, ref T output) 1117 { 1118 import std.algorithm : each; 1119 1120 byIdentifier(iotc).each!(a => output.insert(internString(a))); 1121 } 1122 1123 static istring convertChainToImportPath(const IdentifierChain ic) 1124 { 1125 import std.path : dirSeparator; 1126 import std.array : appender; 1127 auto app = appender!(char[])(); 1128 foreach (i, ident; ic.identifiers) 1129 { 1130 app.put(ident.text); 1131 if (i + 1 < ic.identifiers.length) 1132 app.put(dirSeparator); 1133 } 1134 return internString(cast(string) app.data); 1135 } 1136 1137 class InitializerVisitor : ASTVisitor 1138 { 1139 this (TypeLookup* lookup, bool appendForeach = false) 1140 { 1141 this.lookup = lookup; 1142 this.appendForeach = appendForeach; 1143 } 1144 1145 alias visit = ASTVisitor.visit; 1146 1147 override void visit(const IdentifierOrTemplateInstance ioti) 1148 { 1149 if (on && ioti.identifier != tok!"") 1150 lookup.breadcrumbs.insert(internString(ioti.identifier.text)); 1151 else if (on && ioti.templateInstance.identifier != tok!"") 1152 lookup.breadcrumbs.insert(internString(ioti.templateInstance.identifier.text)); 1153 ioti.accept(this); 1154 } 1155 1156 override void visit(const PrimaryExpression primary) 1157 { 1158 // Add identifiers without processing. Convert literals to strings with 1159 // the prefix '*' so that that the second pass can tell the difference 1160 // between "int.abc" and "10.abc". 1161 if (on && primary.basicType != tok!"") 1162 lookup.breadcrumbs.insert(internString(str(primary.basicType.type))); 1163 if (on) switch (primary.primary.type) 1164 { 1165 case tok!"identifier": 1166 lookup.breadcrumbs.insert(internString(primary.primary.text)); 1167 break; 1168 case tok!"doubleLiteral": 1169 lookup.breadcrumbs.insert(DOUBLE_LITERAL_SYMBOL_NAME); 1170 break; 1171 case tok!"floatLiteral": 1172 lookup.breadcrumbs.insert(FLOAT_LITERAL_SYMBOL_NAME); 1173 break; 1174 case tok!"idoubleLiteral": 1175 lookup.breadcrumbs.insert(IDOUBLE_LITERAL_SYMBOL_NAME); 1176 break; 1177 case tok!"ifloatLiteral": 1178 lookup.breadcrumbs.insert(IFLOAT_LITERAL_SYMBOL_NAME); 1179 break; 1180 case tok!"intLiteral": 1181 lookup.breadcrumbs.insert(INT_LITERAL_SYMBOL_NAME); 1182 break; 1183 case tok!"longLiteral": 1184 lookup.breadcrumbs.insert(LONG_LITERAL_SYMBOL_NAME); 1185 break; 1186 case tok!"realLiteral": 1187 lookup.breadcrumbs.insert(REAL_LITERAL_SYMBOL_NAME); 1188 break; 1189 case tok!"irealLiteral": 1190 lookup.breadcrumbs.insert(IREAL_LITERAL_SYMBOL_NAME); 1191 break; 1192 case tok!"uintLiteral": 1193 lookup.breadcrumbs.insert(UINT_LITERAL_SYMBOL_NAME); 1194 break; 1195 case tok!"ulongLiteral": 1196 lookup.breadcrumbs.insert(ULONG_LITERAL_SYMBOL_NAME); 1197 break; 1198 case tok!"characterLiteral": 1199 lookup.breadcrumbs.insert(CHAR_LITERAL_SYMBOL_NAME); 1200 break; 1201 case tok!"dstringLiteral": 1202 lookup.breadcrumbs.insert(DSTRING_LITERAL_SYMBOL_NAME); 1203 break; 1204 case tok!"stringLiteral": 1205 lookup.breadcrumbs.insert(STRING_LITERAL_SYMBOL_NAME); 1206 break; 1207 case tok!"wstringLiteral": 1208 lookup.breadcrumbs.insert(WSTRING_LITERAL_SYMBOL_NAME); 1209 break; 1210 case tok!"false": 1211 case tok!"true": 1212 lookup.breadcrumbs.insert(BOOL_VALUE_SYMBOL_NAME); 1213 break; 1214 default: 1215 break; 1216 } 1217 primary.accept(this); 1218 } 1219 1220 override void visit(const IndexExpression index) 1221 { 1222 lookup.breadcrumbs.insert(ARRAY_SYMBOL_NAME); 1223 index.accept(this); 1224 } 1225 1226 override void visit(const Initializer initializer) 1227 { 1228 on = true; 1229 initializer.accept(this); 1230 on = false; 1231 } 1232 1233 override void visit(const ArrayInitializer ai) 1234 { 1235 lookup.breadcrumbs.insert(ARRAY_SYMBOL_NAME); 1236 ai.accept(this); 1237 } 1238 1239 // Skip these 1240 override void visit(const ArgumentList) {} 1241 override void visit(const NewAnonClassExpression) {} 1242 1243 override void visit(const Expression expression) 1244 { 1245 on = true; 1246 expression.accept(this); 1247 if (appendForeach) 1248 lookup.breadcrumbs.insert(internString("foreach")); 1249 on = false; 1250 } 1251 1252 override void visit(const ExpressionNode expression) 1253 { 1254 on = true; 1255 expression.accept(this); 1256 if (appendForeach) 1257 lookup.breadcrumbs.insert(internString("foreach")); 1258 on = false; 1259 } 1260 1261 TypeLookup* lookup; 1262 bool on = false; 1263 const bool appendForeach; 1264 }