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