1 2/* @internal */ 3namespace ts { 4 export const enum ModuleInstanceState { 5 NonInstantiated = 0, 6 Instantiated = 1, 7 ConstEnumOnly = 2 8 } 9 10 interface ActiveLabel { 11 next: ActiveLabel | undefined; 12 name: __String; 13 breakTarget: FlowLabel; 14 continueTarget: FlowLabel | undefined; 15 referenced: boolean; 16 } 17 18 export function getModuleInstanceState(node: ModuleDeclaration, visited?: ESMap<number, ModuleInstanceState | undefined>): ModuleInstanceState { 19 if (node.body && !node.body.parent) { 20 // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already 21 setParent(node.body, node); 22 setParentRecursive(node.body, /*incremental*/ false); 23 } 24 return node.body ? getModuleInstanceStateCached(node.body, visited) : ModuleInstanceState.Instantiated; 25 } 26 27 function getModuleInstanceStateCached(node: Node, visited = new Map<number, ModuleInstanceState | undefined>()) { 28 const nodeId = getNodeId(node); 29 if (visited.has(nodeId)) { 30 return visited.get(nodeId) || ModuleInstanceState.NonInstantiated; 31 } 32 visited.set(nodeId, undefined); 33 const result = getModuleInstanceStateWorker(node, visited); 34 visited.set(nodeId, result); 35 return result; 36 } 37 38 function getModuleInstanceStateWorker(node: Node, visited: ESMap<number, ModuleInstanceState | undefined>): ModuleInstanceState { 39 // A module is uninstantiated if it contains only 40 switch (node.kind) { 41 // 1. interface declarations, type alias declarations 42 case SyntaxKind.InterfaceDeclaration: 43 case SyntaxKind.TypeAliasDeclaration: 44 return ModuleInstanceState.NonInstantiated; 45 // 2. const enum declarations 46 case SyntaxKind.EnumDeclaration: 47 if (isEnumConst(node as EnumDeclaration)) { 48 return ModuleInstanceState.ConstEnumOnly; 49 } 50 break; 51 // 3. non-exported import declarations 52 case SyntaxKind.ImportDeclaration: 53 case SyntaxKind.ImportEqualsDeclaration: 54 if (!(hasSyntacticModifier(node, ModifierFlags.Export))) { 55 return ModuleInstanceState.NonInstantiated; 56 } 57 break; 58 // 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain 59 case SyntaxKind.ExportDeclaration: 60 const exportDeclaration = node as ExportDeclaration; 61 if (!exportDeclaration.moduleSpecifier && exportDeclaration.exportClause && exportDeclaration.exportClause.kind === SyntaxKind.NamedExports) { 62 let state = ModuleInstanceState.NonInstantiated; 63 for (const specifier of exportDeclaration.exportClause.elements) { 64 const specifierState = getModuleInstanceStateForAliasTarget(specifier, visited); 65 if (specifierState > state) { 66 state = specifierState; 67 } 68 if (state === ModuleInstanceState.Instantiated) { 69 return state; 70 } 71 } 72 return state; 73 } 74 break; 75 // 5. other uninstantiated module declarations. 76 case SyntaxKind.ModuleBlock: { 77 let state = ModuleInstanceState.NonInstantiated; 78 forEachChild(node, n => { 79 const childState = getModuleInstanceStateCached(n, visited); 80 switch (childState) { 81 case ModuleInstanceState.NonInstantiated: 82 // child is non-instantiated - continue searching 83 return; 84 case ModuleInstanceState.ConstEnumOnly: 85 // child is const enum only - record state and continue searching 86 state = ModuleInstanceState.ConstEnumOnly; 87 return; 88 case ModuleInstanceState.Instantiated: 89 // child is instantiated - record state and stop 90 state = ModuleInstanceState.Instantiated; 91 return true; 92 default: 93 Debug.assertNever(childState); 94 } 95 }); 96 return state; 97 } 98 case SyntaxKind.ModuleDeclaration: 99 return getModuleInstanceState(node as ModuleDeclaration, visited); 100 case SyntaxKind.Identifier: 101 // Only jsdoc typedef definition can exist in jsdoc namespace, and it should 102 // be considered the same as type alias 103 if ((node as Identifier).isInJSDocNamespace) { 104 return ModuleInstanceState.NonInstantiated; 105 } 106 } 107 return ModuleInstanceState.Instantiated; 108 } 109 110 function getModuleInstanceStateForAliasTarget(specifier: ExportSpecifier, visited: ESMap<number, ModuleInstanceState | undefined>) { 111 const name = specifier.propertyName || specifier.name; 112 let p: Node | undefined = specifier.parent; 113 while (p) { 114 if (isBlock(p) || isModuleBlock(p) || isSourceFile(p)) { 115 const statements = p.statements; 116 let found: ModuleInstanceState | undefined; 117 for (const statement of statements) { 118 if (nodeHasName(statement, name)) { 119 if (!statement.parent) { 120 setParent(statement, p); 121 setParentRecursive(statement, /*incremental*/ false); 122 } 123 const state = getModuleInstanceStateCached(statement, visited); 124 if (found === undefined || state > found) { 125 found = state; 126 } 127 if (found === ModuleInstanceState.Instantiated) { 128 return found; 129 } 130 } 131 } 132 if (found !== undefined) { 133 return found; 134 } 135 } 136 p = p.parent; 137 } 138 return ModuleInstanceState.Instantiated; // Couldn't locate, assume could refer to a value 139 } 140 141 const enum ContainerFlags { 142 // The current node is not a container, and no container manipulation should happen before 143 // recursing into it. 144 None = 0, 145 146 // The current node is a container. It should be set as the current container (and block- 147 // container) before recursing into it. The current node does not have locals. Examples: 148 // 149 // Classes, ObjectLiterals, TypeLiterals, Interfaces... 150 IsContainer = 1 << 0, 151 152 // The current node is a block-scoped-container. It should be set as the current block- 153 // container before recursing into it. Examples: 154 // 155 // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... 156 IsBlockScopedContainer = 1 << 1, 157 158 // The current node is the container of a control flow path. The current control flow should 159 // be saved and restored, and a new control flow initialized within the container. 160 IsControlFlowContainer = 1 << 2, 161 162 IsFunctionLike = 1 << 3, 163 IsFunctionExpression = 1 << 4, 164 HasLocals = 1 << 5, 165 IsInterface = 1 << 6, 166 IsObjectLiteralOrClassExpressionMethodOrAccessor = 1 << 7, 167 } 168 169 function initFlowNode<T extends FlowNode>(node: T) { 170 Debug.attachFlowNodeDebugInfo(node); 171 return node; 172 } 173 174 const binder = createBinder(); 175 176 export function bindSourceFile(file: SourceFile, options: CompilerOptions) { 177 performance.mark("beforeBind"); 178 perfLogger.logStartBindFile("" + file.fileName); 179 binder(file, options); 180 perfLogger.logStopBindFile(); 181 performance.mark("afterBind"); 182 performance.measure("Bind", "beforeBind", "afterBind"); 183 } 184 185 function createBinder(): (file: SourceFile, options: CompilerOptions) => void { 186 let file: SourceFile; 187 let options: CompilerOptions; 188 let languageVersion: ScriptTarget; 189 let parent: Node; 190 let container: Node; 191 let thisParentContainer: Node; // Container one level up 192 let blockScopeContainer: Node; 193 let lastContainer: Node; 194 let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag)[]; 195 let seenThisKeyword: boolean; 196 197 // state used by control flow analysis 198 let currentFlow: FlowNode; 199 let currentBreakTarget: FlowLabel | undefined; 200 let currentContinueTarget: FlowLabel | undefined; 201 let currentReturnTarget: FlowLabel | undefined; 202 let currentTrueTarget: FlowLabel | undefined; 203 let currentFalseTarget: FlowLabel | undefined; 204 let currentExceptionTarget: FlowLabel | undefined; 205 let preSwitchCaseFlow: FlowNode | undefined; 206 let activeLabelList: ActiveLabel | undefined; 207 let hasExplicitReturn: boolean; 208 209 // state used for emit helpers 210 let emitFlags: NodeFlags; 211 212 // If this file is an external module, then it is automatically in strict-mode according to 213 // ES6. If it is not an external module, then we'll determine if it is in strict mode or 214 // not depending on if we see "use strict" in certain places or if we hit a class/namespace 215 // or if compiler options contain alwaysStrict. 216 let inStrictMode: boolean; 217 218 // If we are binding an assignment pattern, we will bind certain expressions differently. 219 let inAssignmentPattern = false; 220 221 let symbolCount = 0; 222 223 let Symbol: new (flags: SymbolFlags, name: __String) => Symbol; 224 let classifiableNames: Set<__String>; 225 226 const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; 227 const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; 228 const bindBinaryExpressionFlow = createBindBinaryExpressionFlow(); 229 230 /** 231 * Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file) 232 * If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node) 233 * This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations. 234 */ 235 function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): DiagnosticWithLocation { 236 return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2); 237 } 238 239 function bindSourceFile(f: SourceFile, opts: CompilerOptions) { 240 file = f; 241 options = opts; 242 languageVersion = getEmitScriptTarget(options); 243 inStrictMode = bindInStrictMode(file, opts); 244 classifiableNames = new Set(); 245 symbolCount = 0; 246 247 Symbol = objectAllocator.getSymbolConstructor(); 248 249 // Attach debugging information if necessary 250 Debug.attachFlowNodeDebugInfo(unreachableFlow); 251 Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow); 252 253 if (!file.locals) { 254 tracing?.push(tracing.Phase.Bind, "bindSourceFile", { path: file.path }, /*separateBeginAndEnd*/ true); 255 bind(file); 256 tracing?.pop(); 257 file.symbolCount = symbolCount; 258 file.classifiableNames = classifiableNames; 259 delayedBindJSDocTypedefTag(); 260 } 261 262 file = undefined!; 263 options = undefined!; 264 languageVersion = undefined!; 265 parent = undefined!; 266 container = undefined!; 267 thisParentContainer = undefined!; 268 blockScopeContainer = undefined!; 269 lastContainer = undefined!; 270 delayedTypeAliases = undefined!; 271 seenThisKeyword = false; 272 currentFlow = undefined!; 273 currentBreakTarget = undefined; 274 currentContinueTarget = undefined; 275 currentReturnTarget = undefined; 276 currentTrueTarget = undefined; 277 currentFalseTarget = undefined; 278 currentExceptionTarget = undefined; 279 activeLabelList = undefined; 280 hasExplicitReturn = false; 281 inAssignmentPattern = false; 282 emitFlags = NodeFlags.None; 283 } 284 285 return bindSourceFile; 286 287 function bindInStrictMode(file: SourceFile, opts: CompilerOptions): boolean { 288 if (getStrictOptionValue(opts, "alwaysStrict") && !file.isDeclarationFile) { 289 // bind in strict mode source files with alwaysStrict option 290 return true; 291 } 292 else { 293 return !!file.externalModuleIndicator; 294 } 295 } 296 297 function createSymbol(flags: SymbolFlags, name: __String): Symbol { 298 symbolCount++; 299 return new Symbol(flags, name); 300 } 301 302 function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) { 303 symbol.flags |= symbolFlags; 304 305 node.symbol = symbol; 306 symbol.declarations = appendIfUnique(symbol.declarations, node); 307 308 if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.Module | SymbolFlags.Variable) && !symbol.exports) { 309 symbol.exports = createSymbolTable(); 310 } 311 312 if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && !symbol.members) { 313 symbol.members = createSymbolTable(); 314 } 315 316 // On merge of const enum module with class or function, reset const enum only flag (namespaces will already recalculate) 317 if (symbol.constEnumOnlyModule && (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) { 318 symbol.constEnumOnlyModule = false; 319 } 320 321 if (symbolFlags & SymbolFlags.Value) { 322 setValueDeclaration(symbol, node); 323 } 324 } 325 326 // Should not be called on a declaration with a computed property name, 327 // unless it is a well known Symbol. 328 function getDeclarationName(node: Declaration): __String | undefined { 329 if (node.kind === SyntaxKind.ExportAssignment) { 330 return (node as ExportAssignment).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default; 331 } 332 333 const name = getNameOfDeclaration(node); 334 if (name) { 335 if (isAmbientModule(node)) { 336 const moduleName = getTextOfIdentifierOrLiteral(name as Identifier | StringLiteral); 337 return (isGlobalScopeAugmentation(node as ModuleDeclaration) ? "__global" : `"${moduleName}"`) as __String; 338 } 339 if (name.kind === SyntaxKind.ComputedPropertyName) { 340 const nameExpression = name.expression; 341 // treat computed property names where expression is string/numeric literal as just string/numeric literal 342 if (isStringOrNumericLiteralLike(nameExpression)) { 343 return escapeLeadingUnderscores(nameExpression.text); 344 } 345 if (isSignedNumericLiteral(nameExpression)) { 346 return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String; 347 } 348 else { 349 Debug.fail("Only computed properties with literal names have declaration names"); 350 } 351 } 352 if (isPrivateIdentifier(name)) { 353 // containingClass exists because private names only allowed inside classes 354 const containingClass = getContainingClass(node); 355 if (!containingClass) { 356 // we can get here in cases where there is already a parse error. 357 return undefined; 358 } 359 const containingClassSymbol = containingClass.symbol; 360 return getSymbolNameForPrivateIdentifier(containingClassSymbol, name.escapedText); 361 } 362 return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined; 363 } 364 switch (node.kind) { 365 case SyntaxKind.Constructor: 366 return InternalSymbolName.Constructor; 367 case SyntaxKind.FunctionType: 368 case SyntaxKind.CallSignature: 369 case SyntaxKind.JSDocSignature: 370 return InternalSymbolName.Call; 371 case SyntaxKind.ConstructorType: 372 case SyntaxKind.ConstructSignature: 373 return InternalSymbolName.New; 374 case SyntaxKind.IndexSignature: 375 return InternalSymbolName.Index; 376 case SyntaxKind.ExportDeclaration: 377 return InternalSymbolName.ExportStar; 378 case SyntaxKind.SourceFile: 379 // json file should behave as 380 // module.exports = ... 381 return InternalSymbolName.ExportEquals; 382 case SyntaxKind.BinaryExpression: 383 if (getAssignmentDeclarationKind(node as BinaryExpression) === AssignmentDeclarationKind.ModuleExports) { 384 // module.exports = ... 385 return InternalSymbolName.ExportEquals; 386 } 387 Debug.fail("Unknown binary declaration kind"); 388 case SyntaxKind.JSDocFunctionType: 389 return (isJSDocConstructSignature(node) ? InternalSymbolName.New : InternalSymbolName.Call); 390 case SyntaxKind.Parameter: 391 // Parameters with names are handled at the top of this function. Parameters 392 // without names can only come from JSDocFunctionTypes. 393 Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType, "Impossible parameter parent kind", () => `parent is: ${Debug.formatSyntaxKind(node.parent.kind)}, expected JSDocFunctionType`); 394 const functionType = node.parent as JSDocFunctionType; 395 const index = functionType.parameters.indexOf(node as ParameterDeclaration); 396 return "arg" + index as __String; 397 } 398 } 399 400 function getDisplayName(node: Declaration): string { 401 return isNamedDeclaration(node) ? declarationNameToString(node.name) : unescapeLeadingUnderscores(Debug.checkDefined(getDeclarationName(node))); 402 } 403 404 /** 405 * Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names. 406 * @param symbolTable - The symbol table which node will be added to. 407 * @param parent - node's parent declaration. 408 * @param node - The declaration to be added to the symbol table 409 * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) 410 * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. 411 */ 412 function declareSymbol(symbolTable: SymbolTable, parent: Symbol | undefined, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean, isComputedName?: boolean): Symbol { 413 Debug.assert(isComputedName || !hasDynamicName(node)); 414 415 const isDefaultExport = hasSyntacticModifier(node, ModifierFlags.Default) || isExportSpecifier(node) && node.name.escapedText === "default"; 416 417 // The exported symbol for an export default function/class node is always named "default" 418 const name = isComputedName ? InternalSymbolName.Computed 419 : isDefaultExport && parent ? InternalSymbolName.Default 420 : getDeclarationName(node); 421 422 let symbol: Symbol | undefined; 423 if (name === undefined) { 424 symbol = createSymbol(SymbolFlags.None, InternalSymbolName.Missing); 425 } 426 else { 427 // Check and see if the symbol table already has a symbol with this name. If not, 428 // create a new symbol with this name and add it to the table. Note that we don't 429 // give the new symbol any flags *yet*. This ensures that it will not conflict 430 // with the 'excludes' flags we pass in. 431 // 432 // If we do get an existing symbol, see if it conflicts with the new symbol we're 433 // creating. For example, a 'var' symbol and a 'class' symbol will conflict within 434 // the same symbol table. If we have a conflict, report the issue on each 435 // declaration we have for this symbol, and then create a new symbol for this 436 // declaration. 437 // 438 // Note that when properties declared in Javascript constructors 439 // (marked by isReplaceableByMethod) conflict with another symbol, the property loses. 440 // Always. This allows the common Javascript pattern of overwriting a prototype method 441 // with an bound instance method of the same type: `this.method = this.method.bind(this)` 442 // 443 // If we created a new symbol, either because we didn't have a symbol with this name 444 // in the symbol table, or we conflicted with an existing symbol, then just add this 445 // node as the sole declaration of the new symbol. 446 // 447 // Otherwise, we'll be merging into a compatible existing symbol (for example when 448 // you have multiple 'vars' with the same name in the same container). In this case 449 // just add this node into the declarations list of the symbol. 450 symbol = symbolTable.get(name); 451 452 if (includes & SymbolFlags.Classifiable) { 453 classifiableNames.add(name); 454 } 455 456 if (!symbol) { 457 symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); 458 if (isReplaceableByMethod) symbol.isReplaceableByMethod = true; 459 } 460 else if (isReplaceableByMethod && !symbol.isReplaceableByMethod) { 461 // A symbol already exists, so don't add this as a declaration. 462 return symbol; 463 } 464 else if (symbol.flags & excludes) { 465 if (symbol.isReplaceableByMethod) { 466 // Javascript constructor-declared symbols can be discarded in favor of 467 // prototype symbols like methods. 468 symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); 469 } 470 else if (!(includes & SymbolFlags.Variable && symbol.flags & SymbolFlags.Assignment)) { 471 // Assignment declarations are allowed to merge with variables, no matter what other flags they have. 472 if (isNamedDeclaration(node)) { 473 setParent(node.name, node); 474 } 475 // Report errors every position with duplicate declaration 476 // Report errors on previous encountered declarations 477 let message = symbol.flags & SymbolFlags.BlockScopedVariable 478 ? Diagnostics.Cannot_redeclare_block_scoped_variable_0 479 : Diagnostics.Duplicate_identifier_0; 480 let messageNeedsName = true; 481 482 if (symbol.flags & SymbolFlags.Enum || includes & SymbolFlags.Enum) { 483 message = Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations; 484 messageNeedsName = false; 485 } 486 487 let multipleDefaultExports = false; 488 if (length(symbol.declarations)) { 489 // If the current node is a default export of some sort, then check if 490 // there are any other default exports that we need to error on. 491 // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. 492 if (isDefaultExport) { 493 message = Diagnostics.A_module_cannot_have_multiple_default_exports; 494 messageNeedsName = false; 495 multipleDefaultExports = true; 496 } 497 else { 498 // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. 499 // Error on multiple export default in the following case: 500 // 1. multiple export default of class declaration or function declaration by checking NodeFlags.Default 501 // 2. multiple export default of export assignment. This one doesn't have NodeFlags.Default on (as export default doesn't considered as modifiers) 502 if (symbol.declarations && symbol.declarations.length && 503 (node.kind === SyntaxKind.ExportAssignment && !(node as ExportAssignment).isExportEquals)) { 504 message = Diagnostics.A_module_cannot_have_multiple_default_exports; 505 messageNeedsName = false; 506 multipleDefaultExports = true; 507 } 508 } 509 } 510 511 const relatedInformation: DiagnosticRelatedInformation[] = []; 512 if (isTypeAliasDeclaration(node) && nodeIsMissing(node.type) && hasSyntacticModifier(node, ModifierFlags.Export) && symbol.flags & (SymbolFlags.Alias | SymbolFlags.Type | SymbolFlags.Namespace)) { 513 // export type T; - may have meant export type { T }? 514 relatedInformation.push(createDiagnosticForNode(node, Diagnostics.Did_you_mean_0, `export type { ${unescapeLeadingUnderscores(node.name.escapedText)} }`)); 515 } 516 517 const declarationName = getNameOfDeclaration(node) || node; 518 forEach(symbol.declarations, (declaration, index) => { 519 const decl = getNameOfDeclaration(declaration) || declaration; 520 const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined); 521 file.bindDiagnostics.push( 522 multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag 523 ); 524 if (multipleDefaultExports) { 525 relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here)); 526 } 527 }); 528 529 const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined); 530 file.bindDiagnostics.push(addRelatedInfo(diag, ...relatedInformation)); 531 532 symbol = createSymbol(SymbolFlags.None, name); 533 } 534 } 535 } 536 537 addDeclarationToSymbol(symbol, node, includes); 538 if (symbol.parent) { 539 Debug.assert(symbol.parent === parent, "Existing symbol parent should match new one"); 540 } 541 else { 542 symbol.parent = parent; 543 } 544 545 return symbol; 546 } 547 548 function declareModuleMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { 549 const hasExportModifier = !!(getCombinedModifierFlags(node) & ModifierFlags.Export) || jsdocTreatAsExported(node); 550 if (symbolFlags & SymbolFlags.Alias) { 551 if (node.kind === SyntaxKind.ExportSpecifier || (node.kind === SyntaxKind.ImportEqualsDeclaration && hasExportModifier)) { 552 return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); 553 } 554 else { 555 return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 556 } 557 } 558 else { 559 // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag, 560 // and an associated export symbol with all the correct flags set on it. There are 2 main reasons: 561 // 562 // 1. We treat locals and exports of the same name as mutually exclusive within a container. 563 // That means the binder will issue a Duplicate Identifier error if you mix locals and exports 564 // with the same name in the same container. 565 // TODO: Make this a more specific error and decouple it from the exclusion logic. 566 // 2. When we checkIdentifier in the checker, we set its resolved symbol to the local symbol, 567 // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way 568 // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. 569 570 // NOTE: Nested ambient modules always should go to to 'locals' table to prevent their automatic merge 571 // during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation 572 // and this case is specially handled. Module augmentations should only be merged with original module definition 573 // and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed. 574 if (isJSDocTypeAlias(node)) Debug.assert(isInJSFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file. 575 if (!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) { 576 if (!container.locals || (hasSyntacticModifier(node, ModifierFlags.Default) && !getDeclarationName(node))) { 577 return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default! 578 } 579 const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0; 580 const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes); 581 local.exportSymbol = declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); 582 node.localSymbol = local; 583 return local; 584 } 585 else { 586 return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 587 } 588 } 589 } 590 591 function jsdocTreatAsExported(node: Node) { 592 if (node.parent && isModuleDeclaration(node)) { 593 node = node.parent; 594 } 595 if (!isJSDocTypeAlias(node)) return false; 596 // jsdoc typedef handling is a bit of a doozy, but to summarize, treat the typedef as exported if: 597 // 1. It has an explicit name (since by default typedefs are always directly exported, either at the top level or in a container), or 598 if (!isJSDocEnumTag(node) && !!node.fullName) return true; 599 // 2. The thing a nameless typedef pulls its name from is implicitly a direct export (either by assignment or actual export flag). 600 const declName = getNameOfDeclaration(node); 601 if (!declName) return false; 602 if (isPropertyAccessEntityNameExpression(declName.parent) && isTopLevelNamespaceAssignment(declName.parent)) return true; 603 if (isDeclaration(declName.parent) && getCombinedModifierFlags(declName.parent) & ModifierFlags.Export) return true; 604 // This could potentially be simplified by having `delayedBindJSDocTypedefTag` pass in an override for `hasExportModifier`, since it should 605 // already have calculated and branched on most of this. 606 return false; 607 } 608 609 // All container nodes are kept on a linked list in declaration order. This list is used by 610 // the getLocalNameOfContainer function in the type checker to validate that the local name 611 // used for a container is unique. 612 function bindContainer(node: Mutable<Node>, containerFlags: ContainerFlags) { 613 // Before we recurse into a node's children, we first save the existing parent, container 614 // and block-container. Then after we pop out of processing the children, we restore 615 // these saved values. 616 const saveContainer = container; 617 const saveThisParentContainer = thisParentContainer; 618 const savedBlockScopeContainer = blockScopeContainer; 619 620 // Depending on what kind of node this is, we may have to adjust the current container 621 // and block-container. If the current node is a container, then it is automatically 622 // considered the current block-container as well. Also, for containers that we know 623 // may contain locals, we eagerly initialize the .locals field. We do this because 624 // it's highly likely that the .locals will be needed to place some child in (for example, 625 // a parameter, or variable declaration). 626 // 627 // However, we do not proactively create the .locals for block-containers because it's 628 // totally normal and common for block-containers to never actually have a block-scoped 629 // variable in them. We don't want to end up allocating an object for every 'block' we 630 // run into when most of them won't be necessary. 631 // 632 // Finally, if this is a block-container, then we clear out any existing .locals object 633 // it may contain within it. This happens in incremental scenarios. Because we can be 634 // reusing a node from a previous compilation, that node may have had 'locals' created 635 // for it. We must clear this so we don't accidentally move any stale data forward from 636 // a previous compilation. 637 if (containerFlags & ContainerFlags.IsContainer) { 638 if (node.kind !== SyntaxKind.ArrowFunction) { 639 thisParentContainer = container; 640 } 641 container = blockScopeContainer = node; 642 if (containerFlags & ContainerFlags.HasLocals) { 643 container.locals = createSymbolTable(); 644 } 645 addToContainerChain(container); 646 } 647 else if (containerFlags & ContainerFlags.IsBlockScopedContainer) { 648 blockScopeContainer = node; 649 blockScopeContainer.locals = undefined; 650 } 651 if (containerFlags & ContainerFlags.IsControlFlowContainer) { 652 const saveCurrentFlow = currentFlow; 653 const saveBreakTarget = currentBreakTarget; 654 const saveContinueTarget = currentContinueTarget; 655 const saveReturnTarget = currentReturnTarget; 656 const saveExceptionTarget = currentExceptionTarget; 657 const saveActiveLabelList = activeLabelList; 658 const saveHasExplicitReturn = hasExplicitReturn; 659 const isImmediatelyInvoked = 660 (containerFlags & ContainerFlags.IsFunctionExpression && 661 !hasSyntacticModifier(node, ModifierFlags.Async) && 662 !(node as FunctionLikeDeclaration).asteriskToken && 663 !!getImmediatelyInvokedFunctionExpression(node)) || 664 node.kind === SyntaxKind.ClassStaticBlockDeclaration; 665 // A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave 666 // similarly to break statements that exit to a label just past the statement body. 667 if (!isImmediatelyInvoked) { 668 currentFlow = initFlowNode({ flags: FlowFlags.Start }); 669 if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor)) { 670 currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; 671 } 672 } 673 // We create a return control flow graph for IIFEs and constructors. For constructors 674 // we use the return control flow graph in strict property initialization checks. 675 currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; 676 currentExceptionTarget = undefined; 677 currentBreakTarget = undefined; 678 currentContinueTarget = undefined; 679 activeLabelList = undefined; 680 hasExplicitReturn = false; 681 bindChildren(node); 682 // Reset all reachability check related flags on node (for incremental scenarios) 683 node.flags &= ~NodeFlags.ReachabilityAndEmitFlags; 684 if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).body)) { 685 node.flags |= NodeFlags.HasImplicitReturn; 686 if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn; 687 (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).endFlowNode = currentFlow; 688 } 689 if (node.kind === SyntaxKind.SourceFile) { 690 node.flags |= emitFlags; 691 (node as SourceFile).endFlowNode = currentFlow; 692 } 693 694 if (currentReturnTarget) { 695 addAntecedent(currentReturnTarget, currentFlow); 696 currentFlow = finishFlowLabel(currentReturnTarget); 697 if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { 698 (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow; 699 } 700 } 701 if (!isImmediatelyInvoked) { 702 currentFlow = saveCurrentFlow; 703 } 704 currentBreakTarget = saveBreakTarget; 705 currentContinueTarget = saveContinueTarget; 706 currentReturnTarget = saveReturnTarget; 707 currentExceptionTarget = saveExceptionTarget; 708 activeLabelList = saveActiveLabelList; 709 hasExplicitReturn = saveHasExplicitReturn; 710 } 711 else if (containerFlags & ContainerFlags.IsInterface) { 712 seenThisKeyword = false; 713 bindChildren(node); 714 node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis; 715 } 716 else { 717 bindChildren(node); 718 } 719 720 container = saveContainer; 721 thisParentContainer = saveThisParentContainer; 722 blockScopeContainer = savedBlockScopeContainer; 723 } 724 725 function bindEachFunctionsFirst(nodes: NodeArray<Node> | undefined): void { 726 bindEach(nodes, n => n.kind === SyntaxKind.FunctionDeclaration ? bind(n) : undefined); 727 bindEach(nodes, n => n.kind !== SyntaxKind.FunctionDeclaration ? bind(n) : undefined); 728 } 729 730 function bindEach(nodes: NodeArray<Node> | undefined, bindFunction: (node: Node) => void = bind): void { 731 if (nodes === undefined) { 732 return; 733 } 734 735 forEach(nodes, bindFunction); 736 } 737 738 function bindEachChild(node: Node) { 739 forEachChild(node, bind, bindEach); 740 } 741 742 function bindChildren(node: Node): void { 743 const saveInAssignmentPattern = inAssignmentPattern; 744 // Most nodes aren't valid in an assignment pattern, so we clear the value here 745 // and set it before we descend into nodes that could actually be part of an assignment pattern. 746 inAssignmentPattern = false; 747 if (checkUnreachable(node)) { 748 bindEachChild(node); 749 bindJSDoc(node); 750 inAssignmentPattern = saveInAssignmentPattern; 751 return; 752 } 753 if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) { 754 node.flowNode = currentFlow; 755 } 756 switch (node.kind) { 757 case SyntaxKind.WhileStatement: 758 bindWhileStatement(node as WhileStatement); 759 break; 760 case SyntaxKind.DoStatement: 761 bindDoStatement(node as DoStatement); 762 break; 763 case SyntaxKind.ForStatement: 764 bindForStatement(node as ForStatement); 765 break; 766 case SyntaxKind.ForInStatement: 767 case SyntaxKind.ForOfStatement: 768 bindForInOrForOfStatement(node as ForInOrOfStatement); 769 break; 770 case SyntaxKind.IfStatement: 771 bindIfStatement(node as IfStatement); 772 break; 773 case SyntaxKind.ReturnStatement: 774 case SyntaxKind.ThrowStatement: 775 bindReturnOrThrow(node as ReturnStatement | ThrowStatement); 776 break; 777 case SyntaxKind.BreakStatement: 778 case SyntaxKind.ContinueStatement: 779 bindBreakOrContinueStatement(node as BreakOrContinueStatement); 780 break; 781 case SyntaxKind.TryStatement: 782 bindTryStatement(node as TryStatement); 783 break; 784 case SyntaxKind.SwitchStatement: 785 bindSwitchStatement(node as SwitchStatement); 786 break; 787 case SyntaxKind.CaseBlock: 788 bindCaseBlock(node as CaseBlock); 789 break; 790 case SyntaxKind.CaseClause: 791 bindCaseClause(node as CaseClause); 792 break; 793 case SyntaxKind.ExpressionStatement: 794 bindExpressionStatement(node as ExpressionStatement); 795 break; 796 case SyntaxKind.LabeledStatement: 797 bindLabeledStatement(node as LabeledStatement); 798 break; 799 case SyntaxKind.PrefixUnaryExpression: 800 bindPrefixUnaryExpressionFlow(node as PrefixUnaryExpression); 801 break; 802 case SyntaxKind.PostfixUnaryExpression: 803 bindPostfixUnaryExpressionFlow(node as PostfixUnaryExpression); 804 break; 805 case SyntaxKind.BinaryExpression: 806 if (isDestructuringAssignment(node)) { 807 // Carry over whether we are in an assignment pattern to 808 // binary expressions that could actually be an initializer 809 inAssignmentPattern = saveInAssignmentPattern; 810 bindDestructuringAssignmentFlow(node); 811 return; 812 } 813 bindBinaryExpressionFlow(node as BinaryExpression); 814 break; 815 case SyntaxKind.DeleteExpression: 816 bindDeleteExpressionFlow(node as DeleteExpression); 817 break; 818 case SyntaxKind.ConditionalExpression: 819 bindConditionalExpressionFlow(node as ConditionalExpression); 820 break; 821 case SyntaxKind.VariableDeclaration: 822 bindVariableDeclarationFlow(node as VariableDeclaration); 823 break; 824 case SyntaxKind.PropertyAccessExpression: 825 case SyntaxKind.ElementAccessExpression: 826 bindAccessExpressionFlow(node as AccessExpression); 827 break; 828 case SyntaxKind.CallExpression: 829 bindCallExpressionFlow(node as CallExpression); 830 break; 831 case SyntaxKind.NonNullExpression: 832 bindNonNullExpressionFlow(node as NonNullExpression); 833 break; 834 case SyntaxKind.JSDocTypedefTag: 835 case SyntaxKind.JSDocCallbackTag: 836 case SyntaxKind.JSDocEnumTag: 837 bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag); 838 break; 839 // In source files and blocks, bind functions first to match hoisting that occurs at runtime 840 case SyntaxKind.SourceFile: { 841 bindEachFunctionsFirst((node as SourceFile).statements); 842 bind((node as SourceFile).endOfFileToken); 843 break; 844 } 845 case SyntaxKind.Block: 846 case SyntaxKind.ModuleBlock: 847 bindEachFunctionsFirst((node as Block).statements); 848 break; 849 case SyntaxKind.BindingElement: 850 bindBindingElementFlow(node as BindingElement); 851 break; 852 case SyntaxKind.Parameter: 853 bindParameterFlow(node as ParameterDeclaration); 854 break; 855 case SyntaxKind.ObjectLiteralExpression: 856 case SyntaxKind.ArrayLiteralExpression: 857 case SyntaxKind.PropertyAssignment: 858 case SyntaxKind.SpreadElement: 859 // Carry over whether we are in an assignment pattern of Object and Array literals 860 // as well as their children that are valid assignment targets. 861 inAssignmentPattern = saveInAssignmentPattern; 862 // falls through 863 default: 864 bindEachChild(node); 865 break; 866 } 867 bindJSDoc(node); 868 inAssignmentPattern = saveInAssignmentPattern; 869 } 870 871 function isNarrowingExpression(expr: Expression): boolean { 872 switch (expr.kind) { 873 case SyntaxKind.Identifier: 874 case SyntaxKind.PrivateIdentifier: 875 case SyntaxKind.ThisKeyword: 876 case SyntaxKind.PropertyAccessExpression: 877 case SyntaxKind.ElementAccessExpression: 878 return containsNarrowableReference(expr); 879 case SyntaxKind.CallExpression: 880 return hasNarrowableArgument(expr as CallExpression); 881 case SyntaxKind.ParenthesizedExpression: 882 case SyntaxKind.NonNullExpression: 883 return isNarrowingExpression((expr as ParenthesizedExpression | NonNullExpression).expression); 884 case SyntaxKind.BinaryExpression: 885 return isNarrowingBinaryExpression(expr as BinaryExpression); 886 case SyntaxKind.PrefixUnaryExpression: 887 return (expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr as PrefixUnaryExpression).operand); 888 case SyntaxKind.TypeOfExpression: 889 return isNarrowingExpression((expr as TypeOfExpression).expression); 890 } 891 return false; 892 } 893 894 function isNarrowableReference(expr: Expression): boolean { 895 return isDottedName(expr) 896 || (isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) 897 || isBinaryExpression(expr) && expr.operatorToken.kind === SyntaxKind.CommaToken && isNarrowableReference(expr.right) 898 || isElementAccessExpression(expr) && (isStringOrNumericLiteralLike(expr.argumentExpression) || isEntityNameExpression(expr.argumentExpression)) && isNarrowableReference(expr.expression) 899 || isAssignmentExpression(expr) && isNarrowableReference(expr.left); 900 } 901 902 function containsNarrowableReference(expr: Expression): boolean { 903 return isNarrowableReference(expr) || isOptionalChain(expr) && containsNarrowableReference(expr.expression); 904 } 905 906 function hasNarrowableArgument(expr: CallExpression) { 907 if (expr.arguments) { 908 for (const argument of expr.arguments) { 909 if (containsNarrowableReference(argument)) { 910 return true; 911 } 912 } 913 } 914 if (expr.expression.kind === SyntaxKind.PropertyAccessExpression && 915 containsNarrowableReference((expr.expression as PropertyAccessExpression).expression)) { 916 return true; 917 } 918 return false; 919 } 920 921 function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) { 922 return isTypeOfExpression(expr1) && isNarrowableOperand(expr1.expression) && isStringLiteralLike(expr2); 923 } 924 925 function isNarrowingBinaryExpression(expr: BinaryExpression) { 926 switch (expr.operatorToken.kind) { 927 case SyntaxKind.EqualsToken: 928 case SyntaxKind.BarBarEqualsToken: 929 case SyntaxKind.AmpersandAmpersandEqualsToken: 930 case SyntaxKind.QuestionQuestionEqualsToken: 931 return containsNarrowableReference(expr.left); 932 case SyntaxKind.EqualsEqualsToken: 933 case SyntaxKind.ExclamationEqualsToken: 934 case SyntaxKind.EqualsEqualsEqualsToken: 935 case SyntaxKind.ExclamationEqualsEqualsToken: 936 return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) || 937 isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right); 938 case SyntaxKind.InstanceOfKeyword: 939 return isNarrowableOperand(expr.left); 940 case SyntaxKind.InKeyword: 941 return isNarrowingExpression(expr.right); 942 case SyntaxKind.CommaToken: 943 return isNarrowingExpression(expr.right); 944 } 945 return false; 946 } 947 948 function isNarrowableOperand(expr: Expression): boolean { 949 switch (expr.kind) { 950 case SyntaxKind.ParenthesizedExpression: 951 return isNarrowableOperand((expr as ParenthesizedExpression).expression); 952 case SyntaxKind.BinaryExpression: 953 switch ((expr as BinaryExpression).operatorToken.kind) { 954 case SyntaxKind.EqualsToken: 955 return isNarrowableOperand((expr as BinaryExpression).left); 956 case SyntaxKind.CommaToken: 957 return isNarrowableOperand((expr as BinaryExpression).right); 958 } 959 } 960 return containsNarrowableReference(expr); 961 } 962 963 function createBranchLabel(): FlowLabel { 964 return initFlowNode({ flags: FlowFlags.BranchLabel, antecedents: undefined }); 965 } 966 967 function createLoopLabel(): FlowLabel { 968 return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined }); 969 } 970 971 function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel { 972 return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent }); 973 } 974 975 function setFlowNodeReferenced(flow: FlowNode) { 976 // On first reference we set the Referenced flag, thereafter we set the Shared flag 977 flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced; 978 } 979 980 function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { 981 if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) { 982 (label.antecedents || (label.antecedents = [])).push(antecedent); 983 setFlowNodeReferenced(antecedent); 984 } 985 } 986 987 function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression | undefined): FlowNode { 988 if (antecedent.flags & FlowFlags.Unreachable) { 989 return antecedent; 990 } 991 if (!expression) { 992 return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow; 993 } 994 if ((expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition || 995 expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) && 996 !isExpressionOfOptionalChainRoot(expression) && !isNullishCoalesce(expression.parent)) { 997 return unreachableFlow; 998 } 999 if (!isNarrowingExpression(expression)) { 1000 return antecedent; 1001 } 1002 setFlowNodeReferenced(antecedent); 1003 return initFlowNode({ flags, antecedent, node: expression }); 1004 } 1005 1006 function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode { 1007 setFlowNodeReferenced(antecedent); 1008 return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd }); 1009 } 1010 1011 function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode { 1012 setFlowNodeReferenced(antecedent); 1013 const result = initFlowNode({ flags, antecedent, node }); 1014 if (currentExceptionTarget) { 1015 addAntecedent(currentExceptionTarget, result); 1016 } 1017 return result; 1018 } 1019 1020 function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode { 1021 setFlowNodeReferenced(antecedent); 1022 return initFlowNode({ flags: FlowFlags.Call, antecedent, node }); 1023 } 1024 1025 function finishFlowLabel(flow: FlowLabel): FlowNode { 1026 const antecedents = flow.antecedents; 1027 if (!antecedents) { 1028 return unreachableFlow; 1029 } 1030 if (antecedents.length === 1) { 1031 return antecedents[0]; 1032 } 1033 return flow; 1034 } 1035 1036 function isStatementCondition(node: Node) { 1037 const parent = node.parent; 1038 switch (parent.kind) { 1039 case SyntaxKind.IfStatement: 1040 case SyntaxKind.WhileStatement: 1041 case SyntaxKind.DoStatement: 1042 return (parent as IfStatement | WhileStatement | DoStatement).expression === node; 1043 case SyntaxKind.ForStatement: 1044 case SyntaxKind.ConditionalExpression: 1045 return (parent as ForStatement | ConditionalExpression).condition === node; 1046 } 1047 return false; 1048 } 1049 1050 function isLogicalExpression(node: Node) { 1051 while (true) { 1052 if (node.kind === SyntaxKind.ParenthesizedExpression) { 1053 node = (node as ParenthesizedExpression).expression; 1054 } 1055 else if (node.kind === SyntaxKind.PrefixUnaryExpression && (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) { 1056 node = (node as PrefixUnaryExpression).operand; 1057 } 1058 else { 1059 return node.kind === SyntaxKind.BinaryExpression && ( 1060 (node as BinaryExpression).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || 1061 (node as BinaryExpression).operatorToken.kind === SyntaxKind.BarBarToken || 1062 (node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken); 1063 } 1064 } 1065 } 1066 1067 function isLogicalAssignmentExpression(node: Node) { 1068 node = skipParentheses(node); 1069 return isBinaryExpression(node) && isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind); 1070 } 1071 1072 function isTopLevelLogicalExpression(node: Node): boolean { 1073 while (isParenthesizedExpression(node.parent) || 1074 isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) { 1075 node = node.parent; 1076 } 1077 return !isStatementCondition(node) && 1078 !isLogicalExpression(node.parent) && 1079 !(isOptionalChain(node.parent) && node.parent.expression === node); 1080 } 1081 1082 function doWithConditionalBranches<T>(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1083 const savedTrueTarget = currentTrueTarget; 1084 const savedFalseTarget = currentFalseTarget; 1085 currentTrueTarget = trueTarget; 1086 currentFalseTarget = falseTarget; 1087 action(value); 1088 currentTrueTarget = savedTrueTarget; 1089 currentFalseTarget = savedFalseTarget; 1090 } 1091 1092 function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1093 doWithConditionalBranches(bind, node, trueTarget, falseTarget); 1094 if (!node || !isLogicalAssignmentExpression(node) && !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) { 1095 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1096 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1097 } 1098 } 1099 1100 function bindIterativeStatement(node: Statement, breakTarget: FlowLabel, continueTarget: FlowLabel): void { 1101 const saveBreakTarget = currentBreakTarget; 1102 const saveContinueTarget = currentContinueTarget; 1103 currentBreakTarget = breakTarget; 1104 currentContinueTarget = continueTarget; 1105 bind(node); 1106 currentBreakTarget = saveBreakTarget; 1107 currentContinueTarget = saveContinueTarget; 1108 } 1109 1110 function setContinueTarget(node: Node, target: FlowLabel) { 1111 let label = activeLabelList; 1112 while (label && node.parent.kind === SyntaxKind.LabeledStatement) { 1113 label.continueTarget = target; 1114 label = label.next; 1115 node = node.parent; 1116 } 1117 return target; 1118 } 1119 1120 function bindWhileStatement(node: WhileStatement): void { 1121 const preWhileLabel = setContinueTarget(node, createLoopLabel()); 1122 const preBodyLabel = createBranchLabel(); 1123 const postWhileLabel = createBranchLabel(); 1124 addAntecedent(preWhileLabel, currentFlow); 1125 currentFlow = preWhileLabel; 1126 bindCondition(node.expression, preBodyLabel, postWhileLabel); 1127 currentFlow = finishFlowLabel(preBodyLabel); 1128 bindIterativeStatement(node.statement, postWhileLabel, preWhileLabel); 1129 addAntecedent(preWhileLabel, currentFlow); 1130 currentFlow = finishFlowLabel(postWhileLabel); 1131 } 1132 1133 function bindDoStatement(node: DoStatement): void { 1134 const preDoLabel = createLoopLabel(); 1135 const preConditionLabel = setContinueTarget(node, createBranchLabel()); 1136 const postDoLabel = createBranchLabel(); 1137 addAntecedent(preDoLabel, currentFlow); 1138 currentFlow = preDoLabel; 1139 bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); 1140 addAntecedent(preConditionLabel, currentFlow); 1141 currentFlow = finishFlowLabel(preConditionLabel); 1142 bindCondition(node.expression, preDoLabel, postDoLabel); 1143 currentFlow = finishFlowLabel(postDoLabel); 1144 } 1145 1146 function bindForStatement(node: ForStatement): void { 1147 const preLoopLabel = setContinueTarget(node, createLoopLabel()); 1148 const preBodyLabel = createBranchLabel(); 1149 const postLoopLabel = createBranchLabel(); 1150 bind(node.initializer); 1151 addAntecedent(preLoopLabel, currentFlow); 1152 currentFlow = preLoopLabel; 1153 bindCondition(node.condition, preBodyLabel, postLoopLabel); 1154 currentFlow = finishFlowLabel(preBodyLabel); 1155 bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); 1156 bind(node.incrementor); 1157 addAntecedent(preLoopLabel, currentFlow); 1158 currentFlow = finishFlowLabel(postLoopLabel); 1159 } 1160 1161 function bindForInOrForOfStatement(node: ForInOrOfStatement): void { 1162 const preLoopLabel = setContinueTarget(node, createLoopLabel()); 1163 const postLoopLabel = createBranchLabel(); 1164 bind(node.expression); 1165 addAntecedent(preLoopLabel, currentFlow); 1166 currentFlow = preLoopLabel; 1167 if (node.kind === SyntaxKind.ForOfStatement) { 1168 bind(node.awaitModifier); 1169 } 1170 addAntecedent(postLoopLabel, currentFlow); 1171 bind(node.initializer); 1172 if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) { 1173 bindAssignmentTargetFlow(node.initializer); 1174 } 1175 bindIterativeStatement(node.statement, postLoopLabel, preLoopLabel); 1176 addAntecedent(preLoopLabel, currentFlow); 1177 currentFlow = finishFlowLabel(postLoopLabel); 1178 } 1179 1180 function bindIfStatement(node: IfStatement): void { 1181 const thenLabel = createBranchLabel(); 1182 const elseLabel = createBranchLabel(); 1183 const postIfLabel = createBranchLabel(); 1184 bindCondition(node.expression, thenLabel, elseLabel); 1185 currentFlow = finishFlowLabel(thenLabel); 1186 bind(node.thenStatement); 1187 addAntecedent(postIfLabel, currentFlow); 1188 currentFlow = finishFlowLabel(elseLabel); 1189 bind(node.elseStatement); 1190 addAntecedent(postIfLabel, currentFlow); 1191 currentFlow = finishFlowLabel(postIfLabel); 1192 } 1193 1194 function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void { 1195 bind(node.expression); 1196 if (node.kind === SyntaxKind.ReturnStatement) { 1197 hasExplicitReturn = true; 1198 if (currentReturnTarget) { 1199 addAntecedent(currentReturnTarget, currentFlow); 1200 } 1201 } 1202 currentFlow = unreachableFlow; 1203 } 1204 1205 function findActiveLabel(name: __String) { 1206 for (let label = activeLabelList; label; label = label.next) { 1207 if (label.name === name) { 1208 return label; 1209 } 1210 } 1211 return undefined; 1212 } 1213 1214 function bindBreakOrContinueFlow(node: BreakOrContinueStatement, breakTarget: FlowLabel | undefined, continueTarget: FlowLabel | undefined) { 1215 const flowLabel = node.kind === SyntaxKind.BreakStatement ? breakTarget : continueTarget; 1216 if (flowLabel) { 1217 addAntecedent(flowLabel, currentFlow); 1218 currentFlow = unreachableFlow; 1219 } 1220 } 1221 1222 function bindBreakOrContinueStatement(node: BreakOrContinueStatement): void { 1223 bind(node.label); 1224 if (node.label) { 1225 const activeLabel = findActiveLabel(node.label.escapedText); 1226 if (activeLabel) { 1227 activeLabel.referenced = true; 1228 bindBreakOrContinueFlow(node, activeLabel.breakTarget, activeLabel.continueTarget); 1229 } 1230 } 1231 else { 1232 bindBreakOrContinueFlow(node, currentBreakTarget, currentContinueTarget); 1233 } 1234 } 1235 1236 function bindTryStatement(node: TryStatement): void { 1237 // We conservatively assume that *any* code in the try block can cause an exception, but we only need 1238 // to track code that causes mutations (because only mutations widen the possible control flow type of 1239 // a variable). The exceptionLabel is the target label for control flows that result from exceptions. 1240 // We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible 1241 // antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to 1242 // represent exceptions that occur before any mutations. 1243 const saveReturnTarget = currentReturnTarget; 1244 const saveExceptionTarget = currentExceptionTarget; 1245 const normalExitLabel = createBranchLabel(); 1246 const returnLabel = createBranchLabel(); 1247 let exceptionLabel = createBranchLabel(); 1248 if (node.finallyBlock) { 1249 currentReturnTarget = returnLabel; 1250 } 1251 addAntecedent(exceptionLabel, currentFlow); 1252 currentExceptionTarget = exceptionLabel; 1253 bind(node.tryBlock); 1254 addAntecedent(normalExitLabel, currentFlow); 1255 if (node.catchClause) { 1256 // Start of catch clause is the target of exceptions from try block. 1257 currentFlow = finishFlowLabel(exceptionLabel); 1258 // The currentExceptionTarget now represents control flows from exceptions in the catch clause. 1259 // Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block 1260 // acts like a second try block. 1261 exceptionLabel = createBranchLabel(); 1262 addAntecedent(exceptionLabel, currentFlow); 1263 currentExceptionTarget = exceptionLabel; 1264 bind(node.catchClause); 1265 addAntecedent(normalExitLabel, currentFlow); 1266 } 1267 currentReturnTarget = saveReturnTarget; 1268 currentExceptionTarget = saveExceptionTarget; 1269 if (node.finallyBlock) { 1270 // Possible ways control can reach the finally block: 1271 // 1) Normal completion of try block of a try-finally or try-catch-finally 1272 // 2) Normal completion of catch block (following exception in try block) of a try-catch-finally 1273 // 3) Return in try or catch block of a try-finally or try-catch-finally 1274 // 4) Exception in try block of a try-finally 1275 // 5) Exception in catch block of a try-catch-finally 1276 // When analyzing a control flow graph that starts inside a finally block we want to consider all 1277 // five possibilities above. However, when analyzing a control flow graph that starts outside (past) 1278 // the finally block, we only want to consider the first two (if we're past a finally block then it 1279 // must have completed normally). Likewise, when analyzing a control flow graph from return statements 1280 // in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we 1281 // inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced 1282 // set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel 1283 // node, the pre-finally label is temporarily switched to the reduced antecedent set. 1284 const finallyLabel = createBranchLabel(); 1285 finallyLabel.antecedents = concatenate(concatenate(normalExitLabel.antecedents, exceptionLabel.antecedents), returnLabel.antecedents); 1286 currentFlow = finallyLabel; 1287 bind(node.finallyBlock); 1288 if (currentFlow.flags & FlowFlags.Unreachable) { 1289 // If the end of the finally block is unreachable, the end of the entire try statement is unreachable. 1290 currentFlow = unreachableFlow; 1291 } 1292 else { 1293 // If we have an IIFE return target and return statements in the try or catch blocks, add a control 1294 // flow that goes back through the finally block and back through only the return statements. 1295 if (currentReturnTarget && returnLabel.antecedents) { 1296 addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedents, currentFlow)); 1297 } 1298 // If we have an outer exception target (i.e. a containing try-finally or try-catch-finally), add a 1299 // control flow that goes back through the finally blok and back through each possible exception source. 1300 if (currentExceptionTarget && exceptionLabel.antecedents) { 1301 addAntecedent(currentExceptionTarget, createReduceLabel(finallyLabel, exceptionLabel.antecedents, currentFlow)); 1302 } 1303 // If the end of the finally block is reachable, but the end of the try and catch blocks are not, 1304 // convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should 1305 // result in an unreachable current control flow. 1306 currentFlow = normalExitLabel.antecedents ? createReduceLabel(finallyLabel, normalExitLabel.antecedents, currentFlow) : unreachableFlow; 1307 } 1308 } 1309 else { 1310 currentFlow = finishFlowLabel(normalExitLabel); 1311 } 1312 } 1313 1314 function bindSwitchStatement(node: SwitchStatement): void { 1315 const postSwitchLabel = createBranchLabel(); 1316 bind(node.expression); 1317 const saveBreakTarget = currentBreakTarget; 1318 const savePreSwitchCaseFlow = preSwitchCaseFlow; 1319 currentBreakTarget = postSwitchLabel; 1320 preSwitchCaseFlow = currentFlow; 1321 bind(node.caseBlock); 1322 addAntecedent(postSwitchLabel, currentFlow); 1323 const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); 1324 // We mark a switch statement as possibly exhaustive if it has no default clause and if all 1325 // case clauses have unreachable end points (e.g. they all return). Note, we no longer need 1326 // this property in control flow analysis, it's there only for backwards compatibility. 1327 node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents; 1328 if (!hasDefault) { 1329 addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0)); 1330 } 1331 currentBreakTarget = saveBreakTarget; 1332 preSwitchCaseFlow = savePreSwitchCaseFlow; 1333 currentFlow = finishFlowLabel(postSwitchLabel); 1334 } 1335 1336 function bindCaseBlock(node: CaseBlock): void { 1337 const clauses = node.clauses; 1338 const isNarrowingSwitch = isNarrowingExpression(node.parent.expression); 1339 let fallthroughFlow = unreachableFlow; 1340 for (let i = 0; i < clauses.length; i++) { 1341 const clauseStart = i; 1342 while (!clauses[i].statements.length && i + 1 < clauses.length) { 1343 bind(clauses[i]); 1344 i++; 1345 } 1346 const preCaseLabel = createBranchLabel(); 1347 addAntecedent(preCaseLabel, isNarrowingSwitch ? createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1) : preSwitchCaseFlow!); 1348 addAntecedent(preCaseLabel, fallthroughFlow); 1349 currentFlow = finishFlowLabel(preCaseLabel); 1350 const clause = clauses[i]; 1351 bind(clause); 1352 fallthroughFlow = currentFlow; 1353 if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { 1354 clause.fallthroughFlowNode = currentFlow; 1355 } 1356 } 1357 } 1358 1359 function bindCaseClause(node: CaseClause): void { 1360 const saveCurrentFlow = currentFlow; 1361 currentFlow = preSwitchCaseFlow!; 1362 bind(node.expression); 1363 currentFlow = saveCurrentFlow; 1364 bindEach(node.statements); 1365 } 1366 1367 function bindExpressionStatement(node: ExpressionStatement): void { 1368 bind(node.expression); 1369 maybeBindExpressionFlowIfCall(node.expression); 1370 } 1371 1372 function maybeBindExpressionFlowIfCall(node: Expression) { 1373 // A top level or comma expression call expression with a dotted function name and at least one argument 1374 // is potentially an assertion and is therefore included in the control flow. 1375 if (node.kind === SyntaxKind.CallExpression) { 1376 const call = node as CallExpression; 1377 if (call.expression.kind !== SyntaxKind.SuperKeyword && isDottedName(call.expression)) { 1378 currentFlow = createFlowCall(currentFlow, call); 1379 } 1380 } 1381 } 1382 1383 function bindLabeledStatement(node: LabeledStatement): void { 1384 const postStatementLabel = createBranchLabel(); 1385 activeLabelList = { 1386 next: activeLabelList, 1387 name: node.label.escapedText, 1388 breakTarget: postStatementLabel, 1389 continueTarget: undefined, 1390 referenced: false 1391 }; 1392 bind(node.label); 1393 bind(node.statement); 1394 if (!activeLabelList.referenced && !options.allowUnusedLabels) { 1395 errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label); 1396 } 1397 activeLabelList = activeLabelList.next; 1398 addAntecedent(postStatementLabel, currentFlow); 1399 currentFlow = finishFlowLabel(postStatementLabel); 1400 } 1401 1402 function bindDestructuringTargetFlow(node: Expression) { 1403 if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) { 1404 bindAssignmentTargetFlow((node as BinaryExpression).left); 1405 } 1406 else { 1407 bindAssignmentTargetFlow(node); 1408 } 1409 } 1410 1411 function bindAssignmentTargetFlow(node: Expression) { 1412 if (isNarrowableReference(node)) { 1413 currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node); 1414 } 1415 else if (node.kind === SyntaxKind.ArrayLiteralExpression) { 1416 for (const e of (node as ArrayLiteralExpression).elements) { 1417 if (e.kind === SyntaxKind.SpreadElement) { 1418 bindAssignmentTargetFlow((e as SpreadElement).expression); 1419 } 1420 else { 1421 bindDestructuringTargetFlow(e); 1422 } 1423 } 1424 } 1425 else if (node.kind === SyntaxKind.ObjectLiteralExpression) { 1426 for (const p of (node as ObjectLiteralExpression).properties) { 1427 if (p.kind === SyntaxKind.PropertyAssignment) { 1428 bindDestructuringTargetFlow(p.initializer); 1429 } 1430 else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { 1431 bindAssignmentTargetFlow(p.name); 1432 } 1433 else if (p.kind === SyntaxKind.SpreadAssignment) { 1434 bindAssignmentTargetFlow(p.expression); 1435 } 1436 } 1437 } 1438 } 1439 1440 function bindLogicalLikeExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1441 const preRightLabel = createBranchLabel(); 1442 if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === SyntaxKind.AmpersandAmpersandEqualsToken) { 1443 bindCondition(node.left, preRightLabel, falseTarget); 1444 } 1445 else { 1446 bindCondition(node.left, trueTarget, preRightLabel); 1447 } 1448 currentFlow = finishFlowLabel(preRightLabel); 1449 bind(node.operatorToken); 1450 1451 if (isLogicalOrCoalescingAssignmentOperator(node.operatorToken.kind)) { 1452 doWithConditionalBranches(bind, node.right, trueTarget, falseTarget); 1453 bindAssignmentTargetFlow(node.left); 1454 1455 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1456 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1457 } 1458 else { 1459 bindCondition(node.right, trueTarget, falseTarget); 1460 } 1461 } 1462 1463 function bindPrefixUnaryExpressionFlow(node: PrefixUnaryExpression) { 1464 if (node.operator === SyntaxKind.ExclamationToken) { 1465 const saveTrueTarget = currentTrueTarget; 1466 currentTrueTarget = currentFalseTarget; 1467 currentFalseTarget = saveTrueTarget; 1468 bindEachChild(node); 1469 currentFalseTarget = currentTrueTarget; 1470 currentTrueTarget = saveTrueTarget; 1471 } 1472 else { 1473 bindEachChild(node); 1474 if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { 1475 bindAssignmentTargetFlow(node.operand); 1476 } 1477 } 1478 } 1479 1480 function bindPostfixUnaryExpressionFlow(node: PostfixUnaryExpression) { 1481 bindEachChild(node); 1482 if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { 1483 bindAssignmentTargetFlow(node.operand); 1484 } 1485 } 1486 1487 function bindDestructuringAssignmentFlow(node: DestructuringAssignment) { 1488 if (inAssignmentPattern) { 1489 inAssignmentPattern = false; 1490 bind(node.operatorToken); 1491 bind(node.right); 1492 inAssignmentPattern = true; 1493 bind(node.left); 1494 } 1495 else { 1496 inAssignmentPattern = true; 1497 bind(node.left); 1498 inAssignmentPattern = false; 1499 bind(node.operatorToken); 1500 bind(node.right); 1501 } 1502 bindAssignmentTargetFlow(node.left); 1503 } 1504 1505 function createBindBinaryExpressionFlow() { 1506 interface WorkArea { 1507 stackIndex: number; 1508 skip: boolean; 1509 inStrictModeStack: (boolean | undefined)[]; 1510 parentStack: (Node | undefined)[]; 1511 } 1512 1513 return createBinaryExpressionTrampoline(onEnter, onLeft, onOperator, onRight, onExit, /*foldState*/ undefined); 1514 1515 function onEnter(node: BinaryExpression, state: WorkArea | undefined) { 1516 if (state) { 1517 state.stackIndex++; 1518 // Emulate the work that `bind` does before reaching `bindChildren`. A normal call to 1519 // `bindBinaryExpressionFlow` will already have done this work. 1520 setParent(node, parent); 1521 const saveInStrictMode = inStrictMode; 1522 bindWorker(node); 1523 const saveParent = parent; 1524 parent = node; 1525 state.skip = false; 1526 state.inStrictModeStack[state.stackIndex] = saveInStrictMode; 1527 state.parentStack[state.stackIndex] = saveParent; 1528 } 1529 else { 1530 state = { 1531 stackIndex: 0, 1532 skip: false, 1533 inStrictModeStack: [undefined], 1534 parentStack: [undefined] 1535 }; 1536 } 1537 // TODO: bindLogicalExpression is recursive - if we want to handle deeply nested `&&` expressions 1538 // we'll need to handle the `bindLogicalExpression` scenarios in this state machine, too 1539 // For now, though, since the common cases are chained `+`, leaving it recursive is fine 1540 const operator = node.operatorToken.kind; 1541 if (operator === SyntaxKind.AmpersandAmpersandToken || 1542 operator === SyntaxKind.BarBarToken || 1543 operator === SyntaxKind.QuestionQuestionToken || 1544 isLogicalOrCoalescingAssignmentOperator(operator)) { 1545 if (isTopLevelLogicalExpression(node)) { 1546 const postExpressionLabel = createBranchLabel(); 1547 bindLogicalLikeExpression(node, postExpressionLabel, postExpressionLabel); 1548 currentFlow = finishFlowLabel(postExpressionLabel); 1549 } 1550 else { 1551 bindLogicalLikeExpression(node, currentTrueTarget!, currentFalseTarget!); 1552 } 1553 state.skip = true; 1554 } 1555 return state; 1556 } 1557 1558 function onLeft(left: Expression, state: WorkArea, node: BinaryExpression) { 1559 if (!state.skip) { 1560 const maybeBound = maybeBind(left); 1561 if (node.operatorToken.kind === SyntaxKind.CommaToken) { 1562 maybeBindExpressionFlowIfCall(left); 1563 } 1564 return maybeBound; 1565 } 1566 } 1567 1568 function onOperator(operatorToken: BinaryOperatorToken, state: WorkArea, _node: BinaryExpression) { 1569 if (!state.skip) { 1570 bind(operatorToken); 1571 } 1572 } 1573 1574 function onRight(right: Expression, state: WorkArea, node: BinaryExpression) { 1575 if (!state.skip) { 1576 const maybeBound = maybeBind(right); 1577 if (node.operatorToken.kind === SyntaxKind.CommaToken) { 1578 maybeBindExpressionFlowIfCall(right); 1579 } 1580 return maybeBound; 1581 } 1582 } 1583 1584 function onExit(node: BinaryExpression, state: WorkArea) { 1585 if (!state.skip) { 1586 const operator = node.operatorToken.kind; 1587 if (isAssignmentOperator(operator) && !isAssignmentTarget(node)) { 1588 bindAssignmentTargetFlow(node.left); 1589 if (operator === SyntaxKind.EqualsToken && node.left.kind === SyntaxKind.ElementAccessExpression) { 1590 const elementAccess = node.left as ElementAccessExpression; 1591 if (isNarrowableOperand(elementAccess.expression)) { 1592 currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node); 1593 } 1594 } 1595 } 1596 } 1597 const savedInStrictMode = state.inStrictModeStack[state.stackIndex]; 1598 const savedParent = state.parentStack[state.stackIndex]; 1599 if (savedInStrictMode !== undefined) { 1600 inStrictMode = savedInStrictMode; 1601 } 1602 if (savedParent !== undefined) { 1603 parent = savedParent; 1604 } 1605 state.skip = false; 1606 state.stackIndex--; 1607 } 1608 1609 function maybeBind(node: Node) { 1610 if (node && isBinaryExpression(node) && !isDestructuringAssignment(node)) { 1611 return node; 1612 } 1613 bind(node); 1614 } 1615 } 1616 1617 function bindDeleteExpressionFlow(node: DeleteExpression) { 1618 bindEachChild(node); 1619 if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { 1620 bindAssignmentTargetFlow(node.expression); 1621 } 1622 } 1623 1624 function bindConditionalExpressionFlow(node: ConditionalExpression) { 1625 const trueLabel = createBranchLabel(); 1626 const falseLabel = createBranchLabel(); 1627 const postExpressionLabel = createBranchLabel(); 1628 bindCondition(node.condition, trueLabel, falseLabel); 1629 currentFlow = finishFlowLabel(trueLabel); 1630 bind(node.questionToken); 1631 bind(node.whenTrue); 1632 addAntecedent(postExpressionLabel, currentFlow); 1633 currentFlow = finishFlowLabel(falseLabel); 1634 bind(node.colonToken); 1635 bind(node.whenFalse); 1636 addAntecedent(postExpressionLabel, currentFlow); 1637 currentFlow = finishFlowLabel(postExpressionLabel); 1638 } 1639 1640 function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) { 1641 const name = !isOmittedExpression(node) ? node.name : undefined; 1642 if (isBindingPattern(name)) { 1643 for (const child of name.elements) { 1644 bindInitializedVariableFlow(child); 1645 } 1646 } 1647 else { 1648 currentFlow = createFlowMutation(FlowFlags.Assignment, currentFlow, node); 1649 } 1650 } 1651 1652 function bindVariableDeclarationFlow(node: VariableDeclaration) { 1653 bindEachChild(node); 1654 if (node.initializer || isForInOrOfStatement(node.parent.parent)) { 1655 bindInitializedVariableFlow(node); 1656 } 1657 } 1658 1659 function bindBindingElementFlow(node: BindingElement) { 1660 // When evaluating a binding pattern, the initializer is evaluated before the binding pattern, per: 1661 // - https://tc39.es/ecma262/#sec-destructuring-binding-patterns-runtime-semantics-iteratorbindinginitialization 1662 // - `BindingElement: BindingPattern Initializer?` 1663 // - https://tc39.es/ecma262/#sec-runtime-semantics-keyedbindinginitialization 1664 // - `BindingElement: BindingPattern Initializer?` 1665 bind(node.dotDotDotToken); 1666 bind(node.propertyName); 1667 bindInitializer(node.initializer); 1668 bind(node.name); 1669 } 1670 1671 function bindParameterFlow(node: ParameterDeclaration) { 1672 bindEach(node.modifiers); 1673 bind(node.dotDotDotToken); 1674 bind(node.questionToken); 1675 bind(node.type); 1676 bindInitializer(node.initializer); 1677 bind(node.name); 1678 } 1679 1680 // a BindingElement/Parameter does not have side effects if initializers are not evaluated and used. (see GH#49759) 1681 function bindInitializer(node: Expression | undefined) { 1682 if (!node) { 1683 return; 1684 } 1685 const entryFlow = currentFlow; 1686 bind(node); 1687 if (entryFlow === unreachableFlow || entryFlow === currentFlow) { 1688 return; 1689 } 1690 const exitFlow = createBranchLabel(); 1691 addAntecedent(exitFlow, entryFlow); 1692 addAntecedent(exitFlow, currentFlow); 1693 currentFlow = finishFlowLabel(exitFlow); 1694 } 1695 1696 function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag) { 1697 bind(node.tagName); 1698 if (node.kind !== SyntaxKind.JSDocEnumTag && node.fullName) { 1699 // don't bind the type name yet; that's delayed until delayedBindJSDocTypedefTag 1700 setParent(node.fullName, node); 1701 setParentRecursive(node.fullName, /*incremental*/ false); 1702 } 1703 if (typeof node.comment !== "string") { 1704 bindEach(node.comment); 1705 } 1706 } 1707 1708 function bindJSDocClassTag(node: JSDocClassTag) { 1709 bindEachChild(node); 1710 const host = getHostSignatureFromJSDoc(node); 1711 if (host && host.kind !== SyntaxKind.MethodDeclaration) { 1712 addDeclarationToSymbol(host.symbol, host, SymbolFlags.Class); 1713 } 1714 } 1715 1716 function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1717 doWithConditionalBranches(bind, node, trueTarget, falseTarget); 1718 if (!isOptionalChain(node) || isOutermostOptionalChain(node)) { 1719 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1720 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1721 } 1722 } 1723 1724 function bindOptionalChainRest(node: OptionalChain) { 1725 switch (node.kind) { 1726 case SyntaxKind.PropertyAccessExpression: 1727 bind(node.questionDotToken); 1728 bind(node.name); 1729 break; 1730 case SyntaxKind.ElementAccessExpression: 1731 bind(node.questionDotToken); 1732 bind(node.argumentExpression); 1733 break; 1734 case SyntaxKind.CallExpression: 1735 bind(node.questionDotToken); 1736 bindEach(node.typeArguments); 1737 bindEach(node.arguments); 1738 break; 1739 } 1740 } 1741 1742 function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) { 1743 // For an optional chain, we emulate the behavior of a logical expression: 1744 // 1745 // a?.b -> a && a.b 1746 // a?.b.c -> a && a.b.c 1747 // a?.b?.c -> a && a.b && a.b.c 1748 // a?.[x = 1] -> a && a[x = 1] 1749 // 1750 // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`) 1751 // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest 1752 // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost 1753 // chain node. We then treat the entire node as the right side of the expression. 1754 const preChainLabel = isOptionalChainRoot(node) ? createBranchLabel() : undefined; 1755 bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget); 1756 if (preChainLabel) { 1757 currentFlow = finishFlowLabel(preChainLabel); 1758 } 1759 doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget); 1760 if (isOutermostOptionalChain(node)) { 1761 addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); 1762 addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); 1763 } 1764 } 1765 1766 function bindOptionalChainFlow(node: OptionalChain) { 1767 if (isTopLevelLogicalExpression(node)) { 1768 const postExpressionLabel = createBranchLabel(); 1769 bindOptionalChain(node, postExpressionLabel, postExpressionLabel); 1770 currentFlow = finishFlowLabel(postExpressionLabel); 1771 } 1772 else { 1773 bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!); 1774 } 1775 } 1776 1777 function bindNonNullExpressionFlow(node: NonNullExpression | NonNullChain) { 1778 if (isOptionalChain(node)) { 1779 bindOptionalChainFlow(node); 1780 } 1781 else { 1782 bindEachChild(node); 1783 } 1784 } 1785 1786 function bindAccessExpressionFlow(node: AccessExpression | PropertyAccessChain | ElementAccessChain) { 1787 if (isOptionalChain(node)) { 1788 bindOptionalChainFlow(node); 1789 } 1790 else { 1791 bindEachChild(node); 1792 } 1793 } 1794 1795 function bindCallExpressionFlow(node: CallExpression | CallChain) { 1796 if (isOptionalChain(node)) { 1797 bindOptionalChainFlow(node); 1798 } 1799 else { 1800 // If the target of the call expression is a function expression or arrow function we have 1801 // an immediately invoked function expression (IIFE). Initialize the flowNode property to 1802 // the current control flow (which includes evaluation of the IIFE arguments). 1803 const expr = skipParentheses(node.expression); 1804 if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { 1805 bindEach(node.typeArguments); 1806 bindEach(node.arguments); 1807 bind(node.expression); 1808 } 1809 else { 1810 bindEachChild(node); 1811 if (node.expression.kind === SyntaxKind.SuperKeyword) { 1812 currentFlow = createFlowCall(currentFlow, node); 1813 } 1814 } 1815 } 1816 if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { 1817 const propertyAccess = node.expression as PropertyAccessExpression; 1818 if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) { 1819 currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node); 1820 } 1821 } 1822 } 1823 1824 function getContainerFlags(node: Node): ContainerFlags { 1825 switch (node.kind) { 1826 case SyntaxKind.ClassExpression: 1827 case SyntaxKind.ClassDeclaration: 1828 case SyntaxKind.StructDeclaration: 1829 case SyntaxKind.EnumDeclaration: 1830 case SyntaxKind.ObjectLiteralExpression: 1831 case SyntaxKind.TypeLiteral: 1832 case SyntaxKind.JSDocTypeLiteral: 1833 case SyntaxKind.JsxAttributes: 1834 return ContainerFlags.IsContainer; 1835 1836 case SyntaxKind.InterfaceDeclaration: 1837 return ContainerFlags.IsContainer | ContainerFlags.IsInterface; 1838 1839 case SyntaxKind.ModuleDeclaration: 1840 case SyntaxKind.TypeAliasDeclaration: 1841 case SyntaxKind.MappedType: 1842 case SyntaxKind.IndexSignature: 1843 return ContainerFlags.IsContainer | ContainerFlags.HasLocals; 1844 1845 case SyntaxKind.SourceFile: 1846 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals; 1847 1848 case SyntaxKind.GetAccessor: 1849 case SyntaxKind.SetAccessor: 1850 case SyntaxKind.MethodDeclaration: 1851 if (isObjectLiteralOrClassExpressionMethodOrAccessor(node)) { 1852 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsObjectLiteralOrClassExpressionMethodOrAccessor; 1853 } 1854 // falls through 1855 case SyntaxKind.Constructor: 1856 case SyntaxKind.FunctionDeclaration: 1857 case SyntaxKind.MethodSignature: 1858 case SyntaxKind.CallSignature: 1859 case SyntaxKind.JSDocSignature: 1860 case SyntaxKind.JSDocFunctionType: 1861 case SyntaxKind.FunctionType: 1862 case SyntaxKind.ConstructSignature: 1863 case SyntaxKind.ConstructorType: 1864 case SyntaxKind.ClassStaticBlockDeclaration: 1865 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; 1866 1867 case SyntaxKind.FunctionExpression: 1868 case SyntaxKind.ArrowFunction: 1869 return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression; 1870 1871 case SyntaxKind.ModuleBlock: 1872 return ContainerFlags.IsControlFlowContainer; 1873 case SyntaxKind.PropertyDeclaration: 1874 return (node as PropertyDeclaration).initializer ? ContainerFlags.IsControlFlowContainer : 0; 1875 1876 case SyntaxKind.CatchClause: 1877 case SyntaxKind.ForStatement: 1878 case SyntaxKind.ForInStatement: 1879 case SyntaxKind.ForOfStatement: 1880 case SyntaxKind.CaseBlock: 1881 return ContainerFlags.IsBlockScopedContainer; 1882 1883 case SyntaxKind.Block: 1884 // do not treat blocks directly inside a function as a block-scoped-container. 1885 // Locals that reside in this block should go to the function locals. Otherwise 'x' 1886 // would not appear to be a redeclaration of a block scoped local in the following 1887 // example: 1888 // 1889 // function foo() { 1890 // var x; 1891 // let x; 1892 // } 1893 // 1894 // If we placed 'var x' into the function locals and 'let x' into the locals of 1895 // the block, then there would be no collision. 1896 // 1897 // By not creating a new block-scoped-container here, we ensure that both 'var x' 1898 // and 'let x' go into the Function-container's locals, and we do get a collision 1899 // conflict. 1900 return isFunctionLike(node.parent) || isClassStaticBlockDeclaration(node.parent) ? ContainerFlags.None : ContainerFlags.IsBlockScopedContainer; 1901 } 1902 1903 return ContainerFlags.None; 1904 } 1905 1906 function addToContainerChain(next: Node) { 1907 if (lastContainer) { 1908 lastContainer.nextContainer = next; 1909 } 1910 1911 lastContainer = next; 1912 } 1913 1914 function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol | undefined { 1915 switch (container.kind) { 1916 // Modules, source files, and classes need specialized handling for how their 1917 // members are declared (for example, a member of a class will go into a specific 1918 // symbol table depending on if it is static or not). We defer to specialized 1919 // handlers to take care of declaring these child members. 1920 case SyntaxKind.ModuleDeclaration: 1921 return declareModuleMember(node, symbolFlags, symbolExcludes); 1922 1923 case SyntaxKind.SourceFile: 1924 return declareSourceFileMember(node, symbolFlags, symbolExcludes); 1925 1926 case SyntaxKind.ClassExpression: 1927 case SyntaxKind.ClassDeclaration: 1928 case SyntaxKind.StructDeclaration: 1929 return declareClassMember(node, symbolFlags, symbolExcludes); 1930 1931 case SyntaxKind.EnumDeclaration: 1932 return declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes); 1933 1934 case SyntaxKind.TypeLiteral: 1935 case SyntaxKind.JSDocTypeLiteral: 1936 case SyntaxKind.ObjectLiteralExpression: 1937 case SyntaxKind.InterfaceDeclaration: 1938 case SyntaxKind.JsxAttributes: 1939 // Interface/Object-types always have their children added to the 'members' of 1940 // their container. They are only accessible through an instance of their 1941 // container, and are never in scope otherwise (even inside the body of the 1942 // object / type / interface declaring them). An exception is type parameters, 1943 // which are in scope without qualification (similar to 'locals'). 1944 return declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes); 1945 1946 case SyntaxKind.FunctionType: 1947 case SyntaxKind.ConstructorType: 1948 case SyntaxKind.CallSignature: 1949 case SyntaxKind.ConstructSignature: 1950 case SyntaxKind.JSDocSignature: 1951 case SyntaxKind.IndexSignature: 1952 case SyntaxKind.MethodDeclaration: 1953 case SyntaxKind.MethodSignature: 1954 case SyntaxKind.Constructor: 1955 case SyntaxKind.GetAccessor: 1956 case SyntaxKind.SetAccessor: 1957 case SyntaxKind.FunctionDeclaration: 1958 case SyntaxKind.FunctionExpression: 1959 case SyntaxKind.ArrowFunction: 1960 case SyntaxKind.JSDocFunctionType: 1961 case SyntaxKind.JSDocTypedefTag: 1962 case SyntaxKind.JSDocCallbackTag: 1963 case SyntaxKind.ClassStaticBlockDeclaration: 1964 case SyntaxKind.TypeAliasDeclaration: 1965 case SyntaxKind.MappedType: 1966 // All the children of these container types are never visible through another 1967 // symbol (i.e. through another symbol's 'exports' or 'members'). Instead, 1968 // they're only accessed 'lexically' (i.e. from code that exists underneath 1969 // their container in the tree). To accomplish this, we simply add their declared 1970 // symbol to the 'locals' of the container. These symbols can then be found as 1971 // the type checker walks up the containers, checking them for matching names. 1972 return declareSymbol(container.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 1973 } 1974 } 1975 1976 function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 1977 return isStatic(node) 1978 ? declareSymbol(container.symbol.exports!, container.symbol, node, symbolFlags, symbolExcludes) 1979 : declareSymbol(container.symbol.members!, container.symbol, node, symbolFlags, symbolExcludes); 1980 } 1981 1982 function declareSourceFileMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 1983 return isExternalModule(file) 1984 ? declareModuleMember(node, symbolFlags, symbolExcludes) 1985 : declareSymbol(file.locals!, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 1986 } 1987 1988 function hasExportDeclarations(node: ModuleDeclaration | SourceFile): boolean { 1989 const body = isSourceFile(node) ? node : tryCast(node.body, isModuleBlock); 1990 return !!body && body.statements.some(s => isExportDeclaration(s) || isExportAssignment(s)); 1991 } 1992 1993 function setExportContextFlag(node: Mutable<ModuleDeclaration | SourceFile>) { 1994 // A declaration source file or ambient module declaration that contains no export declarations (but possibly regular 1995 // declarations with export modifiers) is an export context in which declarations are implicitly exported. 1996 if (node.flags & NodeFlags.Ambient && !hasExportDeclarations(node)) { 1997 node.flags |= NodeFlags.ExportContext; 1998 } 1999 else { 2000 node.flags &= ~NodeFlags.ExportContext; 2001 } 2002 } 2003 2004 function bindModuleDeclaration(node: ModuleDeclaration) { 2005 setExportContextFlag(node); 2006 if (isAmbientModule(node)) { 2007 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 2008 errorOnFirstToken(node, Diagnostics.export_modifier_cannot_be_applied_to_ambient_modules_and_module_augmentations_since_they_are_always_visible); 2009 } 2010 if (isModuleAugmentationExternal(node)) { 2011 declareModuleSymbol(node); 2012 } 2013 else { 2014 let pattern: string | Pattern | undefined; 2015 if (node.name.kind === SyntaxKind.StringLiteral) { 2016 const { text } = node.name; 2017 pattern = tryParsePattern(text); 2018 if (pattern === undefined) { 2019 errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text); 2020 } 2021 } 2022 2023 const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes)!; 2024 file.patternAmbientModules = append<PatternAmbientModule>(file.patternAmbientModules, pattern && !isString(pattern) ? { pattern, symbol } : undefined); 2025 } 2026 } 2027 else { 2028 const state = declareModuleSymbol(node); 2029 if (state !== ModuleInstanceState.NonInstantiated) { 2030 const { symbol } = node; 2031 // if module was already merged with some function, class or non-const enum, treat it as non-const-enum-only 2032 symbol.constEnumOnlyModule = (!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.RegularEnum))) 2033 // Current must be `const enum` only 2034 && state === ModuleInstanceState.ConstEnumOnly 2035 // Can't have been set to 'false' in a previous merged symbol. ('undefined' OK) 2036 && symbol.constEnumOnlyModule !== false; 2037 } 2038 } 2039 } 2040 2041 function declareModuleSymbol(node: ModuleDeclaration): ModuleInstanceState { 2042 const state = getModuleInstanceState(node); 2043 const instantiated = state !== ModuleInstanceState.NonInstantiated; 2044 declareSymbolAndAddToSymbolTable(node, 2045 instantiated ? SymbolFlags.ValueModule : SymbolFlags.NamespaceModule, 2046 instantiated ? SymbolFlags.ValueModuleExcludes : SymbolFlags.NamespaceModuleExcludes); 2047 return state; 2048 } 2049 2050 function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocSignature): void { 2051 // For a given function symbol "<...>(...) => T" we want to generate a symbol identical 2052 // to the one we would get for: { <...>(...): T } 2053 // 2054 // We do that by making an anonymous type literal symbol, and then setting the function 2055 // symbol as its sole member. To the rest of the system, this symbol will be indistinguishable 2056 // from an actual type literal symbol you would have gotten had you used the long form. 2057 const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)!); // TODO: GH#18217 2058 addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); 2059 2060 const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type); 2061 addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); 2062 typeLiteralSymbol.members = createSymbolTable(); 2063 typeLiteralSymbol.members.set(symbol.escapedName, symbol); 2064 } 2065 2066 function bindObjectLiteralExpression(node: ObjectLiteralExpression) { 2067 return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.Object); 2068 } 2069 2070 function bindJsxAttributes(node: JsxAttributes) { 2071 return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, InternalSymbolName.JSXAttributes); 2072 } 2073 2074 function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 2075 return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); 2076 } 2077 2078 function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: __String) { 2079 const symbol = createSymbol(symbolFlags, name); 2080 if (symbolFlags & (SymbolFlags.EnumMember | SymbolFlags.ClassMember)) { 2081 symbol.parent = container.symbol; 2082 } 2083 addDeclarationToSymbol(symbol, node, symbolFlags); 2084 return symbol; 2085 } 2086 2087 function bindBlockScopedDeclaration(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 2088 switch (blockScopeContainer.kind) { 2089 case SyntaxKind.ModuleDeclaration: 2090 declareModuleMember(node, symbolFlags, symbolExcludes); 2091 break; 2092 case SyntaxKind.SourceFile: 2093 if (isExternalOrCommonJsModule(container as SourceFile)) { 2094 declareModuleMember(node, symbolFlags, symbolExcludes); 2095 break; 2096 } 2097 // falls through 2098 default: 2099 if (!blockScopeContainer.locals) { 2100 blockScopeContainer.locals = createSymbolTable(); 2101 addToContainerChain(blockScopeContainer); 2102 } 2103 declareSymbol(blockScopeContainer.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes); 2104 } 2105 } 2106 2107 function delayedBindJSDocTypedefTag() { 2108 if (!delayedTypeAliases) { 2109 return; 2110 } 2111 const saveContainer = container; 2112 const saveLastContainer = lastContainer; 2113 const saveBlockScopeContainer = blockScopeContainer; 2114 const saveParent = parent; 2115 const saveCurrentFlow = currentFlow; 2116 for (const typeAlias of delayedTypeAliases) { 2117 const host = typeAlias.parent.parent; 2118 container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file; 2119 blockScopeContainer = getEnclosingBlockScopeContainer(host) || file; 2120 currentFlow = initFlowNode({ flags: FlowFlags.Start }); 2121 parent = typeAlias; 2122 bind(typeAlias.typeExpression); 2123 const declName = getNameOfDeclaration(typeAlias); 2124 if ((isJSDocEnumTag(typeAlias) || !typeAlias.fullName) && declName && isPropertyAccessEntityNameExpression(declName.parent)) { 2125 // typedef anchored to an A.B.C assignment - we need to bind into B's namespace under name C 2126 const isTopLevel = isTopLevelNamespaceAssignment(declName.parent); 2127 if (isTopLevel) { 2128 bindPotentiallyMissingNamespaces(file.symbol, declName.parent, isTopLevel, 2129 !!findAncestor(declName, d => isPropertyAccessExpression(d) && d.name.escapedText === "prototype"), /*containerIsClass*/ false); 2130 const oldContainer = container; 2131 switch (getAssignmentDeclarationPropertyAccessKind(declName.parent)) { 2132 case AssignmentDeclarationKind.ExportsProperty: 2133 case AssignmentDeclarationKind.ModuleExports: 2134 if (!isExternalOrCommonJsModule(file)) { 2135 container = undefined!; 2136 } 2137 else { 2138 container = file; 2139 } 2140 break; 2141 case AssignmentDeclarationKind.ThisProperty: 2142 container = declName.parent.expression; 2143 break; 2144 case AssignmentDeclarationKind.PrototypeProperty: 2145 container = (declName.parent.expression as PropertyAccessExpression).name; 2146 break; 2147 case AssignmentDeclarationKind.Property: 2148 container = isExportsOrModuleExportsOrAlias(file, declName.parent.expression) ? file 2149 : isPropertyAccessExpression(declName.parent.expression) ? declName.parent.expression.name 2150 : declName.parent.expression; 2151 break; 2152 case AssignmentDeclarationKind.None: 2153 return Debug.fail("Shouldn't have detected typedef or enum on non-assignment declaration"); 2154 } 2155 if (container) { 2156 declareModuleMember(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2157 } 2158 container = oldContainer; 2159 } 2160 } 2161 else if (isJSDocEnumTag(typeAlias) || !typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) { 2162 parent = typeAlias.parent; 2163 bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2164 } 2165 else { 2166 bind(typeAlias.fullName); 2167 } 2168 } 2169 container = saveContainer; 2170 lastContainer = saveLastContainer; 2171 blockScopeContainer = saveBlockScopeContainer; 2172 parent = saveParent; 2173 currentFlow = saveCurrentFlow; 2174 } 2175 2176 // The binder visits every node in the syntax tree so it is a convenient place to perform a single localized 2177 // check for reserved words used as identifiers in strict mode code, as well as `yield` or `await` in 2178 // [Yield] or [Await] contexts, respectively. 2179 function checkContextualIdentifier(node: Identifier) { 2180 // Report error only if there are no parse errors in file 2181 if (!file.parseDiagnostics.length && 2182 !(node.flags & NodeFlags.Ambient) && 2183 !(node.flags & NodeFlags.JSDoc) && 2184 !isIdentifierName(node)) { 2185 2186 // strict mode identifiers 2187 if (inStrictMode && 2188 node.originalKeywordKind! >= SyntaxKind.FirstFutureReservedWord && 2189 node.originalKeywordKind! <= SyntaxKind.LastFutureReservedWord) { 2190 file.bindDiagnostics.push(createDiagnosticForNode(node, 2191 getStrictModeIdentifierMessage(node), declarationNameToString(node))); 2192 } 2193 else if (node.originalKeywordKind === SyntaxKind.AwaitKeyword) { 2194 if (isExternalModule(file) && isInTopLevelContext(node)) { 2195 file.bindDiagnostics.push(createDiagnosticForNode(node, 2196 Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, 2197 declarationNameToString(node))); 2198 } 2199 else if (node.flags & NodeFlags.AwaitContext) { 2200 file.bindDiagnostics.push(createDiagnosticForNode(node, 2201 Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, 2202 declarationNameToString(node))); 2203 } 2204 } 2205 else if (node.originalKeywordKind === SyntaxKind.YieldKeyword && node.flags & NodeFlags.YieldContext) { 2206 file.bindDiagnostics.push(createDiagnosticForNode(node, 2207 Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here, 2208 declarationNameToString(node))); 2209 } 2210 } 2211 } 2212 2213 function getStrictModeIdentifierMessage(node: Node) { 2214 // Provide specialized messages to help the user understand why we think they're in 2215 // strict mode. 2216 if (getContainingClass(node)) { 2217 return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode; 2218 } 2219 2220 if (file.externalModuleIndicator) { 2221 return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode; 2222 } 2223 2224 return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode; 2225 } 2226 2227 // The binder visits every node, so this is a good place to check for 2228 // the reserved private name (there is only one) 2229 function checkPrivateIdentifier(node: PrivateIdentifier) { 2230 if (node.escapedText === "#constructor") { 2231 // Report error only if there are no parse errors in file 2232 if (!file.parseDiagnostics.length) { 2233 file.bindDiagnostics.push(createDiagnosticForNode(node, 2234 Diagnostics.constructor_is_a_reserved_word, declarationNameToString(node))); 2235 } 2236 } 2237 } 2238 2239 function checkStrictModeBinaryExpression(node: BinaryExpression) { 2240 if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) { 2241 // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an 2242 // Assignment operator(11.13) or of a PostfixExpression(11.3) 2243 checkStrictModeEvalOrArguments(node, node.left as Identifier); 2244 } 2245 } 2246 2247 function checkStrictModeCatchClause(node: CatchClause) { 2248 // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the 2249 // Catch production is eval or arguments 2250 if (inStrictMode && node.variableDeclaration) { 2251 checkStrictModeEvalOrArguments(node, node.variableDeclaration.name); 2252 } 2253 } 2254 2255 function checkStrictModeDeleteExpression(node: DeleteExpression) { 2256 // Grammar checking 2257 if (inStrictMode && node.expression.kind === SyntaxKind.Identifier) { 2258 // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its 2259 // UnaryExpression is a direct reference to a variable, function argument, or function name 2260 const span = getErrorSpanForNode(file, node.expression); 2261 file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode)); 2262 } 2263 } 2264 2265 function isEvalOrArgumentsIdentifier(node: Node): boolean { 2266 return isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === "arguments"); 2267 } 2268 2269 function checkStrictModeEvalOrArguments(contextNode: Node, name: Node | undefined) { 2270 if (name && name.kind === SyntaxKind.Identifier) { 2271 const identifier = name as Identifier; 2272 if (isEvalOrArgumentsIdentifier(identifier)) { 2273 // We check first if the name is inside class declaration or class expression; if so give explicit message 2274 // otherwise report generic error message. 2275 const span = getErrorSpanForNode(file, name); 2276 file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, 2277 getStrictModeEvalOrArgumentsMessage(contextNode), idText(identifier))); 2278 } 2279 } 2280 } 2281 2282 function getStrictModeEvalOrArgumentsMessage(node: Node) { 2283 // Provide specialized messages to help the user understand why we think they're in 2284 // strict mode. 2285 if (getContainingClass(node)) { 2286 return Diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode; 2287 } 2288 2289 if (file.externalModuleIndicator) { 2290 return Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode; 2291 } 2292 2293 return Diagnostics.Invalid_use_of_0_in_strict_mode; 2294 } 2295 2296 function checkStrictModeFunctionName(node: FunctionLikeDeclaration) { 2297 if (inStrictMode) { 2298 // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)) 2299 checkStrictModeEvalOrArguments(node, node.name); 2300 } 2301 } 2302 2303 function getStrictModeBlockScopeFunctionDeclarationMessage(node: Node) { 2304 // Provide specialized messages to help the user understand why we think they're in 2305 // strict mode. 2306 if (getContainingClass(node)) { 2307 return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Class_definitions_are_automatically_in_strict_mode; 2308 } 2309 2310 if (file.externalModuleIndicator) { 2311 return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5_Modules_are_automatically_in_strict_mode; 2312 } 2313 2314 return Diagnostics.Function_declarations_are_not_allowed_inside_blocks_in_strict_mode_when_targeting_ES3_or_ES5; 2315 } 2316 2317 function checkStrictModeFunctionDeclaration(node: FunctionDeclaration) { 2318 if (languageVersion < ScriptTarget.ES2015) { 2319 // Report error if function is not top level function declaration 2320 if (blockScopeContainer.kind !== SyntaxKind.SourceFile && 2321 blockScopeContainer.kind !== SyntaxKind.ModuleDeclaration && 2322 !isFunctionLikeOrClassStaticBlockDeclaration(blockScopeContainer)) { 2323 // We check first if the name is inside class declaration or class expression; if so give explicit message 2324 // otherwise report generic error message. 2325 const errorSpan = getErrorSpanForNode(file, node); 2326 file.bindDiagnostics.push(createFileDiagnostic(file, errorSpan.start, errorSpan.length, 2327 getStrictModeBlockScopeFunctionDeclarationMessage(node))); 2328 } 2329 } 2330 } 2331 2332 function checkStrictModeNumericLiteral(node: NumericLiteral) { 2333 if (languageVersion < ScriptTarget.ES5 && inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) { 2334 file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode)); 2335 } 2336 } 2337 2338 function checkStrictModePostfixUnaryExpression(node: PostfixUnaryExpression) { 2339 // Grammar checking 2340 // The identifier eval or arguments may not appear as the LeftHandSideExpression of an 2341 // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression 2342 // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. 2343 if (inStrictMode) { 2344 checkStrictModeEvalOrArguments(node, node.operand as Identifier); 2345 } 2346 } 2347 2348 function checkStrictModePrefixUnaryExpression(node: PrefixUnaryExpression) { 2349 // Grammar checking 2350 if (inStrictMode) { 2351 if (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) { 2352 checkStrictModeEvalOrArguments(node, node.operand as Identifier); 2353 } 2354 } 2355 } 2356 2357 function checkStrictModeWithStatement(node: WithStatement) { 2358 // Grammar checking for withStatement 2359 if (inStrictMode) { 2360 errorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode); 2361 } 2362 } 2363 2364 function checkStrictModeLabeledStatement(node: LabeledStatement) { 2365 // Grammar checking for labeledStatement 2366 if (inStrictMode && getEmitScriptTarget(options) >= ScriptTarget.ES2015) { 2367 if (isDeclarationStatement(node.statement) || isVariableStatement(node.statement)) { 2368 errorOnFirstToken(node.label, Diagnostics.A_label_is_not_allowed_here); 2369 } 2370 } 2371 } 2372 2373 function errorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) { 2374 const span = getSpanOfTokenAtPosition(file, node.pos); 2375 file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2)); 2376 } 2377 2378 function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void { 2379 errorOrSuggestionOnRange(isError, node, node, message); 2380 } 2381 2382 function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void { 2383 addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message); 2384 } 2385 2386 function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void { 2387 const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message); 2388 if (isError) { 2389 file.bindDiagnostics.push(diag); 2390 } 2391 else { 2392 file.bindSuggestionDiagnostics = append(file.bindSuggestionDiagnostics, { ...diag, category: DiagnosticCategory.Suggestion }); 2393 } 2394 } 2395 2396 function bind(node: Node | undefined): void { 2397 if (!node) { 2398 return; 2399 } 2400 setParent(node, parent); 2401 if (tracing) (node as TracingNode).tracingPath = file.path; 2402 const saveInStrictMode = inStrictMode; 2403 2404 // Even though in the AST the jsdoc @typedef node belongs to the current node, 2405 // its symbol might be in the same scope with the current node's symbol. Consider: 2406 // 2407 // /** @typedef {string | number} MyType */ 2408 // function foo(); 2409 // 2410 // Here the current node is "foo", which is a container, but the scope of "MyType" should 2411 // not be inside "foo". Therefore we always bind @typedef before bind the parent node, 2412 // and skip binding this tag later when binding all the other jsdoc tags. 2413 2414 // First we bind declaration nodes to a symbol if possible. We'll both create a symbol 2415 // and then potentially add the symbol to an appropriate symbol table. Possible 2416 // destination symbol tables are: 2417 // 2418 // 1) The 'exports' table of the current container's symbol. 2419 // 2) The 'members' table of the current container's symbol. 2420 // 3) The 'locals' table of the current container. 2421 // 2422 // However, not all symbols will end up in any of these tables. 'Anonymous' symbols 2423 // (like TypeLiterals for example) will not be put in any table. 2424 bindWorker(node); 2425 // Then we recurse into the children of the node to bind them as well. For certain 2426 // symbols we do specialized work when we recurse. For example, we'll keep track of 2427 // the current 'container' node when it changes. This helps us know which symbol table 2428 // a local should go into for example. Since terminal nodes are known not to have 2429 // children, as an optimization we don't process those. 2430 if (node.kind > SyntaxKind.LastToken) { 2431 const saveParent = parent; 2432 parent = node; 2433 const containerFlags = getContainerFlags(node); 2434 if (containerFlags === ContainerFlags.None) { 2435 bindChildren(node); 2436 } 2437 else { 2438 bindContainer(node, containerFlags); 2439 } 2440 parent = saveParent; 2441 } 2442 else { 2443 const saveParent = parent; 2444 if (node.kind === SyntaxKind.EndOfFileToken) parent = node; 2445 bindJSDoc(node); 2446 parent = saveParent; 2447 } 2448 inStrictMode = saveInStrictMode; 2449 } 2450 2451 function bindJSDoc(node: Node) { 2452 if (hasJSDocNodes(node)) { 2453 if (isInJSFile(node)) { 2454 for (const j of node.jsDoc!) { 2455 bind(j); 2456 } 2457 } 2458 else { 2459 for (const j of node.jsDoc!) { 2460 setParent(j, node); 2461 setParentRecursive(j, /*incremental*/ false); 2462 } 2463 } 2464 } 2465 } 2466 2467 function updateStrictModeStatementList(statements: NodeArray<Statement>) { 2468 if (!inStrictMode) { 2469 for (const statement of statements) { 2470 if (!isPrologueDirective(statement)) { 2471 return; 2472 } 2473 2474 if (isUseStrictPrologueDirective(statement as ExpressionStatement)) { 2475 inStrictMode = true; 2476 return; 2477 } 2478 } 2479 } 2480 } 2481 2482 /// Should be called only on prologue directives (isPrologueDirective(node) should be true) 2483 function isUseStrictPrologueDirective(node: ExpressionStatement): boolean { 2484 const nodeText = getSourceTextOfNodeFromSourceFile(file, node.expression); 2485 2486 // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the 2487 // string to contain unicode escapes (as per ES5). 2488 return nodeText === '"use strict"' || nodeText === "'use strict'"; 2489 } 2490 2491 function bindWorker(node: Node) { 2492 switch (node.kind) { 2493 /* Strict mode checks */ 2494 case SyntaxKind.Identifier: 2495 // for typedef type names with namespaces, bind the new jsdoc type symbol here 2496 // because it requires all containing namespaces to be in effect, namely the 2497 // current "blockScopeContainer" needs to be set to its immediate namespace parent. 2498 if ((node as Identifier).isInJSDocNamespace) { 2499 let parentNode = node.parent; 2500 while (parentNode && !isJSDocTypeAlias(parentNode)) { 2501 parentNode = parentNode.parent; 2502 } 2503 bindBlockScopedDeclaration(parentNode as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2504 break; 2505 } 2506 // falls through 2507 case SyntaxKind.ThisKeyword: 2508 if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) { 2509 node.flowNode = currentFlow; 2510 } 2511 return checkContextualIdentifier(node as Identifier); 2512 case SyntaxKind.QualifiedName: 2513 if (currentFlow && isPartOfTypeQuery(node)) { 2514 node.flowNode = currentFlow; 2515 } 2516 break; 2517 case SyntaxKind.MetaProperty: 2518 case SyntaxKind.SuperKeyword: 2519 node.flowNode = currentFlow; 2520 break; 2521 case SyntaxKind.PrivateIdentifier: 2522 return checkPrivateIdentifier(node as PrivateIdentifier); 2523 case SyntaxKind.PropertyAccessExpression: 2524 case SyntaxKind.ElementAccessExpression: 2525 const expr = node as PropertyAccessExpression | ElementAccessExpression; 2526 if (currentFlow && isNarrowableReference(expr)) { 2527 expr.flowNode = currentFlow; 2528 } 2529 if (isSpecialPropertyDeclaration(expr)) { 2530 bindSpecialPropertyDeclaration(expr); 2531 } 2532 if (isInJSFile(expr) && 2533 file.commonJsModuleIndicator && 2534 isModuleExportsAccessExpression(expr) && 2535 !lookupSymbolForName(blockScopeContainer, "module" as __String)) { 2536 declareSymbol(file.locals!, /*parent*/ undefined, expr.expression, 2537 SymbolFlags.FunctionScopedVariable | SymbolFlags.ModuleExports, SymbolFlags.FunctionScopedVariableExcludes); 2538 } 2539 break; 2540 case SyntaxKind.BinaryExpression: 2541 const specialKind = getAssignmentDeclarationKind(node as BinaryExpression); 2542 switch (specialKind) { 2543 case AssignmentDeclarationKind.ExportsProperty: 2544 bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); 2545 break; 2546 case AssignmentDeclarationKind.ModuleExports: 2547 bindModuleExportsAssignment(node as BindablePropertyAssignmentExpression); 2548 break; 2549 case AssignmentDeclarationKind.PrototypeProperty: 2550 bindPrototypePropertyAssignment((node as BindableStaticPropertyAssignmentExpression).left, node); 2551 break; 2552 case AssignmentDeclarationKind.Prototype: 2553 bindPrototypeAssignment(node as BindableStaticPropertyAssignmentExpression); 2554 break; 2555 case AssignmentDeclarationKind.ThisProperty: 2556 bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression); 2557 break; 2558 case AssignmentDeclarationKind.Property: 2559 const expression = ((node as BinaryExpression).left as AccessExpression).expression; 2560 if (isInJSFile(node) && isIdentifier(expression)) { 2561 const symbol = lookupSymbolForName(blockScopeContainer, expression.escapedText); 2562 if (isThisInitializedDeclaration(symbol?.valueDeclaration)) { 2563 bindThisPropertyAssignment(node as BindablePropertyAssignmentExpression); 2564 break; 2565 } 2566 } 2567 bindSpecialPropertyAssignment(node as BindablePropertyAssignmentExpression); 2568 break; 2569 case AssignmentDeclarationKind.None: 2570 // Nothing to do 2571 break; 2572 default: 2573 Debug.fail("Unknown binary expression special property assignment kind"); 2574 } 2575 return checkStrictModeBinaryExpression(node as BinaryExpression); 2576 case SyntaxKind.CatchClause: 2577 return checkStrictModeCatchClause(node as CatchClause); 2578 case SyntaxKind.DeleteExpression: 2579 return checkStrictModeDeleteExpression(node as DeleteExpression); 2580 case SyntaxKind.NumericLiteral: 2581 return checkStrictModeNumericLiteral(node as NumericLiteral); 2582 case SyntaxKind.PostfixUnaryExpression: 2583 return checkStrictModePostfixUnaryExpression(node as PostfixUnaryExpression); 2584 case SyntaxKind.PrefixUnaryExpression: 2585 return checkStrictModePrefixUnaryExpression(node as PrefixUnaryExpression); 2586 case SyntaxKind.WithStatement: 2587 return checkStrictModeWithStatement(node as WithStatement); 2588 case SyntaxKind.LabeledStatement: 2589 return checkStrictModeLabeledStatement(node as LabeledStatement); 2590 case SyntaxKind.ThisType: 2591 seenThisKeyword = true; 2592 return; 2593 case SyntaxKind.TypePredicate: 2594 break; // Binding the children will handle everything 2595 case SyntaxKind.TypeParameter: 2596 return bindTypeParameter(node as TypeParameterDeclaration); 2597 case SyntaxKind.Parameter: 2598 return bindParameter(node as ParameterDeclaration); 2599 case SyntaxKind.VariableDeclaration: 2600 return bindVariableDeclarationOrBindingElement(node as VariableDeclaration); 2601 case SyntaxKind.BindingElement: 2602 node.flowNode = currentFlow; 2603 return bindVariableDeclarationOrBindingElement(node as BindingElement); 2604 case SyntaxKind.PropertyDeclaration: 2605 case SyntaxKind.PropertySignature: 2606 return bindPropertyWorker(node as PropertyDeclaration | PropertySignature); 2607 case SyntaxKind.PropertyAssignment: 2608 case SyntaxKind.ShorthandPropertyAssignment: 2609 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Property, SymbolFlags.PropertyExcludes); 2610 case SyntaxKind.EnumMember: 2611 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); 2612 2613 case SyntaxKind.CallSignature: 2614 case SyntaxKind.ConstructSignature: 2615 case SyntaxKind.IndexSignature: 2616 return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Signature, SymbolFlags.None); 2617 case SyntaxKind.MethodDeclaration: 2618 case SyntaxKind.MethodSignature: 2619 // If this is an ObjectLiteralExpression method, then it sits in the same space 2620 // as other properties in the object literal. So we use SymbolFlags.PropertyExcludes 2621 // so that it will conflict with any other object literal members with the same 2622 // name. 2623 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.Method | ((node as MethodDeclaration).questionToken ? SymbolFlags.Optional : SymbolFlags.None), 2624 isObjectLiteralMethod(node) ? SymbolFlags.PropertyExcludes : SymbolFlags.MethodExcludes); 2625 case SyntaxKind.FunctionDeclaration: 2626 return bindFunctionDeclaration(node as FunctionDeclaration); 2627 case SyntaxKind.Constructor: 2628 return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Constructor, /*symbolExcludes:*/ SymbolFlags.None); 2629 case SyntaxKind.GetAccessor: 2630 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.GetAccessor, SymbolFlags.GetAccessorExcludes); 2631 case SyntaxKind.SetAccessor: 2632 return bindPropertyOrMethodOrAccessor(node as Declaration, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); 2633 case SyntaxKind.FunctionType: 2634 case SyntaxKind.JSDocFunctionType: 2635 case SyntaxKind.JSDocSignature: 2636 case SyntaxKind.ConstructorType: 2637 return bindFunctionOrConstructorType(node as SignatureDeclaration | JSDocSignature); 2638 case SyntaxKind.TypeLiteral: 2639 case SyntaxKind.JSDocTypeLiteral: 2640 case SyntaxKind.MappedType: 2641 return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral); 2642 case SyntaxKind.JSDocClassTag: 2643 return bindJSDocClassTag(node as JSDocClassTag); 2644 case SyntaxKind.ObjectLiteralExpression: 2645 return bindObjectLiteralExpression(node as ObjectLiteralExpression); 2646 case SyntaxKind.FunctionExpression: 2647 case SyntaxKind.ArrowFunction: 2648 return bindFunctionExpression(node as FunctionExpression); 2649 2650 case SyntaxKind.CallExpression: 2651 const assignmentKind = getAssignmentDeclarationKind(node as CallExpression); 2652 switch (assignmentKind) { 2653 case AssignmentDeclarationKind.ObjectDefinePropertyValue: 2654 return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall); 2655 case AssignmentDeclarationKind.ObjectDefinePropertyExports: 2656 return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall); 2657 case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: 2658 return bindObjectDefinePrototypeProperty(node as BindableObjectDefinePropertyCall); 2659 case AssignmentDeclarationKind.None: 2660 break; // Nothing to do 2661 default: 2662 return Debug.fail("Unknown call expression assignment declaration kind"); 2663 } 2664 if (isInJSFile(node)) { 2665 bindCallExpression(node as CallExpression); 2666 } 2667 break; 2668 2669 // Members of classes, interfaces, and modules 2670 case SyntaxKind.ClassExpression: 2671 case SyntaxKind.ClassDeclaration: 2672 case SyntaxKind.StructDeclaration: 2673 // All classes are automatically in strict mode in ES6. 2674 inStrictMode = true; 2675 return bindClassLikeDeclaration(node as ClassLikeDeclaration); 2676 case SyntaxKind.InterfaceDeclaration: 2677 return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); 2678 case SyntaxKind.TypeAliasDeclaration: 2679 return bindBlockScopedDeclaration(node as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); 2680 case SyntaxKind.EnumDeclaration: 2681 return bindEnumDeclaration(node as EnumDeclaration); 2682 case SyntaxKind.ModuleDeclaration: 2683 return bindModuleDeclaration(node as ModuleDeclaration); 2684 // Jsx-attributes 2685 case SyntaxKind.JsxAttributes: 2686 return bindJsxAttributes(node as JsxAttributes); 2687 case SyntaxKind.JsxAttribute: 2688 return bindJsxAttribute(node as JsxAttribute, SymbolFlags.Property, SymbolFlags.PropertyExcludes); 2689 2690 // Imports and exports 2691 case SyntaxKind.ImportEqualsDeclaration: 2692 case SyntaxKind.NamespaceImport: 2693 case SyntaxKind.ImportSpecifier: 2694 case SyntaxKind.ExportSpecifier: 2695 return declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2696 case SyntaxKind.NamespaceExportDeclaration: 2697 return bindNamespaceExportDeclaration(node as NamespaceExportDeclaration); 2698 case SyntaxKind.ImportClause: 2699 return bindImportClause(node as ImportClause); 2700 case SyntaxKind.ExportDeclaration: 2701 return bindExportDeclaration(node as ExportDeclaration); 2702 case SyntaxKind.ExportAssignment: 2703 return bindExportAssignment(node as ExportAssignment); 2704 case SyntaxKind.SourceFile: 2705 updateStrictModeStatementList((node as SourceFile).statements); 2706 return bindSourceFileIfExternalModule(); 2707 case SyntaxKind.Block: 2708 if (!isFunctionLikeOrClassStaticBlockDeclaration(node.parent)) { 2709 return; 2710 } 2711 // falls through 2712 case SyntaxKind.ModuleBlock: 2713 return updateStrictModeStatementList((node as Block | ModuleBlock).statements); 2714 2715 case SyntaxKind.JSDocParameterTag: 2716 if (node.parent.kind === SyntaxKind.JSDocSignature) { 2717 return bindParameter(node as JSDocParameterTag); 2718 } 2719 if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) { 2720 break; 2721 } 2722 // falls through 2723 case SyntaxKind.JSDocPropertyTag: 2724 const propTag = node as JSDocPropertyLikeTag; 2725 const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ? 2726 SymbolFlags.Property | SymbolFlags.Optional : 2727 SymbolFlags.Property; 2728 return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes); 2729 case SyntaxKind.JSDocTypedefTag: 2730 case SyntaxKind.JSDocCallbackTag: 2731 case SyntaxKind.JSDocEnumTag: 2732 return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag); 2733 } 2734 } 2735 2736 function bindPropertyWorker(node: PropertyDeclaration | PropertySignature) { 2737 const isAutoAccessor = isAutoAccessorPropertyDeclaration(node); 2738 const includes = isAutoAccessor ? SymbolFlags.Accessor : SymbolFlags.Property; 2739 const excludes = isAutoAccessor ? SymbolFlags.AccessorExcludes : SymbolFlags.PropertyExcludes; 2740 return bindPropertyOrMethodOrAccessor(node, includes | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), excludes); 2741 } 2742 2743 function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) { 2744 return bindAnonymousDeclaration(node as Declaration, SymbolFlags.TypeLiteral, InternalSymbolName.Type); 2745 } 2746 2747 function bindSourceFileIfExternalModule() { 2748 setExportContextFlag(file); 2749 if (isExternalModule(file)) { 2750 bindSourceFileAsExternalModule(); 2751 } 2752 else if (isJsonSourceFile(file)) { 2753 bindSourceFileAsExternalModule(); 2754 // Create symbol equivalent for the module.exports = {} 2755 const originalSymbol = file.symbol; 2756 declareSymbol(file.symbol.exports!, file.symbol, file, SymbolFlags.Property, SymbolFlags.All); 2757 file.symbol = originalSymbol; 2758 } 2759 } 2760 2761 function bindSourceFileAsExternalModule() { 2762 bindAnonymousDeclaration(file, SymbolFlags.ValueModule, `"${removeFileExtension(file.fileName)}"` as __String); 2763 } 2764 2765 function bindExportAssignment(node: ExportAssignment) { 2766 if (!container.symbol || !container.symbol.exports) { 2767 // Incorrect export assignment in some sort of block construct 2768 bindAnonymousDeclaration(node, SymbolFlags.Value, getDeclarationName(node)!); 2769 } 2770 else { 2771 const flags = exportAssignmentIsAlias(node) 2772 // An export default clause with an EntityNameExpression or a class expression exports all meanings of that identifier or expression; 2773 ? SymbolFlags.Alias 2774 // An export default clause with any other expression exports a value 2775 : SymbolFlags.Property; 2776 // If there is an `export default x;` alias declaration, can't `export default` anything else. 2777 // (In contrast, you can still have `export default function f() {}` and `export default interface I {}`.) 2778 const symbol = declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.All); 2779 2780 if (node.isExportEquals) { 2781 // Will be an error later, since the module already has other exports. Just make sure this has a valueDeclaration set. 2782 setValueDeclaration(symbol, node); 2783 } 2784 } 2785 } 2786 2787 function bindNamespaceExportDeclaration(node: NamespaceExportDeclaration) { 2788 if (some(node.modifiers)) { 2789 file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Modifiers_cannot_appear_here)); 2790 } 2791 const diag = !isSourceFile(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_at_top_level 2792 : !isExternalModule(node.parent) ? Diagnostics.Global_module_exports_may_only_appear_in_module_files 2793 : !node.parent.isDeclarationFile ? Diagnostics.Global_module_exports_may_only_appear_in_declaration_files 2794 : undefined; 2795 if (diag) { 2796 file.bindDiagnostics.push(createDiagnosticForNode(node, diag)); 2797 } 2798 else { 2799 file.symbol.globalExports = file.symbol.globalExports || createSymbolTable(); 2800 declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2801 } 2802 } 2803 2804 function bindExportDeclaration(node: ExportDeclaration) { 2805 if (!container.symbol || !container.symbol.exports) { 2806 // Export * in some sort of block construct 2807 bindAnonymousDeclaration(node, SymbolFlags.ExportStar, getDeclarationName(node)!); 2808 } 2809 else if (!node.exportClause) { 2810 // All export * declarations are collected in an __export symbol 2811 declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.ExportStar, SymbolFlags.None); 2812 } 2813 else if (isNamespaceExport(node.exportClause)) { 2814 // declareSymbol walks up parents to find name text, parent _must_ be set 2815 // but won't be set by the normal binder walk until `bindChildren` later on. 2816 setParent(node.exportClause, node); 2817 declareSymbol(container.symbol.exports, container.symbol, node.exportClause, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2818 } 2819 } 2820 2821 function bindImportClause(node: ImportClause) { 2822 if (node.name) { 2823 declareSymbolAndAddToSymbolTable(node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 2824 } 2825 } 2826 2827 function setCommonJsModuleIndicator(node: Node) { 2828 if (file.externalModuleIndicator && file.externalModuleIndicator !== true) { 2829 return false; 2830 } 2831 if (!file.commonJsModuleIndicator) { 2832 file.commonJsModuleIndicator = node; 2833 if (!file.externalModuleIndicator) { 2834 bindSourceFileAsExternalModule(); 2835 } 2836 } 2837 return true; 2838 } 2839 2840 function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) { 2841 if (!setCommonJsModuleIndicator(node)) { 2842 return; 2843 } 2844 const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => { 2845 if (symbol) { 2846 addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment); 2847 } 2848 return symbol; 2849 }); 2850 if (symbol) { 2851 const flags = SymbolFlags.Property | SymbolFlags.ExportValue; 2852 declareSymbol(symbol.exports!, symbol, node, flags, SymbolFlags.None); 2853 } 2854 } 2855 2856 function bindExportsPropertyAssignment(node: BindableStaticPropertyAssignmentExpression) { 2857 // When we create a property via 'exports.foo = bar', the 'exports.foo' property access 2858 // expression is the declaration 2859 if (!setCommonJsModuleIndicator(node)) { 2860 return; 2861 } 2862 const symbol = forEachIdentifierInEntityName(node.left.expression, /*parent*/ undefined, (id, symbol) => { 2863 if (symbol) { 2864 addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment); 2865 } 2866 return symbol; 2867 }); 2868 if (symbol) { 2869 const isAlias = isAliasableExpression(node.right) && (isExportsIdentifier(node.left.expression) || isModuleExportsAccessExpression(node.left.expression)); 2870 const flags = isAlias ? SymbolFlags.Alias : SymbolFlags.Property | SymbolFlags.ExportValue; 2871 setParent(node.left, node); 2872 declareSymbol(symbol.exports!, symbol, node.left, flags, SymbolFlags.None); 2873 } 2874 } 2875 2876 function bindModuleExportsAssignment(node: BindablePropertyAssignmentExpression) { 2877 // A common practice in node modules is to set 'export = module.exports = {}', this ensures that 'exports' 2878 // is still pointing to 'module.exports'. 2879 // We do not want to consider this as 'export=' since a module can have only one of these. 2880 // Similarly we do not want to treat 'module.exports = exports' as an 'export='. 2881 if (!setCommonJsModuleIndicator(node)) { 2882 return; 2883 } 2884 const assignedExpression = getRightMostAssignedExpression(node.right); 2885 if (isEmptyObjectLiteral(assignedExpression) || container === file && isExportsOrModuleExportsOrAlias(file, assignedExpression)) { 2886 return; 2887 } 2888 2889 if (isObjectLiteralExpression(assignedExpression) && every(assignedExpression.properties, isShorthandPropertyAssignment)) { 2890 forEach(assignedExpression.properties, bindExportAssignedObjectMemberAlias); 2891 return; 2892 } 2893 2894 // 'module.exports = expr' assignment 2895 const flags = exportAssignmentIsAlias(node) 2896 ? SymbolFlags.Alias // An export= with an EntityNameExpression or a ClassExpression exports all meanings of that identifier or class 2897 : SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule; 2898 const symbol = declareSymbol(file.symbol.exports!, file.symbol, node, flags | SymbolFlags.Assignment, SymbolFlags.None); 2899 setValueDeclaration(symbol, node); 2900 } 2901 2902 function bindExportAssignedObjectMemberAlias(node: ShorthandPropertyAssignment) { 2903 declareSymbol(file.symbol.exports!, file.symbol, node, SymbolFlags.Alias | SymbolFlags.Assignment, SymbolFlags.None); 2904 } 2905 2906 function bindThisPropertyAssignment(node: BindablePropertyAssignmentExpression | PropertyAccessExpression | LiteralLikeElementAccessExpression) { 2907 Debug.assert(isInJSFile(node)); 2908 // private identifiers *must* be declared (even in JS files) 2909 const hasPrivateIdentifier = (isBinaryExpression(node) && isPropertyAccessExpression(node.left) && isPrivateIdentifier(node.left.name)) 2910 || (isPropertyAccessExpression(node) && isPrivateIdentifier(node.name)); 2911 if (hasPrivateIdentifier) { 2912 return; 2913 } 2914 const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false); 2915 switch (thisContainer.kind) { 2916 case SyntaxKind.FunctionDeclaration: 2917 case SyntaxKind.FunctionExpression: 2918 let constructorSymbol: Symbol | undefined = thisContainer.symbol; 2919 // For `f.prototype.m = function() { this.x = 0; }`, `this.x = 0` should modify `f`'s members, not the function expression. 2920 if (isBinaryExpression(thisContainer.parent) && thisContainer.parent.operatorToken.kind === SyntaxKind.EqualsToken) { 2921 const l = thisContainer.parent.left; 2922 if (isBindableStaticAccessExpression(l) && isPrototypeAccess(l.expression)) { 2923 constructorSymbol = lookupSymbolForPropertyAccess(l.expression.expression, thisParentContainer); 2924 } 2925 } 2926 2927 if (constructorSymbol && constructorSymbol.valueDeclaration) { 2928 // Declare a 'member' if the container is an ES5 class or ES6 constructor 2929 constructorSymbol.members = constructorSymbol.members || createSymbolTable(); 2930 // It's acceptable for multiple 'this' assignments of the same identifier to occur 2931 if (hasDynamicName(node)) { 2932 bindDynamicallyNamedThisPropertyAssignment(node, constructorSymbol, constructorSymbol.members); 2933 } 2934 else { 2935 declareSymbol(constructorSymbol.members, constructorSymbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); 2936 } 2937 addDeclarationToSymbol(constructorSymbol, constructorSymbol.valueDeclaration, SymbolFlags.Class); 2938 } 2939 break; 2940 2941 case SyntaxKind.Constructor: 2942 case SyntaxKind.PropertyDeclaration: 2943 case SyntaxKind.MethodDeclaration: 2944 case SyntaxKind.GetAccessor: 2945 case SyntaxKind.SetAccessor: 2946 case SyntaxKind.ClassStaticBlockDeclaration: 2947 // this.foo assignment in a JavaScript class 2948 // Bind this property to the containing class 2949 const containingClass = thisContainer.parent; 2950 const symbolTable = isStatic(thisContainer) ? containingClass.symbol.exports! : containingClass.symbol.members!; 2951 if (hasDynamicName(node)) { 2952 bindDynamicallyNamedThisPropertyAssignment(node, containingClass.symbol, symbolTable); 2953 } 2954 else { 2955 declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property | SymbolFlags.Assignment, SymbolFlags.None, /*isReplaceableByMethod*/ true); 2956 } 2957 break; 2958 case SyntaxKind.SourceFile: 2959 // this.property = assignment in a source file -- declare symbol in exports for a module, in locals for a script 2960 if (hasDynamicName(node)) { 2961 break; 2962 } 2963 else if ((thisContainer as SourceFile).commonJsModuleIndicator) { 2964 declareSymbol(thisContainer.symbol.exports!, thisContainer.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None); 2965 } 2966 else { 2967 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); 2968 } 2969 break; 2970 2971 default: 2972 Debug.failBadSyntaxKind(thisContainer); 2973 } 2974 } 2975 2976 function bindDynamicallyNamedThisPropertyAssignment(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol, symbolTable: SymbolTable) { 2977 declareSymbol(symbolTable, symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true, /*isComputedName*/ true); 2978 addLateBoundAssignmentDeclarationToSymbol(node, symbol); 2979 } 2980 2981 function addLateBoundAssignmentDeclarationToSymbol(node: BinaryExpression | DynamicNamedDeclaration, symbol: Symbol | undefined) { 2982 if (symbol) { 2983 (symbol.assignmentDeclarationMembers || (symbol.assignmentDeclarationMembers = new Map())).set(getNodeId(node), node); 2984 } 2985 } 2986 2987 function bindSpecialPropertyDeclaration(node: PropertyAccessExpression | LiteralLikeElementAccessExpression) { 2988 if (node.expression.kind === SyntaxKind.ThisKeyword) { 2989 bindThisPropertyAssignment(node); 2990 } 2991 else if (isBindableStaticAccessExpression(node) && node.parent.parent.kind === SyntaxKind.SourceFile) { 2992 if (isPrototypeAccess(node.expression)) { 2993 bindPrototypePropertyAssignment(node, node.parent); 2994 } 2995 else { 2996 bindStaticPropertyAssignment(node); 2997 } 2998 } 2999 } 3000 3001 /** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */ 3002 function bindPrototypeAssignment(node: BindableStaticPropertyAssignmentExpression) { 3003 setParent(node.left, node); 3004 setParent(node.right, node); 3005 bindPropertyAssignment(node.left.expression, node.left, /*isPrototypeProperty*/ false, /*containerIsClass*/ true); 3006 } 3007 3008 function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) { 3009 const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression); 3010 if (namespaceSymbol && namespaceSymbol.valueDeclaration) { 3011 // Ensure the namespace symbol becomes class-like 3012 addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class); 3013 } 3014 bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true); 3015 } 3016 3017 /** 3018 * For `x.prototype.y = z`, declare a member `y` on `x` if `x` is a function or class, or not declared. 3019 * Note that jsdoc preceding an ExpressionStatement like `x.prototype.y;` is also treated as a declaration. 3020 */ 3021 function bindPrototypePropertyAssignment(lhs: BindableStaticAccessExpression, parent: Node) { 3022 // Look up the function in the local scope, since prototype assignments should 3023 // follow the function declaration 3024 const classPrototype = lhs.expression as BindableStaticAccessExpression; 3025 const constructorFunction = classPrototype.expression; 3026 3027 // Fix up parent pointers since we're going to use these nodes before we bind into them 3028 setParent(constructorFunction, classPrototype); 3029 setParent(classPrototype, lhs); 3030 setParent(lhs, parent); 3031 3032 bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true, /*containerIsClass*/ true); 3033 } 3034 3035 function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) { 3036 let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]); 3037 const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile; 3038 namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false, /*containerIsClass*/ false); 3039 bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false); 3040 } 3041 3042 function bindSpecialPropertyAssignment(node: BindablePropertyAssignmentExpression) { 3043 // Class declarations in Typescript do not allow property declarations 3044 const parentSymbol = lookupSymbolForPropertyAccess(node.left.expression, container) || lookupSymbolForPropertyAccess(node.left.expression, blockScopeContainer) ; 3045 if (!isInJSFile(node) && !isFunctionSymbol(parentSymbol)) { 3046 return; 3047 } 3048 const rootExpr = getLeftmostAccessExpression(node.left); 3049 if (isIdentifier(rootExpr) && lookupSymbolForName(container, rootExpr.escapedText)?.flags! & SymbolFlags.Alias) { 3050 return; 3051 } 3052 // Fix up parent pointers since we're going to use these nodes before we bind into them 3053 setParent(node.left, node); 3054 setParent(node.right, node); 3055 if (isIdentifier(node.left.expression) && container === file && isExportsOrModuleExportsOrAlias(file, node.left.expression)) { 3056 // This can be an alias for the 'exports' or 'module.exports' names, e.g. 3057 // var util = module.exports; 3058 // util.property = function ... 3059 bindExportsPropertyAssignment(node as BindableStaticPropertyAssignmentExpression); 3060 } 3061 else if (hasDynamicName(node)) { 3062 bindAnonymousDeclaration(node, SymbolFlags.Property | SymbolFlags.Assignment, InternalSymbolName.Computed); 3063 const sym = bindPotentiallyMissingNamespaces(parentSymbol, node.left.expression, isTopLevelNamespaceAssignment(node.left), /*isPrototype*/ false, /*containerIsClass*/ false); 3064 addLateBoundAssignmentDeclarationToSymbol(node, sym); 3065 } 3066 else { 3067 bindStaticPropertyAssignment(cast(node.left, isBindableStaticNameExpression)); 3068 } 3069 } 3070 3071 /** 3072 * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function (or IIFE) or class or {}, or not declared. 3073 * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y; 3074 */ 3075 function bindStaticPropertyAssignment(node: BindableStaticNameExpression) { 3076 Debug.assert(!isIdentifier(node)); 3077 setParent(node.expression, node); 3078 bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false, /*containerIsClass*/ false); 3079 } 3080 3081 function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: BindableStaticNameExpression, isToplevel: boolean, isPrototypeProperty: boolean, containerIsClass: boolean) { 3082 if (namespaceSymbol?.flags! & SymbolFlags.Alias) { 3083 return namespaceSymbol; 3084 } 3085 if (isToplevel && !isPrototypeProperty) { 3086 // make symbols or add declarations for intermediate containers 3087 const flags = SymbolFlags.Module | SymbolFlags.Assignment; 3088 const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment; 3089 namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => { 3090 if (symbol) { 3091 addDeclarationToSymbol(symbol, id, flags); 3092 return symbol; 3093 } 3094 else { 3095 const table = parent ? parent.exports! : 3096 file.jsGlobalAugmentations || (file.jsGlobalAugmentations = createSymbolTable()); 3097 return declareSymbol(table, parent, id, flags, excludeFlags); 3098 } 3099 }); 3100 } 3101 if (containerIsClass && namespaceSymbol && namespaceSymbol.valueDeclaration) { 3102 addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class); 3103 } 3104 return namespaceSymbol; 3105 } 3106 3107 function bindPotentiallyNewExpandoMemberToNamespace(declaration: BindableStaticAccessExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) { 3108 if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) { 3109 return; 3110 } 3111 3112 // Set up the members collection if it doesn't exist already 3113 const symbolTable = isPrototypeProperty ? 3114 (namespaceSymbol.members || (namespaceSymbol.members = createSymbolTable())) : 3115 (namespaceSymbol.exports || (namespaceSymbol.exports = createSymbolTable())); 3116 3117 let includes = SymbolFlags.None; 3118 let excludes = SymbolFlags.None; 3119 // Method-like 3120 if (isFunctionLikeDeclaration(getAssignedExpandoInitializer(declaration)!)) { 3121 includes = SymbolFlags.Method; 3122 excludes = SymbolFlags.MethodExcludes; 3123 } 3124 // Maybe accessor-like 3125 else if (isCallExpression(declaration) && isBindableObjectDefinePropertyCall(declaration)) { 3126 if (some(declaration.arguments[2].properties, p => { 3127 const id = getNameOfDeclaration(p); 3128 return !!id && isIdentifier(id) && idText(id) === "set"; 3129 })) { 3130 // We mix in `SymbolFLags.Property` so in the checker `getTypeOfVariableParameterOrProperty` is used for this 3131 // symbol, instead of `getTypeOfAccessor` (which will assert as there is no real accessor declaration) 3132 includes |= SymbolFlags.SetAccessor | SymbolFlags.Property; 3133 excludes |= SymbolFlags.SetAccessorExcludes; 3134 } 3135 if (some(declaration.arguments[2].properties, p => { 3136 const id = getNameOfDeclaration(p); 3137 return !!id && isIdentifier(id) && idText(id) === "get"; 3138 })) { 3139 includes |= SymbolFlags.GetAccessor | SymbolFlags.Property; 3140 excludes |= SymbolFlags.GetAccessorExcludes; 3141 } 3142 } 3143 3144 if (includes === SymbolFlags.None) { 3145 includes = SymbolFlags.Property; 3146 excludes = SymbolFlags.PropertyExcludes; 3147 } 3148 3149 declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); 3150 } 3151 3152 function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) { 3153 return isBinaryExpression(propertyAccess.parent) 3154 ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile 3155 : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; 3156 } 3157 3158 function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) { 3159 let namespaceSymbol = lookupSymbolForPropertyAccess(name, container) || lookupSymbolForPropertyAccess(name, blockScopeContainer); 3160 const isToplevel = isTopLevelNamespaceAssignment(propertyAccess); 3161 namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty, containerIsClass); 3162 bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty); 3163 } 3164 3165 /** 3166 * Javascript expando values are: 3167 * - Functions 3168 * - classes 3169 * - namespaces 3170 * - variables initialized with function expressions 3171 * - with class expressions 3172 * - with empty object literals 3173 * - with non-empty object literals if assigned to the prototype property 3174 */ 3175 function isExpandoSymbol(symbol: Symbol): boolean { 3176 if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule)) { 3177 return true; 3178 } 3179 const node = symbol.valueDeclaration; 3180 if (node && isCallExpression(node)) { 3181 return !!getAssignedExpandoInitializer(node); 3182 } 3183 let init = !node ? undefined : 3184 isVariableDeclaration(node) ? node.initializer : 3185 isBinaryExpression(node) ? node.right : 3186 isPropertyAccessExpression(node) && isBinaryExpression(node.parent) ? node.parent.right : 3187 undefined; 3188 init = init && getRightMostAssignedExpression(init); 3189 if (init) { 3190 const isPrototypeAssignment = isPrototypeAccess(isVariableDeclaration(node!) ? node.name : isBinaryExpression(node!) ? node.left : node!); 3191 return !!getExpandoInitializer(isBinaryExpression(init) && (init.operatorToken.kind === SyntaxKind.BarBarToken || init.operatorToken.kind === SyntaxKind.QuestionQuestionToken) ? init.right : init, isPrototypeAssignment); 3192 } 3193 return false; 3194 } 3195 3196 function getParentOfBinaryExpression(expr: Node) { 3197 while (isBinaryExpression(expr.parent)) { 3198 expr = expr.parent; 3199 } 3200 return expr.parent; 3201 } 3202 3203 function lookupSymbolForPropertyAccess(node: BindableStaticNameExpression, lookupContainer: Node = container): Symbol | undefined { 3204 if (isIdentifier(node)) { 3205 return lookupSymbolForName(lookupContainer, node.escapedText); 3206 } 3207 else { 3208 const symbol = lookupSymbolForPropertyAccess(node.expression); 3209 return symbol && symbol.exports && symbol.exports.get(getElementOrPropertyAccessName(node)); 3210 } 3211 } 3212 3213 function forEachIdentifierInEntityName(e: BindableStaticNameExpression, parent: Symbol | undefined, action: (e: Declaration, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined { 3214 if (isExportsOrModuleExportsOrAlias(file, e)) { 3215 return file.symbol; 3216 } 3217 else if (isIdentifier(e)) { 3218 return action(e, lookupSymbolForPropertyAccess(e), parent); 3219 } 3220 else { 3221 const s = forEachIdentifierInEntityName(e.expression, parent, action); 3222 const name = getNameOrArgument(e); 3223 // unreachable 3224 if (isPrivateIdentifier(name)) { 3225 Debug.fail("unexpected PrivateIdentifier"); 3226 } 3227 return action(name, s && s.exports && s.exports.get(getElementOrPropertyAccessName(e)), s); 3228 } 3229 } 3230 3231 function bindCallExpression(node: CallExpression) { 3232 // We're only inspecting call expressions to detect CommonJS modules, so we can skip 3233 // this check if we've already seen the module indicator 3234 if (!file.commonJsModuleIndicator && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ false)) { 3235 setCommonJsModuleIndicator(node); 3236 } 3237 } 3238 3239 function bindClassLikeDeclaration(node: ClassLikeDeclaration) { 3240 if (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.StructDeclaration) { 3241 bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes); 3242 } 3243 else { 3244 const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Class; 3245 bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName); 3246 // Add name of class expression into the map for semantic classifier 3247 if (node.name) { 3248 classifiableNames.add(node.name.escapedText); 3249 } 3250 } 3251 3252 const { symbol } = node; 3253 3254 // TypeScript 1.0 spec (April 2014): 8.4 3255 // Every class automatically contains a static property member named 'prototype', the 3256 // type of which is an instantiation of the class type with type Any supplied as a type 3257 // argument for each type parameter. It is an error to explicitly declare a static 3258 // property member with the name 'prototype'. 3259 // 3260 // Note: we check for this here because this class may be merging into a module. The 3261 // module might have an exported variable called 'prototype'. We can't allow that as 3262 // that would clash with the built-in 'prototype' for the class. 3263 const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype" as __String); 3264 const symbolExport = symbol.exports!.get(prototypeSymbol.escapedName); 3265 if (symbolExport) { 3266 if (node.name) { 3267 setParent(node.name, node); 3268 } 3269 file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol))); 3270 } 3271 symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol); 3272 prototypeSymbol.parent = symbol; 3273 } 3274 3275 function bindEnumDeclaration(node: EnumDeclaration) { 3276 return isEnumConst(node) 3277 ? bindBlockScopedDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes) 3278 : bindBlockScopedDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes); 3279 } 3280 3281 function bindVariableDeclarationOrBindingElement(node: VariableDeclaration | BindingElement) { 3282 if (inStrictMode) { 3283 checkStrictModeEvalOrArguments(node, node.name); 3284 } 3285 3286 if (!isBindingPattern(node.name)) { 3287 const possibleVariableDecl = node.kind === SyntaxKind.VariableDeclaration ? node : node.parent.parent; 3288 if (isInJSFile(node) && 3289 isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) && 3290 !getJSDocTypeTag(node) && 3291 !(getCombinedModifierFlags(node) & ModifierFlags.Export) 3292 ) { 3293 declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes); 3294 } 3295 else if (isBlockOrCatchScoped(node)) { 3296 bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); 3297 } 3298 else if (isParameterDeclaration(node)) { 3299 // It is safe to walk up parent chain to find whether the node is a destructuring parameter declaration 3300 // because its parent chain has already been set up, since parents are set before descending into children. 3301 // 3302 // If node is a binding element in parameter declaration, we need to use ParameterExcludes. 3303 // Using ParameterExcludes flag allows the compiler to report an error on duplicate identifiers in Parameter Declaration 3304 // For example: 3305 // function foo([a,a]) {} // Duplicate Identifier error 3306 // function bar(a,a) {} // Duplicate Identifier error, parameter declaration in this case is handled in bindParameter 3307 // // which correctly set excluded symbols 3308 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); 3309 } 3310 else { 3311 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes); 3312 } 3313 } 3314 } 3315 3316 function bindParameter(node: ParameterDeclaration | JSDocParameterTag) { 3317 if (node.kind === SyntaxKind.JSDocParameterTag && container.kind !== SyntaxKind.JSDocSignature) { 3318 return; 3319 } 3320 if (inStrictMode && !(node.flags & NodeFlags.Ambient)) { 3321 // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a 3322 // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) 3323 checkStrictModeEvalOrArguments(node, node.name); 3324 } 3325 3326 if (isBindingPattern(node.name)) { 3327 bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + (node as ParameterDeclaration).parent.parameters.indexOf(node as ParameterDeclaration) as __String); 3328 } 3329 else { 3330 declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); 3331 } 3332 3333 // If this is a property-parameter, then also declare the property symbol into the 3334 // containing class. 3335 if (isParameterPropertyDeclaration(node, node.parent)) { 3336 const classDeclaration = node.parent.parent; 3337 declareSymbol(classDeclaration.symbol.members!, classDeclaration.symbol, node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); 3338 } 3339 } 3340 3341 function bindFunctionDeclaration(node: FunctionDeclaration) { 3342 if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) { 3343 if (isAsyncFunction(node)) { 3344 emitFlags |= NodeFlags.HasAsyncFunctions; 3345 } 3346 } 3347 3348 checkStrictModeFunctionName(node); 3349 if (inStrictMode) { 3350 checkStrictModeFunctionDeclaration(node); 3351 bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); 3352 } 3353 else { 3354 declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); 3355 } 3356 } 3357 3358 function bindFunctionExpression(node: FunctionExpression) { 3359 if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient)) { 3360 if (isAsyncFunction(node)) { 3361 emitFlags |= NodeFlags.HasAsyncFunctions; 3362 } 3363 } 3364 if (currentFlow) { 3365 node.flowNode = currentFlow; 3366 } 3367 checkStrictModeFunctionName(node); 3368 const bindingName = node.name ? node.name.escapedText : InternalSymbolName.Function; 3369 return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); 3370 } 3371 3372 function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { 3373 if (!file.isDeclarationFile && !(node.flags & NodeFlags.Ambient) && isAsyncFunction(node)) { 3374 emitFlags |= NodeFlags.HasAsyncFunctions; 3375 } 3376 3377 if (currentFlow && isObjectLiteralOrClassExpressionMethodOrAccessor(node)) { 3378 node.flowNode = currentFlow; 3379 } 3380 3381 return hasDynamicName(node) 3382 ? bindAnonymousDeclaration(node, symbolFlags, InternalSymbolName.Computed) 3383 : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); 3384 } 3385 3386 function getInferTypeContainer(node: Node): ConditionalTypeNode | undefined { 3387 const extendsType = findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && n.parent.extendsType === n); 3388 return extendsType && extendsType.parent as ConditionalTypeNode; 3389 } 3390 3391 function bindTypeParameter(node: TypeParameterDeclaration) { 3392 if (isJSDocTemplateTag(node.parent)) { 3393 const container = getEffectiveContainerForJSDocTemplateTag(node.parent); 3394 if (container) { 3395 if (!container.locals) { 3396 container.locals = createSymbolTable(); 3397 } 3398 declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3399 } 3400 else { 3401 declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3402 } 3403 } 3404 else if (node.parent.kind === SyntaxKind.InferType) { 3405 const container = getInferTypeContainer(node.parent); 3406 if (container) { 3407 if (!container.locals) { 3408 container.locals = createSymbolTable(); 3409 } 3410 declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3411 } 3412 else { 3413 bindAnonymousDeclaration(node, SymbolFlags.TypeParameter, getDeclarationName(node)!); // TODO: GH#18217 3414 } 3415 } 3416 else { 3417 declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); 3418 } 3419 } 3420 3421 // reachability checks 3422 3423 function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean { 3424 const instanceState = getModuleInstanceState(node); 3425 return instanceState === ModuleInstanceState.Instantiated || (instanceState === ModuleInstanceState.ConstEnumOnly && shouldPreserveConstEnums(options)); 3426 } 3427 3428 function checkUnreachable(node: Node): boolean { 3429 if (!(currentFlow.flags & FlowFlags.Unreachable)) { 3430 return false; 3431 } 3432 if (currentFlow === unreachableFlow) { 3433 const reportError = 3434 // report error on all statements except empty ones 3435 (isStatementButNotDeclaration(node) && node.kind !== SyntaxKind.EmptyStatement) || 3436 // report error on class declarations 3437 node.kind === SyntaxKind.ClassDeclaration || 3438 // report error on instantiated modules or const-enums only modules if preserveConstEnums is set 3439 (node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(node as ModuleDeclaration)); 3440 3441 if (reportError) { 3442 currentFlow = reportedUnreachableFlow; 3443 3444 if (!options.allowUnreachableCode) { 3445 // unreachable code is reported if 3446 // - user has explicitly asked about it AND 3447 // - statement is in not ambient context (statements in ambient context is already an error 3448 // so we should not report extras) AND 3449 // - node is not variable statement OR 3450 // - node is block scoped variable statement OR 3451 // - node is not block scoped variable statement and at least one variable declaration has initializer 3452 // Rationale: we don't want to report errors on non-initialized var's since they are hoisted 3453 // On the other side we do want to report errors on non-initialized 'lets' because of TDZ 3454 const isError = 3455 unreachableCodeIsError(options) && 3456 !(node.flags & NodeFlags.Ambient) && 3457 ( 3458 !isVariableStatement(node) || 3459 !!(getCombinedNodeFlags(node.declarationList) & NodeFlags.BlockScoped) || 3460 node.declarationList.declarations.some(d => !!d.initializer) 3461 ); 3462 3463 eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected)); 3464 } 3465 } 3466 } 3467 return true; 3468 } 3469 } 3470 3471 function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void { 3472 if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) { 3473 const { statements } = node.parent; 3474 const slice = sliceAfter(statements, node); 3475 getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1])); 3476 } 3477 else { 3478 cb(node, node); 3479 } 3480 } 3481 // As opposed to a pure declaration like an `interface` 3482 function isExecutableStatement(s: Statement): boolean { 3483 // Don't remove statements that can validly be used before they appear. 3484 return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) && !isEnumDeclaration(s) && 3485 // `var x;` may declare a variable used above 3486 !(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer)); 3487 } 3488 3489 function isPurelyTypeDeclaration(s: Statement): boolean { 3490 switch (s.kind) { 3491 case SyntaxKind.InterfaceDeclaration: 3492 case SyntaxKind.TypeAliasDeclaration: 3493 return true; 3494 case SyntaxKind.ModuleDeclaration: 3495 return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated; 3496 case SyntaxKind.EnumDeclaration: 3497 return hasSyntacticModifier(s, ModifierFlags.Const); 3498 default: 3499 return false; 3500 } 3501 } 3502 3503 export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean { 3504 let i = 0; 3505 const q = createQueue<Expression>(); 3506 q.enqueue(node); 3507 while (!q.isEmpty() && i < 100) { 3508 i++; 3509 node = q.dequeue(); 3510 if (isExportsIdentifier(node) || isModuleExportsAccessExpression(node)) { 3511 return true; 3512 } 3513 else if (isIdentifier(node)) { 3514 const symbol = lookupSymbolForName(sourceFile, node.escapedText); 3515 if (!!symbol && !!symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) && !!symbol.valueDeclaration.initializer) { 3516 const init = symbol.valueDeclaration.initializer; 3517 q.enqueue(init); 3518 if (isAssignmentExpression(init, /*excludeCompoundAssignment*/ true)) { 3519 q.enqueue(init.left); 3520 q.enqueue(init.right); 3521 } 3522 } 3523 } 3524 } 3525 return false; 3526 } 3527 3528 function lookupSymbolForName(container: Node, name: __String): Symbol | undefined { 3529 const local = container.locals && container.locals.get(name); 3530 if (local) { 3531 return local.exportSymbol || local; 3532 } 3533 if (isSourceFile(container) && container.jsGlobalAugmentations && container.jsGlobalAugmentations.has(name)) { 3534 return container.jsGlobalAugmentations.get(name); 3535 } 3536 return container.symbol && container.symbol.exports && container.symbol.exports.get(name); 3537 } 3538} 3539