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