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.second; 20 21 import dsymbol.semantic; 22 import dsymbol.string_interning; 23 import dsymbol.symbol; 24 import dsymbol.scope_; 25 import dsymbol.builtin.names; 26 import dsymbol.builtin.symbols; 27 import dsymbol.type_lookup; 28 import dsymbol.deferred; 29 import dsymbol.import_; 30 import dsymbol.modulecache; 31 import stdx.allocator; 32 import stdx.allocator.gc_allocator : GCAllocator; 33 import std.experimental.logger; 34 import dparse.ast; 35 import dparse.lexer; 36 37 alias SymbolAllocator = GCAllocator; // NOTE using cache.symbolAllocator instead fails when analyzing Phobos master 38 39 void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCache cache) 40 { 41 with (CompletionKind) final switch (currentSymbol.acSymbol.kind) 42 { 43 case className: 44 case interfaceName: 45 resolveInheritance(currentSymbol.acSymbol, currentSymbol.typeLookups, 46 moduleScope, cache); 47 break; 48 case withSymbol: 49 case variableName: 50 case memberVariableName: 51 case functionName: 52 case aliasName: 53 // type may not be null in the case of a renamed import 54 if (currentSymbol.acSymbol.type is null) 55 { 56 resolveType(currentSymbol.acSymbol, currentSymbol.typeLookups, 57 moduleScope, cache); 58 } 59 break; 60 case importSymbol: 61 if (currentSymbol.acSymbol.type is null) 62 resolveImport(currentSymbol.acSymbol, currentSymbol.typeLookups, cache); 63 break; 64 case variadicTmpParam: 65 currentSymbol.acSymbol.type = variadicTmpParamSymbol; 66 break; 67 case typeTmpParam: 68 currentSymbol.acSymbol.type = typeTmpParamSymbol; 69 break; 70 case structName: 71 case unionName: 72 case enumName: 73 case keyword: 74 case enumMember: 75 case packageName: 76 case moduleName: 77 case dummy: 78 case templateName: 79 case mixinTemplateName: 80 break; 81 } 82 83 foreach (child; currentSymbol.children) 84 secondPass(child, moduleScope, cache); 85 86 // Alias this and mixin templates are resolved after child nodes are 87 // resolved so that the correct symbol information will be available. 88 with (CompletionKind) switch (currentSymbol.acSymbol.kind) 89 { 90 case className: 91 case interfaceName: 92 case structName: 93 case unionName: 94 resolveAliasThis(currentSymbol.acSymbol, currentSymbol.typeLookups, moduleScope, cache); 95 resolveMixinTemplates(currentSymbol.acSymbol, currentSymbol.typeLookups, 96 moduleScope, cache); 97 break; 98 default: 99 break; 100 } 101 } 102 103 void resolveImport(DSymbol* acSymbol, ref TypeLookups typeLookups, 104 ref ModuleCache cache) 105 in 106 { 107 assert(acSymbol.kind == CompletionKind.importSymbol); 108 assert(acSymbol.symbolFile !is null); 109 } 110 do 111 { 112 DSymbol* moduleSymbol = cache.cacheModule(acSymbol.symbolFile); 113 if (acSymbol.qualifier == SymbolQualifier.selectiveImport) 114 { 115 if (moduleSymbol is null) 116 { 117 tryAgain: 118 DeferredSymbol* deferred = TypeLookupsAllocator.instance.make!DeferredSymbol(acSymbol); 119 deferred.typeLookups.insert(typeLookups[]); 120 // Get rid of the old references to the lookups, this new deferred 121 // symbol owns them now 122 typeLookups.clear(); 123 cache.deferredSymbols.insert(deferred); 124 } 125 else 126 { 127 immutable size_t breadcrumbCount = typeLookups.front.breadcrumbs.length; 128 assert(breadcrumbCount <= 2 && breadcrumbCount > 0, "Malformed selective import"); 129 130 istring symbolName = typeLookups.front.breadcrumbs.front; 131 DSymbol* selected = moduleSymbol.getFirstPartNamed(symbolName); 132 if (selected is null) 133 goto tryAgain; 134 acSymbol.type = selected; 135 acSymbol.ownType = false; 136 137 // count of 1 means selective import 138 // count of 2 means a renamed selective import 139 if (breadcrumbCount == 2) 140 { 141 acSymbol.kind = CompletionKind.aliasName; 142 acSymbol.symbolFile = acSymbol.altFile; 143 } 144 } 145 } 146 else 147 { 148 if (moduleSymbol is null) 149 { 150 DeferredSymbol* deferred = DeferredSymbolsAllocator.instance.make!DeferredSymbol(acSymbol); 151 cache.deferredSymbols.insert(deferred); 152 } 153 else 154 { 155 acSymbol.type = moduleSymbol; 156 acSymbol.ownType = false; 157 } 158 } 159 } 160 161 void resolveTypeFromType(DSymbol* symbol, TypeLookup* lookup, Scope* moduleScope, 162 ref ModuleCache cache, Imports* imports) 163 in 164 { 165 if (imports !is null) 166 foreach (i; imports.opSlice()) 167 assert(i.kind == CompletionKind.importSymbol); 168 } 169 do 170 { 171 // The left-most suffix 172 DSymbol* suffix; 173 // The right-most suffix 174 DSymbol* lastSuffix; 175 176 // Create symbols for the type suffixes such as array and 177 // associative array 178 while (!lookup.breadcrumbs.empty) 179 { 180 auto back = lookup.breadcrumbs.back; 181 immutable bool isArr = back == ARRAY_SYMBOL_NAME; 182 immutable bool isAssoc = back == ASSOC_ARRAY_SYMBOL_NAME; 183 immutable bool isFunction = back == FUNCTION_SYMBOL_NAME; 184 if (back == POINTER_SYMBOL_NAME) 185 { 186 lastSuffix.isPointer = true; 187 lookup.breadcrumbs.popBack(); 188 continue; 189 } 190 if (!isArr && !isAssoc && !isFunction) 191 break; 192 immutable qualifier = isAssoc ? SymbolQualifier.assocArray : 193 (isFunction ? SymbolQualifier.func : SymbolQualifier.array); 194 lastSuffix = SymbolAllocator.instance.make!DSymbol(back, CompletionKind.dummy, lastSuffix); 195 lastSuffix.qualifier = qualifier; 196 lastSuffix.ownType = true; 197 if (isFunction) 198 { 199 lookup.breadcrumbs.popBack(); 200 lastSuffix.callTip = lookup.breadcrumbs.back(); 201 } 202 else 203 lastSuffix.addChildren(isArr ? arraySymbols[] : assocArraySymbols[], false); 204 205 if (suffix is null) 206 suffix = lastSuffix; 207 lookup.breadcrumbs.popBack(); 208 } 209 210 Imports remainingImports; 211 212 DSymbol* currentSymbol; 213 214 void getSymbolFromImports(Imports* importList, istring name) 215 { 216 foreach (im; importList.opSlice()) 217 { 218 assert(im.symbolFile !is null); 219 // Try to find a cached version of the module 220 DSymbol* moduleSymbol = cache.getModuleSymbol(im.symbolFile); 221 // If the module has not been cached yet, store it in the 222 // remaining imports list 223 if (moduleSymbol is null) 224 { 225 remainingImports.insert(im); 226 continue; 227 } 228 // Try to get the symbol from the imported module 229 currentSymbol = moduleSymbol.getFirstPartNamed(name); 230 if (currentSymbol is null) 231 continue; 232 } 233 } 234 235 // Follow all the names and try to resolve them 236 size_t i = 0; 237 foreach (part; lookup.breadcrumbs[]) 238 { 239 if (i == 0) 240 { 241 if (moduleScope is null) 242 getSymbolFromImports(imports, part); 243 else 244 { 245 auto symbols = moduleScope.getSymbolsByNameAndCursor(part, symbol.location); 246 if (symbols.length > 0) 247 currentSymbol = symbols[0]; 248 else 249 return; 250 } 251 } 252 else 253 { 254 if (currentSymbol.kind == CompletionKind.aliasName) 255 currentSymbol = currentSymbol.type; 256 if (currentSymbol is null) 257 return; 258 if (currentSymbol.kind == CompletionKind.moduleName && currentSymbol.type !is null) 259 currentSymbol = currentSymbol.type; 260 if (currentSymbol is null) 261 return; 262 if (currentSymbol.kind == CompletionKind.importSymbol) 263 currentSymbol = currentSymbol.type; 264 if (currentSymbol is null) 265 return; 266 currentSymbol = currentSymbol.getFirstPartNamed(part); 267 } 268 ++i; 269 if (currentSymbol is null) 270 return; 271 } 272 273 if (lastSuffix !is null) 274 { 275 assert(suffix !is null); 276 suffix.type = currentSymbol; 277 suffix.ownType = false; 278 symbol.type = lastSuffix; 279 symbol.ownType = true; 280 if (currentSymbol is null && !remainingImports.empty) 281 { 282 // info("Deferring type resolution for ", symbol.name); 283 auto deferred = DeferredSymbolsAllocator.instance.make!DeferredSymbol(suffix); 284 // TODO: The scope has ownership of the import information 285 deferred.imports.insert(remainingImports[]); 286 deferred.typeLookups.insert(lookup); 287 cache.deferredSymbols.insert(deferred); 288 } 289 } 290 else if (currentSymbol !is null) 291 { 292 symbol.type = currentSymbol; 293 symbol.ownType = false; 294 } 295 else if (!remainingImports.empty) 296 { 297 auto deferred = DeferredSymbolsAllocator.instance.make!DeferredSymbol(symbol); 298 // info("Deferring type resolution for ", symbol.name); 299 // TODO: The scope has ownership of the import information 300 deferred.imports.insert(remainingImports[]); 301 deferred.typeLookups.insert(lookup); 302 cache.deferredSymbols.insert(deferred); 303 } 304 } 305 306 private: 307 308 void resolveInheritance(DSymbol* symbol, ref TypeLookups typeLookups, 309 Scope* moduleScope, ref ModuleCache cache) 310 { 311 import std.algorithm : filter; 312 313 outer: foreach (TypeLookup* lookup; typeLookups[]) 314 { 315 if (lookup.kind != TypeLookupKind.inherit) 316 continue; 317 DSymbol* baseClass; 318 assert(lookup.breadcrumbs.length > 0); 319 320 // TODO: Delayed type lookup 321 auto symbolScope = moduleScope.getScopeByCursor( 322 symbol.location + symbol.name.length); 323 auto symbols = moduleScope.getSymbolsByNameAndCursor(lookup.breadcrumbs.front, 324 symbol.location); 325 if (symbols.length == 0) 326 continue; 327 328 baseClass = symbols[0]; 329 lookup.breadcrumbs.popFront(); 330 foreach (part; lookup.breadcrumbs[]) 331 { 332 symbols = baseClass.getPartsByName(part); 333 if (symbols.length == 0) 334 continue outer; 335 baseClass = symbols[0]; 336 } 337 338 DSymbol* imp = SymbolAllocator.instance.make!DSymbol(IMPORT_SYMBOL_NAME, 339 CompletionKind.importSymbol, baseClass); 340 symbol.addChild(imp, true); 341 symbolScope.addSymbol(imp, false); 342 if (baseClass.kind == CompletionKind.className) 343 { 344 auto s = SymbolAllocator.instance.make!DSymbol(SUPER_SYMBOL_NAME, 345 CompletionKind.variableName, baseClass); 346 symbolScope.addSymbol(s, true); 347 } 348 } 349 } 350 351 void resolveAliasThis(DSymbol* symbol, 352 ref TypeLookups typeLookups, Scope* moduleScope, ref ModuleCache cache) 353 { 354 import std.algorithm : filter; 355 356 foreach (aliasThis; typeLookups[].filter!(a => a.kind == TypeLookupKind.aliasThis)) 357 { 358 assert(aliasThis.breadcrumbs.length > 0); 359 auto parts = symbol.getPartsByName(aliasThis.breadcrumbs.front); 360 if (parts.length == 0 || parts[0].type is null) 361 continue; 362 DSymbol* s = SymbolAllocator.instance.make!DSymbol(IMPORT_SYMBOL_NAME, 363 CompletionKind.importSymbol, parts[0].type); 364 symbol.addChild(s, true); 365 auto symbolScope = moduleScope.getScopeByCursor(s.location); 366 if (symbolScope !is null) 367 symbolScope.addSymbol(s, false); 368 } 369 } 370 371 void resolveMixinTemplates(DSymbol* symbol, 372 ref TypeLookups typeLookups, Scope* moduleScope, ref ModuleCache cache) 373 { 374 import std.algorithm : filter; 375 376 foreach (mix; typeLookups[].filter!(a => a.kind == TypeLookupKind.mixinTemplate)) 377 { 378 assert(mix.breadcrumbs.length > 0); 379 auto symbols = moduleScope.getSymbolsByNameAndCursor(mix.breadcrumbs.front, 380 symbol.location); 381 if (symbols.length == 0) 382 continue; 383 auto currentSymbol = symbols[0]; 384 mix.breadcrumbs.popFront(); 385 foreach (m; mix.breadcrumbs[]) 386 { 387 auto s = currentSymbol.getPartsByName(m); 388 if (s.length == 0) 389 { 390 currentSymbol = null; 391 break; 392 } 393 else 394 currentSymbol = s[0]; 395 } 396 if (currentSymbol !is null) 397 { 398 auto i = SymbolAllocator.instance.make!DSymbol(IMPORT_SYMBOL_NAME, 399 CompletionKind.importSymbol, currentSymbol); 400 i.ownType = false; 401 symbol.addChild(i, true); 402 } 403 } 404 } 405 406 void resolveType(DSymbol* symbol, ref TypeLookups typeLookups, 407 Scope* moduleScope, ref ModuleCache cache) 408 { 409 410 import std.conv; 411 412 if (typeLookups.length == 0) 413 return; 414 assert(typeLookups.length == 1); 415 auto lookup = typeLookups.front; 416 if (lookup.kind == TypeLookupKind.varOrFunType) 417 resolveTypeFromType(symbol, lookup, moduleScope, cache, null); 418 else if (lookup.kind == TypeLookupKind.initializer) 419 resolveTypeFromInitializer(symbol, lookup, moduleScope, cache); 420 // issue 94 421 else if (lookup.kind == TypeLookupKind.inherit) 422 resolveInheritance(symbol, typeLookups, moduleScope, cache); 423 else 424 assert(false, "How did this happen?"); 425 } 426 427 428 void resolveTypeFromInitializer(DSymbol* symbol, TypeLookup* lookup, 429 Scope* moduleScope, ref ModuleCache cache) 430 { 431 if (lookup.breadcrumbs.length == 0) 432 return; 433 DSymbol* currentSymbol = null; 434 size_t i = 0; 435 436 auto crumbs = lookup.breadcrumbs[]; 437 foreach (crumb; crumbs) 438 { 439 if (i == 0) 440 { 441 currentSymbol = moduleScope.getFirstSymbolByNameAndCursor( 442 symbolNameToTypeName(crumb), symbol.location); 443 444 if (currentSymbol is null) 445 return; 446 } 447 else 448 if (crumb == ARRAY_LITERAL_SYMBOL_NAME) 449 { 450 auto arr = SymbolAllocator.instance.make!(DSymbol)(ARRAY_LITERAL_SYMBOL_NAME, CompletionKind.dummy, currentSymbol); 451 arr.qualifier = SymbolQualifier.array; 452 currentSymbol = arr; 453 } 454 else if (crumb == ARRAY_SYMBOL_NAME) 455 { 456 typeSwap(currentSymbol); 457 if (currentSymbol is null) 458 return; 459 460 // Index expressions can be an array index or an AA index 461 if (currentSymbol.qualifier == SymbolQualifier.array 462 || currentSymbol.qualifier == SymbolQualifier.assocArray 463 || currentSymbol.kind == CompletionKind.aliasName) 464 { 465 if (currentSymbol.type !is null) 466 currentSymbol = currentSymbol.type; 467 else 468 return; 469 } 470 else 471 { 472 auto opIndex = currentSymbol.getFirstPartNamed(internString("opIndex")); 473 if (opIndex !is null) 474 currentSymbol = opIndex.type; 475 else 476 return; 477 } 478 } 479 else if (crumb == "foreach") 480 { 481 typeSwap(currentSymbol); 482 if (currentSymbol is null) 483 return; 484 if (currentSymbol.qualifier == SymbolQualifier.array 485 || currentSymbol.qualifier == SymbolQualifier.assocArray) 486 { 487 currentSymbol = currentSymbol.type; 488 break; 489 } 490 auto front = currentSymbol.getFirstPartNamed(internString("front")); 491 if (front !is null) 492 { 493 currentSymbol = front.type; 494 break; 495 } 496 auto opApply = currentSymbol.getFirstPartNamed(internString("opApply")); 497 if (opApply !is null) 498 { 499 currentSymbol = opApply.type; 500 break; 501 } 502 } 503 else 504 { 505 typeSwap(currentSymbol); 506 if (currentSymbol is null ) 507 return; 508 currentSymbol = currentSymbol.getFirstPartNamed(crumb); 509 } 510 ++i; 511 if (currentSymbol is null) 512 return; 513 } 514 typeSwap(currentSymbol); 515 symbol.type = currentSymbol; 516 symbol.ownType = false; 517 } 518 519 void typeSwap(ref DSymbol* currentSymbol) 520 { 521 while (currentSymbol !is null && currentSymbol.type !is currentSymbol 522 && (currentSymbol.kind == CompletionKind.variableName 523 || currentSymbol.kind == CompletionKind.importSymbol 524 || currentSymbol.kind == CompletionKind.withSymbol 525 || currentSymbol.kind == CompletionKind.aliasName)) 526 currentSymbol = currentSymbol.type; 527 }