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