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