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