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