• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts {
3    export const resolvingEmptyArray: never[] = [];
4
5    export const externalHelpersModuleNameText = "tslib";
6
7    export const defaultMaximumTruncationLength = 160;
8    export const noTruncationMaximumTruncationLength = 1_000_000;
9
10    export function getDeclarationOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T | undefined {
11        const declarations = symbol.declarations;
12        if (declarations) {
13            for (const declaration of declarations) {
14                if (declaration.kind === kind) {
15                    return declaration as T;
16                }
17            }
18        }
19
20        return undefined;
21    }
22
23    export function getDeclarationsOfKind<T extends Declaration>(symbol: Symbol, kind: T["kind"]): T[] {
24        return filter(symbol.declarations || emptyArray, d => d.kind === kind) as T[];
25    }
26
27    export function createSymbolTable(symbols?: readonly Symbol[]): SymbolTable {
28        const result = new Map<__String, Symbol>();
29        if (symbols) {
30            for (const symbol of symbols) {
31                result.set(symbol.escapedName, symbol);
32            }
33        }
34        return result;
35    }
36
37    export function isTransientSymbol(symbol: Symbol): symbol is TransientSymbol {
38        return (symbol.flags & SymbolFlags.Transient) !== 0;
39    }
40
41    const stringWriter = createSingleLineStringWriter();
42
43    function createSingleLineStringWriter(): EmitTextWriter {
44        let str = "";
45        const writeText: (text: string) => void = text => str += text;
46        return {
47            getText: () => str,
48            write: writeText,
49            rawWrite: writeText,
50            writeKeyword: writeText,
51            writeOperator: writeText,
52            writePunctuation: writeText,
53            writeSpace: writeText,
54            writeStringLiteral: writeText,
55            writeLiteral: writeText,
56            writeParameter: writeText,
57            writeProperty: writeText,
58            writeSymbol: (s, _) => writeText(s),
59            writeTrailingSemicolon: writeText,
60            writeComment: writeText,
61            getTextPos: () => str.length,
62            getLine: () => 0,
63            getColumn: () => 0,
64            getIndent: () => 0,
65            isAtStartOfLine: () => false,
66            hasTrailingComment: () => false,
67            hasTrailingWhitespace: () => !!str.length && isWhiteSpaceLike(str.charCodeAt(str.length - 1)),
68
69            // Completely ignore indentation for string writers.  And map newlines to
70            // a single space.
71            writeLine: () => str += " ",
72            increaseIndent: noop,
73            decreaseIndent: noop,
74            clear: () => str = "",
75            trackSymbol: () => false,
76            reportInaccessibleThisError: noop,
77            reportInaccessibleUniqueSymbolError: noop,
78            reportPrivateInBaseOfClassExpression: noop,
79        };
80    }
81
82    export function changesAffectModuleResolution(oldOptions: CompilerOptions, newOptions: CompilerOptions): boolean {
83        return oldOptions.configFilePath !== newOptions.configFilePath ||
84            optionsHaveModuleResolutionChanges(oldOptions, newOptions);
85    }
86
87    export function optionsHaveModuleResolutionChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions) {
88        return optionsHaveChanges(oldOptions, newOptions, moduleResolutionOptionDeclarations);
89    }
90
91    export function changesAffectingProgramStructure(oldOptions: CompilerOptions, newOptions: CompilerOptions) {
92        return optionsHaveChanges(oldOptions, newOptions, optionsAffectingProgramStructure);
93    }
94
95    export function optionsHaveChanges(oldOptions: CompilerOptions, newOptions: CompilerOptions, optionDeclarations: readonly CommandLineOption[]) {
96        return oldOptions !== newOptions && optionDeclarations.some(o =>
97            !isJsonEqual(getCompilerOptionValue(oldOptions, o), getCompilerOptionValue(newOptions, o)));
98    }
99
100    export function forEachAncestor<T>(node: Node, callback: (n: Node) => T | undefined | "quit"): T | undefined {
101        while (true) {
102            const res = callback(node);
103            if (res === "quit") return undefined;
104            if (res !== undefined) return res;
105            if (isSourceFile(node)) return undefined;
106            node = node.parent;
107        }
108    }
109
110    /**
111     * Calls `callback` for each entry in the map, returning the first truthy result.
112     * Use `map.forEach` instead for normal iteration.
113     */
114    export function forEachEntry<K, V, U>(map: ReadonlyESMap<K, V>, callback: (value: V, key: K) => U | undefined): U | undefined {
115        const iterator = map.entries();
116        for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) {
117            const [key, value] = iterResult.value;
118            const result = callback(value, key);
119            if (result) {
120                return result;
121            }
122        }
123        return undefined;
124    }
125
126    /** `forEachEntry` for just keys. */
127    export function forEachKey<K, T>(map: ReadonlyCollection<K>, callback: (key: K) => T | undefined): T | undefined {
128        const iterator = map.keys();
129        for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) {
130            const result = callback(iterResult.value);
131            if (result) {
132                return result;
133            }
134        }
135        return undefined;
136    }
137
138    /** Copy entries from `source` to `target`. */
139    export function copyEntries<K, V>(source: ReadonlyESMap<K, V>, target: ESMap<K, V>): void {
140        source.forEach((value, key) => {
141            target.set(key, value);
142        });
143    }
144
145    export function usingSingleLineStringWriter(action: (writer: EmitTextWriter) => void): string {
146        const oldString = stringWriter.getText();
147        try {
148            action(stringWriter);
149            return stringWriter.getText();
150        }
151        finally {
152            stringWriter.clear();
153            stringWriter.writeKeyword(oldString);
154        }
155    }
156
157    export function getFullWidth(node: Node) {
158        return node.end - node.pos;
159    }
160
161    export function getResolvedModule(sourceFile: SourceFile | undefined, moduleNameText: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): ResolvedModuleFull | undefined {
162        return sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText, mode);
163    }
164
165    export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull | undefined, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): void {
166        if (!sourceFile.resolvedModules) {
167            sourceFile.resolvedModules = createModeAwareCache();
168        }
169
170        sourceFile.resolvedModules.set(moduleNameText, mode, resolvedModule);
171    }
172
173    export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective?: ResolvedTypeReferenceDirective): void {
174        if (!sourceFile.resolvedTypeReferenceDirectiveNames) {
175            sourceFile.resolvedTypeReferenceDirectiveNames = createModeAwareCache();
176        }
177
178        sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, /*mode*/ undefined, resolvedTypeReferenceDirective);
179    }
180
181    export function projectReferenceIsEqualTo(oldRef: ProjectReference, newRef: ProjectReference) {
182        return oldRef.path === newRef.path &&
183            !oldRef.prepend === !newRef.prepend &&
184            !oldRef.circular === !newRef.circular;
185    }
186
187    export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean {
188        return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport &&
189            oldResolution.extension === newResolution.extension &&
190            oldResolution.resolvedFileName === newResolution.resolvedFileName &&
191            oldResolution.originalPath === newResolution.originalPath &&
192            packageIdIsEqual(oldResolution.packageId, newResolution.packageId);
193    }
194
195    function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
196        return a === b || !!a && !!b && a.name === b.name && a.subModuleName === b.subModuleName && a.version === b.version;
197    }
198
199    export function packageIdToPackageName({ name, subModuleName }: PackageId): string {
200        return subModuleName ? `${name}/${subModuleName}` : name;
201    }
202
203    export function packageIdToString(packageId: PackageId): string {
204        return `${packageIdToPackageName(packageId)}@${packageId.version}`;
205    }
206
207    export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
208        return oldResolution.resolvedFileName === newResolution.resolvedFileName
209            && oldResolution.primary === newResolution.primary
210            && oldResolution.originalPath === newResolution.originalPath;
211    }
212
213    export function hasChangesInResolutions<T>(
214        names: readonly string[] | readonly FileReference[],
215        newResolutions: readonly T[],
216        oldResolutions: ModeAwareCache<T> | undefined,
217        oldSourceFile: SourceFile | undefined,
218        comparer: (oldResolution: T, newResolution: T) => boolean): boolean {
219        Debug.assert(names.length === newResolutions.length);
220
221        for (let i = 0; i < names.length; i++) {
222            const newResolution = newResolutions[i];
223            const entry = names[i];
224            // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
225            const name = !isString(entry) ? entry.fileName.toLowerCase() : entry;
226            const mode = !isString(entry) ? getModeForFileReference(entry, oldSourceFile?.impliedNodeFormat) : oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, i);
227            const oldResolution = oldResolutions && oldResolutions.get(name, mode);
228            const changed =
229                oldResolution
230                    ? !newResolution || !comparer(oldResolution, newResolution)
231                    : newResolution;
232            if (changed) {
233                return true;
234            }
235        }
236        return false;
237    }
238
239    // Returns true if this node contains a parse error anywhere underneath it.
240    export function containsParseError(node: Node): boolean {
241        aggregateChildData(node);
242        return (node.flags & NodeFlags.ThisNodeOrAnySubNodesHasError) !== 0;
243    }
244
245    function aggregateChildData(node: Node): void {
246        if (!(node.flags & NodeFlags.HasAggregatedChildData)) {
247            // A node is considered to contain a parse error if:
248            //  a) the parser explicitly marked that it had an error
249            //  b) any of it's children reported that it had an error.
250            const thisNodeOrAnySubNodesHasError = ((node.flags & NodeFlags.ThisNodeHasError) !== 0) ||
251                forEachChild(node, containsParseError);
252
253            // If so, mark ourselves accordingly.
254            if (thisNodeOrAnySubNodesHasError) {
255                (node as Mutable<Node>).flags |= NodeFlags.ThisNodeOrAnySubNodesHasError;
256            }
257
258            // Also mark that we've propagated the child information to this node.  This way we can
259            // always consult the bit directly on this node without needing to check its children
260            // again.
261            (node as Mutable<Node>).flags |= NodeFlags.HasAggregatedChildData;
262        }
263    }
264
265    export function getSourceFileOfNode(node: Node): SourceFile;
266    export function getSourceFileOfNode(node: Node | undefined): SourceFile | undefined;
267    export function getSourceFileOfNode(node: Node): SourceFile {
268        while (node && node.kind !== SyntaxKind.SourceFile) {
269            node = node.parent;
270        }
271        return node as SourceFile;
272    }
273
274    export function getSourceFileOfModule(module: Symbol) {
275        return getSourceFileOfNode(module.valueDeclaration || getNonAugmentationDeclaration(module));
276    }
277
278    export function isPlainJsFile(file: SourceFile | undefined, checkJs: boolean | undefined): boolean {
279        return !!file && (file.scriptKind === ScriptKind.JS || file.scriptKind === ScriptKind.JSX) && !file.checkJsDirective && checkJs === undefined;
280    }
281
282    export function isStatementWithLocals(node: Node) {
283        switch (node.kind) {
284            case SyntaxKind.Block:
285            case SyntaxKind.CaseBlock:
286            case SyntaxKind.ForStatement:
287            case SyntaxKind.ForInStatement:
288            case SyntaxKind.ForOfStatement:
289                return true;
290        }
291        return false;
292    }
293
294    export function getStartPositionOfLine(line: number, sourceFile: SourceFileLike): number {
295        Debug.assert(line >= 0);
296        return getLineStarts(sourceFile)[line];
297    }
298
299    // This is a useful function for debugging purposes.
300    export function nodePosToString(node: Node): string {
301        const file = getSourceFileOfNode(node);
302        const loc = getLineAndCharacterOfPosition(file, node.pos);
303        return `${file.fileName}(${loc.line + 1},${loc.character + 1})`;
304    }
305
306    export function getEndLinePosition(line: number, sourceFile: SourceFileLike): number {
307        Debug.assert(line >= 0);
308        const lineStarts = getLineStarts(sourceFile);
309
310        const lineIndex = line;
311        const sourceText = sourceFile.text;
312        if (lineIndex + 1 === lineStarts.length) {
313            // last line - return EOF
314            return sourceText.length - 1;
315        }
316        else {
317            // current line start
318            const start = lineStarts[lineIndex];
319            // take the start position of the next line - 1 = it should be some line break
320            let pos = lineStarts[lineIndex + 1] - 1;
321            Debug.assert(isLineBreak(sourceText.charCodeAt(pos)));
322            // walk backwards skipping line breaks, stop the the beginning of current line.
323            // i.e:
324            // <some text>
325            // $ <- end of line for this position should match the start position
326            while (start <= pos && isLineBreak(sourceText.charCodeAt(pos))) {
327                pos--;
328            }
329            return pos;
330        }
331    }
332
333    /**
334     * Returns a value indicating whether a name is unique globally or within the current file.
335     * Note: This does not consider whether a name appears as a free identifier or not, so at the expression `x.y` this includes both `x` and `y`.
336     */
337    export function isFileLevelUniqueName(sourceFile: SourceFile, name: string, hasGlobalName?: PrintHandlers["hasGlobalName"]): boolean {
338        return !(hasGlobalName && hasGlobalName(name)) && !sourceFile.identifiers.has(name);
339    }
340
341    // Returns true if this node is missing from the actual source code. A 'missing' node is different
342    // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes
343    // in the tree), it is definitely missing. However, a node may be defined, but still be
344    // missing.  This happens whenever the parser knows it needs to parse something, but can't
345    // get anything in the source code that it expects at that location. For example:
346    //
347    //          let a: ;
348    //
349    // Here, the Type in the Type-Annotation is not-optional (as there is a colon in the source
350    // code). So the parser will attempt to parse out a type, and will create an actual node.
351    // However, this node will be 'missing' in the sense that no actual source-code/tokens are
352    // contained within it.
353    export function nodeIsMissing(node: Node | undefined): boolean {
354        if (node === undefined) {
355            return true;
356        }
357
358        // if node type is virtual, do not judge position
359        if(node.virtual){
360            return false;
361        }
362
363        return node.pos === node.end && node.pos >= 0 && node.kind !== SyntaxKind.EndOfFileToken;
364    }
365
366    export function nodeIsPresent(node: Node | undefined): boolean {
367        return !nodeIsMissing(node);
368    }
369
370    function insertStatementsAfterPrologue<T extends Statement>(to: T[], from: readonly T[] | undefined, isPrologueDirective: (node: Node) => boolean): T[] {
371        if (from === undefined || from.length === 0) return to;
372        let statementIndex = 0;
373        // skip all prologue directives to insert at the correct position
374        for (; statementIndex < to.length; ++statementIndex) {
375            if (!isPrologueDirective(to[statementIndex])) {
376                break;
377            }
378        }
379        to.splice(statementIndex, 0, ...from);
380        return to;
381    }
382
383    function insertStatementAfterPrologue<T extends Statement>(to: T[], statement: T | undefined, isPrologueDirective: (node: Node) => boolean): T[] {
384        if (statement === undefined) return to;
385        let statementIndex = 0;
386        // skip all prologue directives to insert at the correct position
387        for (; statementIndex < to.length; ++statementIndex) {
388            if (!isPrologueDirective(to[statementIndex])) {
389                break;
390            }
391        }
392        to.splice(statementIndex, 0, statement);
393        return to;
394    }
395
396
397    function isAnyPrologueDirective(node: Node) {
398        return isPrologueDirective(node) || !!(getEmitFlags(node) & EmitFlags.CustomPrologue);
399    }
400
401    /**
402     * Prepends statements to an array while taking care of prologue directives.
403     */
404    export function insertStatementsAfterStandardPrologue<T extends Statement>(to: T[], from: readonly T[] | undefined): T[] {
405        return insertStatementsAfterPrologue(to, from, isPrologueDirective);
406    }
407
408    export function insertStatementsAfterCustomPrologue<T extends Statement>(to: T[], from: readonly T[] | undefined): T[] {
409        return insertStatementsAfterPrologue(to, from, isAnyPrologueDirective);
410    }
411
412    /**
413     * Prepends statements to an array while taking care of prologue directives.
414     */
415    export function insertStatementAfterStandardPrologue<T extends Statement>(to: T[], statement: T | undefined): T[] {
416        return insertStatementAfterPrologue(to, statement, isPrologueDirective);
417    }
418
419    export function insertStatementAfterCustomPrologue<T extends Statement>(to: T[], statement: T | undefined): T[] {
420        return insertStatementAfterPrologue(to, statement, isAnyPrologueDirective);
421    }
422
423    export function getEtsLibs(program: TypeCheckerHost): string[] {
424        const etsComponentsLibNames: string[] = [];
425        const etsComponentsLib = program.getCompilerOptions().ets?.libs ?? [];
426        if (etsComponentsLib.length) {
427            forEach(etsComponentsLib, libFileName => {
428                etsComponentsLibNames.push(sys.resolvePath(libFileName));
429                const sourceFile = getSourceFileByName(program, libFileName);
430                forEach(sourceFile?.referencedFiles, ref => {
431                    const referencedFileName = sys.resolvePath(resolveTripleslashReference(ref.fileName, sourceFile!.fileName));
432                    etsComponentsLibNames.push(referencedFileName);
433                });
434            });
435        }
436        return etsComponentsLibNames;
437    }
438
439    function getSourceFileByName(program: TypeCheckerHost, libFileName: string): SourceFile | undefined {
440        if(!libFileName) {
441            return undefined;
442        }
443
444        const originFileName = sys.resolvePath(libFileName);
445        for (const file of program.getSourceFiles()) {
446            if(!file.fileName) {
447                continue;
448            }
449            const sourceFileName = sys.resolvePath(file.fileName);
450            if (sourceFileName === originFileName) {
451                return file;
452            }
453        }
454        return undefined;
455    }
456
457    /**
458     * Determine if the given comment is a triple-slash
459     *
460     * @return true if the comment is a triple-slash comment else false
461     */
462    export function isRecognizedTripleSlashComment(text: string, commentPos: number, commentEnd: number) {
463        // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
464        // so that we don't end up computing comment string and doing match for all // comments
465        if (text.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
466            commentPos + 2 < commentEnd &&
467            text.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
468            const textSubStr = text.substring(commentPos, commentEnd);
469            return fullTripleSlashReferencePathRegEx.test(textSubStr) ||
470                fullTripleSlashAMDReferencePathRegEx.test(textSubStr) ||
471                fullTripleSlashReferenceTypeReferenceDirectiveRegEx.test(textSubStr) ||
472                defaultLibReferenceRegEx.test(textSubStr) ?
473                true : false;
474        }
475        return false;
476    }
477
478    export function isPinnedComment(text: string, start: number) {
479        return text.charCodeAt(start + 1) === CharacterCodes.asterisk &&
480            text.charCodeAt(start + 2) === CharacterCodes.exclamation;
481    }
482
483    export function createCommentDirectivesMap(sourceFile: SourceFile, commentDirectives: CommentDirective[]): CommentDirectivesMap {
484        const directivesByLine = new Map(
485            commentDirectives.map(commentDirective => ([
486                `${getLineAndCharacterOfPosition(sourceFile, commentDirective.range.end).line}`,
487                commentDirective,
488            ]))
489        );
490
491        const usedLines = new Map<string, boolean>();
492
493        return { getUnusedExpectations, markUsed };
494
495        function getUnusedExpectations() {
496            return arrayFrom(directivesByLine.entries())
497                .filter(([line, directive]) => directive.type === CommentDirectiveType.ExpectError && !usedLines.get(line))
498                .map(([_, directive]) => directive);
499        }
500
501        function markUsed(line: number) {
502            if (!directivesByLine.has(`${line}`)) {
503                return false;
504            }
505
506            usedLines.set(`${line}`, true);
507            return true;
508        }
509    }
510
511    export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: boolean): number {
512        // With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
513        // want to skip trivia because this will launch us forward to the next token.
514        if (nodeIsMissing(node)) {
515            return node.pos;
516        }
517
518        if (isJSDocNode(node) || node.kind === SyntaxKind.JsxText) {
519            // JsxText cannot actually contain comments, even though the scanner will think it sees comments
520            return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
521        }
522
523        if (includeJsDoc && hasJSDocNodes(node)) {
524            return getTokenPosOfNode(node.jsDoc![0], sourceFile);
525        }
526
527        // For a syntax list, it is possible that one of its children has JSDocComment nodes, while
528        // the syntax list itself considers them as normal trivia. Therefore if we simply skip
529        // trivia for the list, we may have skipped the JSDocComment as well. So we should process its
530        // first child to determine the actual position of its first token.
531        if (node.kind === SyntaxKind.SyntaxList && (node as SyntaxList)._children.length > 0) {
532            return getTokenPosOfNode((node as SyntaxList)._children[0], sourceFile, includeJsDoc);
533        }
534
535        return node.virtual ? node.pos : skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false,
536          /*stopAtComments*/ false, isInJSDoc(node));
537    }
538
539    export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFileLike): number {
540        const lastDecorator = !nodeIsMissing(node) && canHaveModifiers(node) ? findLast(node.modifiers, isDecorator) : undefined;
541        if (!lastDecorator) {
542            return getTokenPosOfNode(node, sourceFile);
543        }
544
545        return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, lastDecorator.end);
546    }
547
548    export function getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node: Node, includeTrivia = false): string {
549        return getTextOfNodeFromSourceText(sourceFile.text, node, includeTrivia);
550    }
551
552    function isJSDocTypeExpressionOrChild(node: Node): boolean {
553        return !!findAncestor(node, isJSDocTypeExpression);
554    }
555
556    export function isExportNamespaceAsDefaultDeclaration(node: Node): boolean {
557        return !!(isExportDeclaration(node) && node.exportClause && isNamespaceExport(node.exportClause) && node.exportClause.name.escapedText === "default");
558    }
559
560    export function getTextOfNodeFromSourceText(sourceText: string, node: Node, includeTrivia = false): string {
561        if (nodeIsMissing(node)) {
562            return "";
563        }
564
565        let text = sourceText.substring(includeTrivia ? node.pos : skipTrivia(sourceText, node.pos), node.end);
566
567        if (isJSDocTypeExpressionOrChild(node)) {
568            // strip space + asterisk at line start
569            text = text.split(/\r\n|\n|\r/).map(line => trimStringStart(line.replace(/^\s*\*/, ""))).join("\n");
570        }
571
572        return text;
573    }
574
575    export function getTextOfNode(node: Node, includeTrivia = false): string {
576        return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
577    }
578
579    function getPos(range: Node) {
580        return range.pos;
581    }
582
583    /**
584     * Note: it is expected that the `nodeArray` and the `node` are within the same file.
585     * For example, searching for a `SourceFile` in a `SourceFile[]` wouldn't work.
586     */
587    export function indexOfNode(nodeArray: readonly Node[], node: Node) {
588        return binarySearch(nodeArray, node, getPos, compareValues);
589    }
590
591    /**
592     * Gets flags that control emit behavior of a node.
593     */
594    export function getEmitFlags(node: Node): EmitFlags {
595        const emitNode = node.emitNode;
596        return emitNode && emitNode.flags || 0;
597    }
598
599    interface ScriptTargetFeatures {
600        [key: string]: { [key: string]: string[] | undefined };
601    }
602
603    export function getScriptTargetFeatures(): ScriptTargetFeatures {
604        return {
605            es2015: {
606                Array: ["find", "findIndex", "fill", "copyWithin", "entries", "keys", "values"],
607                RegExp: ["flags", "sticky", "unicode"],
608                Reflect: ["apply", "construct", "defineProperty", "deleteProperty", "get"," getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"],
609                ArrayConstructor: ["from", "of"],
610                ObjectConstructor: ["assign", "getOwnPropertySymbols", "keys", "is", "setPrototypeOf"],
611                NumberConstructor: ["isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt"],
612                Math: ["clz32", "imul", "sign", "log10", "log2", "log1p", "expm1", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "hypot", "trunc", "fround", "cbrt"],
613                Map: ["entries", "keys", "values"],
614                Set: ["entries", "keys", "values"],
615                Promise: emptyArray,
616                PromiseConstructor: ["all", "race", "reject", "resolve"],
617                Symbol: ["for", "keyFor"],
618                WeakMap: ["entries", "keys", "values"],
619                WeakSet: ["entries", "keys", "values"],
620                Iterator: emptyArray,
621                AsyncIterator: emptyArray,
622                String: ["codePointAt", "includes", "endsWith", "normalize", "repeat", "startsWith", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "sup"],
623                StringConstructor: ["fromCodePoint", "raw"]
624            },
625            es2016: {
626                Array: ["includes"]
627            },
628            es2017: {
629                Atomics: emptyArray,
630                SharedArrayBuffer: emptyArray,
631                String: ["padStart", "padEnd"],
632                ObjectConstructor: ["values", "entries", "getOwnPropertyDescriptors"],
633                DateTimeFormat: ["formatToParts"]
634            },
635            es2018: {
636                Promise: ["finally"],
637                RegExpMatchArray: ["groups"],
638                RegExpExecArray: ["groups"],
639                RegExp: ["dotAll"],
640                Intl: ["PluralRules"],
641                AsyncIterable: emptyArray,
642                AsyncIterableIterator: emptyArray,
643                AsyncGenerator: emptyArray,
644                AsyncGeneratorFunction: emptyArray,
645                NumberFormat: ["formatToParts"]
646            },
647            es2019: {
648                Array: ["flat", "flatMap"],
649                ObjectConstructor: ["fromEntries"],
650                String: ["trimStart", "trimEnd", "trimLeft", "trimRight"],
651                Symbol: ["description"]
652            },
653            es2020: {
654                BigInt: emptyArray,
655                BigInt64Array: emptyArray,
656                BigUint64Array: emptyArray,
657                PromiseConstructor: ["allSettled"],
658                SymbolConstructor: ["matchAll"],
659                String: ["matchAll"],
660                DataView: ["setBigInt64", "setBigUint64", "getBigInt64", "getBigUint64"],
661                RelativeTimeFormat: ["format", "formatToParts", "resolvedOptions"]
662            },
663            es2021: {
664                PromiseConstructor: ["any"],
665                String: ["replaceAll"]
666            },
667            es2022: {
668                Array: ["at"],
669                String: ["at"],
670                Int8Array: ["at"],
671                Uint8Array: ["at"],
672                Uint8ClampedArray: ["at"],
673                Int16Array: ["at"],
674                Uint16Array: ["at"],
675                Int32Array: ["at"],
676                Uint32Array: ["at"],
677                Float32Array: ["at"],
678                Float64Array: ["at"],
679                BigInt64Array: ["at"],
680                BigUint64Array: ["at"],
681                ObjectConstructor: ["hasOwn"],
682                Error: ["cause"]
683            }
684        };
685    }
686
687    export const enum GetLiteralTextFlags {
688        None = 0,
689        NeverAsciiEscape = 1 << 0,
690        JsxAttributeEscape = 1 << 1,
691        TerminateUnterminatedLiterals = 1 << 2,
692        AllowNumericSeparator = 1 << 3
693    }
694
695    export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile | undefined, flags: GetLiteralTextFlags) {
696        // If we don't need to downlevel and we can reach the original source text using
697        // the node's parent reference, then simply get the text as it was originally written.
698        if (sourceFile && canUseOriginalText(node, flags)) {
699            return getSourceTextOfNodeFromSourceFile(sourceFile, node);
700        }
701
702        // If we can't reach the original source text, use the canonical form if it's a number,
703        // or a (possibly escaped) quoted form of the original text if it's string-like.
704        switch (node.kind) {
705            case SyntaxKind.StringLiteral: {
706                const escapeText = flags & GetLiteralTextFlags.JsxAttributeEscape ? escapeJsxAttributeString :
707                    flags & GetLiteralTextFlags.NeverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString :
708                    escapeNonAsciiString;
709                if ((node as StringLiteral).singleQuote) {
710                    return "'" + escapeText(node.text, CharacterCodes.singleQuote) + "'";
711                }
712                else {
713                    return '"' + escapeText(node.text, CharacterCodes.doubleQuote) + '"';
714                }
715            }
716            case SyntaxKind.NoSubstitutionTemplateLiteral:
717            case SyntaxKind.TemplateHead:
718            case SyntaxKind.TemplateMiddle:
719            case SyntaxKind.TemplateTail: {
720                // If a NoSubstitutionTemplateLiteral appears to have a substitution in it, the original text
721                // had to include a backslash: `not \${a} substitution`.
722                const escapeText = flags & GetLiteralTextFlags.NeverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ? escapeString :
723                    escapeNonAsciiString;
724
725                const rawText = (node as TemplateLiteralLikeNode).rawText ?? escapeTemplateSubstitution(escapeText(node.text, CharacterCodes.backtick));
726                switch (node.kind) {
727                    case SyntaxKind.NoSubstitutionTemplateLiteral:
728                        return "`" + rawText + "`";
729                    case SyntaxKind.TemplateHead:
730                        return "`" + rawText + "${";
731                    case SyntaxKind.TemplateMiddle:
732                        return "}" + rawText + "${";
733                    case SyntaxKind.TemplateTail:
734                        return "}" + rawText + "`";
735                }
736            }
737            case SyntaxKind.NumericLiteral:
738            case SyntaxKind.BigIntLiteral:
739                return node.text;
740            case SyntaxKind.RegularExpressionLiteral:
741                if (flags & GetLiteralTextFlags.TerminateUnterminatedLiterals && node.isUnterminated) {
742                    return node.text + (node.text.charCodeAt(node.text.length - 1) === CharacterCodes.backslash ? " /" : "/");
743                }
744                return node.text;
745        }
746
747        return Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
748    }
749
750    function canUseOriginalText(node: LiteralLikeNode, flags: GetLiteralTextFlags): boolean {
751        if (nodeIsSynthesized(node) || !node.parent || (flags & GetLiteralTextFlags.TerminateUnterminatedLiterals && node.isUnterminated)) {
752            return false;
753        }
754
755        if (isNumericLiteral(node) && node.numericLiteralFlags & TokenFlags.ContainsSeparator) {
756            return !!(flags & GetLiteralTextFlags.AllowNumericSeparator);
757        }
758
759        return !isBigIntLiteral(node);
760    }
761
762    export function getTextOfConstantValue(value: string | number) {
763        return isString(value) ? '"' + escapeNonAsciiString(value) + '"' : "" + value;
764    }
765
766    // Make an identifier from an external module name by extracting the string after the last "/" and replacing
767    // all non-alphanumeric characters with underscores
768    export function makeIdentifierFromModuleName(moduleName: string): string {
769        return getBaseFileName(moduleName).replace(/^(\d)/, "_$1").replace(/\W/g, "_");
770    }
771
772    export function isBlockOrCatchScoped(declaration: Declaration) {
773        return (getCombinedNodeFlags(declaration) & NodeFlags.BlockScoped) !== 0 ||
774            isCatchClauseVariableDeclarationOrBindingElement(declaration);
775    }
776
777    export function isCatchClauseVariableDeclarationOrBindingElement(declaration: Declaration) {
778        const node = getRootDeclaration(declaration);
779        return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause;
780    }
781
782    export function isAmbientModule(node: Node): node is AmbientModuleDeclaration {
783        return isModuleDeclaration(node) && (node.name.kind === SyntaxKind.StringLiteral || isGlobalScopeAugmentation(node));
784    }
785
786    export function isModuleWithStringLiteralName(node: Node): node is ModuleDeclaration {
787        return isModuleDeclaration(node) && node.name.kind === SyntaxKind.StringLiteral;
788    }
789
790    export function isNonGlobalAmbientModule(node: Node): node is ModuleDeclaration & { name: StringLiteral } {
791        return isModuleDeclaration(node) && isStringLiteral(node.name);
792    }
793
794    /**
795     * An effective module (namespace) declaration is either
796     * 1. An actual declaration: namespace X { ... }
797     * 2. A Javascript declaration, which is:
798     *    An identifier in a nested property access expression: Y in `X.Y.Z = { ... }`
799     */
800    export function isEffectiveModuleDeclaration(node: Node) {
801        return isModuleDeclaration(node) || isIdentifier(node);
802    }
803
804    /** Given a symbol for a module, checks that it is a shorthand ambient module. */
805    export function isShorthandAmbientModuleSymbol(moduleSymbol: Symbol): boolean {
806        return isShorthandAmbientModule(moduleSymbol.valueDeclaration);
807    }
808
809    function isShorthandAmbientModule(node: Node | undefined): boolean {
810        // The only kind of module that can be missing a body is a shorthand ambient module.
811        return !!node && node.kind === SyntaxKind.ModuleDeclaration && (!(node as ModuleDeclaration).body);
812    }
813
814    export function isBlockScopedContainerTopLevel(node: Node): boolean {
815        return node.kind === SyntaxKind.SourceFile ||
816            node.kind === SyntaxKind.ModuleDeclaration ||
817            isFunctionLikeOrClassStaticBlockDeclaration(node);
818    }
819
820    export function isGlobalScopeAugmentation(module: ModuleDeclaration): boolean {
821        return !!(module.flags & NodeFlags.GlobalAugmentation);
822    }
823
824    export function isExternalModuleAugmentation(node: Node): node is AmbientModuleDeclaration {
825        return isAmbientModule(node) && isModuleAugmentationExternal(node);
826    }
827
828    export function isModuleAugmentationExternal(node: AmbientModuleDeclaration) {
829        // external module augmentation is a ambient module declaration that is either:
830        // - defined in the top level scope and source file is an external module
831        // - defined inside ambient module declaration located in the top level scope and source file not an external module
832        switch (node.parent.kind) {
833            case SyntaxKind.SourceFile:
834                return isExternalModule(node.parent);
835            case SyntaxKind.ModuleBlock:
836                return isAmbientModule(node.parent.parent) && isSourceFile(node.parent.parent.parent) && !isExternalModule(node.parent.parent.parent);
837        }
838        return false;
839    }
840
841    export function getNonAugmentationDeclaration(symbol: Symbol) {
842        return symbol.declarations?.find(d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d)));
843    }
844
845    function isCommonJSContainingModuleKind(kind: ModuleKind) {
846        return kind === ModuleKind.CommonJS || kind === ModuleKind.Node16 || kind === ModuleKind.NodeNext;
847    }
848
849    export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) {
850        return isExternalModule(node) || compilerOptions.isolatedModules || (isCommonJSContainingModuleKind(getEmitModuleKind(compilerOptions)) && !!node.commonJsModuleIndicator);
851    }
852
853    /**
854     * Returns whether the source file will be treated as if it were in strict mode at runtime.
855     */
856    export function isEffectiveStrictModeSourceFile(node: SourceFile, compilerOptions: CompilerOptions) {
857        // We can only verify strict mode for JS/TS files
858        switch (node.scriptKind) {
859            case ScriptKind.JS:
860            case ScriptKind.TS:
861            case ScriptKind.JSX:
862            case ScriptKind.TSX:
863            case ScriptKind.ETS:
864                break;
865            default:
866                return false;
867        }
868        // Strict mode does not matter for declaration files.
869        if (node.isDeclarationFile) {
870            return false;
871        }
872        // If `alwaysStrict` is set, then treat the file as strict.
873        if (getStrictOptionValue(compilerOptions, "alwaysStrict")) {
874            return true;
875        }
876        // Starting with a "use strict" directive indicates the file is strict.
877        if (startsWithUseStrict(node.statements)) {
878            return true;
879        }
880        if (isExternalModule(node) || compilerOptions.isolatedModules) {
881            // ECMAScript Modules are always strict.
882            if (getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) {
883                return true;
884            }
885            // Other modules are strict unless otherwise specified.
886            return !compilerOptions.noImplicitUseStrict;
887        }
888        return false;
889    }
890
891    export function isAmbientPropertyDeclaration(node: PropertyDeclaration) {
892        return !!(node.flags & NodeFlags.Ambient) || hasSyntacticModifier(node, ModifierFlags.Ambient);
893    }
894
895    export function isBlockScope(node: Node, parentNode: Node | undefined): boolean {
896        switch (node.kind) {
897            case SyntaxKind.SourceFile:
898            case SyntaxKind.CaseBlock:
899            case SyntaxKind.CatchClause:
900            case SyntaxKind.ModuleDeclaration:
901            case SyntaxKind.ForStatement:
902            case SyntaxKind.ForInStatement:
903            case SyntaxKind.ForOfStatement:
904            case SyntaxKind.Constructor:
905            case SyntaxKind.MethodDeclaration:
906            case SyntaxKind.GetAccessor:
907            case SyntaxKind.SetAccessor:
908            case SyntaxKind.FunctionDeclaration:
909            case SyntaxKind.FunctionExpression:
910            case SyntaxKind.ArrowFunction:
911            case SyntaxKind.PropertyDeclaration:
912            case SyntaxKind.ClassStaticBlockDeclaration:
913                return true;
914
915            case SyntaxKind.Block:
916                // function block is not considered block-scope container
917                // see comment in binder.ts: bind(...), case for SyntaxKind.Block
918                return !isFunctionLikeOrClassStaticBlockDeclaration(parentNode);
919        }
920
921        return false;
922    }
923
924    export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters;
925    export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
926        switch (node.kind) {
927            case SyntaxKind.JSDocCallbackTag:
928            case SyntaxKind.JSDocTypedefTag:
929            case SyntaxKind.JSDocSignature:
930                return true;
931            default:
932                assertType<DeclarationWithTypeParameterChildren>(node);
933                return isDeclarationWithTypeParameterChildren(node);
934        }
935    }
936
937    export function isDeclarationWithTypeParameterChildren(node: Node): node is DeclarationWithTypeParameterChildren;
938    export function isDeclarationWithTypeParameterChildren(node: DeclarationWithTypeParameterChildren): node is DeclarationWithTypeParameterChildren {
939        switch (node.kind) {
940            case SyntaxKind.CallSignature:
941            case SyntaxKind.ConstructSignature:
942            case SyntaxKind.MethodSignature:
943            case SyntaxKind.IndexSignature:
944            case SyntaxKind.FunctionType:
945            case SyntaxKind.ConstructorType:
946            case SyntaxKind.JSDocFunctionType:
947            case SyntaxKind.ClassDeclaration:
948            case SyntaxKind.StructDeclaration:
949            case SyntaxKind.ClassExpression:
950            case SyntaxKind.InterfaceDeclaration:
951            case SyntaxKind.TypeAliasDeclaration:
952            case SyntaxKind.JSDocTemplateTag:
953            case SyntaxKind.FunctionDeclaration:
954            case SyntaxKind.MethodDeclaration:
955            case SyntaxKind.Constructor:
956            case SyntaxKind.GetAccessor:
957            case SyntaxKind.SetAccessor:
958            case SyntaxKind.FunctionExpression:
959            case SyntaxKind.ArrowFunction:
960                return true;
961            default:
962                assertType<never>(node);
963                return false;
964        }
965    }
966
967    export function isAnyImportSyntax(node: Node): node is AnyImportSyntax {
968        switch (node.kind) {
969            case SyntaxKind.ImportDeclaration:
970            case SyntaxKind.ImportEqualsDeclaration:
971                return true;
972            default:
973                return false;
974        }
975    }
976
977    export function isAnyImportOrBareOrAccessedRequire(node: Node): node is AnyImportOrBareOrAccessedRequire {
978        return isAnyImportSyntax(node) || isVariableDeclarationInitializedToBareOrAccessedRequire(node);
979    }
980
981    export function isLateVisibilityPaintedStatement(node: Node): node is LateVisibilityPaintedStatement {
982        switch (node.kind) {
983            case SyntaxKind.ImportDeclaration:
984            case SyntaxKind.ImportEqualsDeclaration:
985            case SyntaxKind.VariableStatement:
986            case SyntaxKind.ClassDeclaration:
987            case SyntaxKind.StructDeclaration:
988            case SyntaxKind.FunctionDeclaration:
989            case SyntaxKind.ModuleDeclaration:
990            case SyntaxKind.TypeAliasDeclaration:
991            case SyntaxKind.InterfaceDeclaration:
992            case SyntaxKind.EnumDeclaration:
993                return true;
994            default:
995                return false;
996        }
997    }
998
999    export function hasPossibleExternalModuleReference(node: Node): node is AnyImportOrReExport | ModuleDeclaration | ImportTypeNode | ImportCall {
1000        return isAnyImportOrReExport(node) || isModuleDeclaration(node) || isImportTypeNode(node) || isImportCall(node);
1001    }
1002
1003    export function isAnyImportOrReExport(node: Node): node is AnyImportOrReExport {
1004        return isAnyImportSyntax(node) || isExportDeclaration(node);
1005    }
1006
1007    // Gets the nearest enclosing block scope container that has the provided node
1008    // as a descendant, that is not the provided node.
1009    export function getEnclosingBlockScopeContainer(node: Node): Node {
1010        return findAncestor(node.parent, current => isBlockScope(current, current.parent))!;
1011    }
1012
1013    export function forEachEnclosingBlockScopeContainer(node: Node, cb: (container: Node) => void): void {
1014        let container = getEnclosingBlockScopeContainer(node);
1015        while (container) {
1016            cb(container);
1017            container = getEnclosingBlockScopeContainer(container);
1018        }
1019    }
1020
1021    // Return display name of an identifier
1022    // Computed property names will just be emitted as "[<expr>]", where <expr> is the source
1023    // text of the expression in the computed property.
1024    export function declarationNameToString(name: DeclarationName | QualifiedName | undefined) {
1025        if (name && name.virtual && name.kind === SyntaxKind.Identifier) {
1026            return name.escapedText.toString();
1027        }
1028        else {
1029            return !name || getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name);
1030        }
1031    }
1032
1033    export function getNameFromIndexInfo(info: IndexInfo): string | undefined {
1034        return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined;
1035    }
1036
1037    export function isComputedNonLiteralName(name: PropertyName): boolean {
1038        return name.kind === SyntaxKind.ComputedPropertyName && !isStringOrNumericLiteralLike(name.expression);
1039    }
1040
1041    export function tryGetTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String | undefined {
1042        switch (name.kind) {
1043            case SyntaxKind.Identifier:
1044            case SyntaxKind.PrivateIdentifier:
1045                return name.autoGenerateFlags ? undefined : name.escapedText;
1046            case SyntaxKind.StringLiteral:
1047            case SyntaxKind.NumericLiteral:
1048            case SyntaxKind.NoSubstitutionTemplateLiteral:
1049                return escapeLeadingUnderscores(name.text);
1050            case SyntaxKind.ComputedPropertyName:
1051                if (isStringOrNumericLiteralLike(name.expression)) return escapeLeadingUnderscores(name.expression.text);
1052                return undefined;
1053            default:
1054                return Debug.assertNever(name);
1055        }
1056    }
1057
1058    export function getTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String {
1059        return Debug.checkDefined(tryGetTextOfPropertyName(name));
1060    }
1061
1062    export function entityNameToString(name: EntityNameOrEntityNameExpression | JSDocMemberName | JsxTagNameExpression | PrivateIdentifier): string {
1063        switch (name.kind) {
1064            case SyntaxKind.ThisKeyword:
1065                return "this";
1066            case SyntaxKind.PrivateIdentifier:
1067            case SyntaxKind.Identifier:
1068                return getFullWidth(name) === 0 ? idText(name) : getTextOfNode(name);
1069            case SyntaxKind.QualifiedName:
1070                return entityNameToString(name.left) + "." + entityNameToString(name.right);
1071            case SyntaxKind.PropertyAccessExpression:
1072                if (isIdentifier(name.name) || isPrivateIdentifier(name.name)) {
1073                    return entityNameToString(name.expression) + "." + entityNameToString(name.name);
1074                }
1075                else {
1076                    return Debug.assertNever(name.name);
1077                }
1078            case SyntaxKind.JSDocMemberName:
1079                return entityNameToString(name.left) + entityNameToString(name.right);
1080            default:
1081                return Debug.assertNever(name);
1082        }
1083    }
1084
1085    export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation {
1086        const sourceFile = getSourceFileOfNode(node);
1087        return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3);
1088    }
1089
1090    export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray<Node>, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation {
1091        const start = skipTrivia(sourceFile.text, nodes.pos);
1092        return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2, arg3);
1093    }
1094
1095    export function createDiagnosticForNodeInSourceFile(sourceFile: SourceFile, node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): DiagnosticWithLocation {
1096        const span = getErrorSpanForNode(sourceFile, node);
1097        return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2, arg3);
1098    }
1099
1100    export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation {
1101        const sourceFile = getSourceFileOfNode(node);
1102        const span = getErrorSpanForNode(sourceFile, node);
1103        return createFileDiagnosticFromMessageChain(sourceFile, span.start, span.length, messageChain, relatedInformation);
1104    }
1105
1106    function assertDiagnosticLocation(file: SourceFile | undefined, start: number, length: number) {
1107        Debug.assertGreaterThanOrEqual(start, 0);
1108        Debug.assertGreaterThanOrEqual(length, 0);
1109
1110        if (file) {
1111            Debug.assertLessThanOrEqual(start, file.text.length);
1112            Debug.assertLessThanOrEqual(start + length, file.text.length);
1113        }
1114    }
1115
1116    export function createFileDiagnosticFromMessageChain(file: SourceFile, start: number, length: number, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation {
1117        assertDiagnosticLocation(file, start, length);
1118        return {
1119            file,
1120            start,
1121            length,
1122            code: messageChain.code,
1123            category: messageChain.category,
1124            messageText: messageChain.next ? messageChain : messageChain.messageText,
1125            relatedInformation
1126        };
1127    }
1128
1129    export function createDiagnosticForFileFromMessageChain(sourceFile: SourceFile, messageChain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): DiagnosticWithLocation {
1130        return {
1131            file: sourceFile,
1132            start: 0,
1133            length: 0,
1134            code: messageChain.code,
1135            category: messageChain.category,
1136            messageText: messageChain.next ? messageChain : messageChain.messageText,
1137            relatedInformation
1138        };
1139    }
1140
1141    export function createDiagnosticMessageChainFromDiagnostic(diagnostic: DiagnosticRelatedInformation): DiagnosticMessageChain {
1142        return typeof diagnostic.messageText === "string" ? {
1143            code: diagnostic.code,
1144            category: diagnostic.category,
1145            messageText: diagnostic.messageText,
1146            next: (diagnostic as DiagnosticMessageChain).next,
1147        } : diagnostic.messageText;
1148    }
1149
1150    export function createDiagnosticForRange(sourceFile: SourceFile, range: TextRange, message: DiagnosticMessage): DiagnosticWithLocation {
1151        return {
1152            file: sourceFile,
1153            start: range.pos,
1154            length: range.end - range.pos,
1155            code: message.code,
1156            category: message.category,
1157            messageText: message.message,
1158        };
1159    }
1160
1161    export function getSpanOfTokenAtPosition(sourceFile: SourceFile, pos: number): TextSpan {
1162        const scanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ true, sourceFile.languageVariant, sourceFile.text, /*onError:*/ undefined, pos);
1163        scanner.scan();
1164        const start = scanner.getTokenPos();
1165        return createTextSpanFromBounds(start, scanner.getTextPos());
1166    }
1167
1168    function getErrorSpanForArrowFunction(sourceFile: SourceFile, node: ArrowFunction): TextSpan {
1169        const pos = skipTrivia(sourceFile.text, node.pos);
1170        if (node.body && node.body.kind === SyntaxKind.Block) {
1171            const { line: startLine } = getLineAndCharacterOfPosition(sourceFile, node.body.pos);
1172            const { line: endLine } = getLineAndCharacterOfPosition(sourceFile, node.body.end);
1173            if (startLine < endLine) {
1174                // The arrow function spans multiple lines,
1175                // make the error span be the first line, inclusive.
1176                return createTextSpan(pos, getEndLinePosition(startLine, sourceFile) - pos + 1);
1177            }
1178        }
1179        return createTextSpanFromBounds(pos, node.end);
1180    }
1181
1182    export function getErrorSpanForNode(sourceFile: SourceFile, node: Node): TextSpan {
1183        let errorNode: Node | undefined = node;
1184        switch (node.kind) {
1185            case SyntaxKind.SourceFile:
1186                const pos = skipTrivia(sourceFile.text, 0, /*stopAfterLineBreak*/ false);
1187                if (pos === sourceFile.text.length) {
1188                    // file is empty - return span for the beginning of the file
1189                    return createTextSpan(0, 0);
1190                }
1191                return getSpanOfTokenAtPosition(sourceFile, pos);
1192            // This list is a work in progress. Add missing node kinds to improve their error
1193            // spans.
1194            case SyntaxKind.VariableDeclaration:
1195            case SyntaxKind.BindingElement:
1196            case SyntaxKind.ClassDeclaration:
1197            case SyntaxKind.ClassExpression:
1198            case SyntaxKind.StructDeclaration:
1199            case SyntaxKind.InterfaceDeclaration:
1200            case SyntaxKind.ModuleDeclaration:
1201            case SyntaxKind.EnumDeclaration:
1202            case SyntaxKind.EnumMember:
1203            case SyntaxKind.FunctionDeclaration:
1204            case SyntaxKind.FunctionExpression:
1205            case SyntaxKind.MethodDeclaration:
1206            case SyntaxKind.GetAccessor:
1207            case SyntaxKind.SetAccessor:
1208            case SyntaxKind.TypeAliasDeclaration:
1209            case SyntaxKind.PropertyDeclaration:
1210            case SyntaxKind.PropertySignature:
1211            case SyntaxKind.NamespaceImport:
1212                errorNode = (node as NamedDeclaration).name;
1213                break;
1214            case SyntaxKind.ArrowFunction:
1215                return getErrorSpanForArrowFunction(sourceFile, node as ArrowFunction);
1216            case SyntaxKind.CaseClause:
1217            case SyntaxKind.DefaultClause:
1218                const start = skipTrivia(sourceFile.text, (node as CaseOrDefaultClause).pos);
1219                const end = (node as CaseOrDefaultClause).statements.length > 0 ? (node as CaseOrDefaultClause).statements[0].pos : (node as CaseOrDefaultClause).end;
1220                return createTextSpanFromBounds(start, end);
1221        }
1222
1223        if (errorNode === undefined) {
1224            // If we don't have a better node, then just set the error on the first token of
1225            // construct.
1226            return getSpanOfTokenAtPosition(sourceFile, node.pos);
1227        }
1228
1229        Debug.assert(!isJSDoc(errorNode));
1230
1231        const isMissing = nodeIsMissing(errorNode);
1232        const pos = isMissing || isJsxText(node) || node.virtual
1233            ? errorNode.pos
1234            : skipTrivia(sourceFile.text, errorNode.pos);
1235
1236        // These asserts should all be satisfied for a properly constructed `errorNode`.
1237        if (isMissing) {
1238            Debug.assert(pos === errorNode.pos, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809");
1239            Debug.assert(pos === errorNode.end, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809");
1240        }
1241        else {
1242            Debug.assert(pos >= errorNode.pos, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809");
1243            Debug.assert(pos <= errorNode.end, "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809");
1244        }
1245
1246        return createTextSpanFromBounds(pos, errorNode.end);
1247    }
1248
1249    export function isExternalOrCommonJsModule(file: SourceFile): boolean {
1250        return (file.externalModuleIndicator || file.commonJsModuleIndicator) !== undefined;
1251    }
1252
1253
1254    export function isJsonSourceFile(file: SourceFile): file is JsonSourceFile {
1255        return file.scriptKind === ScriptKind.JSON;
1256    }
1257
1258    export function isEmitNodeModulesFiles(emitNodeModulesFiles: boolean | undefined): boolean {
1259        return !!emitNodeModulesFiles;
1260    }
1261
1262    export function isEnumConst(node: EnumDeclaration): boolean {
1263        return !!(getCombinedModifierFlags(node) & ModifierFlags.Const);
1264    }
1265
1266    export function isDeclarationReadonly(declaration: Declaration): boolean {
1267        return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration, declaration.parent));
1268    }
1269
1270    export function isVarConst(node: VariableDeclaration | VariableDeclarationList): boolean {
1271        return !!(getCombinedNodeFlags(node) & NodeFlags.Const);
1272    }
1273
1274    export function isLet(node: Node): boolean {
1275        return !!(getCombinedNodeFlags(node) & NodeFlags.Let);
1276    }
1277
1278    export function isSuperCall(n: Node): n is SuperCall {
1279        return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.SuperKeyword;
1280    }
1281
1282    export function isImportCall(n: Node): n is ImportCall {
1283        return n.kind === SyntaxKind.CallExpression && (n as CallExpression).expression.kind === SyntaxKind.ImportKeyword;
1284    }
1285
1286    export function isImportMeta(n: Node): n is ImportMetaProperty {
1287        return isMetaProperty(n)
1288            && n.keywordToken === SyntaxKind.ImportKeyword
1289            && n.name.escapedText === "meta";
1290    }
1291
1292    export function isLiteralImportTypeNode(n: Node): n is LiteralImportTypeNode {
1293        return isImportTypeNode(n) && isLiteralTypeNode(n.argument) && isStringLiteral(n.argument.literal);
1294    }
1295
1296    export function isPrologueDirective(node: Node): node is PrologueDirective {
1297        return node.kind === SyntaxKind.ExpressionStatement
1298            && (node as ExpressionStatement).expression.kind === SyntaxKind.StringLiteral;
1299    }
1300
1301    export function isCustomPrologue(node: Statement) {
1302        return !!(getEmitFlags(node) & EmitFlags.CustomPrologue);
1303    }
1304
1305    export function isHoistedFunction(node: Statement) {
1306        return isCustomPrologue(node)
1307            && isFunctionDeclaration(node);
1308    }
1309
1310    function isHoistedVariable(node: VariableDeclaration) {
1311        return isIdentifier(node.name)
1312            && !node.initializer;
1313    }
1314
1315    export function isHoistedVariableStatement(node: Statement) {
1316        return isCustomPrologue(node)
1317            && isVariableStatement(node)
1318            && every(node.declarationList.declarations, isHoistedVariable);
1319    }
1320
1321    export function getJSDocCommentRanges(node: Node, text: string) {
1322        const commentRanges = (node.kind === SyntaxKind.Parameter ||
1323            node.kind === SyntaxKind.TypeParameter ||
1324            node.kind === SyntaxKind.FunctionExpression ||
1325            node.kind === SyntaxKind.ArrowFunction ||
1326            node.kind === SyntaxKind.ParenthesizedExpression ||
1327            node.kind === SyntaxKind.VariableDeclaration ||
1328            node.kind === SyntaxKind.ExportSpecifier) ?
1329            concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
1330            getLeadingCommentRanges(text, node.pos);
1331        // True if the comment starts with '/**' but not if it is '/**/'
1332        return filter(commentRanges, comment =>
1333            text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
1334            text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk &&
1335            text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash);
1336    }
1337
1338    export const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)(('[^']*')|("[^"]*")).*?\/>/;
1339    const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)(('[^']*')|("[^"]*")).*?\/>/;
1340    export const fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)(('[^']*')|("[^"]*")).*?\/>/;
1341    const defaultLibReferenceRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)(('[^']*')|("[^"]*"))\s*\/>/;
1342
1343    export function isPartOfTypeNode(node: Node): boolean {
1344        if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) {
1345            return true;
1346        }
1347
1348        switch (node.kind) {
1349            case SyntaxKind.AnyKeyword:
1350            case SyntaxKind.UnknownKeyword:
1351            case SyntaxKind.NumberKeyword:
1352            case SyntaxKind.BigIntKeyword:
1353            case SyntaxKind.StringKeyword:
1354            case SyntaxKind.BooleanKeyword:
1355            case SyntaxKind.SymbolKeyword:
1356            case SyntaxKind.ObjectKeyword:
1357            case SyntaxKind.UndefinedKeyword:
1358            case SyntaxKind.NeverKeyword:
1359                return true;
1360            case SyntaxKind.VoidKeyword:
1361                return node.parent.kind !== SyntaxKind.VoidExpression;
1362            case SyntaxKind.ExpressionWithTypeArguments:
1363                return isHeritageClause(node.parent) && !isExpressionWithTypeArgumentsInClassExtendsClause(node);
1364            case SyntaxKind.TypeParameter:
1365                return node.parent.kind === SyntaxKind.MappedType || node.parent.kind === SyntaxKind.InferType;
1366
1367            // Identifiers and qualified names may be type nodes, depending on their context. Climb
1368            // above them to find the lowest container
1369            case SyntaxKind.Identifier:
1370                // If the identifier is the RHS of a qualified name, then it's a type iff its parent is.
1371                if (node.parent.kind === SyntaxKind.QualifiedName && (node.parent as QualifiedName).right === node) {
1372                    node = node.parent;
1373                }
1374                else if (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent as PropertyAccessExpression).name === node) {
1375                    node = node.parent;
1376                }
1377                // At this point, node is either a qualified name or an identifier
1378                Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression,
1379                    "'node' was expected to be a qualified name, identifier or property access in 'isPartOfTypeNode'.");
1380                // falls through
1381
1382            case SyntaxKind.QualifiedName:
1383            case SyntaxKind.PropertyAccessExpression:
1384            case SyntaxKind.ThisKeyword: {
1385                const { parent } = node;
1386                if (parent.kind === SyntaxKind.TypeQuery) {
1387                    return false;
1388                }
1389                if (parent.kind === SyntaxKind.ImportType) {
1390                    return !(parent as ImportTypeNode).isTypeOf;
1391                }
1392                // Do not recursively call isPartOfTypeNode on the parent. In the example:
1393                //
1394                //     let a: A.B.C;
1395                //
1396                // Calling isPartOfTypeNode would consider the qualified name A.B a type node.
1397                // Only C and A.B.C are type nodes.
1398                if (SyntaxKind.FirstTypeNode <= parent.kind && parent.kind <= SyntaxKind.LastTypeNode) {
1399                    return true;
1400                }
1401                switch (parent.kind) {
1402                    case SyntaxKind.ExpressionWithTypeArguments:
1403                        return isHeritageClause(parent.parent) && !isExpressionWithTypeArgumentsInClassExtendsClause(parent);
1404                    case SyntaxKind.TypeParameter:
1405                        return node === (parent as TypeParameterDeclaration).constraint;
1406                    case SyntaxKind.JSDocTemplateTag:
1407                        return node === (parent as JSDocTemplateTag).constraint;
1408                    case SyntaxKind.PropertyDeclaration:
1409                    case SyntaxKind.PropertySignature:
1410                    case SyntaxKind.Parameter:
1411                    case SyntaxKind.VariableDeclaration:
1412                        return node === (parent as HasType).type;
1413                    case SyntaxKind.FunctionDeclaration:
1414                    case SyntaxKind.FunctionExpression:
1415                    case SyntaxKind.ArrowFunction:
1416                    case SyntaxKind.Constructor:
1417                    case SyntaxKind.MethodDeclaration:
1418                    case SyntaxKind.MethodSignature:
1419                    case SyntaxKind.GetAccessor:
1420                    case SyntaxKind.SetAccessor:
1421                        return node === (parent as FunctionLikeDeclaration).type;
1422                    case SyntaxKind.CallSignature:
1423                    case SyntaxKind.ConstructSignature:
1424                    case SyntaxKind.IndexSignature:
1425                        return node === (parent as SignatureDeclaration).type;
1426                    case SyntaxKind.TypeAssertionExpression:
1427                        return node === (parent as TypeAssertion).type;
1428                    case SyntaxKind.CallExpression:
1429                    case SyntaxKind.NewExpression:
1430                        return contains((parent as CallExpression).typeArguments, node);
1431                    case SyntaxKind.TaggedTemplateExpression:
1432                        // TODO (drosen): TaggedTemplateExpressions may eventually support type arguments.
1433                        return false;
1434                }
1435            }
1436        }
1437
1438        return false;
1439    }
1440
1441    export function isChildOfNodeWithKind(node: Node, kind: SyntaxKind): boolean {
1442        while (node) {
1443            if (node.kind === kind) {
1444                return true;
1445            }
1446            node = node.parent;
1447        }
1448        return false;
1449    }
1450
1451    // Warning: This has the same semantics as the forEach family of functions,
1452    //          in that traversal terminates in the event that 'visitor' supplies a truthy value.
1453    export function forEachReturnStatement<T>(body: Block | Statement, visitor: (stmt: ReturnStatement) => T): T | undefined {
1454
1455        return traverse(body);
1456
1457        function traverse(node: Node): T | undefined {
1458            switch (node.kind) {
1459                case SyntaxKind.ReturnStatement:
1460                    return visitor(node as ReturnStatement);
1461                case SyntaxKind.CaseBlock:
1462                case SyntaxKind.Block:
1463                case SyntaxKind.IfStatement:
1464                case SyntaxKind.DoStatement:
1465                case SyntaxKind.WhileStatement:
1466                case SyntaxKind.ForStatement:
1467                case SyntaxKind.ForInStatement:
1468                case SyntaxKind.ForOfStatement:
1469                case SyntaxKind.WithStatement:
1470                case SyntaxKind.SwitchStatement:
1471                case SyntaxKind.CaseClause:
1472                case SyntaxKind.DefaultClause:
1473                case SyntaxKind.LabeledStatement:
1474                case SyntaxKind.TryStatement:
1475                case SyntaxKind.CatchClause:
1476                    return forEachChild(node, traverse);
1477            }
1478        }
1479    }
1480
1481    export function forEachYieldExpression(body: Block, visitor: (expr: YieldExpression) => void): void {
1482
1483        return traverse(body);
1484
1485        function traverse(node: Node): void {
1486            switch (node.kind) {
1487                case SyntaxKind.YieldExpression:
1488                    visitor(node as YieldExpression);
1489                    const operand = (node as YieldExpression).expression;
1490                    if (operand) {
1491                        traverse(operand);
1492                    }
1493                    return;
1494                case SyntaxKind.EnumDeclaration:
1495                case SyntaxKind.InterfaceDeclaration:
1496                case SyntaxKind.ModuleDeclaration:
1497                case SyntaxKind.TypeAliasDeclaration:
1498                    // These are not allowed inside a generator now, but eventually they may be allowed
1499                    // as local types. Regardless, skip them to avoid the work.
1500                    return;
1501                default:
1502                    if (isFunctionLike(node)) {
1503                        if (node.name && node.name.kind === SyntaxKind.ComputedPropertyName) {
1504                            // Note that we will not include methods/accessors of a class because they would require
1505                            // first descending into the class. This is by design.
1506                            traverse(node.name.expression);
1507                            return;
1508                        }
1509                    }
1510                    else if (!isPartOfTypeNode(node)) {
1511                        // This is the general case, which should include mostly expressions and statements.
1512                        // Also includes NodeArrays.
1513                        forEachChild(node, traverse);
1514                    }
1515            }
1516        }
1517    }
1518
1519    /**
1520     * Gets the most likely element type for a TypeNode. This is not an exhaustive test
1521     * as it assumes a rest argument can only be an array type (either T[], or Array<T>).
1522     *
1523     * @param node The type node.
1524     */
1525    export function getRestParameterElementType(node: TypeNode | undefined) {
1526        if (node && node.kind === SyntaxKind.ArrayType) {
1527            return (node as ArrayTypeNode).elementType;
1528        }
1529        else if (node && node.kind === SyntaxKind.TypeReference) {
1530            return singleOrUndefined((node as TypeReferenceNode).typeArguments);
1531        }
1532        else {
1533            return undefined;
1534        }
1535    }
1536
1537    export function getMembersOfDeclaration(node: Declaration): NodeArray<ClassElement | TypeElement | ObjectLiteralElement> | undefined {
1538        switch (node.kind) {
1539            case SyntaxKind.InterfaceDeclaration:
1540            case SyntaxKind.ClassDeclaration:
1541            case SyntaxKind.ClassExpression:
1542            case SyntaxKind.StructDeclaration:
1543            case SyntaxKind.TypeLiteral:
1544                return (node as ObjectTypeDeclaration).members;
1545            case SyntaxKind.ObjectLiteralExpression:
1546                return (node as ObjectLiteralExpression).properties;
1547        }
1548    }
1549
1550    export function isVariableLike(node: Node): node is VariableLikeDeclaration {
1551        if (node) {
1552            switch (node.kind) {
1553                case SyntaxKind.BindingElement:
1554                case SyntaxKind.EnumMember:
1555                case SyntaxKind.Parameter:
1556                case SyntaxKind.PropertyAssignment:
1557                case SyntaxKind.PropertyDeclaration:
1558                case SyntaxKind.PropertySignature:
1559                case SyntaxKind.ShorthandPropertyAssignment:
1560                case SyntaxKind.VariableDeclaration:
1561                    return true;
1562            }
1563        }
1564        return false;
1565    }
1566
1567    export function isVariableLikeOrAccessor(node: Node): node is AccessorDeclaration | VariableLikeDeclaration {
1568        return isVariableLike(node) || isAccessor(node);
1569    }
1570
1571    export function isVariableDeclarationInVariableStatement(node: VariableDeclaration) {
1572        return node.parent.kind === SyntaxKind.VariableDeclarationList
1573            && node.parent.parent.kind === SyntaxKind.VariableStatement;
1574    }
1575
1576    export function isCommonJsExportedExpression(node: Node) {
1577        if (!isInJSFile(node)) return false;
1578        return (isObjectLiteralExpression(node.parent) && isBinaryExpression(node.parent.parent) && getAssignmentDeclarationKind(node.parent.parent) === AssignmentDeclarationKind.ModuleExports) ||
1579            isCommonJsExportPropertyAssignment(node.parent);
1580    }
1581
1582    export function isCommonJsExportPropertyAssignment(node: Node) {
1583        if (!isInJSFile(node)) return false;
1584        return (isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ExportsProperty);
1585    }
1586
1587    export function isValidESSymbolDeclaration(node: Node): boolean {
1588        return (isVariableDeclaration(node) ? isVarConst(node) && isIdentifier(node.name) && isVariableDeclarationInVariableStatement(node) :
1589            isPropertyDeclaration(node) ? hasEffectiveReadonlyModifier(node) && hasStaticModifier(node) :
1590            isPropertySignature(node) && hasEffectiveReadonlyModifier(node)) || isCommonJsExportPropertyAssignment(node);
1591    }
1592
1593    export function introducesArgumentsExoticObject(node: Node) {
1594        switch (node.kind) {
1595            case SyntaxKind.MethodDeclaration:
1596            case SyntaxKind.MethodSignature:
1597            case SyntaxKind.Constructor:
1598            case SyntaxKind.GetAccessor:
1599            case SyntaxKind.SetAccessor:
1600            case SyntaxKind.FunctionDeclaration:
1601            case SyntaxKind.FunctionExpression:
1602                return true;
1603        }
1604        return false;
1605    }
1606
1607    export function unwrapInnermostStatementOfLabel(node: LabeledStatement, beforeUnwrapLabelCallback?: (node: LabeledStatement) => void): Statement {
1608        while (true) {
1609            if (beforeUnwrapLabelCallback) {
1610                beforeUnwrapLabelCallback(node);
1611            }
1612            if (node.statement.kind !== SyntaxKind.LabeledStatement) {
1613                return node.statement;
1614            }
1615            node = node.statement as LabeledStatement;
1616        }
1617    }
1618
1619    export function isFunctionBlock(node: Node): boolean {
1620        return node && node.kind === SyntaxKind.Block && isFunctionLike(node.parent);
1621    }
1622
1623    export function isObjectLiteralMethod(node: Node): node is MethodDeclaration {
1624        return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression;
1625    }
1626
1627    export function isObjectLiteralOrClassExpressionMethodOrAccessor(node: Node): node is MethodDeclaration {
1628        return (node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor) &&
1629            (node.parent.kind === SyntaxKind.ObjectLiteralExpression ||
1630                node.parent.kind === SyntaxKind.ClassExpression);
1631    }
1632
1633    export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate {
1634        return predicate && predicate.kind === TypePredicateKind.Identifier;
1635    }
1636
1637    export function isThisTypePredicate(predicate: TypePredicate): predicate is ThisTypePredicate {
1638        return predicate && predicate.kind === TypePredicateKind.This;
1639    }
1640
1641    export function getPropertyAssignment(objectLiteral: ObjectLiteralExpression, key: string, key2?: string): readonly PropertyAssignment[] {
1642        return objectLiteral.properties.filter((property): property is PropertyAssignment => {
1643            if (property.kind === SyntaxKind.PropertyAssignment) {
1644                const propName = tryGetTextOfPropertyName(property.name);
1645                return key === propName || (!!key2 && key2 === propName);
1646            }
1647            return false;
1648        });
1649    }
1650
1651    export function getPropertyArrayElementValue(objectLiteral: ObjectLiteralExpression, propKey: string, elementValue: string): StringLiteral | undefined {
1652        return firstDefined(getPropertyAssignment(objectLiteral, propKey), property =>
1653            isArrayLiteralExpression(property.initializer) ?
1654                find(property.initializer.elements, (element): element is StringLiteral => isStringLiteral(element) && element.text === elementValue) :
1655                undefined);
1656    }
1657
1658    export function getTsConfigObjectLiteralExpression(tsConfigSourceFile: TsConfigSourceFile | undefined): ObjectLiteralExpression | undefined {
1659        if (tsConfigSourceFile && tsConfigSourceFile.statements.length) {
1660            const expression = tsConfigSourceFile.statements[0].expression;
1661            return tryCast(expression, isObjectLiteralExpression);
1662        }
1663    }
1664
1665    export function getTsConfigPropArrayElementValue(tsConfigSourceFile: TsConfigSourceFile | undefined, propKey: string, elementValue: string): StringLiteral | undefined {
1666        return firstDefined(getTsConfigPropArray(tsConfigSourceFile, propKey), property =>
1667            isArrayLiteralExpression(property.initializer) ?
1668                find(property.initializer.elements, (element): element is StringLiteral => isStringLiteral(element) && element.text === elementValue) :
1669                undefined);
1670    }
1671
1672    export function getTsConfigPropArray(tsConfigSourceFile: TsConfigSourceFile | undefined, propKey: string): readonly PropertyAssignment[] {
1673        const jsonObjectLiteral = getTsConfigObjectLiteralExpression(tsConfigSourceFile);
1674        return jsonObjectLiteral ? getPropertyAssignment(jsonObjectLiteral, propKey) : emptyArray;
1675    }
1676
1677    export function getContainingFunction(node: Node): SignatureDeclaration | undefined {
1678        return findAncestor(node.parent, isFunctionLike);
1679    }
1680
1681    export function getContainingFunctionDeclaration(node: Node): FunctionLikeDeclaration | undefined {
1682        return findAncestor(node.parent, isFunctionLikeDeclaration);
1683    }
1684
1685    export function getContainingClass(node: Node): ClassLikeDeclaration | undefined {
1686        return findAncestor(node.parent, isClassLike);
1687    }
1688
1689    export function getContaningConstructorDeclaration(node?: Node): ConstructorDeclaration | undefined {
1690        return node ? findAncestor(node, isConstructorDeclaration): undefined;
1691    }
1692    export function getContainingClassStaticBlock(node: Node): Node | undefined {
1693        return findAncestor(node.parent, n => {
1694            if (isClassLike(n) || isFunctionLike(n)) {
1695                return "quit";
1696            }
1697            return isClassStaticBlockDeclaration(n);
1698        });
1699    }
1700
1701    export function getContainingFunctionOrClassStaticBlock(node: Node): SignatureDeclaration | ClassStaticBlockDeclaration | undefined {
1702        return findAncestor(node.parent, isFunctionLikeOrClassStaticBlockDeclaration);
1703    }
1704
1705    export function getContainingStruct(node: Node): StructDeclaration | undefined {
1706        return findAncestor(node.parent, isStruct);
1707    }
1708
1709    export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node {
1710        Debug.assert(node.kind !== SyntaxKind.SourceFile);
1711        while (true) {
1712            node = node.parent;
1713            if (!node) {
1714                return Debug.fail(); // If we never pass in a SourceFile, this should be unreachable, since we'll stop when we reach that.
1715            }
1716            switch (node.kind) {
1717                case SyntaxKind.ComputedPropertyName:
1718                    // If the grandparent node is an object literal (as opposed to a class),
1719                    // then the computed property is not a 'this' container.
1720                    // A computed property name in a class needs to be a this container
1721                    // so that we can error on it.
1722                    if (isClassLike(node.parent.parent)) {
1723                        return node;
1724                    }
1725                    // If this is a computed property, then the parent should not
1726                    // make it a this container. The parent might be a property
1727                    // in an object literal, like a method or accessor. But in order for
1728                    // such a parent to be a this container, the reference must be in
1729                    // the *body* of the container.
1730                    node = node.parent;
1731                    break;
1732                case SyntaxKind.Decorator:
1733                    // Decorators are always applied outside of the body of a class or method.
1734                    if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) {
1735                        // If the decorator's parent is a Parameter, we resolve the this container from
1736                        // the grandparent class declaration.
1737                        node = node.parent.parent;
1738                    }
1739                    else if (isClassElement(node.parent)) {
1740                        // If the decorator's parent is a class element, we resolve the 'this' container
1741                        // from the parent class declaration.
1742                        node = node.parent;
1743                    }
1744                    break;
1745                case SyntaxKind.ArrowFunction:
1746                    if (!includeArrowFunctions) {
1747                        continue;
1748                    }
1749                    // falls through
1750
1751                case SyntaxKind.FunctionDeclaration:
1752                case SyntaxKind.FunctionExpression:
1753                case SyntaxKind.ModuleDeclaration:
1754                case SyntaxKind.ClassStaticBlockDeclaration:
1755                case SyntaxKind.PropertyDeclaration:
1756                case SyntaxKind.PropertySignature:
1757                case SyntaxKind.MethodDeclaration:
1758                case SyntaxKind.MethodSignature:
1759                case SyntaxKind.Constructor:
1760                case SyntaxKind.GetAccessor:
1761                case SyntaxKind.SetAccessor:
1762                case SyntaxKind.CallSignature:
1763                case SyntaxKind.ConstructSignature:
1764                case SyntaxKind.IndexSignature:
1765                case SyntaxKind.EnumDeclaration:
1766                case SyntaxKind.SourceFile:
1767                    return node;
1768            }
1769        }
1770    }
1771
1772    /**
1773     * @returns Whether the node creates a new 'this' scope for its children.
1774     */
1775    export function isThisContainerOrFunctionBlock(node: Node): boolean {
1776        switch (node.kind) {
1777            // Arrow functions use the same scope, but may do so in a "delayed" manner
1778            // For example, `const getThis = () => this` may be before a super() call in a derived constructor
1779            case SyntaxKind.ArrowFunction:
1780            case SyntaxKind.FunctionDeclaration:
1781            case SyntaxKind.FunctionExpression:
1782            case SyntaxKind.PropertyDeclaration:
1783                return true;
1784            case SyntaxKind.Block:
1785                switch (node.parent.kind) {
1786                    case SyntaxKind.Constructor:
1787                    case SyntaxKind.MethodDeclaration:
1788                    case SyntaxKind.GetAccessor:
1789                    case SyntaxKind.SetAccessor:
1790                        // Object properties can have computed names; only method-like bodies start a new scope
1791                        return true;
1792                    default:
1793                        return false;
1794                }
1795            default:
1796                return false;
1797        }
1798    }
1799
1800    export function isInTopLevelContext(node: Node) {
1801        // The name of a class or function declaration is a BindingIdentifier in its surrounding scope.
1802        if (isIdentifier(node) && (isClassDeclaration(node.parent) || isFunctionDeclaration(node.parent)) && node.parent.name === node) {
1803            node = node.parent;
1804        }
1805        const container = getThisContainer(node, /*includeArrowFunctions*/ true);
1806        return isSourceFile(container);
1807    }
1808
1809    export function getNewTargetContainer(node: Node) {
1810        const container = getThisContainer(node, /*includeArrowFunctions*/ false);
1811        if (container) {
1812            switch (container.kind) {
1813                case SyntaxKind.Constructor:
1814                case SyntaxKind.FunctionDeclaration:
1815                case SyntaxKind.FunctionExpression:
1816                    return container;
1817            }
1818        }
1819
1820        return undefined;
1821    }
1822
1823    /**
1824     * Given an super call/property node, returns the closest node where
1825     * - a super call/property access is legal in the node and not legal in the parent node the node.
1826     *   i.e. super call is legal in constructor but not legal in the class body.
1827     * - the container is an arrow function (so caller might need to call getSuperContainer again in case it needs to climb higher)
1828     * - a super call/property is definitely illegal in the container (but might be legal in some subnode)
1829     *   i.e. super property access is illegal in function declaration but can be legal in the statement list
1830     */
1831    export function getSuperContainer(node: Node, stopOnFunctions: boolean): Node {
1832        while (true) {
1833            node = node.parent;
1834            if (!node) {
1835                return node;
1836            }
1837            switch (node.kind) {
1838                case SyntaxKind.ComputedPropertyName:
1839                    node = node.parent;
1840                    break;
1841                case SyntaxKind.FunctionDeclaration:
1842                case SyntaxKind.FunctionExpression:
1843                case SyntaxKind.ArrowFunction:
1844                    if (!stopOnFunctions) {
1845                        continue;
1846                    }
1847                    // falls through
1848
1849                case SyntaxKind.PropertyDeclaration:
1850                case SyntaxKind.PropertySignature:
1851                case SyntaxKind.MethodDeclaration:
1852                case SyntaxKind.MethodSignature:
1853                case SyntaxKind.Constructor:
1854                case SyntaxKind.GetAccessor:
1855                case SyntaxKind.SetAccessor:
1856                case SyntaxKind.ClassStaticBlockDeclaration:
1857                    return node;
1858                case SyntaxKind.Decorator:
1859                    // Decorators are always applied outside of the body of a class or method.
1860                    if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) {
1861                        // If the decorator's parent is a Parameter, we resolve the this container from
1862                        // the grandparent class declaration.
1863                        node = node.parent.parent;
1864                    }
1865                    else if (isClassElement(node.parent)) {
1866                        // If the decorator's parent is a class element, we resolve the 'this' container
1867                        // from the parent class declaration.
1868                        node = node.parent;
1869                    }
1870                    break;
1871            }
1872        }
1873    }
1874
1875    export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression | undefined {
1876        if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
1877            let prev = func;
1878            let parent = func.parent;
1879            while (parent.kind === SyntaxKind.ParenthesizedExpression) {
1880                prev = parent;
1881                parent = parent.parent;
1882            }
1883            if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
1884                return parent as CallExpression;
1885            }
1886        }
1887    }
1888
1889    export function isSuperOrSuperProperty(node: Node): node is SuperExpression | SuperProperty {
1890        return node.kind === SyntaxKind.SuperKeyword
1891            || isSuperProperty(node);
1892    }
1893
1894    /**
1895     * Determines whether a node is a property or element access expression for `super`.
1896     */
1897    export function isSuperProperty(node: Node): node is SuperProperty {
1898        const kind = node.kind;
1899        return (kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression)
1900            && (node as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.SuperKeyword;
1901    }
1902
1903    /**
1904     * Determines whether a node is a property or element access expression for `this`.
1905     */
1906    export function isThisProperty(node: Node): boolean {
1907        const kind = node.kind;
1908        return (kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression)
1909            && (node as PropertyAccessExpression | ElementAccessExpression).expression.kind === SyntaxKind.ThisKeyword;
1910    }
1911
1912    export function isThisInitializedDeclaration(node: Node | undefined): boolean {
1913        return !!node && isVariableDeclaration(node) && node.initializer?.kind === SyntaxKind.ThisKeyword;
1914    }
1915
1916    export function isThisInitializedObjectBindingExpression(node: Node | undefined): boolean {
1917        return !!node
1918            && (isShorthandPropertyAssignment(node) || isPropertyAssignment(node))
1919            && isBinaryExpression(node.parent.parent)
1920            && node.parent.parent.operatorToken.kind === SyntaxKind.EqualsToken
1921            && node.parent.parent.right.kind === SyntaxKind.ThisKeyword;
1922    }
1923
1924    export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression | undefined {
1925        switch (node.kind) {
1926            case SyntaxKind.TypeReference:
1927                return (node as TypeReferenceNode).typeName;
1928
1929            case SyntaxKind.ExpressionWithTypeArguments:
1930                return isEntityNameExpression((node as ExpressionWithTypeArguments).expression)
1931                    ? (node as ExpressionWithTypeArguments).expression as EntityNameExpression
1932                    : undefined;
1933
1934            // TODO(rbuckton): These aren't valid TypeNodes, but we treat them as such because of `isPartOfTypeNode`, which returns `true` for things that aren't `TypeNode`s.
1935            case SyntaxKind.Identifier as TypeNodeSyntaxKind:
1936            case SyntaxKind.QualifiedName as TypeNodeSyntaxKind:
1937                return (node as Node as EntityName);
1938        }
1939
1940        return undefined;
1941    }
1942
1943    export function getInvokedExpression(node: CallLikeExpression): Expression {
1944        switch (node.kind) {
1945            case SyntaxKind.TaggedTemplateExpression:
1946                return node.tag;
1947            case SyntaxKind.JsxOpeningElement:
1948            case SyntaxKind.JsxSelfClosingElement:
1949                return node.tagName;
1950            default:
1951                return node.expression;
1952        }
1953    }
1954
1955    export function nodeCanBeDecorated(node: ClassDeclaration): true;
1956    export function nodeCanBeDecorated(node: ClassElement, parent: Node): boolean;
1957    export function nodeCanBeDecorated(node: Node, parent: Node, grandparent: Node, compilerOptions: CompilerOptions): boolean;
1958    export function nodeCanBeDecorated(node: Node, parent: Node, grandparent: Node, compilerOptions?: CompilerOptions): boolean;
1959    export function nodeCanBeDecorated(node: Node, parent?: Node, grandparent?: Node, compilerOptions?: CompilerOptions): boolean {
1960        // private names cannot be used with decorators yet
1961        if (isNamedDeclaration(node) && isPrivateIdentifier(node.name)) {
1962            return false;
1963        }
1964        switch (node.kind) {
1965            case SyntaxKind.ClassDeclaration:
1966            case SyntaxKind.StructDeclaration:
1967                // classes are valid targets
1968                return true;
1969
1970            case SyntaxKind.PropertyDeclaration:
1971                // property declarations are valid if their parent is a class declaration.
1972                return parent!.kind === SyntaxKind.ClassDeclaration || parent!.kind === SyntaxKind.StructDeclaration;
1973
1974            case SyntaxKind.GetAccessor:
1975            case SyntaxKind.SetAccessor:
1976            case SyntaxKind.MethodDeclaration:
1977                // if this method has a body and its parent is a class declaration, this is a valid target.
1978                return (node as FunctionLikeDeclaration).body !== undefined
1979                    && (parent!.kind === SyntaxKind.ClassDeclaration || parent!.kind === SyntaxKind.StructDeclaration);
1980
1981            case SyntaxKind.Parameter:
1982                // if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target;
1983                return (parent as FunctionLikeDeclaration).body !== undefined
1984                    && (parent!.kind === SyntaxKind.Constructor
1985                        || parent!.kind === SyntaxKind.MethodDeclaration
1986                        || parent!.kind === SyntaxKind.SetAccessor)
1987                    && (grandparent!.kind === SyntaxKind.ClassDeclaration || grandparent!.kind === SyntaxKind.StructDeclaration);
1988            case SyntaxKind.FunctionDeclaration:
1989                return isArkTsDecorator(node, compilerOptions) || isSendableFunctionOrType(node);
1990            case SyntaxKind.TypeAliasDeclaration:
1991                return isSendableFunctionOrType(node);
1992        }
1993
1994        return false;
1995    }
1996
1997    export function nodeIsDecorated(node: ClassDeclaration): boolean;
1998    export function nodeIsDecorated(node: ClassElement, parent: Node): boolean;
1999    export function nodeIsDecorated(node: Node, parent: Node, grandparent: Node): boolean;
2000    export function nodeIsDecorated(node: Node, parent?: Node, grandparent?: Node): boolean {
2001        return hasDecorators(node)
2002            && nodeCanBeDecorated(node, parent!, grandparent!); // TODO: GH#18217
2003    }
2004
2005    export function nodeOrChildIsDecorated(node: ClassDeclaration): boolean;
2006    export function nodeOrChildIsDecorated(node: ClassElement, parent: Node): boolean;
2007    export function nodeOrChildIsDecorated(node: Node, parent: Node, grandparent: Node): boolean;
2008    export function nodeOrChildIsDecorated(node: Node, parent?: Node, grandparent?: Node): boolean {
2009        return nodeIsDecorated(node, parent!, grandparent!) || childIsDecorated(node, parent!); // TODO: GH#18217
2010    }
2011
2012    export function childIsDecorated(node: ClassDeclaration): boolean;
2013    export function childIsDecorated(node: Node, parent: Node): boolean;
2014    export function childIsDecorated(node: Node, parent?: Node): boolean {
2015        switch (node.kind) {
2016            case SyntaxKind.ClassDeclaration:
2017                return some((node as ClassDeclaration).members, m => nodeOrChildIsDecorated(m, node, parent!)); // TODO: GH#18217
2018            case SyntaxKind.StructDeclaration:
2019                return some((node as StructDeclaration).members, m => nodeOrChildIsDecorated(m, node, parent!)); // TODO: GH#18217
2020            case SyntaxKind.MethodDeclaration:
2021            case SyntaxKind.SetAccessor:
2022            case SyntaxKind.Constructor:
2023                return some((node as FunctionLikeDeclaration).parameters, p => nodeIsDecorated(p, node, parent!)); // TODO: GH#18217
2024            default:
2025                return false;
2026        }
2027    }
2028
2029    export function classOrConstructorParameterIsDecorated(node: ClassDeclaration): boolean {
2030        if (nodeIsDecorated(node)) return true;
2031        const constructor = getFirstConstructorWithBody(node);
2032        return !!constructor && childIsDecorated(constructor, node);
2033    }
2034
2035    export function isJSXTagName(node: Node) {
2036        const { parent } = node;
2037        if (parent.kind === SyntaxKind.JsxOpeningElement ||
2038            parent.kind === SyntaxKind.JsxSelfClosingElement ||
2039            parent.kind === SyntaxKind.JsxClosingElement) {
2040            return (parent as JsxOpeningLikeElement).tagName === node;
2041        }
2042        return false;
2043    }
2044
2045    export function isExpressionNode(node: Node): boolean {
2046        switch (node.kind) {
2047            case SyntaxKind.SuperKeyword:
2048            case SyntaxKind.NullKeyword:
2049            case SyntaxKind.TrueKeyword:
2050            case SyntaxKind.FalseKeyword:
2051            case SyntaxKind.RegularExpressionLiteral:
2052            case SyntaxKind.ArrayLiteralExpression:
2053            case SyntaxKind.ObjectLiteralExpression:
2054            case SyntaxKind.PropertyAccessExpression:
2055            case SyntaxKind.EtsComponentExpression:
2056            case SyntaxKind.ElementAccessExpression:
2057            case SyntaxKind.CallExpression:
2058            case SyntaxKind.NewExpression:
2059            case SyntaxKind.TaggedTemplateExpression:
2060            case SyntaxKind.AsExpression:
2061            case SyntaxKind.TypeAssertionExpression:
2062            case SyntaxKind.SatisfiesExpression:
2063            case SyntaxKind.NonNullExpression:
2064            case SyntaxKind.ParenthesizedExpression:
2065            case SyntaxKind.FunctionExpression:
2066            case SyntaxKind.ClassExpression:
2067            case SyntaxKind.ArrowFunction:
2068            case SyntaxKind.VoidExpression:
2069            case SyntaxKind.DeleteExpression:
2070            case SyntaxKind.TypeOfExpression:
2071            case SyntaxKind.PrefixUnaryExpression:
2072            case SyntaxKind.PostfixUnaryExpression:
2073            case SyntaxKind.BinaryExpression:
2074            case SyntaxKind.ConditionalExpression:
2075            case SyntaxKind.SpreadElement:
2076            case SyntaxKind.TemplateExpression:
2077            case SyntaxKind.OmittedExpression:
2078            case SyntaxKind.JsxElement:
2079            case SyntaxKind.JsxSelfClosingElement:
2080            case SyntaxKind.JsxFragment:
2081            case SyntaxKind.YieldExpression:
2082            case SyntaxKind.AwaitExpression:
2083            case SyntaxKind.MetaProperty:
2084                return true;
2085            case SyntaxKind.ExpressionWithTypeArguments:
2086                return !isHeritageClause(node.parent);
2087            case SyntaxKind.QualifiedName:
2088                while (node.parent.kind === SyntaxKind.QualifiedName) {
2089                    node = node.parent;
2090                }
2091                return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node);
2092            case SyntaxKind.JSDocMemberName:
2093                while (isJSDocMemberName(node.parent)) {
2094                    node = node.parent;
2095                }
2096                return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node);
2097            case SyntaxKind.PrivateIdentifier:
2098                return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword;
2099            case SyntaxKind.Identifier:
2100                if (node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node)) {
2101                    return true;
2102                }
2103                // falls through
2104
2105            case SyntaxKind.NumericLiteral:
2106            case SyntaxKind.BigIntLiteral:
2107            case SyntaxKind.StringLiteral:
2108            case SyntaxKind.NoSubstitutionTemplateLiteral:
2109            case SyntaxKind.ThisKeyword:
2110                return isInExpressionContext(node);
2111            default:
2112                return false;
2113        }
2114    }
2115
2116    export function isInExpressionContext(node: Node): boolean {
2117        const { parent } = node;
2118        switch (parent.kind) {
2119            case SyntaxKind.VariableDeclaration:
2120            case SyntaxKind.Parameter:
2121            case SyntaxKind.PropertyDeclaration:
2122            case SyntaxKind.PropertySignature:
2123            case SyntaxKind.EnumMember:
2124            case SyntaxKind.PropertyAssignment:
2125            case SyntaxKind.BindingElement:
2126                return (parent as HasInitializer).initializer === node;
2127            case SyntaxKind.ExpressionStatement:
2128            case SyntaxKind.IfStatement:
2129            case SyntaxKind.DoStatement:
2130            case SyntaxKind.WhileStatement:
2131            case SyntaxKind.ReturnStatement:
2132            case SyntaxKind.WithStatement:
2133            case SyntaxKind.SwitchStatement:
2134            case SyntaxKind.CaseClause:
2135            case SyntaxKind.ThrowStatement:
2136                return (parent as ExpressionStatement).expression === node;
2137            case SyntaxKind.ForStatement:
2138                const forStatement = parent as ForStatement;
2139                return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
2140                    forStatement.condition === node ||
2141                    forStatement.incrementor === node;
2142            case SyntaxKind.ForInStatement:
2143            case SyntaxKind.ForOfStatement:
2144                const forInStatement = parent as ForInStatement | ForOfStatement;
2145                return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
2146                    forInStatement.expression === node;
2147            case SyntaxKind.TypeAssertionExpression:
2148            case SyntaxKind.AsExpression:
2149                return node === (parent as AssertionExpression).expression;
2150            case SyntaxKind.TemplateSpan:
2151                return node === (parent as TemplateSpan).expression;
2152            case SyntaxKind.ComputedPropertyName:
2153                return node === (parent as ComputedPropertyName).expression;
2154            case SyntaxKind.Decorator:
2155            case SyntaxKind.JsxExpression:
2156            case SyntaxKind.JsxSpreadAttribute:
2157            case SyntaxKind.SpreadAssignment:
2158                return true;
2159            case SyntaxKind.ExpressionWithTypeArguments:
2160                return (parent as ExpressionWithTypeArguments).expression === node && !isPartOfTypeNode(parent);
2161            case SyntaxKind.ShorthandPropertyAssignment:
2162                return (parent as ShorthandPropertyAssignment).objectAssignmentInitializer === node;
2163            case SyntaxKind.SatisfiesExpression:
2164                return node === (parent as SatisfiesExpression).expression;
2165            default:
2166                return isExpressionNode(parent);
2167        }
2168    }
2169
2170    export function isPartOfTypeQuery(node: Node) {
2171        while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) {
2172            node = node.parent;
2173        }
2174        return node.kind === SyntaxKind.TypeQuery;
2175    }
2176
2177    export function isNamespaceReexportDeclaration(node: Node): boolean {
2178        return isNamespaceExport(node) && !!node.parent.moduleSpecifier;
2179    }
2180
2181    export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } {
2182        return node.kind === SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration).moduleReference.kind === SyntaxKind.ExternalModuleReference;
2183    }
2184
2185    export function getExternalModuleImportEqualsDeclarationExpression(node: Node) {
2186        Debug.assert(isExternalModuleImportEqualsDeclaration(node));
2187        return ((node as ImportEqualsDeclaration).moduleReference as ExternalModuleReference).expression;
2188    }
2189
2190    export function getExternalModuleRequireArgument(node: Node) {
2191        return isVariableDeclarationInitializedToBareOrAccessedRequire(node) && (getLeftmostAccessExpression(node.initializer) as CallExpression).arguments[0] as StringLiteral;
2192    }
2193
2194    export function isInternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration {
2195        return node.kind === SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration).moduleReference.kind !== SyntaxKind.ExternalModuleReference;
2196    }
2197
2198    export function isSourceFileJS(file: SourceFile): boolean {
2199        return isInJSFile(file);
2200    }
2201
2202    export function isSourceFileNotJS(file: SourceFile): boolean {
2203        return !isInJSFile(file);
2204    }
2205
2206    export function isInJSFile(node: Node | undefined): boolean {
2207        return !!node && !!(node.flags & NodeFlags.JavaScriptFile);
2208    }
2209
2210    export function isInJsonFile(node: Node | undefined): boolean {
2211        return !!node && !!(node.flags & NodeFlags.JsonFile);
2212    }
2213
2214    export function isSourceFileNotJson(file: SourceFile) {
2215        return !isJsonSourceFile(file);
2216    }
2217
2218    export function isInJSDoc(node: Node | undefined): boolean {
2219        return !!node && !!(node.flags & NodeFlags.JSDoc);
2220    }
2221
2222    export function isJSDocIndexSignature(node: TypeReferenceNode | ExpressionWithTypeArguments) {
2223        return isTypeReferenceNode(node) &&
2224            isIdentifier(node.typeName) &&
2225            node.typeName.escapedText === "Object" &&
2226            node.typeArguments && node.typeArguments.length === 2 &&
2227            (node.typeArguments[0].kind === SyntaxKind.StringKeyword || node.typeArguments[0].kind === SyntaxKind.NumberKeyword);
2228    }
2229
2230    export function isInETSFile(node: Node | undefined): boolean {
2231        return !!node && getSourceFileOfNode(node).scriptKind === ScriptKind.ETS;
2232    }
2233
2234    export function isInBuildOrPageTransitionContext(node: Node | undefined, compilerOptions: CompilerOptions): boolean {
2235        if (!node) {
2236            return false;
2237        }
2238        const methodNames = compilerOptions.ets?.render?.method;
2239        const decoratorNames = compilerOptions.ets?.render?.decorator;
2240        if (!methodNames && !decoratorNames) {
2241            return false;
2242        }
2243
2244        let container = getContainingFunctionDeclaration(node);
2245        while (container) {
2246            // check if is in build or pageTransition method
2247            if (methodNames && isMethodDeclaration(container) && isInStruct(container)) {
2248                const containerMethodName = getTextOfPropertyName(container.name).toString();
2249                if (methodNames.some(name => name === containerMethodName)) {
2250                    return true;
2251                }
2252            }
2253
2254            // check if is in function or method with the decorator "@Builder @LocalBuilder"
2255            const decorators = getAllDecorators(container);
2256            if (decoratorNames && decoratorNames.length &&
2257                (isMethodDeclaration(container) || isFunctionDeclaration(container)) &&
2258                decorators && decorators.some(
2259                    decorator => isIdentifier(decorator.expression) && decoratorNames.includes(getTextOfPropertyName(decorator.expression).toString()))) {
2260                        return true;
2261            }
2262
2263            container = getContainingFunctionDeclaration(container);
2264        }
2265
2266        return false;
2267    }
2268
2269    function isInStruct(node: MethodDeclaration): boolean {
2270        const container = getContainingClass(node);
2271        return !!container && isStruct(container);
2272    }
2273
2274    /**
2275     * Returns true if the node is a CallExpression to the identifier 'require' with
2276     * exactly one argument (of the form 'require("name")').
2277     * This function does not test if the node is in a JavaScript file or not.
2278     */
2279    export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: true): callExpression is RequireOrImportCall & { expression: Identifier, arguments: [StringLiteralLike] };
2280    export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression;
2281    export function isRequireCall(callExpression: Node, requireStringLiteralLikeArgument: boolean): callExpression is CallExpression {
2282        if (callExpression.kind !== SyntaxKind.CallExpression) {
2283            return false;
2284        }
2285        const { expression, arguments: args } = callExpression as CallExpression;
2286
2287        if (expression.kind !== SyntaxKind.Identifier || (expression as Identifier).escapedText !== "require") {
2288            return false;
2289        }
2290
2291        if (args.length !== 1) {
2292            return false;
2293        }
2294        const arg = args[0];
2295        return !requireStringLiteralLikeArgument || isStringLiteralLike(arg);
2296    }
2297
2298    /**
2299     * Returns true if the node is a VariableDeclaration initialized to a require call (see `isRequireCall`).
2300     * This function does not test if the node is in a JavaScript file or not.
2301     */
2302    export function isVariableDeclarationInitializedToRequire(node: Node): node is VariableDeclarationInitializedTo<RequireOrImportCall> {
2303        return isVariableDeclarationInitializedWithRequireHelper(node, /*allowAccessedRequire*/ false);
2304    }
2305
2306    /**
2307     * Like {@link isVariableDeclarationInitializedToRequire} but allows things like `require("...").foo.bar` or `require("...")["baz"]`.
2308     */
2309    export function isVariableDeclarationInitializedToBareOrAccessedRequire(node: Node): node is VariableDeclarationInitializedTo<RequireOrImportCall | AccessExpression> {
2310        return isVariableDeclarationInitializedWithRequireHelper(node, /*allowAccessedRequire*/ true);
2311    }
2312
2313    function isVariableDeclarationInitializedWithRequireHelper(node: Node, allowAccessedRequire: boolean) {
2314        return isVariableDeclaration(node) &&
2315            !!node.initializer &&
2316            isRequireCall(allowAccessedRequire ? getLeftmostAccessExpression(node.initializer) : node.initializer, /*requireStringLiteralLikeArgument*/ true);
2317    }
2318
2319    export function isRequireVariableStatement(node: Node): node is RequireVariableStatement {
2320        return isVariableStatement(node)
2321            && node.declarationList.declarations.length > 0
2322            && every(node.declarationList.declarations, decl => isVariableDeclarationInitializedToRequire(decl));
2323    }
2324
2325    export function isSingleOrDoubleQuote(charCode: number) {
2326        return charCode === CharacterCodes.singleQuote || charCode === CharacterCodes.doubleQuote;
2327    }
2328
2329    export function isStringDoubleQuoted(str: StringLiteralLike, sourceFile: SourceFile): boolean {
2330        return getSourceTextOfNodeFromSourceFile(sourceFile, str).charCodeAt(0) === CharacterCodes.doubleQuote;
2331    }
2332
2333    export function isAssignmentDeclaration(decl: Declaration) {
2334        return isBinaryExpression(decl) || isAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl);
2335    }
2336
2337    /** Get the initializer, taking into account defaulted Javascript initializers */
2338    export function getEffectiveInitializer(node: HasExpressionInitializer) {
2339        if (isInJSFile(node) && node.initializer &&
2340            isBinaryExpression(node.initializer) &&
2341                (node.initializer.operatorToken.kind === SyntaxKind.BarBarToken || node.initializer.operatorToken.kind === SyntaxKind.QuestionQuestionToken) &&
2342            node.name && isEntityNameExpression(node.name) && isSameEntityName(node.name, node.initializer.left)) {
2343            return node.initializer.right;
2344        }
2345        return node.initializer;
2346    }
2347
2348    /** Get the declaration initializer when it is container-like (See getExpandoInitializer). */
2349    export function getDeclaredExpandoInitializer(node: HasExpressionInitializer) {
2350        const init = getEffectiveInitializer(node);
2351        return init && getExpandoInitializer(init, isPrototypeAccess(node.name));
2352    }
2353
2354    function hasExpandoValueProperty(node: ObjectLiteralExpression, isPrototypeAssignment: boolean) {
2355        return forEach(node.properties, p =>
2356            isPropertyAssignment(p) &&
2357            isIdentifier(p.name) &&
2358            p.name.escapedText === "value" &&
2359            p.initializer &&
2360            getExpandoInitializer(p.initializer, isPrototypeAssignment));
2361    }
2362
2363    /**
2364     * Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer).
2365     * We treat the right hand side of assignments with container-like initializers as declarations.
2366     */
2367    export function getAssignedExpandoInitializer(node: Node | undefined): Expression | undefined {
2368        if (node && node.parent && isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken) {
2369            const isPrototypeAssignment = isPrototypeAccess(node.parent.left);
2370            return getExpandoInitializer(node.parent.right, isPrototypeAssignment) ||
2371                getDefaultedExpandoInitializer(node.parent.left, node.parent.right, isPrototypeAssignment);
2372        }
2373        if (node && isCallExpression(node) && isBindableObjectDefinePropertyCall(node)) {
2374            const result = hasExpandoValueProperty(node.arguments[2], node.arguments[1].text === "prototype");
2375            if (result) {
2376                return result;
2377            }
2378        }
2379    }
2380
2381    /**
2382     * Recognized expando initializers are:
2383     * 1. (function() {})() -- IIFEs
2384     * 2. function() { } -- Function expressions
2385     * 3. class { } -- Class expressions
2386     * 4. {} -- Empty object literals
2387     * 5. { ... } -- Non-empty object literals, when used to initialize a prototype, like `C.prototype = { m() { } }`
2388     *
2389     * This function returns the provided initializer, or undefined if it is not valid.
2390     */
2391    export function getExpandoInitializer(initializer: Node, isPrototypeAssignment: boolean): Expression | undefined {
2392        if (isCallExpression(initializer)) {
2393            const e = skipParentheses(initializer.expression);
2394            return e.kind === SyntaxKind.FunctionExpression || e.kind === SyntaxKind.ArrowFunction ? initializer : undefined;
2395        }
2396        if (initializer.kind === SyntaxKind.FunctionExpression ||
2397            initializer.kind === SyntaxKind.ClassExpression ||
2398            initializer.kind === SyntaxKind.ArrowFunction) {
2399            return initializer as Expression;
2400        }
2401        if (isObjectLiteralExpression(initializer) && (initializer.properties.length === 0 || isPrototypeAssignment)) {
2402            return initializer;
2403        }
2404    }
2405
2406    /**
2407     * A defaulted expando initializer matches the pattern
2408     * `Lhs = Lhs || ExpandoInitializer`
2409     * or `var Lhs = Lhs || ExpandoInitializer`
2410     *
2411     * The second Lhs is required to be the same as the first except that it may be prefixed with
2412     * 'window.', 'global.' or 'self.' The second Lhs is otherwise ignored by the binder and checker.
2413     */
2414    function getDefaultedExpandoInitializer(name: Expression, initializer: Expression, isPrototypeAssignment: boolean) {
2415        const e = isBinaryExpression(initializer)
2416            && (initializer.operatorToken.kind === SyntaxKind.BarBarToken || initializer.operatorToken.kind === SyntaxKind.QuestionQuestionToken)
2417            && getExpandoInitializer(initializer.right, isPrototypeAssignment);
2418        if (e && isSameEntityName(name, initializer.left)) {
2419            return e;
2420        }
2421    }
2422
2423    export function isDefaultedExpandoInitializer(node: BinaryExpression) {
2424        const name = isVariableDeclaration(node.parent) ? node.parent.name :
2425            isBinaryExpression(node.parent) && node.parent.operatorToken.kind === SyntaxKind.EqualsToken ? node.parent.left :
2426            undefined;
2427        return name && getExpandoInitializer(node.right, isPrototypeAccess(name)) && isEntityNameExpression(name) && isSameEntityName(name, node.left);
2428    }
2429
2430    /** Given an expando initializer, return its declaration name, or the left-hand side of the assignment if it's part of an assignment declaration. */
2431    export function getNameOfExpando(node: Declaration): DeclarationName | undefined {
2432        if (isBinaryExpression(node.parent)) {
2433            const parent = ((node.parent.operatorToken.kind === SyntaxKind.BarBarToken || node.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken) && isBinaryExpression(node.parent.parent)) ? node.parent.parent : node.parent;
2434            if (parent.operatorToken.kind === SyntaxKind.EqualsToken && isIdentifier(parent.left)) {
2435                return parent.left;
2436            }
2437        }
2438        else if (isVariableDeclaration(node.parent)) {
2439            return node.parent.name;
2440        }
2441    }
2442
2443    /**
2444     * Is the 'declared' name the same as the one in the initializer?
2445     * @return true for identical entity names, as well as ones where the initializer is prefixed with
2446     * 'window', 'self' or 'global'. For example:
2447     *
2448     * var my = my || {}
2449     * var min = window.min || {}
2450     * my.app = self.my.app || class { }
2451     */
2452    export function isSameEntityName(name: Expression, initializer: Expression): boolean {
2453        if (isPropertyNameLiteral(name) && isPropertyNameLiteral(initializer)) {
2454            return getTextOfIdentifierOrLiteral(name) === getTextOfIdentifierOrLiteral(initializer);
2455        }
2456        if (isMemberName(name) && isLiteralLikeAccess(initializer) &&
2457            (initializer.expression.kind === SyntaxKind.ThisKeyword ||
2458                isIdentifier(initializer.expression) &&
2459                (initializer.expression.escapedText === "window" ||
2460                    initializer.expression.escapedText === "self" ||
2461                    initializer.expression.escapedText === "global"))) {
2462            return isSameEntityName(name, getNameOrArgument(initializer));
2463        }
2464        if (isLiteralLikeAccess(name) && isLiteralLikeAccess(initializer)) {
2465            return getElementOrPropertyAccessName(name) === getElementOrPropertyAccessName(initializer)
2466                && isSameEntityName(name.expression, initializer.expression);
2467        }
2468        return false;
2469    }
2470
2471    export function getRightMostAssignedExpression(node: Expression): Expression {
2472        while (isAssignmentExpression(node, /*excludeCompoundAssignments*/ true)) {
2473            node = node.right;
2474        }
2475        return node;
2476    }
2477
2478    export function isExportsIdentifier(node: Node) {
2479        return isIdentifier(node) && node.escapedText === "exports";
2480    }
2481
2482    export function isModuleIdentifier(node: Node) {
2483        return isIdentifier(node) && node.escapedText === "module";
2484    }
2485
2486    export function isModuleExportsAccessExpression(node: Node): node is LiteralLikeElementAccessExpression & { expression: Identifier } {
2487        return (isPropertyAccessExpression(node) || isLiteralLikeElementAccess(node))
2488            && isModuleIdentifier(node.expression)
2489            && getElementOrPropertyAccessName(node) === "exports";
2490    }
2491
2492    /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
2493    /// assignments we treat as special in the binder
2494    export function getAssignmentDeclarationKind(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind {
2495        const special = getAssignmentDeclarationKindWorker(expr);
2496        return special === AssignmentDeclarationKind.Property || isInJSFile(expr) ? special : AssignmentDeclarationKind.None;
2497    }
2498
2499    export function isBindableObjectDefinePropertyCall(expr: CallExpression): expr is BindableObjectDefinePropertyCall {
2500        return length(expr.arguments) === 3 &&
2501            isPropertyAccessExpression(expr.expression) &&
2502            isIdentifier(expr.expression.expression) &&
2503            idText(expr.expression.expression) === "Object" &&
2504            idText(expr.expression.name) === "defineProperty" &&
2505            isStringOrNumericLiteralLike(expr.arguments[1]) &&
2506            isBindableStaticNameExpression(expr.arguments[0], /*excludeThisKeyword*/ true);
2507    }
2508
2509    /** x.y OR x[0] */
2510    export function isLiteralLikeAccess(node: Node): node is LiteralLikeElementAccessExpression | PropertyAccessExpression {
2511        return isPropertyAccessExpression(node) || isLiteralLikeElementAccess(node);
2512    }
2513
2514    /** x[0] OR x['a'] OR x[Symbol.y] */
2515    export function isLiteralLikeElementAccess(node: Node): node is LiteralLikeElementAccessExpression {
2516        return isElementAccessExpression(node) && isStringOrNumericLiteralLike(node.argumentExpression);
2517    }
2518
2519    /** Any series of property and element accesses. */
2520    export function isBindableStaticAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticAccessExpression {
2521        return isPropertyAccessExpression(node) && (!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword || isIdentifier(node.name) && isBindableStaticNameExpression(node.expression, /*excludeThisKeyword*/ true))
2522            || isBindableStaticElementAccessExpression(node, excludeThisKeyword);
2523    }
2524
2525    /** Any series of property and element accesses, ending in a literal element access */
2526    export function isBindableStaticElementAccessExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticElementAccessExpression {
2527        return isLiteralLikeElementAccess(node)
2528            && ((!excludeThisKeyword && node.expression.kind === SyntaxKind.ThisKeyword) ||
2529                isEntityNameExpression(node.expression) ||
2530                isBindableStaticAccessExpression(node.expression, /*excludeThisKeyword*/ true));
2531    }
2532
2533    export function isBindableStaticNameExpression(node: Node, excludeThisKeyword?: boolean): node is BindableStaticNameExpression {
2534        return isEntityNameExpression(node) || isBindableStaticAccessExpression(node, excludeThisKeyword);
2535    }
2536
2537    export function getNameOrArgument(expr: PropertyAccessExpression | LiteralLikeElementAccessExpression) {
2538        if (isPropertyAccessExpression(expr)) {
2539            return expr.name;
2540        }
2541        return expr.argumentExpression;
2542    }
2543
2544    function getAssignmentDeclarationKindWorker(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind {
2545        if (isCallExpression(expr)) {
2546            if (!isBindableObjectDefinePropertyCall(expr)) {
2547                return AssignmentDeclarationKind.None;
2548            }
2549            const entityName = expr.arguments[0];
2550            if (isExportsIdentifier(entityName) || isModuleExportsAccessExpression(entityName)) {
2551                return AssignmentDeclarationKind.ObjectDefinePropertyExports;
2552            }
2553            if (isBindableStaticAccessExpression(entityName) && getElementOrPropertyAccessName(entityName) === "prototype") {
2554                return AssignmentDeclarationKind.ObjectDefinePrototypeProperty;
2555            }
2556            return AssignmentDeclarationKind.ObjectDefinePropertyValue;
2557        }
2558        if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || !isAccessExpression(expr.left) || isVoidZero(getRightMostAssignedExpression(expr))) {
2559            return AssignmentDeclarationKind.None;
2560        }
2561        if (isBindableStaticNameExpression(expr.left.expression, /*excludeThisKeyword*/ true) && getElementOrPropertyAccessName(expr.left) === "prototype" && isObjectLiteralExpression(getInitializerOfBinaryExpression(expr))) {
2562            // F.prototype = { ... }
2563            return AssignmentDeclarationKind.Prototype;
2564        }
2565        return getAssignmentDeclarationPropertyAccessKind(expr.left);
2566    }
2567
2568    function isVoidZero(node: Node) {
2569        return isVoidExpression(node) && isNumericLiteral(node.expression) && node.expression.text === "0";
2570    }
2571
2572    /**
2573     * Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions
2574     * throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand)
2575     */
2576    /* @internal */
2577    export function getElementOrPropertyAccessArgumentExpressionOrName(node: AccessExpression): Identifier | PrivateIdentifier | StringLiteralLike | NumericLiteral | ElementAccessExpression | undefined {
2578        if (isPropertyAccessExpression(node)) {
2579            return node.name;
2580        }
2581        const arg = skipParentheses(node.argumentExpression);
2582        if (isNumericLiteral(arg) || isStringLiteralLike(arg)) {
2583            return arg;
2584        }
2585        return node;
2586    }
2587
2588    /* @internal */
2589    export function getElementOrPropertyAccessName(node: LiteralLikeElementAccessExpression | PropertyAccessExpression): __String;
2590    export function getElementOrPropertyAccessName(node: AccessExpression): __String | undefined;
2591    export function getElementOrPropertyAccessName(node: AccessExpression): __String | undefined {
2592        const name = getElementOrPropertyAccessArgumentExpressionOrName(node);
2593        if (name) {
2594            if (isIdentifier(name)) {
2595                return name.escapedText;
2596            }
2597            if (isStringLiteralLike(name) || isNumericLiteral(name)) {
2598                return escapeLeadingUnderscores(name.text);
2599            }
2600        }
2601        return undefined;
2602    }
2603
2604    export function getAssignmentDeclarationPropertyAccessKind(lhs: AccessExpression): AssignmentDeclarationKind {
2605        if (lhs.expression.kind === SyntaxKind.ThisKeyword) {
2606            return AssignmentDeclarationKind.ThisProperty;
2607        }
2608        else if (isModuleExportsAccessExpression(lhs)) {
2609            // module.exports = expr
2610            return AssignmentDeclarationKind.ModuleExports;
2611        }
2612        else if (isBindableStaticNameExpression(lhs.expression, /*excludeThisKeyword*/ true)) {
2613            if (isPrototypeAccess(lhs.expression)) {
2614                // F.G....prototype.x = expr
2615                return AssignmentDeclarationKind.PrototypeProperty;
2616            }
2617
2618            let nextToLast = lhs;
2619            while (!isIdentifier(nextToLast.expression)) {
2620                nextToLast = nextToLast.expression as Exclude<BindableStaticNameExpression, Identifier>;
2621            }
2622            const id = nextToLast.expression;
2623            if ((id.escapedText === "exports" ||
2624                id.escapedText === "module" && getElementOrPropertyAccessName(nextToLast) === "exports") &&
2625                // ExportsProperty does not support binding with computed names
2626                isBindableStaticAccessExpression(lhs)) {
2627                // exports.name = expr OR module.exports.name = expr OR exports["name"] = expr ...
2628                return AssignmentDeclarationKind.ExportsProperty;
2629            }
2630            if (isBindableStaticNameExpression(lhs, /*excludeThisKeyword*/ true) || (isElementAccessExpression(lhs) && isDynamicName(lhs))) {
2631                // F.G...x = expr
2632                return AssignmentDeclarationKind.Property;
2633            }
2634        }
2635
2636        return AssignmentDeclarationKind.None;
2637    }
2638
2639    export function getInitializerOfBinaryExpression(expr: BinaryExpression) {
2640        while (isBinaryExpression(expr.right)) {
2641            expr = expr.right;
2642        }
2643        return expr.right;
2644    }
2645
2646    export function isPrototypePropertyAssignment(node: Node): node is BinaryExpression {
2647        return isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.PrototypeProperty;
2648    }
2649
2650    export function isSpecialPropertyDeclaration(expr: PropertyAccessExpression | ElementAccessExpression): expr is PropertyAccessExpression | LiteralLikeElementAccessExpression {
2651        return isInJSFile(expr) &&
2652            expr.parent && expr.parent.kind === SyntaxKind.ExpressionStatement &&
2653            (!isElementAccessExpression(expr) || isLiteralLikeElementAccess(expr)) &&
2654            !!getJSDocTypeTag(expr.parent);
2655    }
2656
2657    export function setValueDeclaration(symbol: Symbol, node: Declaration): void {
2658        const { valueDeclaration } = symbol;
2659        if (!valueDeclaration ||
2660            !(node.flags & NodeFlags.Ambient && !(valueDeclaration.flags & NodeFlags.Ambient)) &&
2661            (isAssignmentDeclaration(valueDeclaration) && !isAssignmentDeclaration(node)) ||
2662            (valueDeclaration.kind !== node.kind && isEffectiveModuleDeclaration(valueDeclaration))) {
2663            // other kinds of value declarations take precedence over modules and assignment declarations
2664            symbol.valueDeclaration = node;
2665        }
2666    }
2667
2668    export function isFunctionSymbol(symbol: Symbol | undefined) {
2669        if (!symbol || !symbol.valueDeclaration) {
2670            return false;
2671        }
2672        const decl = symbol.valueDeclaration;
2673        return decl.kind === SyntaxKind.FunctionDeclaration || isVariableDeclaration(decl) && decl.initializer && isFunctionLike(decl.initializer);
2674    }
2675
2676    export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire): StringLiteralLike | undefined {
2677        switch (node.kind) {
2678            case SyntaxKind.VariableDeclaration:
2679                return findAncestor(node.initializer, (node): node is RequireOrImportCall => isRequireCall(node, /*requireStringLiteralLikeArgument*/ true))?.arguments[0];
2680            case SyntaxKind.ImportDeclaration:
2681                return tryCast(node.moduleSpecifier, isStringLiteralLike);
2682            case SyntaxKind.ImportEqualsDeclaration:
2683                return tryCast(tryCast(node.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike);
2684            default:
2685                Debug.assertNever(node);
2686        }
2687    }
2688
2689    export function importFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport {
2690        return tryGetImportFromModuleSpecifier(node) || Debug.failBadSyntaxKind(node.parent);
2691    }
2692
2693    export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyValidImportOrReExport | undefined {
2694        switch (node.parent.kind) {
2695            case SyntaxKind.ImportDeclaration:
2696            case SyntaxKind.ExportDeclaration:
2697                return node.parent as AnyValidImportOrReExport;
2698            case SyntaxKind.ExternalModuleReference:
2699                return (node.parent as ExternalModuleReference).parent as AnyValidImportOrReExport;
2700            case SyntaxKind.CallExpression:
2701                return isImportCall(node.parent) || isRequireCall(node.parent, /*checkArg*/ false) ? node.parent as RequireOrImportCall : undefined;
2702            case SyntaxKind.LiteralType:
2703                Debug.assert(isStringLiteral(node));
2704                return tryCast(node.parent.parent, isImportTypeNode) as ValidImportTypeNode | undefined;
2705            default:
2706                return undefined;
2707        }
2708    }
2709
2710    export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode | ImportCall | ModuleDeclaration): Expression | undefined {
2711        switch (node.kind) {
2712            case SyntaxKind.ImportDeclaration:
2713            case SyntaxKind.ExportDeclaration:
2714                return node.moduleSpecifier;
2715            case SyntaxKind.ImportEqualsDeclaration:
2716                return node.moduleReference.kind === SyntaxKind.ExternalModuleReference ? node.moduleReference.expression : undefined;
2717            case SyntaxKind.ImportType:
2718                return isLiteralImportTypeNode(node) ? node.argument.literal : undefined;
2719            case SyntaxKind.CallExpression:
2720                return node.arguments[0];
2721            case SyntaxKind.ModuleDeclaration:
2722                return node.name.kind === SyntaxKind.StringLiteral ? node.name : undefined;
2723            default:
2724                return Debug.assertNever(node);
2725        }
2726    }
2727
2728    export function getNamespaceDeclarationNode(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): ImportEqualsDeclaration | NamespaceImport | NamespaceExport | undefined {
2729        switch (node.kind) {
2730            case SyntaxKind.ImportDeclaration:
2731                return node.importClause && tryCast(node.importClause.namedBindings, isNamespaceImport);
2732            case SyntaxKind.ImportEqualsDeclaration:
2733                return node;
2734            case SyntaxKind.ExportDeclaration:
2735                return node.exportClause && tryCast(node.exportClause, isNamespaceExport);
2736            default:
2737                return Debug.assertNever(node);
2738        }
2739    }
2740
2741    export function isDefaultImport(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean {
2742        return node.kind === SyntaxKind.ImportDeclaration && !!node.importClause && !!node.importClause.name;
2743    }
2744
2745    export function forEachImportClauseDeclaration<T>(node: ImportClause, action: (declaration: ImportClause | NamespaceImport | ImportSpecifier) => T | undefined): T | undefined {
2746        if (node.name) {
2747            const result = action(node);
2748            if (result) return result;
2749        }
2750        if (node.namedBindings) {
2751            const result = isNamespaceImport(node.namedBindings)
2752                ? action(node.namedBindings)
2753                : forEach(node.namedBindings.elements, action);
2754            if (result) return result;
2755        }
2756    }
2757
2758    export function hasQuestionToken(node: Node) {
2759        if (node) {
2760            switch (node.kind) {
2761                case SyntaxKind.Parameter:
2762                case SyntaxKind.MethodDeclaration:
2763                case SyntaxKind.MethodSignature:
2764                case SyntaxKind.ShorthandPropertyAssignment:
2765                case SyntaxKind.PropertyAssignment:
2766                case SyntaxKind.PropertyDeclaration:
2767                case SyntaxKind.PropertySignature:
2768                    return (node as ParameterDeclaration | MethodDeclaration | PropertyDeclaration).questionToken !== undefined;
2769            }
2770        }
2771
2772        return false;
2773    }
2774
2775    export function isJSDocConstructSignature(node: Node) {
2776        const param = isJSDocFunctionType(node) ? firstOrUndefined(node.parameters) : undefined;
2777        const name = tryCast(param && param.name, isIdentifier);
2778        return !!name && name.escapedText === "new";
2779    }
2780
2781    export function isJSDocTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag {
2782        return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag || node.kind === SyntaxKind.JSDocEnumTag;
2783    }
2784
2785    export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag | TypeAliasDeclaration {
2786        return isJSDocTypeAlias(node) || isTypeAliasDeclaration(node);
2787    }
2788
2789    function getSourceOfAssignment(node: Node): Node | undefined {
2790        return isExpressionStatement(node) &&
2791            isBinaryExpression(node.expression) &&
2792            node.expression.operatorToken.kind === SyntaxKind.EqualsToken
2793            ? getRightMostAssignedExpression(node.expression)
2794            : undefined;
2795    }
2796
2797    function getSourceOfDefaultedAssignment(node: Node): Node | undefined {
2798        return isExpressionStatement(node) &&
2799            isBinaryExpression(node.expression) &&
2800            getAssignmentDeclarationKind(node.expression) !== AssignmentDeclarationKind.None &&
2801            isBinaryExpression(node.expression.right) &&
2802            (node.expression.right.operatorToken.kind === SyntaxKind.BarBarToken || node.expression.right.operatorToken.kind === SyntaxKind.QuestionQuestionToken)
2803            ? node.expression.right.right
2804            : undefined;
2805    }
2806
2807    export function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined {
2808        switch (node.kind) {
2809            case SyntaxKind.VariableStatement:
2810                const v = getSingleVariableOfVariableStatement(node);
2811                return v && v.initializer;
2812            case SyntaxKind.PropertyDeclaration:
2813                return (node as PropertyDeclaration).initializer;
2814            case SyntaxKind.PropertyAssignment:
2815                return (node as PropertyAssignment).initializer;
2816        }
2817    }
2818
2819    export function getSingleVariableOfVariableStatement(node: Node): VariableDeclaration | undefined {
2820        return isVariableStatement(node) ? firstOrUndefined(node.declarationList.declarations) : undefined;
2821    }
2822
2823    function getNestedModuleDeclaration(node: Node): Node | undefined {
2824        return isModuleDeclaration(node) &&
2825            node.body &&
2826            node.body.kind === SyntaxKind.ModuleDeclaration
2827            ? node.body
2828            : undefined;
2829    }
2830
2831    export function getJSDocCommentsAndTags(hostNode: Node, noCache?: boolean): readonly (JSDoc | JSDocTag)[] {
2832        let result: (JSDoc | JSDocTag)[] | undefined;
2833        // Pull parameter comments from declaring function as well
2834        if (isVariableLike(hostNode) && hasInitializer(hostNode) && hasJSDocNodes(hostNode.initializer!)) {
2835            result = addRange(result, filterOwnedJSDocTags(hostNode, last((hostNode.initializer as HasJSDoc).jsDoc!)));
2836        }
2837
2838        let node: Node | undefined = hostNode;
2839        while (node && node.parent) {
2840            if (hasJSDocNodes(node)) {
2841                result = addRange(result, filterOwnedJSDocTags(hostNode, last(node.jsDoc!)));
2842            }
2843
2844            if (node.kind === SyntaxKind.Parameter) {
2845                result = addRange(result, (noCache ? getJSDocParameterTagsNoCache : getJSDocParameterTags)(node as ParameterDeclaration));
2846                break;
2847            }
2848            if (node.kind === SyntaxKind.TypeParameter) {
2849                result = addRange(result, (noCache ? getJSDocTypeParameterTagsNoCache : getJSDocTypeParameterTags)(node as TypeParameterDeclaration));
2850                break;
2851            }
2852            node = getNextJSDocCommentLocation(node);
2853        }
2854        return result || emptyArray;
2855    }
2856
2857    function filterOwnedJSDocTags(hostNode: Node, jsDoc: JSDoc | JSDocTag) {
2858        if (isJSDoc(jsDoc)) {
2859            const ownedTags = filter(jsDoc.tags, tag => ownsJSDocTag(hostNode, tag));
2860            return jsDoc.tags === ownedTags ? [jsDoc] : ownedTags;
2861        }
2862        return ownsJSDocTag(hostNode, jsDoc) ? [jsDoc] : undefined;
2863    }
2864
2865    /**
2866     * Determines whether a host node owns a jsDoc tag. A `@type` tag attached to a
2867     * a ParenthesizedExpression belongs only to the ParenthesizedExpression.
2868     */
2869    function ownsJSDocTag(hostNode: Node, tag: JSDocTag) {
2870        return !isJSDocTypeTag(tag)
2871            || !tag.parent
2872            || !isJSDoc(tag.parent)
2873            || !isParenthesizedExpression(tag.parent.parent)
2874            || tag.parent.parent === hostNode;
2875    }
2876
2877    export function getNextJSDocCommentLocation(node: Node) {
2878        const parent = node.parent;
2879        if (parent.kind === SyntaxKind.PropertyAssignment ||
2880            parent.kind === SyntaxKind.ExportAssignment ||
2881            parent.kind === SyntaxKind.PropertyDeclaration ||
2882            parent.kind === SyntaxKind.ExpressionStatement && node.kind === SyntaxKind.PropertyAccessExpression ||
2883            parent.kind === SyntaxKind.ReturnStatement ||
2884            getNestedModuleDeclaration(parent) ||
2885            isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.EqualsToken) {
2886            return parent;
2887        }
2888        // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
2889        // /**
2890        //   * @param {number} name
2891        //   * @returns {number}
2892        //   */
2893        // var x = function(name) { return name.length; }
2894        else if (parent.parent &&
2895            (getSingleVariableOfVariableStatement(parent.parent) === node ||
2896            isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.EqualsToken)) {
2897            return parent.parent;
2898        }
2899        else if (parent.parent && parent.parent.parent &&
2900            (getSingleVariableOfVariableStatement(parent.parent.parent) ||
2901            getSingleInitializerOfVariableStatementOrPropertyDeclaration(parent.parent.parent) === node ||
2902            getSourceOfDefaultedAssignment(parent.parent.parent))) {
2903            return parent.parent.parent;
2904        }
2905    }
2906
2907    /** Does the opposite of `getJSDocParameterTags`: given a JSDoc parameter, finds the parameter corresponding to it. */
2908    export function getParameterSymbolFromJSDoc(node: JSDocParameterTag): Symbol | undefined {
2909        if (node.symbol) {
2910            return node.symbol;
2911        }
2912        if (!isIdentifier(node.name)) {
2913            return undefined;
2914        }
2915        const name = node.name.escapedText;
2916        const decl = getHostSignatureFromJSDoc(node);
2917        if (!decl) {
2918            return undefined;
2919        }
2920        const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name);
2921        return parameter && parameter.symbol;
2922    }
2923
2924    export function getEffectiveContainerForJSDocTemplateTag(node: JSDocTemplateTag) {
2925        if (isJSDoc(node.parent) && node.parent.tags) {
2926            // A @template tag belongs to any @typedef, @callback, or @enum tags in the same comment block, if they exist.
2927            const typeAlias = find(node.parent.tags, isJSDocTypeAlias);
2928            if (typeAlias) {
2929                return typeAlias;
2930            }
2931        }
2932        // otherwise it belongs to the host it annotates
2933        return getHostSignatureFromJSDoc(node);
2934    }
2935
2936    export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined {
2937        const host = getEffectiveJSDocHost(node);
2938        if (host) {
2939            return isPropertySignature(host) && host.type && isFunctionLike(host.type) ? host.type :
2940                isFunctionLike(host) ? host : undefined;
2941        }
2942        return undefined;
2943    }
2944
2945    export function getEffectiveJSDocHost(node: Node): Node | undefined {
2946        const host = getJSDocHost(node);
2947        if (host) {
2948            return getSourceOfDefaultedAssignment(host)
2949                || getSourceOfAssignment(host)
2950                || getSingleInitializerOfVariableStatementOrPropertyDeclaration(host)
2951                || getSingleVariableOfVariableStatement(host)
2952                || getNestedModuleDeclaration(host)
2953                || host;
2954        }
2955    }
2956
2957    /** Use getEffectiveJSDocHost if you additionally need to look for jsdoc on parent nodes, like assignments. */
2958    export function getJSDocHost(node: Node): HasJSDoc | undefined {
2959        const jsDoc = getJSDocRoot(node);
2960        if (!jsDoc) {
2961            return undefined;
2962        }
2963
2964        const host = jsDoc.parent;
2965        if (host && host.jsDoc && jsDoc === lastOrUndefined(host.jsDoc)) {
2966            return host;
2967        }
2968    }
2969
2970    export function getJSDocRoot(node: Node): JSDoc | undefined {
2971        return findAncestor(node.parent, isJSDoc);
2972    }
2973
2974    export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined {
2975        const name = node.name.escapedText;
2976        const { typeParameters } = (node.parent.parent.parent as SignatureDeclaration | InterfaceDeclaration | ClassDeclaration);
2977        return typeParameters && find(typeParameters, p => p.name.escapedText === name);
2978    }
2979
2980    export function hasTypeArguments(node: Node): node is HasTypeArguments {
2981        return !!(node as HasTypeArguments).typeArguments;
2982    }
2983
2984    export const enum AssignmentKind {
2985        None, Definite, Compound
2986    }
2987
2988    export function getAssignmentTargetKind(node: Node): AssignmentKind {
2989        let parent = node.parent;
2990        while (true) {
2991            switch (parent.kind) {
2992                case SyntaxKind.BinaryExpression:
2993                    const binaryOperator = (parent as BinaryExpression).operatorToken.kind;
2994                    return isAssignmentOperator(binaryOperator) && (parent as BinaryExpression).left === node ?
2995                        binaryOperator === SyntaxKind.EqualsToken || isLogicalOrCoalescingAssignmentOperator(binaryOperator) ? AssignmentKind.Definite : AssignmentKind.Compound :
2996                        AssignmentKind.None;
2997                case SyntaxKind.PrefixUnaryExpression:
2998                case SyntaxKind.PostfixUnaryExpression:
2999                    const unaryOperator = (parent as PrefixUnaryExpression | PostfixUnaryExpression).operator;
3000                    return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? AssignmentKind.Compound : AssignmentKind.None;
3001                case SyntaxKind.ForInStatement:
3002                case SyntaxKind.ForOfStatement:
3003                    return (parent as ForInOrOfStatement).initializer === node ? AssignmentKind.Definite : AssignmentKind.None;
3004                case SyntaxKind.ParenthesizedExpression:
3005                case SyntaxKind.ArrayLiteralExpression:
3006                case SyntaxKind.SpreadElement:
3007                case SyntaxKind.NonNullExpression:
3008                    node = parent;
3009                    break;
3010                case SyntaxKind.SpreadAssignment:
3011                    node = parent.parent;
3012                    break;
3013                case SyntaxKind.ShorthandPropertyAssignment:
3014                    if ((parent as ShorthandPropertyAssignment).name !== node) {
3015                        return AssignmentKind.None;
3016                    }
3017                    node = parent.parent;
3018                    break;
3019                case SyntaxKind.PropertyAssignment:
3020                    if ((parent as ShorthandPropertyAssignment).name === node) {
3021                        return AssignmentKind.None;
3022                    }
3023                    node = parent.parent;
3024                    break;
3025                default:
3026                    return AssignmentKind.None;
3027            }
3028            parent = node.parent;
3029        }
3030    }
3031
3032    // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property
3033    // assignment in an object literal that is an assignment target, or if it is parented by an array literal that is
3034    // an assignment target. Examples include 'a = xxx', '{ p: a } = xxx', '[{ a }] = xxx'.
3035    // (Note that `p` is not a target in the above examples, only `a`.)
3036    export function isAssignmentTarget(node: Node): boolean {
3037        return getAssignmentTargetKind(node) !== AssignmentKind.None;
3038    }
3039
3040    export type NodeWithPossibleHoistedDeclaration =
3041        | Block
3042        | VariableStatement
3043        | WithStatement
3044        | IfStatement
3045        | SwitchStatement
3046        | CaseBlock
3047        | CaseClause
3048        | DefaultClause
3049        | LabeledStatement
3050        | ForStatement
3051        | ForInStatement
3052        | ForOfStatement
3053        | DoStatement
3054        | WhileStatement
3055        | TryStatement
3056        | CatchClause;
3057
3058    /**
3059     * Indicates whether a node could contain a `var` VariableDeclarationList that contributes to
3060     * the same `var` declaration scope as the node's parent.
3061     */
3062    export function isNodeWithPossibleHoistedDeclaration(node: Node): node is NodeWithPossibleHoistedDeclaration {
3063        switch (node.kind) {
3064            case SyntaxKind.Block:
3065            case SyntaxKind.VariableStatement:
3066            case SyntaxKind.WithStatement:
3067            case SyntaxKind.IfStatement:
3068            case SyntaxKind.SwitchStatement:
3069            case SyntaxKind.CaseBlock:
3070            case SyntaxKind.CaseClause:
3071            case SyntaxKind.DefaultClause:
3072            case SyntaxKind.LabeledStatement:
3073            case SyntaxKind.ForStatement:
3074            case SyntaxKind.ForInStatement:
3075            case SyntaxKind.ForOfStatement:
3076            case SyntaxKind.DoStatement:
3077            case SyntaxKind.WhileStatement:
3078            case SyntaxKind.TryStatement:
3079            case SyntaxKind.CatchClause:
3080                return true;
3081        }
3082        return false;
3083    }
3084
3085    export type ValueSignatureDeclaration =
3086        | FunctionDeclaration
3087        | MethodDeclaration
3088        | ConstructorDeclaration
3089        | AccessorDeclaration
3090        | FunctionExpression
3091        | ArrowFunction;
3092
3093    export function isValueSignatureDeclaration(node: Node): node is ValueSignatureDeclaration {
3094        return isFunctionExpression(node) || isArrowFunction(node) || isMethodOrAccessor(node) || isFunctionDeclaration(node) || isConstructorDeclaration(node);
3095    }
3096
3097    function walkUp(node: Node, kind: SyntaxKind) {
3098        while (node && node.kind === kind) {
3099            node = node.parent;
3100        }
3101        return node;
3102    }
3103
3104    export function walkUpParenthesizedTypes(node: Node) {
3105        return walkUp(node, SyntaxKind.ParenthesizedType);
3106    }
3107
3108    export function walkUpParenthesizedExpressions(node: Node) {
3109        return walkUp(node, SyntaxKind.ParenthesizedExpression);
3110    }
3111
3112    /**
3113     * Walks up parenthesized types.
3114     * It returns both the outermost parenthesized type and its parent.
3115     * If given node is not a parenthesiezd type, undefined is return as the former.
3116     */
3117    export function walkUpParenthesizedTypesAndGetParentAndChild(node: Node): [ParenthesizedTypeNode | undefined, Node] {
3118        let child: ParenthesizedTypeNode | undefined;
3119        while (node && node.kind === SyntaxKind.ParenthesizedType) {
3120            child = node as ParenthesizedTypeNode;
3121            node = node.parent;
3122        }
3123        return [child, node];
3124    }
3125
3126    export function skipTypeParentheses(node: TypeNode): TypeNode {
3127        while (isParenthesizedTypeNode(node)) node = node.type;
3128        return node;
3129    }
3130
3131    export function skipParentheses(node: Expression, excludeJSDocTypeAssertions?: boolean): Expression;
3132    export function skipParentheses(node: Node, excludeJSDocTypeAssertions?: boolean): Node;
3133    export function skipParentheses(node: Node, excludeJSDocTypeAssertions?: boolean): Node {
3134        const flags = excludeJSDocTypeAssertions ?
3135            OuterExpressionKinds.Parentheses | OuterExpressionKinds.ExcludeJSDocTypeAssertion :
3136            OuterExpressionKinds.Parentheses;
3137        return skipOuterExpressions(node, flags);
3138    }
3139
3140    // a node is delete target iff. it is PropertyAccessExpression/ElementAccessExpression with parentheses skipped
3141    export function isDeleteTarget(node: Node): boolean {
3142        if (node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
3143            return false;
3144        }
3145        node = walkUpParenthesizedExpressions(node.parent);
3146        return node && node.kind === SyntaxKind.DeleteExpression;
3147    }
3148
3149    export function isNodeDescendantOf(node: Node, ancestor: Node | undefined): boolean {
3150        while (node) {
3151            if (node === ancestor) return true;
3152            node = node.parent;
3153        }
3154        return false;
3155    }
3156
3157    // True if `name` is the name of a declaration node
3158    export function isDeclarationName(name: Node): boolean {
3159        return !isSourceFile(name) && !isBindingPattern(name) && isDeclaration(name.parent) && name.parent.name === name;
3160    }
3161
3162    // See GH#16030
3163    export function getDeclarationFromName(name: Node): Declaration | undefined {
3164        const parent = name.parent;
3165        switch (name.kind) {
3166            case SyntaxKind.StringLiteral:
3167            case SyntaxKind.NoSubstitutionTemplateLiteral:
3168            case SyntaxKind.NumericLiteral:
3169                if (isComputedPropertyName(parent)) return parent.parent;
3170                // falls through
3171            case SyntaxKind.Identifier:
3172                if (isDeclaration(parent)) {
3173                    return parent.name === name ? parent : undefined;
3174                }
3175                else if (isQualifiedName(parent)) {
3176                    const tag = parent.parent;
3177                    return isJSDocParameterTag(tag) && tag.name === parent ? tag : undefined;
3178                }
3179                else {
3180                    const binExp = parent.parent;
3181                    return isBinaryExpression(binExp) &&
3182                        getAssignmentDeclarationKind(binExp) !== AssignmentDeclarationKind.None &&
3183                        (binExp.left.symbol || binExp.symbol) &&
3184                        getNameOfDeclaration(binExp) === name
3185                        ? binExp
3186                        : undefined;
3187                }
3188            case SyntaxKind.PrivateIdentifier:
3189                return isDeclaration(parent) && parent.name === name ? parent : undefined;
3190            default:
3191                return undefined;
3192        }
3193    }
3194
3195    export function isLiteralComputedPropertyDeclarationName(node: Node) {
3196        return isStringOrNumericLiteralLike(node) &&
3197            node.parent.kind === SyntaxKind.ComputedPropertyName &&
3198            isDeclaration(node.parent.parent);
3199    }
3200
3201    // Return true if the given identifier is classified as an IdentifierName
3202    export function isIdentifierName(node: Identifier): boolean {
3203        const parent = node.parent;
3204        switch (parent.kind) {
3205            case SyntaxKind.PropertyDeclaration:
3206            case SyntaxKind.PropertySignature:
3207            case SyntaxKind.MethodDeclaration:
3208            case SyntaxKind.MethodSignature:
3209            case SyntaxKind.GetAccessor:
3210            case SyntaxKind.SetAccessor:
3211            case SyntaxKind.EnumMember:
3212            case SyntaxKind.PropertyAssignment:
3213            case SyntaxKind.PropertyAccessExpression:
3214                // Name in member declaration or property name in property access
3215                return (parent as NamedDeclaration | PropertyAccessExpression).name === node;
3216            case SyntaxKind.QualifiedName:
3217                // Name on right hand side of dot in a type query or type reference
3218                return (parent as QualifiedName).right === node;
3219            case SyntaxKind.BindingElement:
3220            case SyntaxKind.ImportSpecifier:
3221                // Property name in binding element or import specifier
3222                return (parent as BindingElement | ImportSpecifier).propertyName === node;
3223            case SyntaxKind.ExportSpecifier:
3224            case SyntaxKind.JsxAttribute:
3225            case SyntaxKind.JsxSelfClosingElement:
3226            case SyntaxKind.JsxOpeningElement:
3227            case SyntaxKind.JsxClosingElement:
3228                // Any name in an export specifier or JSX Attribute or Jsx Element
3229                return true;
3230        }
3231        return false;
3232    }
3233
3234    // An alias symbol is created by one of the following declarations:
3235    // import <symbol> = ...
3236    // import <symbol> from ...
3237    // import * as <symbol> from ...
3238    // import { x as <symbol> } from ...
3239    // export { x as <symbol> } from ...
3240    // export * as ns <symbol> from ...
3241    // export = <EntityNameExpression>
3242    // export default <EntityNameExpression>
3243    // module.exports = <EntityNameExpression>
3244    // module.exports.x = <EntityNameExpression>
3245    // const x = require("...")
3246    // const { x } = require("...")
3247    // const x = require("...").y
3248    // const { x } = require("...").y
3249    export function isAliasSymbolDeclaration(node: Node): boolean {
3250        if (node.kind === SyntaxKind.ImportEqualsDeclaration ||
3251            node.kind === SyntaxKind.NamespaceExportDeclaration ||
3252            node.kind === SyntaxKind.ImportClause && !!(node as ImportClause).name ||
3253            node.kind === SyntaxKind.NamespaceImport ||
3254            node.kind === SyntaxKind.NamespaceExport ||
3255            node.kind === SyntaxKind.ImportSpecifier ||
3256            node.kind === SyntaxKind.ExportSpecifier ||
3257            node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node as ExportAssignment)
3258        ) {
3259            return true;
3260        }
3261
3262        return isInJSFile(node) && (
3263            isBinaryExpression(node) && getAssignmentDeclarationKind(node) === AssignmentDeclarationKind.ModuleExports && exportAssignmentIsAlias(node) ||
3264            isPropertyAccessExpression(node)
3265                && isBinaryExpression(node.parent)
3266                && node.parent.left === node
3267                && node.parent.operatorToken.kind === SyntaxKind.EqualsToken
3268                && isAliasableExpression(node.parent.right));
3269    }
3270
3271    export function getAliasDeclarationFromName(node: EntityName): Declaration | undefined {
3272        switch (node.parent.kind) {
3273            case SyntaxKind.ImportClause:
3274            case SyntaxKind.ImportSpecifier:
3275            case SyntaxKind.NamespaceImport:
3276            case SyntaxKind.ExportSpecifier:
3277            case SyntaxKind.ExportAssignment:
3278            case SyntaxKind.ImportEqualsDeclaration:
3279            case SyntaxKind.NamespaceExport:
3280                return node.parent as Declaration;
3281            case SyntaxKind.QualifiedName:
3282                do {
3283                    node = node.parent as QualifiedName;
3284                } while (node.parent.kind === SyntaxKind.QualifiedName);
3285                return getAliasDeclarationFromName(node);
3286        }
3287    }
3288
3289    export function isAliasableExpression(e: Expression) {
3290        return isEntityNameExpression(e) || isClassExpression(e);
3291    }
3292
3293    export function exportAssignmentIsAlias(node: ExportAssignment | BinaryExpression): boolean {
3294        const e = getExportAssignmentExpression(node);
3295        return isAliasableExpression(e);
3296    }
3297
3298    export function getExportAssignmentExpression(node: ExportAssignment | BinaryExpression): Expression {
3299        return isExportAssignment(node) ? node.expression : node.right;
3300    }
3301
3302    export function getPropertyAssignmentAliasLikeExpression(node: PropertyAssignment | ShorthandPropertyAssignment | PropertyAccessExpression): Expression {
3303        return node.kind === SyntaxKind.ShorthandPropertyAssignment ? node.name : node.kind === SyntaxKind.PropertyAssignment ? node.initializer :
3304            (node.parent as BinaryExpression).right;
3305    }
3306
3307    export function getEffectiveBaseTypeNode(node: ClassLikeDeclaration | InterfaceDeclaration) {
3308        const baseType = getClassExtendsHeritageElement(node);
3309        if (baseType && isInJSFile(node)) {
3310            // Prefer an @augments tag because it may have type parameters.
3311            const tag = getJSDocAugmentsTag(node);
3312            if (tag) {
3313                return tag.class;
3314            }
3315        }
3316        return baseType;
3317    }
3318
3319    export function getClassExtendsHeritageElement(node: ClassLikeDeclaration | InterfaceDeclaration) {
3320        const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
3321        return heritageClause && heritageClause.types.length > 0 ? heritageClause.types[0] : undefined;
3322    }
3323
3324    export function getEffectiveImplementsTypeNodes(node: ClassLikeDeclaration): undefined | readonly ExpressionWithTypeArguments[]{
3325        if (isInJSFile(node)) {
3326            return getJSDocImplementsTags(node).map(n => n.class);
3327        }
3328        else {
3329            const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ImplementsKeyword);
3330            return heritageClause?.types;
3331        }
3332    }
3333
3334    /** Returns the node in an `extends` or `implements` clause of a class or interface. */
3335    export function getAllSuperTypeNodes(node: Node): readonly TypeNode[] {
3336        return isInterfaceDeclaration(node) ? getInterfaceBaseTypeNodes(node) || emptyArray :
3337            isClassLike(node) ? concatenate(singleElementArray(getEffectiveBaseTypeNode(node)), getEffectiveImplementsTypeNodes(node)) || emptyArray :
3338            emptyArray;
3339    }
3340
3341    export function getInterfaceBaseTypeNodes(node: InterfaceDeclaration) {
3342        const heritageClause = getHeritageClause(node.heritageClauses, SyntaxKind.ExtendsKeyword);
3343        return heritageClause ? heritageClause.types : undefined;
3344    }
3345
3346    export function getHeritageClause(clauses: NodeArray<HeritageClause> | undefined, kind: SyntaxKind) {
3347        if (clauses) {
3348            for (const clause of clauses) {
3349                if (clause.token === kind) {
3350                    return clause;
3351                }
3352            }
3353        }
3354
3355        return undefined;
3356    }
3357
3358    export function getAncestor(node: Node | undefined, kind: SyntaxKind): Node | undefined {
3359        while (node) {
3360            if (node.kind === kind) {
3361                return node;
3362            }
3363            node = node.parent;
3364        }
3365        return undefined;
3366    }
3367
3368    export function getRootEtsComponent(node: Node | undefined): EtsComponentExpression | undefined {
3369        while (node) {
3370            if (isEtsComponentExpression(node)) {
3371                return node;
3372            }
3373            node = (node as PropertyAccessExpression | CallExpression).expression;
3374        }
3375        return undefined;
3376    }
3377
3378    export function getRootComponent(node: Node | undefined, compilerOptions: CompilerOptions):
3379        [EtsComponentExpression | CallExpression | undefined, string | undefined] {
3380        while (node) {
3381            if (isEtsComponentExpression(node)) {
3382                return [node, 'etsComponentType'];
3383            } else if (isCallExpression(node) && isIdentifier(node.expression)) {
3384                if (compilerOptions.ets?.syntaxComponents?.attrUICallback?.map((item: any) => item.name).includes(node.expression.escapedText.toString())) {
3385                    return [node, 'callExpressionComponentType'];
3386                } else {
3387                    return [node, 'otherType'];
3388                }
3389
3390            }
3391            node = (node as PropertyAccessExpression | CallExpression).expression;
3392        }
3393        return [undefined, undefined];
3394    }
3395
3396    export function getVirtualEtsComponent(node: Node | undefined): PropertyAccessExpression | undefined {
3397        while (node) {
3398            if (isPropertyAccessExpression(node) && node.expression && node.expression.virtual) {
3399                return node;
3400            }
3401            node = (node as PropertyAccessExpression | CallExpression).expression;
3402        }
3403        return undefined;
3404    }
3405
3406    export function isKeyword(token: SyntaxKind): token is KeywordSyntaxKind {
3407        return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
3408    }
3409
3410    export function isContextualKeyword(token: SyntaxKind): boolean {
3411        return SyntaxKind.FirstContextualKeyword <= token && token <= SyntaxKind.LastContextualKeyword;
3412    }
3413
3414    export function isNonContextualKeyword(token: SyntaxKind): boolean {
3415        return isKeyword(token) && !isContextualKeyword(token);
3416    }
3417
3418    export function isFutureReservedKeyword(token: SyntaxKind): boolean {
3419        return SyntaxKind.FirstFutureReservedWord <= token && token <= SyntaxKind.LastFutureReservedWord;
3420    }
3421
3422    export function isStringANonContextualKeyword(name: string) {
3423        const token = stringToToken(name);
3424        return token !== undefined && isNonContextualKeyword(token);
3425    }
3426
3427    export function isStringAKeyword(name: string) {
3428        const token = stringToToken(name);
3429        return token !== undefined && isKeyword(token);
3430    }
3431
3432    export function isIdentifierANonContextualKeyword({ originalKeywordKind }: Identifier): boolean {
3433        return !!originalKeywordKind && !isContextualKeyword(originalKeywordKind);
3434    }
3435
3436    export function isTrivia(token: SyntaxKind): token is TriviaSyntaxKind {
3437        return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
3438    }
3439
3440    export const enum FunctionFlags {
3441        Normal = 0,             // Function is a normal function
3442        Generator = 1 << 0,     // Function is a generator function or async generator function
3443        Async = 1 << 1,         // Function is an async function or an async generator function
3444        Invalid = 1 << 2,       // Function is a signature or overload and does not have a body.
3445        AsyncGenerator = Async | Generator, // Function is an async generator function
3446    }
3447
3448    export function getFunctionFlags(node: SignatureDeclaration | undefined) {
3449        if (!node) {
3450            return FunctionFlags.Invalid;
3451        }
3452
3453        let flags = FunctionFlags.Normal;
3454        switch (node.kind) {
3455            case SyntaxKind.FunctionDeclaration:
3456            case SyntaxKind.FunctionExpression:
3457            case SyntaxKind.MethodDeclaration:
3458                if (node.asteriskToken) {
3459                    flags |= FunctionFlags.Generator;
3460                }
3461                // falls through
3462
3463            case SyntaxKind.ArrowFunction:
3464                if (hasSyntacticModifier(node, ModifierFlags.Async)) {
3465                    flags |= FunctionFlags.Async;
3466                }
3467                break;
3468        }
3469
3470        if (!(node as FunctionLikeDeclaration).body) {
3471            flags |= FunctionFlags.Invalid;
3472        }
3473
3474        return flags;
3475    }
3476
3477    export function isAsyncFunction(node: Node): boolean {
3478        switch (node.kind) {
3479            case SyntaxKind.FunctionDeclaration:
3480            case SyntaxKind.FunctionExpression:
3481            case SyntaxKind.ArrowFunction:
3482            case SyntaxKind.MethodDeclaration:
3483                return (node as FunctionLikeDeclaration).body !== undefined
3484                    && (node as FunctionLikeDeclaration).asteriskToken === undefined
3485                    && hasSyntacticModifier(node, ModifierFlags.Async);
3486        }
3487        return false;
3488    }
3489
3490    export function isStringOrNumericLiteralLike(node: Node): node is StringLiteralLike | NumericLiteral {
3491        return isStringLiteralLike(node) || isNumericLiteral(node);
3492    }
3493
3494    export function isSignedNumericLiteral(node: Node): node is PrefixUnaryExpression & { operand: NumericLiteral } {
3495        return isPrefixUnaryExpression(node) && (node.operator === SyntaxKind.PlusToken || node.operator === SyntaxKind.MinusToken) && isNumericLiteral(node.operand);
3496    }
3497
3498    /**
3499     * A declaration has a dynamic name if all of the following are true:
3500     *   1. The declaration has a computed property name.
3501     *   2. The computed name is *not* expressed as a StringLiteral.
3502     *   3. The computed name is *not* expressed as a NumericLiteral.
3503     *   4. The computed name is *not* expressed as a PlusToken or MinusToken
3504     *      immediately followed by a NumericLiteral.
3505     */
3506    export function hasDynamicName(declaration: Declaration): declaration is DynamicNamedDeclaration | DynamicNamedBinaryExpression {
3507        const name = getNameOfDeclaration(declaration);
3508        return !!name && isDynamicName(name);
3509    }
3510
3511    export function isDynamicName(name: DeclarationName): boolean {
3512        if (!(name.kind === SyntaxKind.ComputedPropertyName || name.kind === SyntaxKind.ElementAccessExpression)) {
3513            return false;
3514        }
3515        const expr = isElementAccessExpression(name) ? skipParentheses(name.argumentExpression) : name.expression;
3516        return !isStringOrNumericLiteralLike(expr) &&
3517            !isSignedNumericLiteral(expr);
3518    }
3519
3520    export function getPropertyNameForPropertyNameNode(name: PropertyName): __String | undefined {
3521        switch (name.kind) {
3522            case SyntaxKind.Identifier:
3523            case SyntaxKind.PrivateIdentifier:
3524                return name.escapedText;
3525            case SyntaxKind.StringLiteral:
3526            case SyntaxKind.NumericLiteral:
3527                return escapeLeadingUnderscores(name.text);
3528            case SyntaxKind.ComputedPropertyName:
3529                const nameExpression = name.expression;
3530                if (isStringOrNumericLiteralLike(nameExpression)) {
3531                    return escapeLeadingUnderscores(nameExpression.text);
3532                }
3533                else if (isSignedNumericLiteral(nameExpression)) {
3534                    if (nameExpression.operator === SyntaxKind.MinusToken) {
3535                        return tokenToString(nameExpression.operator) + nameExpression.operand.text as __String;
3536                    }
3537                    return nameExpression.operand.text as __String;
3538                }
3539                return undefined;
3540            default:
3541                return Debug.assertNever(name);
3542        }
3543    }
3544
3545    export function isPropertyNameLiteral(node: Node): node is PropertyNameLiteral {
3546        switch (node.kind) {
3547            case SyntaxKind.Identifier:
3548            case SyntaxKind.StringLiteral:
3549            case SyntaxKind.NoSubstitutionTemplateLiteral:
3550            case SyntaxKind.NumericLiteral:
3551                return true;
3552            default:
3553                return false;
3554        }
3555    }
3556    export function getTextOfIdentifierOrLiteral(node: PropertyNameLiteral | PrivateIdentifier): string {
3557        return isMemberName(node) ? idText(node) : node.text;
3558    }
3559
3560    export function getEscapedTextOfIdentifierOrLiteral(node: PropertyNameLiteral): __String {
3561        return isMemberName(node) ? node.escapedText : escapeLeadingUnderscores(node.text);
3562    }
3563
3564    export function getPropertyNameForUniqueESSymbol(symbol: Symbol): __String {
3565        return `__@${getSymbolId(symbol)}@${symbol.escapedName}` as __String;
3566    }
3567
3568    export function getSymbolNameForPrivateIdentifier(containingClassSymbol: Symbol, description: __String): __String {
3569        return `__#${getSymbolId(containingClassSymbol)}@${description}` as __String;
3570    }
3571
3572    export function isKnownSymbol(symbol: Symbol): boolean {
3573        return startsWith(symbol.escapedName as string, "__@");
3574    }
3575
3576    export function isPrivateIdentifierSymbol(symbol: Symbol): boolean {
3577        return startsWith(symbol.escapedName as string, "__#");
3578    }
3579
3580    /**
3581     * Includes the word "Symbol" with unicode escapes
3582     */
3583    export function isESSymbolIdentifier(node: Node): boolean {
3584        return node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "Symbol";
3585    }
3586
3587    export function isPushOrUnshiftIdentifier(node: Identifier) {
3588        return node.escapedText === "push" || node.escapedText === "unshift";
3589    }
3590
3591    export function isParameterDeclaration(node: VariableLikeDeclaration): boolean {
3592        const root = getRootDeclaration(node);
3593        return root.kind === SyntaxKind.Parameter;
3594    }
3595
3596    export function getRootDeclaration(node: Node): Node {
3597        while (node.kind === SyntaxKind.BindingElement) {
3598            node = node.parent.parent;
3599        }
3600        return node;
3601    }
3602
3603    export function nodeStartsNewLexicalEnvironment(node: Node): boolean {
3604        const kind = node.kind;
3605        return kind === SyntaxKind.Constructor
3606            || kind === SyntaxKind.FunctionExpression
3607            || kind === SyntaxKind.FunctionDeclaration
3608            || kind === SyntaxKind.ArrowFunction
3609            || kind === SyntaxKind.MethodDeclaration
3610            || kind === SyntaxKind.GetAccessor
3611            || kind === SyntaxKind.SetAccessor
3612            || kind === SyntaxKind.ModuleDeclaration
3613            || kind === SyntaxKind.SourceFile;
3614    }
3615
3616    export function nodeIsSynthesized(range: TextRange): boolean {
3617        return positionIsSynthesized(range.pos)
3618            || positionIsSynthesized(range.end);
3619    }
3620
3621    export function getOriginalSourceFile(sourceFile: SourceFile) {
3622        return getParseTreeNode(sourceFile, isSourceFile) || sourceFile;
3623    }
3624
3625    export const enum Associativity {
3626        Left,
3627        Right
3628    }
3629
3630    export function getExpressionAssociativity(expression: Expression) {
3631        const operator = getOperator(expression);
3632        const hasArguments = expression.kind === SyntaxKind.NewExpression && (expression as NewExpression).arguments !== undefined;
3633        return getOperatorAssociativity(expression.kind, operator, hasArguments);
3634    }
3635
3636    export function getOperatorAssociativity(kind: SyntaxKind, operator: SyntaxKind, hasArguments?: boolean) {
3637        switch (kind) {
3638            case SyntaxKind.NewExpression:
3639                return hasArguments ? Associativity.Left : Associativity.Right;
3640
3641            case SyntaxKind.PrefixUnaryExpression:
3642            case SyntaxKind.TypeOfExpression:
3643            case SyntaxKind.VoidExpression:
3644            case SyntaxKind.DeleteExpression:
3645            case SyntaxKind.AwaitExpression:
3646            case SyntaxKind.ConditionalExpression:
3647            case SyntaxKind.YieldExpression:
3648                return Associativity.Right;
3649
3650            case SyntaxKind.BinaryExpression:
3651                switch (operator) {
3652                    case SyntaxKind.AsteriskAsteriskToken:
3653                    case SyntaxKind.EqualsToken:
3654                    case SyntaxKind.PlusEqualsToken:
3655                    case SyntaxKind.MinusEqualsToken:
3656                    case SyntaxKind.AsteriskAsteriskEqualsToken:
3657                    case SyntaxKind.AsteriskEqualsToken:
3658                    case SyntaxKind.SlashEqualsToken:
3659                    case SyntaxKind.PercentEqualsToken:
3660                    case SyntaxKind.LessThanLessThanEqualsToken:
3661                    case SyntaxKind.GreaterThanGreaterThanEqualsToken:
3662                    case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
3663                    case SyntaxKind.AmpersandEqualsToken:
3664                    case SyntaxKind.CaretEqualsToken:
3665                    case SyntaxKind.BarEqualsToken:
3666                    case SyntaxKind.BarBarEqualsToken:
3667                    case SyntaxKind.AmpersandAmpersandEqualsToken:
3668                    case SyntaxKind.QuestionQuestionEqualsToken:
3669                        return Associativity.Right;
3670                }
3671        }
3672        return Associativity.Left;
3673    }
3674
3675    export function getExpressionPrecedence(expression: Expression) {
3676        const operator = getOperator(expression);
3677        const hasArguments = expression.kind === SyntaxKind.NewExpression && (expression as NewExpression).arguments !== undefined;
3678        return getOperatorPrecedence(expression.kind, operator, hasArguments);
3679    }
3680
3681    export function getOperator(expression: Expression): SyntaxKind {
3682        if (expression.kind === SyntaxKind.BinaryExpression) {
3683            return (expression as BinaryExpression).operatorToken.kind;
3684        }
3685        else if (expression.kind === SyntaxKind.PrefixUnaryExpression || expression.kind === SyntaxKind.PostfixUnaryExpression) {
3686            return (expression as PrefixUnaryExpression | PostfixUnaryExpression).operator;
3687        }
3688        else {
3689            return expression.kind;
3690        }
3691    }
3692
3693    export const enum OperatorPrecedence {
3694        // Expression:
3695        //     AssignmentExpression
3696        //     Expression `,` AssignmentExpression
3697        Comma,
3698
3699        // NOTE: `Spread` is higher than `Comma` due to how it is parsed in |ElementList|
3700        // SpreadElement:
3701        //     `...` AssignmentExpression
3702        Spread,
3703
3704        // AssignmentExpression:
3705        //     ConditionalExpression
3706        //     YieldExpression
3707        //     ArrowFunction
3708        //     AsyncArrowFunction
3709        //     LeftHandSideExpression `=` AssignmentExpression
3710        //     LeftHandSideExpression AssignmentOperator AssignmentExpression
3711        //
3712        // NOTE: AssignmentExpression is broken down into several precedences due to the requirements
3713        //       of the parenthesizer rules.
3714
3715        // AssignmentExpression: YieldExpression
3716        // YieldExpression:
3717        //     `yield`
3718        //     `yield` AssignmentExpression
3719        //     `yield` `*` AssignmentExpression
3720        Yield,
3721
3722        // AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression
3723        // AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression
3724        // AssignmentOperator: one of
3725        //     `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=`
3726        Assignment,
3727
3728        // NOTE: `Conditional` is considered higher than `Assignment` here, but in reality they have
3729        //       the same precedence.
3730        // AssignmentExpression: ConditionalExpression
3731        // ConditionalExpression:
3732        //     ShortCircuitExpression
3733        //     ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression
3734        // ShortCircuitExpression:
3735        //     LogicalORExpression
3736        //     CoalesceExpression
3737        Conditional,
3738
3739        // CoalesceExpression:
3740        //     CoalesceExpressionHead `??` BitwiseORExpression
3741        // CoalesceExpressionHead:
3742        //     CoalesceExpression
3743        //     BitwiseORExpression
3744        Coalesce = Conditional, // NOTE: This is wrong
3745
3746        // LogicalORExpression:
3747        //     LogicalANDExpression
3748        //     LogicalORExpression `||` LogicalANDExpression
3749        LogicalOR,
3750
3751        // LogicalANDExpression:
3752        //     BitwiseORExpression
3753        //     LogicalANDExprerssion `&&` BitwiseORExpression
3754        LogicalAND,
3755
3756        // BitwiseORExpression:
3757        //     BitwiseXORExpression
3758        //     BitwiseORExpression `^` BitwiseXORExpression
3759        BitwiseOR,
3760
3761        // BitwiseXORExpression:
3762        //     BitwiseANDExpression
3763        //     BitwiseXORExpression `^` BitwiseANDExpression
3764        BitwiseXOR,
3765
3766        // BitwiseANDExpression:
3767        //     EqualityExpression
3768        //     BitwiseANDExpression `^` EqualityExpression
3769        BitwiseAND,
3770
3771        // EqualityExpression:
3772        //     RelationalExpression
3773        //     EqualityExpression `==` RelationalExpression
3774        //     EqualityExpression `!=` RelationalExpression
3775        //     EqualityExpression `===` RelationalExpression
3776        //     EqualityExpression `!==` RelationalExpression
3777        Equality,
3778
3779        // RelationalExpression:
3780        //     ShiftExpression
3781        //     RelationalExpression `<` ShiftExpression
3782        //     RelationalExpression `>` ShiftExpression
3783        //     RelationalExpression `<=` ShiftExpression
3784        //     RelationalExpression `>=` ShiftExpression
3785        //     RelationalExpression `instanceof` ShiftExpression
3786        //     RelationalExpression `in` ShiftExpression
3787        //     [+TypeScript] RelationalExpression `as` Type
3788        Relational,
3789
3790        // ShiftExpression:
3791        //     AdditiveExpression
3792        //     ShiftExpression `<<` AdditiveExpression
3793        //     ShiftExpression `>>` AdditiveExpression
3794        //     ShiftExpression `>>>` AdditiveExpression
3795        Shift,
3796
3797        // AdditiveExpression:
3798        //     MultiplicativeExpression
3799        //     AdditiveExpression `+` MultiplicativeExpression
3800        //     AdditiveExpression `-` MultiplicativeExpression
3801        Additive,
3802
3803        // MultiplicativeExpression:
3804        //     ExponentiationExpression
3805        //     MultiplicativeExpression MultiplicativeOperator ExponentiationExpression
3806        // MultiplicativeOperator: one of `*`, `/`, `%`
3807        Multiplicative,
3808
3809        // ExponentiationExpression:
3810        //     UnaryExpression
3811        //     UpdateExpression `**` ExponentiationExpression
3812        Exponentiation,
3813
3814        // UnaryExpression:
3815        //     UpdateExpression
3816        //     `delete` UnaryExpression
3817        //     `void` UnaryExpression
3818        //     `typeof` UnaryExpression
3819        //     `+` UnaryExpression
3820        //     `-` UnaryExpression
3821        //     `~` UnaryExpression
3822        //     `!` UnaryExpression
3823        //     AwaitExpression
3824        // UpdateExpression:            // TODO: Do we need to investigate the precedence here?
3825        //     `++` UnaryExpression
3826        //     `--` UnaryExpression
3827        Unary,
3828
3829
3830        // UpdateExpression:
3831        //     LeftHandSideExpression
3832        //     LeftHandSideExpression `++`
3833        //     LeftHandSideExpression `--`
3834        Update,
3835
3836        // LeftHandSideExpression:
3837        //     NewExpression
3838        //     CallExpression
3839        // NewExpression:
3840        //     MemberExpression
3841        //     `new` NewExpression
3842        LeftHandSide,
3843
3844        // CallExpression:
3845        //     CoverCallExpressionAndAsyncArrowHead
3846        //     SuperCall
3847        //     ImportCall
3848        //     CallExpression Arguments
3849        //     CallExpression `[` Expression `]`
3850        //     CallExpression `.` IdentifierName
3851        //     CallExpression TemplateLiteral
3852        // MemberExpression:
3853        //     PrimaryExpression
3854        //     MemberExpression `[` Expression `]`
3855        //     MemberExpression `.` IdentifierName
3856        //     MemberExpression TemplateLiteral
3857        //     SuperProperty
3858        //     MetaProperty
3859        //     `new` MemberExpression Arguments
3860        Member,
3861
3862        // TODO: JSXElement?
3863        // PrimaryExpression:
3864        //     `this`
3865        //     IdentifierReference
3866        //     Literal
3867        //     ArrayLiteral
3868        //     ObjectLiteral
3869        //     FunctionExpression
3870        //     ClassExpression
3871        //     GeneratorExpression
3872        //     AsyncFunctionExpression
3873        //     AsyncGeneratorExpression
3874        //     RegularExpressionLiteral
3875        //     TemplateLiteral
3876        //     CoverParenthesizedExpressionAndArrowParameterList
3877        Primary,
3878
3879        Highest = Primary,
3880        Lowest = Comma,
3881        // -1 is lower than all other precedences. Returning it will cause binary expression
3882        // parsing to stop.
3883        Invalid = -1,
3884    }
3885
3886    export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: SyntaxKind, hasArguments?: boolean) {
3887        switch (nodeKind) {
3888            case SyntaxKind.CommaListExpression:
3889                return OperatorPrecedence.Comma;
3890
3891            case SyntaxKind.SpreadElement:
3892                return OperatorPrecedence.Spread;
3893
3894            case SyntaxKind.YieldExpression:
3895                return OperatorPrecedence.Yield;
3896
3897            case SyntaxKind.ConditionalExpression:
3898                return OperatorPrecedence.Conditional;
3899
3900            case SyntaxKind.BinaryExpression:
3901                switch (operatorKind) {
3902                    case SyntaxKind.CommaToken:
3903                        return OperatorPrecedence.Comma;
3904
3905                    case SyntaxKind.EqualsToken:
3906                    case SyntaxKind.PlusEqualsToken:
3907                    case SyntaxKind.MinusEqualsToken:
3908                    case SyntaxKind.AsteriskAsteriskEqualsToken:
3909                    case SyntaxKind.AsteriskEqualsToken:
3910                    case SyntaxKind.SlashEqualsToken:
3911                    case SyntaxKind.PercentEqualsToken:
3912                    case SyntaxKind.LessThanLessThanEqualsToken:
3913                    case SyntaxKind.GreaterThanGreaterThanEqualsToken:
3914                    case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
3915                    case SyntaxKind.AmpersandEqualsToken:
3916                    case SyntaxKind.CaretEqualsToken:
3917                    case SyntaxKind.BarEqualsToken:
3918                    case SyntaxKind.BarBarEqualsToken:
3919                    case SyntaxKind.AmpersandAmpersandEqualsToken:
3920                    case SyntaxKind.QuestionQuestionEqualsToken:
3921                        return OperatorPrecedence.Assignment;
3922
3923                    default:
3924                        return getBinaryOperatorPrecedence(operatorKind);
3925                }
3926
3927            // TODO: Should prefix `++` and `--` be moved to the `Update` precedence?
3928            case SyntaxKind.TypeAssertionExpression:
3929            case SyntaxKind.NonNullExpression:
3930            case SyntaxKind.PrefixUnaryExpression:
3931            case SyntaxKind.TypeOfExpression:
3932            case SyntaxKind.VoidExpression:
3933            case SyntaxKind.DeleteExpression:
3934            case SyntaxKind.AwaitExpression:
3935                return OperatorPrecedence.Unary;
3936
3937            case SyntaxKind.PostfixUnaryExpression:
3938                return OperatorPrecedence.Update;
3939
3940            case SyntaxKind.CallExpression:
3941                return OperatorPrecedence.LeftHandSide;
3942
3943            case SyntaxKind.NewExpression:
3944                return hasArguments ? OperatorPrecedence.Member : OperatorPrecedence.LeftHandSide;
3945
3946            case SyntaxKind.TaggedTemplateExpression:
3947            case SyntaxKind.PropertyAccessExpression:
3948            case SyntaxKind.ElementAccessExpression:
3949            case SyntaxKind.MetaProperty:
3950                return OperatorPrecedence.Member;
3951
3952            case SyntaxKind.AsExpression:
3953            case SyntaxKind.SatisfiesExpression:
3954                return OperatorPrecedence.Relational;
3955
3956            case SyntaxKind.ThisKeyword:
3957            case SyntaxKind.SuperKeyword:
3958            case SyntaxKind.Identifier:
3959            case SyntaxKind.PrivateIdentifier:
3960            case SyntaxKind.NullKeyword:
3961            case SyntaxKind.TrueKeyword:
3962            case SyntaxKind.FalseKeyword:
3963            case SyntaxKind.NumericLiteral:
3964            case SyntaxKind.BigIntLiteral:
3965            case SyntaxKind.StringLiteral:
3966            case SyntaxKind.ArrayLiteralExpression:
3967            case SyntaxKind.ObjectLiteralExpression:
3968            case SyntaxKind.FunctionExpression:
3969            case SyntaxKind.ArrowFunction:
3970            case SyntaxKind.ClassExpression:
3971            case SyntaxKind.RegularExpressionLiteral:
3972            case SyntaxKind.NoSubstitutionTemplateLiteral:
3973            case SyntaxKind.TemplateExpression:
3974            case SyntaxKind.ParenthesizedExpression:
3975            case SyntaxKind.OmittedExpression:
3976            case SyntaxKind.JsxElement:
3977            case SyntaxKind.JsxSelfClosingElement:
3978            case SyntaxKind.JsxFragment:
3979                return OperatorPrecedence.Primary;
3980
3981            default:
3982                return OperatorPrecedence.Invalid;
3983        }
3984    }
3985
3986    export function getBinaryOperatorPrecedence(kind: SyntaxKind): OperatorPrecedence {
3987        switch (kind) {
3988            case SyntaxKind.QuestionQuestionToken:
3989                return OperatorPrecedence.Coalesce;
3990            case SyntaxKind.BarBarToken:
3991                return OperatorPrecedence.LogicalOR;
3992            case SyntaxKind.AmpersandAmpersandToken:
3993                return OperatorPrecedence.LogicalAND;
3994            case SyntaxKind.BarToken:
3995                return OperatorPrecedence.BitwiseOR;
3996            case SyntaxKind.CaretToken:
3997                return OperatorPrecedence.BitwiseXOR;
3998            case SyntaxKind.AmpersandToken:
3999                return OperatorPrecedence.BitwiseAND;
4000            case SyntaxKind.EqualsEqualsToken:
4001            case SyntaxKind.ExclamationEqualsToken:
4002            case SyntaxKind.EqualsEqualsEqualsToken:
4003            case SyntaxKind.ExclamationEqualsEqualsToken:
4004                return OperatorPrecedence.Equality;
4005            case SyntaxKind.LessThanToken:
4006            case SyntaxKind.GreaterThanToken:
4007            case SyntaxKind.LessThanEqualsToken:
4008            case SyntaxKind.GreaterThanEqualsToken:
4009            case SyntaxKind.InstanceOfKeyword:
4010            case SyntaxKind.InKeyword:
4011            case SyntaxKind.AsKeyword:
4012            case SyntaxKind.SatisfiesKeyword:
4013                return OperatorPrecedence.Relational;
4014            case SyntaxKind.LessThanLessThanToken:
4015            case SyntaxKind.GreaterThanGreaterThanToken:
4016            case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
4017                return OperatorPrecedence.Shift;
4018            case SyntaxKind.PlusToken:
4019            case SyntaxKind.MinusToken:
4020                return OperatorPrecedence.Additive;
4021            case SyntaxKind.AsteriskToken:
4022            case SyntaxKind.SlashToken:
4023            case SyntaxKind.PercentToken:
4024                return OperatorPrecedence.Multiplicative;
4025            case SyntaxKind.AsteriskAsteriskToken:
4026                return OperatorPrecedence.Exponentiation;
4027        }
4028
4029        // -1 is lower than all other precedences.  Returning it will cause binary expression
4030        // parsing to stop.
4031        return -1;
4032    }
4033
4034    export function getSemanticJsxChildren(children: readonly JsxChild[]) {
4035        return filter(children, i => {
4036            switch (i.kind) {
4037                case SyntaxKind.JsxExpression:
4038                    return !!i.expression;
4039                case SyntaxKind.JsxText:
4040                    return !i.containsOnlyTriviaWhiteSpaces;
4041                default:
4042                    return true;
4043            }
4044        });
4045    }
4046
4047    export function createDiagnosticCollection(): DiagnosticCollection {
4048        let nonFileDiagnostics = [] as Diagnostic[] as SortedArray<Diagnostic>; // See GH#19873
4049        const filesWithDiagnostics = [] as string[] as SortedArray<string>;
4050        const fileDiagnostics = new Map<string, SortedArray<DiagnosticWithLocation>>();
4051        let hasReadNonFileDiagnostics = false;
4052
4053        return {
4054            add,
4055            lookup,
4056            getGlobalDiagnostics,
4057            getDiagnostics,
4058        };
4059
4060        function lookup(diagnostic: Diagnostic): Diagnostic | undefined {
4061            let diagnostics: SortedArray<Diagnostic> | undefined;
4062            if (diagnostic.file) {
4063                diagnostics = fileDiagnostics.get(diagnostic.file.fileName);
4064            }
4065            else {
4066                diagnostics = nonFileDiagnostics;
4067            }
4068            if (!diagnostics) {
4069                return undefined;
4070            }
4071            const result = binarySearch(diagnostics, diagnostic, identity, compareDiagnosticsSkipRelatedInformation);
4072            if (result >= 0) {
4073                return diagnostics[result];
4074            }
4075            return undefined;
4076        }
4077
4078        function add(diagnostic: Diagnostic): void {
4079            let diagnostics: SortedArray<Diagnostic> | undefined;
4080            if (diagnostic.file) {
4081                diagnostics = fileDiagnostics.get(diagnostic.file.fileName);
4082                if (!diagnostics) {
4083                    diagnostics = [] as Diagnostic[] as SortedArray<DiagnosticWithLocation>; // See GH#19873
4084                    fileDiagnostics.set(diagnostic.file.fileName, diagnostics as SortedArray<DiagnosticWithLocation>);
4085                    insertSorted(filesWithDiagnostics, diagnostic.file.fileName, compareStringsCaseSensitive);
4086                }
4087            }
4088            else {
4089                // If we've already read the non-file diagnostics, do not modify the existing array.
4090                if (hasReadNonFileDiagnostics) {
4091                    hasReadNonFileDiagnostics = false;
4092                    nonFileDiagnostics = nonFileDiagnostics.slice() as SortedArray<Diagnostic>;
4093                }
4094
4095                diagnostics = nonFileDiagnostics;
4096            }
4097
4098            insertSorted(diagnostics, diagnostic, compareDiagnosticsSkipRelatedInformation);
4099        }
4100
4101        function getGlobalDiagnostics(): Diagnostic[] {
4102            hasReadNonFileDiagnostics = true;
4103            return nonFileDiagnostics;
4104        }
4105
4106        function getDiagnostics(fileName: string): DiagnosticWithLocation[];
4107        function getDiagnostics(): Diagnostic[];
4108        function getDiagnostics(fileName?: string): Diagnostic[] {
4109            if (fileName) {
4110                return fileDiagnostics.get(fileName) || [];
4111            }
4112
4113            const fileDiags: Diagnostic[] = flatMapToMutable(filesWithDiagnostics, f => fileDiagnostics.get(f));
4114            if (!nonFileDiagnostics.length) {
4115                return fileDiags;
4116            }
4117            fileDiags.unshift(...nonFileDiagnostics);
4118            return fileDiags;
4119        }
4120    }
4121
4122    const templateSubstitutionRegExp = /\$\{/g;
4123    function escapeTemplateSubstitution(str: string): string {
4124        return str.replace(templateSubstitutionRegExp, "\\${");
4125    }
4126
4127    /** @internal */
4128    export function hasInvalidEscape(template: TemplateLiteral): boolean {
4129        return template && !!(isNoSubstitutionTemplateLiteral(template)
4130            ? template.templateFlags
4131            : (template.head.templateFlags || some(template.templateSpans, span => !!span.literal.templateFlags)));
4132    }
4133
4134    // This consists of the first 19 unprintable ASCII characters, canonical escapes, lineSeparator,
4135    // paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in
4136    // the language service. These characters should be escaped when printing, and if any characters are added,
4137    // the map below must be updated. Note that this regexp *does not* include the 'delete' character.
4138    // There is no reason for this other than that JSON.stringify does not handle it either.
4139    const doubleQuoteEscapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
4140    const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
4141    // Template strings preserve simple LF newlines, still encode CRLF (or CR)
4142    const backtickQuoteEscapedCharsRegExp = /\r\n|[\\\`\u0000-\u001f\t\v\f\b\r\u2028\u2029\u0085]/g;
4143    const escapedCharsMap = new Map(getEntries({
4144        "\t": "\\t",
4145        "\v": "\\v",
4146        "\f": "\\f",
4147        "\b": "\\b",
4148        "\r": "\\r",
4149        "\n": "\\n",
4150        "\\": "\\\\",
4151        "\"": "\\\"",
4152        "\'": "\\\'",
4153        "\`": "\\\`",
4154        "\u2028": "\\u2028", // lineSeparator
4155        "\u2029": "\\u2029", // paragraphSeparator
4156        "\u0085": "\\u0085", // nextLine
4157        "\r\n": "\\r\\n", // special case for CRLFs in backticks
4158    }));
4159
4160    function encodeUtf16EscapeSequence(charCode: number): string {
4161        const hexCharCode = charCode.toString(16).toUpperCase();
4162        const paddedHexCode = ("0000" + hexCharCode).slice(-4);
4163        return "\\u" + paddedHexCode;
4164    }
4165
4166    function getReplacement(c: string, offset: number, input: string) {
4167        if (c.charCodeAt(0) === CharacterCodes.nullCharacter) {
4168            const lookAhead = input.charCodeAt(offset + c.length);
4169            if (lookAhead >= CharacterCodes._0 && lookAhead <= CharacterCodes._9) {
4170                // If the null character is followed by digits, print as a hex escape to prevent the result from parsing as an octal (which is forbidden in strict mode)
4171                return "\\x00";
4172            }
4173            // Otherwise, keep printing a literal \0 for the null character
4174            return "\\0";
4175        }
4176        return escapedCharsMap.get(c) || encodeUtf16EscapeSequence(c.charCodeAt(0));
4177    }
4178
4179    /**
4180     * Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
4181     * but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine)
4182     * Note that this doesn't actually wrap the input in double quotes.
4183     */
4184    export function escapeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string {
4185        const escapedCharsRegExp =
4186            quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp :
4187            quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp :
4188            doubleQuoteEscapedCharsRegExp;
4189        return s.replace(escapedCharsRegExp, getReplacement);
4190    }
4191
4192    const nonAsciiCharacters = /[^\u0000-\u007F]/g;
4193    export function escapeNonAsciiString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string {
4194        s = escapeString(s, quoteChar);
4195        // Replace non-ASCII characters with '\uNNNN' escapes if any exist.
4196        // Otherwise just return the original string.
4197        return nonAsciiCharacters.test(s) ?
4198            s.replace(nonAsciiCharacters, c => encodeUtf16EscapeSequence(c.charCodeAt(0))) :
4199            s;
4200    }
4201
4202    // This consists of the first 19 unprintable ASCII characters, JSX canonical escapes, lineSeparator,
4203    // paragraphSeparator, and nextLine. The latter three are just desirable to suppress new lines in
4204    // the language service. These characters should be escaped when printing, and if any characters are added,
4205    // the map below must be updated.
4206    const jsxDoubleQuoteEscapedCharsRegExp = /[\"\u0000-\u001f\u2028\u2029\u0085]/g;
4207    const jsxSingleQuoteEscapedCharsRegExp = /[\'\u0000-\u001f\u2028\u2029\u0085]/g;
4208    const jsxEscapedCharsMap = new Map(getEntries({
4209        "\"": "&quot;",
4210        "\'": "&apos;"
4211    }));
4212
4213    function encodeJsxCharacterEntity(charCode: number): string {
4214        const hexCharCode = charCode.toString(16).toUpperCase();
4215        return "&#x" + hexCharCode + ";";
4216    }
4217
4218    function getJsxAttributeStringReplacement(c: string) {
4219        if (c.charCodeAt(0) === CharacterCodes.nullCharacter) {
4220            return "&#0;";
4221        }
4222        return jsxEscapedCharsMap.get(c) || encodeJsxCharacterEntity(c.charCodeAt(0));
4223    }
4224
4225    export function escapeJsxAttributeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote) {
4226        const escapedCharsRegExp =
4227            quoteChar === CharacterCodes.singleQuote ? jsxSingleQuoteEscapedCharsRegExp :
4228            jsxDoubleQuoteEscapedCharsRegExp;
4229        return s.replace(escapedCharsRegExp, getJsxAttributeStringReplacement);
4230    }
4231
4232    /**
4233     * Strip off existed surrounding single quotes, double quotes, or backticks from a given string
4234     *
4235     * @return non-quoted string
4236     */
4237    export function stripQuotes(name: string) {
4238        const length = name.length;
4239        if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) {
4240            return name.substring(1, length - 1);
4241        }
4242        return name;
4243    }
4244
4245    function isQuoteOrBacktick(charCode: number) {
4246        return charCode === CharacterCodes.singleQuote ||
4247            charCode === CharacterCodes.doubleQuote ||
4248            charCode === CharacterCodes.backtick;
4249    }
4250
4251    export function isIntrinsicJsxName(name: __String | string) {
4252        const ch = (name as string).charCodeAt(0);
4253        return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains((name as string), "-") || stringContains((name as string), ":");
4254    }
4255
4256    const indentStrings: string[] = ["", "    "];
4257    export function getIndentString(level: number) {
4258        // prepopulate cache
4259        const singleLevel = indentStrings[1];
4260        for (let current = indentStrings.length; current <= level; current++) {
4261            indentStrings.push(indentStrings[current - 1] + singleLevel);
4262        }
4263        return indentStrings[level];
4264    }
4265
4266    export function getIndentSize() {
4267        return indentStrings[1].length;
4268    }
4269
4270    export function isNightly() {
4271        return stringContains(version, "-dev") || stringContains(version, "-insiders");
4272    }
4273
4274    export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): EmitTextWriter {
4275        let pendingTrailingSemicolon = false;
4276
4277        function commitPendingTrailingSemicolon() {
4278            if (pendingTrailingSemicolon) {
4279                writer.writeTrailingSemicolon(";");
4280                pendingTrailingSemicolon = false;
4281            }
4282        }
4283
4284        return {
4285            ...writer,
4286            writeTrailingSemicolon() {
4287                pendingTrailingSemicolon = true;
4288            },
4289            writeLiteral(s) {
4290                commitPendingTrailingSemicolon();
4291                writer.writeLiteral(s);
4292            },
4293            writeStringLiteral(s) {
4294                commitPendingTrailingSemicolon();
4295                writer.writeStringLiteral(s);
4296            },
4297            writeSymbol(s, sym) {
4298                commitPendingTrailingSemicolon();
4299                writer.writeSymbol(s, sym);
4300            },
4301            writePunctuation(s) {
4302                commitPendingTrailingSemicolon();
4303                writer.writePunctuation(s);
4304            },
4305            writeKeyword(s) {
4306                commitPendingTrailingSemicolon();
4307                writer.writeKeyword(s);
4308            },
4309            writeOperator(s) {
4310                commitPendingTrailingSemicolon();
4311                writer.writeOperator(s);
4312            },
4313            writeParameter(s) {
4314                commitPendingTrailingSemicolon();
4315                writer.writeParameter(s);
4316            },
4317            writeSpace(s) {
4318                commitPendingTrailingSemicolon();
4319                writer.writeSpace(s);
4320            },
4321            writeProperty(s) {
4322                commitPendingTrailingSemicolon();
4323                writer.writeProperty(s);
4324            },
4325            writeComment(s) {
4326                commitPendingTrailingSemicolon();
4327                writer.writeComment(s);
4328            },
4329            writeLine() {
4330                commitPendingTrailingSemicolon();
4331                writer.writeLine();
4332            },
4333            increaseIndent() {
4334                commitPendingTrailingSemicolon();
4335                writer.increaseIndent();
4336            },
4337            decreaseIndent() {
4338                commitPendingTrailingSemicolon();
4339                writer.decreaseIndent();
4340            },
4341        };
4342    }
4343
4344    export function hostUsesCaseSensitiveFileNames(host: { useCaseSensitiveFileNames?(): boolean; }): boolean {
4345        return host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : false;
4346    }
4347
4348    export function hostGetCanonicalFileName(host: { useCaseSensitiveFileNames?(): boolean; }): GetCanonicalFileName {
4349        return createGetCanonicalFileName(hostUsesCaseSensitiveFileNames(host));
4350    }
4351
4352    export interface ResolveModuleNameResolutionHost {
4353        getCanonicalFileName(p: string): string;
4354        getCommonSourceDirectory(): string;
4355        getCurrentDirectory(): string;
4356    }
4357
4358    export function getResolvedExternalModuleName(host: ResolveModuleNameResolutionHost, file: SourceFile, referenceFile?: SourceFile): string {
4359        return file.moduleName || getExternalModuleNameFromPath(host, file.fileName, referenceFile && referenceFile.fileName);
4360    }
4361
4362    function getCanonicalAbsolutePath(host: ResolveModuleNameResolutionHost, path: string) {
4363        return host.getCanonicalFileName(getNormalizedAbsolutePath(path, host.getCurrentDirectory()));
4364    }
4365
4366    export function getExternalModuleNameFromDeclaration(host: ResolveModuleNameResolutionHost, resolver: EmitResolver, declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration | ImportTypeNode): string | undefined {
4367        const file = resolver.getExternalModuleFileFromDeclaration(declaration);
4368        if (!file || file.isDeclarationFile) {
4369            return undefined;
4370        }
4371        // If the declaration already uses a non-relative name, and is outside the common source directory, continue to use it
4372        const specifier = getExternalModuleName(declaration);
4373        if (specifier && isStringLiteralLike(specifier) && !pathIsRelative(specifier.text) &&
4374            getCanonicalAbsolutePath(host, file.path).indexOf(getCanonicalAbsolutePath(host, ensureTrailingDirectorySeparator(host.getCommonSourceDirectory()))) === -1) {
4375            return undefined;
4376        }
4377        return getResolvedExternalModuleName(host, file);
4378    }
4379
4380    /**
4381     * Resolves a local path to a path which is absolute to the base of the emit
4382     */
4383    export function getExternalModuleNameFromPath(host: ResolveModuleNameResolutionHost, fileName: string, referencePath?: string): string {
4384        const getCanonicalFileName = (f: string) => host.getCanonicalFileName(f);
4385        const dir = toPath(referencePath ? getDirectoryPath(referencePath) : host.getCommonSourceDirectory(), host.getCurrentDirectory(), getCanonicalFileName);
4386        const filePath = getNormalizedAbsolutePath(fileName, host.getCurrentDirectory());
4387        const relativePath = getRelativePathToDirectoryOrUrl(dir, filePath, dir, getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
4388        const extensionless = removeFileExtension(relativePath);
4389        return referencePath ? ensurePathIsNonModuleName(extensionless) : extensionless;
4390    }
4391
4392    export function getOwnEmitOutputFilePath(fileName: string, host: EmitHost, extension: string) {
4393        const compilerOptions = host.getCompilerOptions();
4394        let emitOutputFilePathWithoutExtension: string;
4395        if (compilerOptions.outDir) {
4396            emitOutputFilePathWithoutExtension = removeFileExtension(getSourceFilePathInNewDir(fileName, host, compilerOptions.outDir));
4397        }
4398        else {
4399            emitOutputFilePathWithoutExtension = removeFileExtension(fileName);
4400        }
4401
4402        return emitOutputFilePathWithoutExtension + extension;
4403    }
4404
4405    export function getDeclarationEmitOutputFilePath(fileName: string, host: EmitHost) {
4406        return getDeclarationEmitOutputFilePathWorker(fileName, host.getCompilerOptions(), host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f));
4407    }
4408
4409    export function getDeclarationEmitOutputFilePathWorker(fileName: string, options: CompilerOptions, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string {
4410        const outputDir = options.declarationDir || options.outDir; // Prefer declaration folder if specified
4411
4412        const path = outputDir
4413            ? getSourceFilePathInNewDirWorker(fileName, outputDir, currentDirectory, commonSourceDirectory, getCanonicalFileName)
4414            : fileName;
4415        const declarationExtension = getDeclarationEmitExtensionForPath(path);
4416        return removeFileExtension(path) + declarationExtension;
4417    }
4418
4419    export function getDeclarationEmitExtensionForPath(path: string) {
4420        return fileExtensionIsOneOf(path, [Extension.Mjs, Extension.Mts]) ? Extension.Dmts :
4421            fileExtensionIsOneOf(path, [Extension.Cjs, Extension.Cts]) ? Extension.Dcts :
4422            fileExtensionIsOneOf(path, [Extension.Json]) ? `.json.d.ts` : // Drive-by redefinition of json declaration file output name so if it's ever enabled, it behaves well
4423            fileExtensionIsOneOf(path, [Extension.Ets]) ? Extension.Dets : Extension.Dts;
4424    }
4425
4426    /**
4427     * This function is an inverse of `getDeclarationEmitExtensionForPath`.
4428     */
4429    export function getPossibleOriginalInputExtensionForExtension(path: string) {
4430        return fileExtensionIsOneOf(path, [Extension.Dmts, Extension.Mjs, Extension.Mts]) ? [Extension.Mts, Extension.Mjs] :
4431            fileExtensionIsOneOf(path, [Extension.Dcts, Extension.Cjs, Extension.Cts]) ? [Extension.Cts, Extension.Cjs]:
4432            fileExtensionIsOneOf(path, [`.json.d.ts`]) ? [Extension.Json] :
4433            [Extension.Tsx, Extension.Ts, Extension.Jsx, Extension.Js];
4434    }
4435
4436    export function outFile(options: CompilerOptions) {
4437        return options.outFile || options.out;
4438    }
4439
4440    /** Returns 'undefined' if and only if 'options.paths' is undefined. */
4441    export function getPathsBasePath(options: CompilerOptions, host: { getCurrentDirectory?(): string }) {
4442        if (!options.paths) return undefined;
4443        return options.baseUrl ?? Debug.checkDefined(options.pathsBasePath || host.getCurrentDirectory?.(), "Encountered 'paths' without a 'baseUrl', config file, or host 'getCurrentDirectory'.");
4444    }
4445
4446    export interface EmitFileNames {
4447        jsFilePath?: string | undefined;
4448        sourceMapFilePath?: string | undefined;
4449        declarationFilePath?: string | undefined;
4450        declarationMapPath?: string | undefined;
4451        buildInfoPath?: string | undefined;
4452    }
4453
4454    /**
4455     * Gets the source files that are expected to have an emit output.
4456     *
4457     * Originally part of `forEachExpectedEmitFile`, this functionality was extracted to support
4458     * transformations.
4459     *
4460     * @param host An EmitHost.
4461     * @param targetSourceFile An optional target source file to emit.
4462     */
4463    export function getSourceFilesToEmit(host: EmitHost, targetSourceFile?: SourceFile, forceDtsEmit?: boolean): readonly SourceFile[] {
4464        const options = host.getCompilerOptions();
4465        if (outFile(options)) {
4466            const moduleKind = getEmitModuleKind(options);
4467            const moduleEmitEnabled = options.emitDeclarationOnly || moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.System;
4468            // Can emit only sources that are not declaration file and are either non module code or module with --module or --target es6 specified
4469            return filter(
4470                host.getSourceFiles(),
4471                sourceFile =>
4472                    (moduleEmitEnabled || !isExternalModule(sourceFile)) &&
4473                    sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit)
4474            );
4475        }
4476        else {
4477            const sourceFiles = targetSourceFile === undefined ? host.getSourceFiles() : [targetSourceFile];
4478            return filter(
4479                sourceFiles,
4480                sourceFile => sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit)
4481            );
4482        }
4483    }
4484
4485    /** Don't call this for `--outFile`, just for `--outDir` or plain emit. `--outFile` needs additional checks. */
4486    export function sourceFileMayBeEmitted(sourceFile: SourceFile, host: SourceFileMayBeEmittedHost, forceDtsEmit?: boolean) {
4487        const options = host.getCompilerOptions();
4488        return !(options.noEmitForJsFiles && isSourceFileJS(sourceFile)) &&
4489            !sourceFile.isDeclarationFile &&
4490            (!host.isSourceFileFromExternalLibrary(sourceFile) || isEmitNodeModulesFiles(host.getCompilerOptions().emitNodeModulesFiles)) &&
4491            (forceDtsEmit || (
4492                !(isJsonSourceFile(sourceFile) && host.getResolvedProjectReferenceToRedirect(sourceFile.fileName)) &&
4493                !host.isSourceOfProjectReferenceRedirect(sourceFile.fileName)
4494            ));
4495    }
4496
4497    export function getSourceFilePathInNewDir(fileName: string, host: EmitHost, newDirPath: string): string {
4498        return getSourceFilePathInNewDirWorker(fileName, newDirPath, host.getCurrentDirectory(), host.getCommonSourceDirectory(), f => host.getCanonicalFileName(f));
4499    }
4500
4501    export function getSourceFilePathInNewDirWorker(fileName: string, newDirPath: string, currentDirectory: string, commonSourceDirectory: string, getCanonicalFileName: GetCanonicalFileName): string {
4502        let sourceFilePath = getNormalizedAbsolutePath(fileName, currentDirectory);
4503        const isSourceFileInCommonSourceDirectory = getCanonicalFileName(sourceFilePath).indexOf(getCanonicalFileName(commonSourceDirectory)) === 0;
4504        sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.substring(commonSourceDirectory.length) : sourceFilePath;
4505        return combinePaths(newDirPath, sourceFilePath);
4506    }
4507
4508    export function writeFile(host: { writeFile: WriteFileCallback; }, diagnostics: DiagnosticCollection, fileName: string, text: string, writeByteOrderMark: boolean, sourceFiles?: readonly SourceFile[], data?: WriteFileCallbackData) {
4509        host.writeFile(fileName, text, writeByteOrderMark, hostErrorMessage => {
4510            diagnostics.add(createCompilerDiagnostic(Diagnostics.Could_not_write_file_0_Colon_1, fileName, hostErrorMessage));
4511        }, sourceFiles, data);
4512    }
4513
4514    function ensureDirectoriesExist(
4515        directoryPath: string,
4516        createDirectory: (path: string) => void,
4517        directoryExists: (path: string) => boolean): void {
4518        if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
4519            const parentDirectory = getDirectoryPath(directoryPath);
4520            ensureDirectoriesExist(parentDirectory, createDirectory, directoryExists);
4521            createDirectory(directoryPath);
4522        }
4523    }
4524
4525    export function writeFileEnsuringDirectories(
4526        path: string,
4527        data: string,
4528        writeByteOrderMark: boolean,
4529        writeFile: (path: string, data: string, writeByteOrderMark: boolean) => void,
4530        createDirectory: (path: string) => void,
4531        directoryExists: (path: string) => boolean): void {
4532
4533        // PERF: Checking for directory existence is expensive.  Instead, assume the directory exists
4534        // and fall back to creating it if the file write fails.
4535        try {
4536            writeFile(path, data, writeByteOrderMark);
4537        }
4538        catch {
4539            ensureDirectoriesExist(getDirectoryPath(normalizePath(path)), createDirectory, directoryExists);
4540            writeFile(path, data, writeByteOrderMark);
4541        }
4542    }
4543
4544    export function getLineOfLocalPosition(sourceFile: SourceFile, pos: number) {
4545        const lineStarts = getLineStarts(sourceFile);
4546        return computeLineOfPosition(lineStarts, pos);
4547    }
4548
4549    export function getLineOfLocalPositionFromLineMap(lineMap: readonly number[], pos: number) {
4550        return computeLineOfPosition(lineMap, pos);
4551    }
4552
4553    export function getFirstConstructorWithBody(node: ClassLikeDeclaration): ConstructorDeclaration & { body: FunctionBody } | undefined {
4554        return find(node.members, (member): member is ConstructorDeclaration & { body: FunctionBody } => isConstructorDeclaration(member) && nodeIsPresent(member.body));
4555    }
4556
4557    export function getSetAccessorValueParameter(accessor: SetAccessorDeclaration): ParameterDeclaration | undefined {
4558        if (accessor && accessor.parameters.length > 0) {
4559            const hasThis = accessor.parameters.length === 2 && parameterIsThisKeyword(accessor.parameters[0]);
4560            return accessor.parameters[hasThis ? 1 : 0];
4561        }
4562    }
4563
4564    /** Get the type annotation for the value parameter. */
4565    export function getSetAccessorTypeAnnotationNode(accessor: SetAccessorDeclaration): TypeNode | undefined {
4566        const parameter = getSetAccessorValueParameter(accessor);
4567        return parameter && parameter.type;
4568    }
4569
4570    export function getThisParameter(signature: SignatureDeclaration | JSDocSignature): ParameterDeclaration | undefined {
4571        // callback tags do not currently support this parameters
4572        if (signature.parameters.length && !isJSDocSignature(signature)) {
4573            const thisParameter = signature.parameters[0];
4574            if (parameterIsThisKeyword(thisParameter)) {
4575                return thisParameter;
4576            }
4577        }
4578    }
4579
4580    export function parameterIsThisKeyword(parameter: ParameterDeclaration): boolean {
4581        return isThisIdentifier(parameter.name);
4582    }
4583
4584    export function isThisIdentifier(node: Node | undefined): boolean {
4585        return !!node && node.kind === SyntaxKind.Identifier && identifierIsThisKeyword(node as Identifier);
4586    }
4587
4588    export function isThisInTypeQuery(node: Node): boolean {
4589        if (!isThisIdentifier(node)) {
4590            return false;
4591        }
4592
4593        while (isQualifiedName(node.parent) && node.parent.left === node) {
4594            node = node.parent;
4595        }
4596
4597        return node.parent.kind === SyntaxKind.TypeQuery;
4598    }
4599
4600    export function identifierIsThisKeyword(id: Identifier): boolean {
4601        return id.originalKeywordKind === SyntaxKind.ThisKeyword;
4602    }
4603
4604    export function getAllAccessorDeclarations(declarations: readonly Declaration[], accessor: AccessorDeclaration): AllAccessorDeclarations {
4605        // TODO: GH#18217
4606        let firstAccessor!: AccessorDeclaration;
4607        let secondAccessor!: AccessorDeclaration;
4608        let getAccessor!: GetAccessorDeclaration;
4609        let setAccessor!: SetAccessorDeclaration;
4610        if (hasDynamicName(accessor)) {
4611            firstAccessor = accessor;
4612            if (accessor.kind === SyntaxKind.GetAccessor) {
4613                getAccessor = accessor;
4614            }
4615            else if (accessor.kind === SyntaxKind.SetAccessor) {
4616                setAccessor = accessor;
4617            }
4618            else {
4619                Debug.fail("Accessor has wrong kind");
4620            }
4621        }
4622        else {
4623            forEach(declarations, member => {
4624                if (isAccessor(member)
4625                    && isStatic(member) === isStatic(accessor)) {
4626                    const memberName = getPropertyNameForPropertyNameNode(member.name);
4627                    const accessorName = getPropertyNameForPropertyNameNode(accessor.name);
4628                    if (memberName === accessorName) {
4629                        if (!firstAccessor) {
4630                            firstAccessor = member;
4631                        }
4632                        else if (!secondAccessor) {
4633                            secondAccessor = member;
4634                        }
4635
4636                        if (member.kind === SyntaxKind.GetAccessor && !getAccessor) {
4637                            getAccessor = member;
4638                        }
4639
4640                        if (member.kind === SyntaxKind.SetAccessor && !setAccessor) {
4641                            setAccessor = member;
4642                        }
4643                    }
4644                }
4645            });
4646        }
4647        return {
4648            firstAccessor,
4649            secondAccessor,
4650            getAccessor,
4651            setAccessor
4652        };
4653    }
4654
4655    /**
4656     * Gets the effective type annotation of a variable, parameter, or property. If the node was
4657     * parsed in a JavaScript file, gets the type annotation from JSDoc.  Also gets the type of
4658     * functions only the JSDoc case.
4659     */
4660    export function getEffectiveTypeAnnotationNode(node: Node): TypeNode | undefined {
4661        if (!isInJSFile(node) && isFunctionDeclaration(node)) return undefined;
4662        const type = (node as HasType).type;
4663        if (type || !isInJSFile(node)) return type;
4664        return isJSDocPropertyLikeTag(node) ? node.typeExpression && node.typeExpression.type : getJSDocType(node);
4665    }
4666
4667    export function getTypeAnnotationNode(node: Node): TypeNode | undefined {
4668        return (node as HasType).type;
4669    }
4670
4671    /**
4672     * Gets the effective return type annotation of a signature. If the node was parsed in a
4673     * JavaScript file, gets the return type annotation from JSDoc.
4674     */
4675    export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
4676        return isJSDocSignature(node) ?
4677            node.type && node.type.typeExpression && node.type.typeExpression.type :
4678            node.type || (isInJSFile(node) ? getJSDocReturnType(node) : undefined);
4679    }
4680
4681    export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[] {
4682        return flatMap(getJSDocTags(node), tag => isNonTypeAliasTemplate(tag) ? tag.typeParameters : undefined);
4683    }
4684
4685    /** template tags are only available when a typedef isn't already using them */
4686    function isNonTypeAliasTemplate(tag: JSDocTag): tag is JSDocTemplateTag {
4687        return isJSDocTemplateTag(tag) && !(tag.parent.kind === SyntaxKind.JSDoc && tag.parent.tags!.some(isJSDocTypeAlias));
4688    }
4689
4690    /**
4691     * Gets the effective type annotation of the value parameter of a set accessor. If the node
4692     * was parsed in a JavaScript file, gets the type annotation from JSDoc.
4693     */
4694    export function getEffectiveSetAccessorTypeAnnotationNode(node: SetAccessorDeclaration): TypeNode | undefined {
4695        const parameter = getSetAccessorValueParameter(node);
4696        return parameter && getEffectiveTypeAnnotationNode(parameter);
4697    }
4698
4699    export function emitNewLineBeforeLeadingComments(lineMap: readonly number[], writer: EmitTextWriter, node: TextRange, leadingComments: readonly CommentRange[] | undefined) {
4700        emitNewLineBeforeLeadingCommentsOfPosition(lineMap, writer, node.pos, leadingComments);
4701    }
4702
4703    export function emitNewLineBeforeLeadingCommentsOfPosition(lineMap: readonly number[], writer: EmitTextWriter, pos: number, leadingComments: readonly CommentRange[] | undefined) {
4704        // If the leading comments start on different line than the start of node, write new line
4705        if (leadingComments && leadingComments.length && pos !== leadingComments[0].pos &&
4706            getLineOfLocalPositionFromLineMap(lineMap, pos) !== getLineOfLocalPositionFromLineMap(lineMap, leadingComments[0].pos)) {
4707            writer.writeLine();
4708        }
4709    }
4710
4711    export function emitNewLineBeforeLeadingCommentOfPosition(lineMap: readonly number[], writer: EmitTextWriter, pos: number, commentPos: number) {
4712        // If the leading comments start on different line than the start of node, write new line
4713        if (pos !== commentPos &&
4714            getLineOfLocalPositionFromLineMap(lineMap, pos) !== getLineOfLocalPositionFromLineMap(lineMap, commentPos)) {
4715            writer.writeLine();
4716        }
4717    }
4718
4719    export function emitComments(
4720        text: string,
4721        lineMap: readonly number[],
4722        writer: EmitTextWriter,
4723        comments: readonly CommentRange[] | undefined,
4724        leadingSeparator: boolean,
4725        trailingSeparator: boolean,
4726        newLine: string,
4727        writeComment: (text: string, lineMap: readonly number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void) {
4728        if (comments && comments.length > 0) {
4729            if (leadingSeparator) {
4730                writer.writeSpace(" ");
4731            }
4732
4733            let emitInterveningSeparator = false;
4734            for (const comment of comments) {
4735                if (emitInterveningSeparator) {
4736                    writer.writeSpace(" ");
4737                    emitInterveningSeparator = false;
4738                }
4739
4740                writeComment(text, lineMap, writer, comment.pos, comment.end, newLine);
4741                if (comment.hasTrailingNewLine) {
4742                    writer.writeLine();
4743                }
4744                else {
4745                    emitInterveningSeparator = true;
4746                }
4747            }
4748
4749            if (emitInterveningSeparator && trailingSeparator) {
4750                writer.writeSpace(" ");
4751            }
4752        }
4753    }
4754
4755    /**
4756     * Detached comment is a comment at the top of file or function body that is separated from
4757     * the next statement by space.
4758     */
4759    export function emitDetachedComments(text: string, lineMap: readonly number[], writer: EmitTextWriter,
4760        writeComment: (text: string, lineMap: readonly number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void,
4761        node: TextRange, newLine: string, removeComments: boolean) {
4762        let leadingComments: CommentRange[] | undefined;
4763        let currentDetachedCommentInfo: { nodePos: number, detachedCommentEndPos: number } | undefined;
4764        if (removeComments) {
4765            // removeComments is true, only reserve pinned comment at the top of file
4766            // For example:
4767            //      /*! Pinned Comment */
4768            //
4769            //      var x = 10;
4770            if (node.pos === 0) {
4771                leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedCommentLocal);
4772            }
4773        }
4774        else {
4775            // removeComments is false, just get detached as normal and bypass the process to filter comment
4776            leadingComments = getLeadingCommentRanges(text, node.pos);
4777        }
4778
4779        if (leadingComments) {
4780            const detachedComments: CommentRange[] = [];
4781            let lastComment: CommentRange | undefined;
4782
4783            for (const comment of leadingComments) {
4784                if (lastComment) {
4785                    const lastCommentLine = getLineOfLocalPositionFromLineMap(lineMap, lastComment.end);
4786                    const commentLine = getLineOfLocalPositionFromLineMap(lineMap, comment.pos);
4787
4788                    if (commentLine >= lastCommentLine + 2) {
4789                        // There was a blank line between the last comment and this comment.  This
4790                        // comment is not part of the copyright comments.  Return what we have so
4791                        // far.
4792                        break;
4793                    }
4794                }
4795
4796                detachedComments.push(comment);
4797                lastComment = comment;
4798            }
4799
4800            if (detachedComments.length) {
4801                // All comments look like they could have been part of the copyright header.  Make
4802                // sure there is at least one blank line between it and the node.  If not, it's not
4803                // a copyright header.
4804                const lastCommentLine = getLineOfLocalPositionFromLineMap(lineMap, last(detachedComments).end);
4805                const nodeLine = getLineOfLocalPositionFromLineMap(lineMap, skipTrivia(text, node.pos));
4806                if (nodeLine >= lastCommentLine + 2) {
4807                    // Valid detachedComments
4808                    emitNewLineBeforeLeadingComments(lineMap, writer, node, leadingComments);
4809                    emitComments(text, lineMap, writer, detachedComments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment);
4810                    currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: last(detachedComments).end };
4811                }
4812            }
4813        }
4814
4815        return currentDetachedCommentInfo;
4816
4817        function isPinnedCommentLocal(comment: CommentRange) {
4818            return isPinnedComment(text, comment.pos);
4819        }
4820
4821    }
4822
4823    export function writeCommentRange(text: string, lineMap: readonly number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
4824        if (text.charCodeAt(commentPos + 1) === CharacterCodes.asterisk) {
4825            const firstCommentLineAndCharacter = computeLineAndCharacterOfPosition(lineMap, commentPos);
4826            const lineCount = lineMap.length;
4827            let firstCommentLineIndent: number | undefined;
4828            for (let pos = commentPos, currentLine = firstCommentLineAndCharacter.line; pos < commentEnd; currentLine++) {
4829                const nextLineStart = (currentLine + 1) === lineCount
4830                    ? text.length + 1
4831                    : lineMap[currentLine + 1];
4832
4833                if (pos !== commentPos) {
4834                    // If we are not emitting first line, we need to write the spaces to adjust the alignment
4835                    if (firstCommentLineIndent === undefined) {
4836                        firstCommentLineIndent = calculateIndent(text, lineMap[firstCommentLineAndCharacter.line], commentPos);
4837                    }
4838
4839                    // These are number of spaces writer is going to write at current indent
4840                    const currentWriterIndentSpacing = writer.getIndent() * getIndentSize();
4841
4842                    // Number of spaces we want to be writing
4843                    // eg: Assume writer indent
4844                    // module m {
4845                    //         /* starts at character 9 this is line 1
4846                    //    * starts at character pos 4 line                        --1  = 8 - 8 + 3
4847                    //   More left indented comment */                            --2  = 8 - 8 + 2
4848                    //     class c { }
4849                    // }
4850                    // module m {
4851                    //     /* this is line 1 -- Assume current writer indent 8
4852                    //      * line                                                --3 = 8 - 4 + 5
4853                    //            More right indented comment */                  --4 = 8 - 4 + 11
4854                    //     class c { }
4855                    // }
4856                    const spacesToEmit = currentWriterIndentSpacing - firstCommentLineIndent + calculateIndent(text, pos, nextLineStart);
4857                    if (spacesToEmit > 0) {
4858                        let numberOfSingleSpacesToEmit = spacesToEmit % getIndentSize();
4859                        const indentSizeSpaceString = getIndentString((spacesToEmit - numberOfSingleSpacesToEmit) / getIndentSize());
4860
4861                        // Write indent size string ( in eg 1: = "", 2: "" , 3: string with 8 spaces 4: string with 12 spaces
4862                        writer.rawWrite(indentSizeSpaceString);
4863
4864                        // Emit the single spaces (in eg: 1: 3 spaces, 2: 2 spaces, 3: 1 space, 4: 3 spaces)
4865                        while (numberOfSingleSpacesToEmit) {
4866                            writer.rawWrite(" ");
4867                            numberOfSingleSpacesToEmit--;
4868                        }
4869                    }
4870                    else {
4871                        // No spaces to emit write empty string
4872                        writer.rawWrite("");
4873                    }
4874                }
4875
4876                // Write the comment line text
4877                writeTrimmedCurrentLine(text, commentEnd, writer, newLine, pos, nextLineStart);
4878
4879                pos = nextLineStart;
4880            }
4881        }
4882        else {
4883            // Single line comment of style //....
4884            writer.writeComment(text.substring(commentPos, commentEnd));
4885        }
4886    }
4887
4888    function writeTrimmedCurrentLine(text: string, commentEnd: number, writer: EmitTextWriter, newLine: string, pos: number, nextLineStart: number) {
4889        const end = Math.min(commentEnd, nextLineStart - 1);
4890        const currentLineText = trimString(text.substring(pos, end));
4891        if (currentLineText) {
4892            // trimmed forward and ending spaces text
4893            writer.writeComment(currentLineText);
4894            if (end !== commentEnd) {
4895                writer.writeLine();
4896            }
4897        }
4898        else {
4899            // Empty string - make sure we write empty line
4900            writer.rawWrite(newLine);
4901        }
4902    }
4903
4904    function calculateIndent(text: string, pos: number, end: number) {
4905        let currentLineIndent = 0;
4906        for (; pos < end && isWhiteSpaceSingleLine(text.charCodeAt(pos)); pos++) {
4907            if (text.charCodeAt(pos) === CharacterCodes.tab) {
4908                // Tabs = TabSize = indent size and go to next tabStop
4909                currentLineIndent += getIndentSize() - (currentLineIndent % getIndentSize());
4910            }
4911            else {
4912                // Single space
4913                currentLineIndent++;
4914            }
4915        }
4916
4917        return currentLineIndent;
4918    }
4919
4920    export function hasEffectiveModifiers(node: Node) {
4921        return getEffectiveModifierFlags(node) !== ModifierFlags.None;
4922    }
4923
4924    export function hasSyntacticModifiers(node: Node) {
4925        return getSyntacticModifierFlags(node) !== ModifierFlags.None;
4926    }
4927
4928    export function hasEffectiveModifier(node: Node, flags: ModifierFlags): boolean {
4929        return !!getSelectedEffectiveModifierFlags(node, flags);
4930    }
4931
4932    export function hasSyntacticModifier(node: Node, flags: ModifierFlags): boolean {
4933        return !!getSelectedSyntacticModifierFlags(node, flags);
4934    }
4935
4936    export function isStatic(node: Node) {
4937        // https://tc39.es/ecma262/#sec-static-semantics-isstatic
4938        return isClassElement(node) && hasStaticModifier(node) || isClassStaticBlockDeclaration(node);
4939    }
4940
4941    export function hasIllegalDecorators(node: Node): boolean {
4942        return canHaveIllegalDecorators(node);
4943    }
4944
4945    export function hasStaticModifier(node: Node): boolean {
4946        return hasSyntacticModifier(node, ModifierFlags.Static);
4947    }
4948
4949    export function hasOverrideModifier(node: Node): boolean {
4950        return hasEffectiveModifier(node, ModifierFlags.Override);
4951    }
4952
4953    export function hasAbstractModifier(node: Node): boolean {
4954        return hasSyntacticModifier(node, ModifierFlags.Abstract);
4955    }
4956
4957    export function hasAmbientModifier(node: Node): boolean {
4958        return hasSyntacticModifier(node, ModifierFlags.Ambient);
4959    }
4960
4961    export function hasAccessorModifier(node: Node): boolean {
4962        return hasSyntacticModifier(node, ModifierFlags.Accessor);
4963    }
4964
4965    export function hasEffectiveReadonlyModifier(node: Node): boolean {
4966        return hasEffectiveModifier(node, ModifierFlags.Readonly);
4967    }
4968
4969    export function hasDecorators(node: Node): boolean {
4970        return hasSyntacticModifier(node, ModifierFlags.Decorator);
4971    }
4972
4973    export function getSelectedEffectiveModifierFlags(node: Node, flags: ModifierFlags): ModifierFlags {
4974        return getEffectiveModifierFlags(node) & flags;
4975    }
4976
4977    export function getSelectedSyntacticModifierFlags(node: Node, flags: ModifierFlags): ModifierFlags {
4978        return getSyntacticModifierFlags(node) & flags;
4979    }
4980
4981    function getModifierFlagsWorker(node: Node, includeJSDoc: boolean, alwaysIncludeJSDoc?: boolean): ModifierFlags {
4982        if (node.kind >= SyntaxKind.FirstToken && node.kind <= SyntaxKind.LastToken) {
4983            return ModifierFlags.None;
4984        }
4985
4986        if (!(node.modifierFlagsCache & ModifierFlags.HasComputedFlags)) {
4987            node.modifierFlagsCache = getSyntacticModifierFlagsNoCache(node) | ModifierFlags.HasComputedFlags;
4988        }
4989
4990        if (includeJSDoc && !(node.modifierFlagsCache & ModifierFlags.HasComputedJSDocModifiers) && (alwaysIncludeJSDoc || isInJSFile(node)) && node.parent) {
4991            node.modifierFlagsCache |= getJSDocModifierFlagsNoCache(node) | ModifierFlags.HasComputedJSDocModifiers;
4992        }
4993
4994        return node.modifierFlagsCache & ~(ModifierFlags.HasComputedFlags | ModifierFlags.HasComputedJSDocModifiers);
4995    }
4996
4997    /**
4998     * Gets the effective ModifierFlags for the provided node, including JSDoc modifiers. The modifiers will be cached on the node to improve performance.
4999     *
5000     * NOTE: This function may use `parent` pointers.
5001     */
5002    export function getEffectiveModifierFlags(node: Node): ModifierFlags {
5003        return getModifierFlagsWorker(node, /*includeJSDoc*/ true);
5004    }
5005
5006    export function getEffectiveModifierFlagsAlwaysIncludeJSDoc(node: Node): ModifierFlags {
5007        return getModifierFlagsWorker(node, /*includeJSDOc*/ true, /*alwaysIncludeJSDOc*/ true);
5008    }
5009
5010    /**
5011     * Gets the ModifierFlags for syntactic modifiers on the provided node. The modifiers will be cached on the node to improve performance.
5012     *
5013     * NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc.
5014     */
5015    export function getSyntacticModifierFlags(node: Node): ModifierFlags {
5016        return getModifierFlagsWorker(node, /*includeJSDoc*/ false);
5017    }
5018
5019    function getJSDocModifierFlagsNoCache(node: Node): ModifierFlags {
5020        let flags = ModifierFlags.None;
5021        if (!!node.parent && !isParameter(node)) {
5022            if (isInJSFile(node)) {
5023                if (getJSDocPublicTagNoCache(node)) flags |= ModifierFlags.Public;
5024                if (getJSDocPrivateTagNoCache(node)) flags |= ModifierFlags.Private;
5025                if (getJSDocProtectedTagNoCache(node)) flags |= ModifierFlags.Protected;
5026                if (getJSDocReadonlyTagNoCache(node)) flags |= ModifierFlags.Readonly;
5027                if (getJSDocOverrideTagNoCache(node)) flags |= ModifierFlags.Override;
5028            }
5029            if (getJSDocDeprecatedTagNoCache(node)) flags |= ModifierFlags.Deprecated;
5030        }
5031
5032        return flags;
5033    }
5034
5035    /**
5036     * Gets the effective ModifierFlags for the provided node, including JSDoc modifiers. The modifier flags cache on the node is ignored.
5037     *
5038     * NOTE: This function may use `parent` pointers.
5039     */
5040    export function getEffectiveModifierFlagsNoCache(node: Node): ModifierFlags {
5041        return getSyntacticModifierFlagsNoCache(node) | getJSDocModifierFlagsNoCache(node);
5042    }
5043
5044    /**
5045     * Gets the ModifierFlags for syntactic modifiers on the provided node. The modifier flags cache on the node is ignored.
5046     *
5047     * NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc.
5048     */
5049    export function getSyntacticModifierFlagsNoCache(node: Node): ModifierFlags {
5050        let flags = canHaveModifiers(node) ? modifiersToFlags(node.modifiers) : ModifierFlags.None;
5051        if (node.flags & NodeFlags.NestedNamespace || (node.kind === SyntaxKind.Identifier && (node as Identifier).isInJSDocNamespace)) {
5052            flags |= ModifierFlags.Export;
5053        }
5054        return flags;
5055    }
5056
5057    export function modifiersToFlags(modifiers: readonly ModifierLike[] | undefined) {
5058        let flags = ModifierFlags.None;
5059        if (modifiers) {
5060            for (const modifier of modifiers) {
5061                flags |= modifierToFlag(modifier.kind);
5062            }
5063        }
5064        return flags;
5065    }
5066
5067    export function modifierToFlag(token: SyntaxKind): ModifierFlags {
5068        switch (token) {
5069            case SyntaxKind.StaticKeyword: return ModifierFlags.Static;
5070            case SyntaxKind.PublicKeyword: return ModifierFlags.Public;
5071            case SyntaxKind.ProtectedKeyword: return ModifierFlags.Protected;
5072            case SyntaxKind.PrivateKeyword: return ModifierFlags.Private;
5073            case SyntaxKind.AbstractKeyword: return ModifierFlags.Abstract;
5074            case SyntaxKind.AccessorKeyword: return ModifierFlags.Accessor;
5075            case SyntaxKind.ExportKeyword: return ModifierFlags.Export;
5076            case SyntaxKind.DeclareKeyword: return ModifierFlags.Ambient;
5077            case SyntaxKind.ConstKeyword: return ModifierFlags.Const;
5078            case SyntaxKind.DefaultKeyword: return ModifierFlags.Default;
5079            case SyntaxKind.AsyncKeyword: return ModifierFlags.Async;
5080            case SyntaxKind.ReadonlyKeyword: return ModifierFlags.Readonly;
5081            case SyntaxKind.OverrideKeyword: return ModifierFlags.Override;
5082            case SyntaxKind.InKeyword: return ModifierFlags.In;
5083            case SyntaxKind.OutKeyword: return ModifierFlags.Out;
5084            case SyntaxKind.Decorator: return ModifierFlags.Decorator;
5085        }
5086        return ModifierFlags.None;
5087    }
5088
5089    export function isLogicalOperator(token: SyntaxKind): boolean {
5090        return token === SyntaxKind.BarBarToken
5091            || token === SyntaxKind.AmpersandAmpersandToken
5092            || token === SyntaxKind.ExclamationToken;
5093    }
5094
5095    export function isLogicalOrCoalescingAssignmentOperator(token: SyntaxKind): token is LogicalOrCoalescingAssignmentOperator {
5096        return token === SyntaxKind.BarBarEqualsToken
5097            || token === SyntaxKind.AmpersandAmpersandEqualsToken
5098            || token === SyntaxKind.QuestionQuestionEqualsToken;
5099    }
5100
5101    export function isLogicalOrCoalescingAssignmentExpression(expr: BinaryExpression): expr is AssignmentExpression<Token<LogicalOrCoalescingAssignmentOperator>> {
5102        return isLogicalOrCoalescingAssignmentOperator(expr.operatorToken.kind);
5103    }
5104
5105    export function isAssignmentOperator(token: SyntaxKind): boolean {
5106        return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment;
5107    }
5108
5109    /** Get `C` given `N` if `N` is in the position `class C extends N` where `N` is an ExpressionWithTypeArguments. */
5110    export function tryGetClassExtendingExpressionWithTypeArguments(node: Node): ClassLikeDeclaration | undefined {
5111        const cls = tryGetClassImplementingOrExtendingExpressionWithTypeArguments(node);
5112        return cls && !cls.isImplements ? cls.class : undefined;
5113    }
5114
5115    export interface ClassImplementingOrExtendingExpressionWithTypeArguments {
5116        readonly class: ClassLikeDeclaration;
5117        readonly isImplements: boolean;
5118    }
5119    export function tryGetClassImplementingOrExtendingExpressionWithTypeArguments(node: Node): ClassImplementingOrExtendingExpressionWithTypeArguments | undefined {
5120        return isExpressionWithTypeArguments(node)
5121            && isHeritageClause(node.parent)
5122            && isClassLike(node.parent.parent)
5123            ? { class: node.parent.parent, isImplements: node.parent.token === SyntaxKind.ImplementsKeyword }
5124            : undefined;
5125    }
5126
5127    export function isAssignmentExpression(node: Node, excludeCompoundAssignment: true): node is AssignmentExpression<EqualsToken>;
5128    export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: false): node is AssignmentExpression<AssignmentOperatorToken>;
5129    export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: boolean): node is AssignmentExpression<AssignmentOperatorToken> {
5130        return isBinaryExpression(node)
5131            && (excludeCompoundAssignment
5132                ? node.operatorToken.kind === SyntaxKind.EqualsToken
5133                : isAssignmentOperator(node.operatorToken.kind))
5134            && isLeftHandSideExpression(node.left);
5135    }
5136
5137    export function isLeftHandSideOfAssignment(node: Node) {
5138        return isAssignmentExpression(node.parent) && node.parent.left === node;
5139    }
5140    export function isDestructuringAssignment(node: Node): node is DestructuringAssignment {
5141        if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) {
5142            const kind = node.left.kind;
5143            return kind === SyntaxKind.ObjectLiteralExpression
5144                || kind === SyntaxKind.ArrayLiteralExpression;
5145        }
5146
5147        return false;
5148    }
5149
5150    export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): node is ExpressionWithTypeArguments {
5151        return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined;
5152    }
5153
5154    export function isEntityNameExpression(node: Node): node is EntityNameExpression {
5155        return node.kind === SyntaxKind.Identifier || isPropertyAccessEntityNameExpression(node);
5156    }
5157
5158    export function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier {
5159        switch (node.kind) {
5160            case SyntaxKind.Identifier:
5161                return node;
5162            case SyntaxKind.QualifiedName:
5163                do {
5164                    node = node.left;
5165                } while (node.kind !== SyntaxKind.Identifier);
5166                return node;
5167            case SyntaxKind.PropertyAccessExpression:
5168                do {
5169                    node = node.expression;
5170                } while (node.kind !== SyntaxKind.Identifier);
5171                return node;
5172        }
5173    }
5174
5175    export function isDottedName(node: Expression): boolean {
5176        return node.kind === SyntaxKind.Identifier
5177            || node.kind === SyntaxKind.ThisKeyword
5178            || node.kind === SyntaxKind.SuperKeyword
5179            || node.kind === SyntaxKind.MetaProperty
5180            || node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((node as PropertyAccessExpression).expression)
5181            || node.kind === SyntaxKind.ParenthesizedExpression && isDottedName((node as ParenthesizedExpression).expression);
5182    }
5183
5184    export function isPropertyAccessEntityNameExpression(node: Node): node is PropertyAccessEntityNameExpression {
5185        return isPropertyAccessExpression(node) && isIdentifier(node.name) && isEntityNameExpression(node.expression);
5186    }
5187
5188    export function tryGetPropertyAccessOrIdentifierToString(expr: Expression): string | undefined {
5189        if (isPropertyAccessExpression(expr)) {
5190            const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression);
5191            if (baseStr !== undefined) {
5192                return baseStr + "." + entityNameToString(expr.name);
5193            }
5194        }
5195        else if (isElementAccessExpression(expr)) {
5196            const baseStr = tryGetPropertyAccessOrIdentifierToString(expr.expression);
5197            if (baseStr !== undefined && isPropertyName(expr.argumentExpression)) {
5198                return baseStr + "." + getPropertyNameForPropertyNameNode(expr.argumentExpression);
5199            }
5200        }
5201        else if (isIdentifier(expr)) {
5202            return unescapeLeadingUnderscores(expr.escapedText);
5203        }
5204        return undefined;
5205    }
5206
5207    export function isPrototypeAccess(node: Node): node is BindableStaticAccessExpression {
5208        return isBindableStaticAccessExpression(node) && getElementOrPropertyAccessName(node) === "prototype";
5209    }
5210
5211    export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) {
5212        return (node.parent.kind === SyntaxKind.QualifiedName && (node.parent as QualifiedName).right === node) ||
5213            (node.parent.kind === SyntaxKind.PropertyAccessExpression && (node.parent as PropertyAccessExpression).name === node);
5214    }
5215
5216    export function isRightSideOfAccessExpression(node: Node) {
5217        return isPropertyAccessExpression(node.parent) && node.parent.name === node
5218            || isElementAccessExpression(node.parent) && node.parent.argumentExpression === node;
5219    }
5220
5221    export function isRightSideOfQualifiedNameOrPropertyAccessOrJSDocMemberName(node: Node) {
5222        return isQualifiedName(node.parent) && node.parent.right === node
5223            || isPropertyAccessExpression(node.parent) && node.parent.name === node
5224            || isJSDocMemberName(node.parent) && node.parent.right === node;
5225    }
5226
5227    export function isEmptyObjectLiteral(expression: Node): boolean {
5228        return expression.kind === SyntaxKind.ObjectLiteralExpression &&
5229            (expression as ObjectLiteralExpression).properties.length === 0;
5230    }
5231
5232    export function isEmptyArrayLiteral(expression: Node): boolean {
5233        return expression.kind === SyntaxKind.ArrayLiteralExpression &&
5234            (expression as ArrayLiteralExpression).elements.length === 0;
5235    }
5236
5237    export function getLocalSymbolForExportDefault(symbol: Symbol) {
5238        if (!isExportDefaultSymbol(symbol) || !symbol.declarations) return undefined;
5239        for (const decl of symbol.declarations) {
5240            if (decl.localSymbol) return decl.localSymbol;
5241        }
5242        return undefined;
5243    }
5244
5245    function isExportDefaultSymbol(symbol: Symbol): boolean {
5246        return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations![0], ModifierFlags.Default);
5247    }
5248
5249    /** Return ".ts", ".d.ts", or ".tsx", if that is the extension. */
5250    export function tryExtractTSExtension(fileName: string): string | undefined {
5251        return find(supportedTSExtensionsForExtractExtension, extension => fileExtensionIs(fileName, extension));
5252    }
5253    /**
5254     * Replace each instance of non-ascii characters by one, two, three, or four escape sequences
5255     * representing the UTF-8 encoding of the character, and return the expanded char code list.
5256     */
5257    function getExpandedCharCodes(input: string): number[] {
5258        const output: number[] = [];
5259        const length = input.length;
5260
5261        for (let i = 0; i < length; i++) {
5262            const charCode = input.charCodeAt(i);
5263
5264            // handle utf8
5265            if (charCode < 0x80) {
5266                output.push(charCode);
5267            }
5268            else if (charCode < 0x800) {
5269                output.push((charCode >> 6) | 0B11000000);
5270                output.push((charCode & 0B00111111) | 0B10000000);
5271            }
5272            else if (charCode < 0x10000) {
5273                output.push((charCode >> 12) | 0B11100000);
5274                output.push(((charCode >> 6) & 0B00111111) | 0B10000000);
5275                output.push((charCode & 0B00111111) | 0B10000000);
5276            }
5277            else if (charCode < 0x20000) {
5278                output.push((charCode >> 18) | 0B11110000);
5279                output.push(((charCode >> 12) & 0B00111111) | 0B10000000);
5280                output.push(((charCode >> 6) & 0B00111111) | 0B10000000);
5281                output.push((charCode & 0B00111111) | 0B10000000);
5282            }
5283            else {
5284                Debug.assert(false, "Unexpected code point");
5285            }
5286        }
5287
5288        return output;
5289    }
5290
5291    const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
5292
5293    /**
5294     * Converts a string to a base-64 encoded ASCII string.
5295     */
5296    export function convertToBase64(input: string): string {
5297        let result = "";
5298        const charCodes = getExpandedCharCodes(input);
5299        let i = 0;
5300        const length = charCodes.length;
5301        let byte1: number, byte2: number, byte3: number, byte4: number;
5302
5303        while (i < length) {
5304            // Convert every 6-bits in the input 3 character points
5305            // into a base64 digit
5306            byte1 = charCodes[i] >> 2;
5307            byte2 = (charCodes[i] & 0B00000011) << 4 | charCodes[i + 1] >> 4;
5308            byte3 = (charCodes[i + 1] & 0B00001111) << 2 | charCodes[i + 2] >> 6;
5309            byte4 = charCodes[i + 2] & 0B00111111;
5310
5311            // We are out of characters in the input, set the extra
5312            // digits to 64 (padding character).
5313            if (i + 1 >= length) {
5314                byte3 = byte4 = 64;
5315            }
5316            else if (i + 2 >= length) {
5317                byte4 = 64;
5318            }
5319
5320            // Write to the output
5321            result += base64Digits.charAt(byte1) + base64Digits.charAt(byte2) + base64Digits.charAt(byte3) + base64Digits.charAt(byte4);
5322
5323            i += 3;
5324        }
5325
5326        return result;
5327    }
5328
5329    function getStringFromExpandedCharCodes(codes: number[]): string {
5330        let output = "";
5331        let i = 0;
5332        const length = codes.length;
5333        while (i < length) {
5334            const charCode = codes[i];
5335
5336            if (charCode < 0x80) {
5337                output += String.fromCharCode(charCode);
5338                i++;
5339            }
5340            else if ((charCode & 0B11000000) === 0B11000000) {
5341                let value = charCode & 0B00111111;
5342                i++;
5343                let nextCode: number = codes[i];
5344                while ((nextCode & 0B11000000) === 0B10000000) {
5345                    value = (value << 6) | (nextCode & 0B00111111);
5346                    i++;
5347                    nextCode = codes[i];
5348                }
5349                // `value` may be greater than 10FFFF (the maximum unicode codepoint) - JS will just make this into an invalid character for us
5350                output += String.fromCharCode(value);
5351            }
5352            else {
5353                // We don't want to kill the process when decoding fails (due to a following char byte not
5354                // following a leading char), so we just print the (bad) value
5355                output += String.fromCharCode(charCode);
5356                i++;
5357            }
5358        }
5359        return output;
5360    }
5361
5362    export function base64encode(host: { base64encode?(input: string): string } | undefined, input: string): string {
5363        if (host && host.base64encode) {
5364            return host.base64encode(input);
5365        }
5366        return convertToBase64(input);
5367    }
5368
5369    export function base64decode(host: { base64decode?(input: string): string } | undefined, input: string): string {
5370        if (host && host.base64decode) {
5371            return host.base64decode(input);
5372        }
5373        const length = input.length;
5374        const expandedCharCodes: number[] = [];
5375        let i = 0;
5376        while (i < length) {
5377            // Stop decoding once padding characters are present
5378            if (input.charCodeAt(i) === base64Digits.charCodeAt(64)) {
5379                break;
5380            }
5381            // convert 4 input digits into three characters, ignoring padding characters at the end
5382            const ch1 = base64Digits.indexOf(input[i]);
5383            const ch2 = base64Digits.indexOf(input[i + 1]);
5384            const ch3 = base64Digits.indexOf(input[i + 2]);
5385            const ch4 = base64Digits.indexOf(input[i + 3]);
5386
5387            const code1 = ((ch1 & 0B00111111) << 2) | ((ch2 >> 4) & 0B00000011);
5388            const code2 = ((ch2 & 0B00001111) << 4) | ((ch3 >> 2) & 0B00001111);
5389            const code3 = ((ch3 & 0B00000011) << 6) | (ch4 & 0B00111111);
5390
5391            if (code2 === 0 && ch3 !== 0) { // code2 decoded to zero, but ch3 was padding - elide code2 and code3
5392                expandedCharCodes.push(code1);
5393            }
5394            else if (code3 === 0 && ch4 !== 0) { // code3 decoded to zero, but ch4 was padding, elide code3
5395                expandedCharCodes.push(code1, code2);
5396            }
5397            else {
5398                expandedCharCodes.push(code1, code2, code3);
5399            }
5400            i += 4;
5401        }
5402        return getStringFromExpandedCharCodes(expandedCharCodes);
5403    }
5404
5405    export function readJsonOrUndefined(path: string, hostOrText: { readFile(fileName: string): string | undefined } | string): object | undefined {
5406        const jsonText = isString(hostOrText) ? hostOrText : hostOrText.readFile(path);
5407        if (!jsonText) return undefined;
5408        // gracefully handle if readFile fails or returns not JSON
5409        const result = parseConfigFileTextToJson(path, jsonText);
5410        return !result.error ? result.config : undefined;
5411    }
5412
5413    export function readJson(path: string, host: { readFile(fileName: string): string | undefined }): object {
5414        return readJsonOrUndefined(path, host) || {};
5415    }
5416
5417    export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean }): boolean {
5418        // if host does not support 'directoryExists' assume that directory will exist
5419        return !host.directoryExists || host.directoryExists(directoryName);
5420    }
5421
5422    const carriageReturnLineFeed = "\r\n";
5423    const lineFeed = "\n";
5424    export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, getNewLine?: () => string): string {
5425        switch (options.newLine) {
5426            case NewLineKind.CarriageReturnLineFeed:
5427                return carriageReturnLineFeed;
5428            case NewLineKind.LineFeed:
5429                return lineFeed;
5430        }
5431        return getNewLine ? getNewLine() : sys ? sys.newLine : carriageReturnLineFeed;
5432    }
5433
5434    /**
5435     * Creates a new TextRange from the provided pos and end.
5436     *
5437     * @param pos The start position.
5438     * @param end The end position.
5439     */
5440    export function createRange(pos: number, end: number = pos): TextRange {
5441        Debug.assert(end >= pos || end === -1);
5442        return { pos, end };
5443    }
5444
5445    /**
5446     * Creates a new TextRange from a provided range with a new end position.
5447     *
5448     * @param range A TextRange.
5449     * @param end The new end position.
5450     */
5451    export function moveRangeEnd(range: TextRange, end: number): TextRange {
5452        return createRange(range.pos, end);
5453    }
5454
5455    /**
5456     * Creates a new TextRange from a provided range with a new start position.
5457     *
5458     * @param range A TextRange.
5459     * @param pos The new Start position.
5460     */
5461    export function moveRangePos(range: TextRange, pos: number): TextRange {
5462        return createRange(pos, range.end);
5463    }
5464
5465    /**
5466     * Moves the start position of a range past any decorators.
5467     */
5468    export function moveRangePastDecorators(node: Node): TextRange {
5469        const lastDecorator = canHaveModifiers(node) ? findLast(node.modifiers, isDecorator) : undefined;
5470        return lastDecorator && !positionIsSynthesized(lastDecorator.end)
5471            ? moveRangePos(node, lastDecorator.end)
5472            : node;
5473    }
5474
5475    /**
5476     * Moves the start position of a range past any decorators or modifiers.
5477     */
5478    export function moveRangePastModifiers(node: Node): TextRange {
5479        const lastModifier = canHaveModifiers(node) ? lastOrUndefined(node.modifiers) : undefined;
5480        return lastModifier && !positionIsSynthesized(lastModifier.end)
5481            ? moveRangePos(node, lastModifier.end)
5482            : moveRangePastDecorators(node);
5483    }
5484
5485    /**
5486     * Determines whether a TextRange has the same start and end positions.
5487     *
5488     * @param range A TextRange.
5489     */
5490    export function isCollapsedRange(range: TextRange) {
5491        return range.pos === range.end;
5492    }
5493
5494    /**
5495     * Creates a new TextRange for a token at the provides start position.
5496     *
5497     * @param pos The start position.
5498     * @param token The token.
5499     */
5500    export function createTokenRange(pos: number, token: SyntaxKind): TextRange {
5501        return createRange(pos, pos + tokenToString(token)!.length);
5502    }
5503
5504    export function rangeIsOnSingleLine(range: TextRange, sourceFile: SourceFile) {
5505        return rangeStartIsOnSameLineAsRangeEnd(range, range, sourceFile);
5506    }
5507
5508    export function rangeStartPositionsAreOnSameLine(range1: TextRange, range2: TextRange, sourceFile: SourceFile) {
5509        return positionsAreOnSameLine(
5510            getStartPositionOfRange(range1, sourceFile, /*includeComments*/ false),
5511            getStartPositionOfRange(range2, sourceFile, /*includeComments*/ false),
5512            sourceFile);
5513    }
5514
5515    export function rangeEndPositionsAreOnSameLine(range1: TextRange, range2: TextRange, sourceFile: SourceFile) {
5516        return positionsAreOnSameLine(range1.end, range2.end, sourceFile);
5517    }
5518
5519    export function rangeStartIsOnSameLineAsRangeEnd(range1: TextRange, range2: TextRange, sourceFile: SourceFile) {
5520        return positionsAreOnSameLine(getStartPositionOfRange(range1, sourceFile, /*includeComments*/ false), range2.end, sourceFile);
5521    }
5522
5523    export function rangeEndIsOnSameLineAsRangeStart(range1: TextRange, range2: TextRange, sourceFile: SourceFile) {
5524        return positionsAreOnSameLine(range1.end, getStartPositionOfRange(range2, sourceFile, /*includeComments*/ false), sourceFile);
5525    }
5526
5527    export function getLinesBetweenRangeEndAndRangeStart(range1: TextRange, range2: TextRange, sourceFile: SourceFile, includeSecondRangeComments: boolean) {
5528        const range2Start = getStartPositionOfRange(range2, sourceFile, includeSecondRangeComments);
5529        return getLinesBetweenPositions(sourceFile, range1.end, range2Start);
5530    }
5531
5532    export function getLinesBetweenRangeEndPositions(range1: TextRange, range2: TextRange, sourceFile: SourceFile) {
5533        return getLinesBetweenPositions(sourceFile, range1.end, range2.end);
5534    }
5535
5536    export function isNodeArrayMultiLine(list: NodeArray<Node>, sourceFile: SourceFile): boolean {
5537        return !positionsAreOnSameLine(list.pos, list.end, sourceFile);
5538    }
5539
5540    export function positionsAreOnSameLine(pos1: number, pos2: number, sourceFile: SourceFile) {
5541        return getLinesBetweenPositions(sourceFile, pos1, pos2) === 0;
5542    }
5543
5544    export function getStartPositionOfRange(range: TextRange, sourceFile: SourceFile, includeComments: boolean) {
5545        return positionIsSynthesized(range.pos) ? -1 : skipTrivia(sourceFile.text, range.pos, /*stopAfterLineBreak*/ false, includeComments);
5546    }
5547
5548    export function getLinesBetweenPositionAndPrecedingNonWhitespaceCharacter(pos: number, stopPos: number, sourceFile: SourceFile, includeComments?: boolean) {
5549        const startPos = skipTrivia(sourceFile.text, pos, /*stopAfterLineBreak*/ false, includeComments);
5550        const prevPos = getPreviousNonWhitespacePosition(startPos, stopPos, sourceFile);
5551        return getLinesBetweenPositions(sourceFile, prevPos ?? stopPos, startPos);
5552    }
5553
5554    export function getLinesBetweenPositionAndNextNonWhitespaceCharacter(pos: number, stopPos: number, sourceFile: SourceFile, includeComments?: boolean) {
5555        const nextPos = skipTrivia(sourceFile.text, pos, /*stopAfterLineBreak*/ false, includeComments);
5556        return getLinesBetweenPositions(sourceFile, pos, Math.min(stopPos, nextPos));
5557    }
5558
5559    function getPreviousNonWhitespacePosition(pos: number, stopPos = 0, sourceFile: SourceFile) {
5560        while (pos-- > stopPos) {
5561            if (!isWhiteSpaceLike(sourceFile.text.charCodeAt(pos))) {
5562                return pos;
5563            }
5564        }
5565    }
5566
5567    /**
5568     * Determines whether a name was originally the declaration name of an enum or namespace
5569     * declaration.
5570     */
5571    export function isDeclarationNameOfEnumOrNamespace(node: Identifier) {
5572        const parseNode = getParseTreeNode(node);
5573        if (parseNode) {
5574            switch (parseNode.parent.kind) {
5575                case SyntaxKind.EnumDeclaration:
5576                case SyntaxKind.ModuleDeclaration:
5577                    return parseNode === (parseNode.parent as EnumDeclaration | ModuleDeclaration).name;
5578            }
5579        }
5580        return false;
5581    }
5582
5583    export function getInitializedVariables(node: VariableDeclarationList) {
5584        return filter(node.declarations, isInitializedVariable);
5585    }
5586
5587    function isInitializedVariable(node: VariableDeclaration): node is InitializedVariableDeclaration {
5588        return node.initializer !== undefined;
5589    }
5590
5591    export function isWatchSet(options: CompilerOptions) {
5592        // Firefox has Object.prototype.watch
5593        return options.watch && hasProperty(options, "watch");
5594    }
5595
5596    export function closeFileWatcher(watcher: FileWatcher) {
5597        watcher.close();
5598    }
5599
5600    export function getCheckFlags(symbol: Symbol): CheckFlags {
5601        return symbol.flags & SymbolFlags.Transient ? (symbol as TransientSymbol).checkFlags : 0;
5602    }
5603
5604    export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false): ModifierFlags {
5605        if (s.valueDeclaration) {
5606            const declaration = (isWrite && s.declarations && find(s.declarations, isSetAccessorDeclaration))
5607                || (s.flags & SymbolFlags.GetAccessor && find(s.declarations, isGetAccessorDeclaration)) || s.valueDeclaration;
5608            const flags = getCombinedModifierFlags(declaration);
5609            return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier;
5610        }
5611        if (getCheckFlags(s) & CheckFlags.Synthetic) {
5612            const checkFlags = (s as TransientSymbol).checkFlags;
5613            const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private :
5614                checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public :
5615                ModifierFlags.Protected;
5616            const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0;
5617            return accessModifier | staticModifier;
5618        }
5619        if (s.flags & SymbolFlags.Prototype) {
5620            return ModifierFlags.Public | ModifierFlags.Static;
5621        }
5622        return 0;
5623    }
5624
5625    export function skipAlias(symbol: Symbol, checker: TypeChecker) {
5626        return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
5627    }
5628
5629    /** See comment on `declareModuleMember` in `binder.ts`. */
5630    export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
5631        return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
5632    }
5633
5634    export function isWriteOnlyAccess(node: Node) {
5635        return accessKind(node) === AccessKind.Write;
5636    }
5637
5638    export function isWriteAccess(node: Node) {
5639        return accessKind(node) !== AccessKind.Read;
5640    }
5641
5642    const enum AccessKind {
5643        /** Only reads from a variable. */
5644        Read,
5645        /** Only writes to a variable without using the result. E.g.: `x++;`. */
5646        Write,
5647        /** Writes to a variable and uses the result as an expression. E.g.: `f(x++);`. */
5648        ReadWrite
5649    }
5650    function accessKind(node: Node): AccessKind {
5651        const { parent } = node;
5652        if (!parent) return AccessKind.Read;
5653
5654        switch (parent.kind) {
5655            case SyntaxKind.ParenthesizedExpression:
5656                return accessKind(parent);
5657            case SyntaxKind.PostfixUnaryExpression:
5658            case SyntaxKind.PrefixUnaryExpression:
5659                const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
5660                return operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken ? writeOrReadWrite() : AccessKind.Read;
5661            case SyntaxKind.BinaryExpression:
5662                const { left, operatorToken } = parent as BinaryExpression;
5663                return left === node && isAssignmentOperator(operatorToken.kind) ?
5664                    operatorToken.kind === SyntaxKind.EqualsToken ? AccessKind.Write : writeOrReadWrite()
5665                    : AccessKind.Read;
5666            case SyntaxKind.PropertyAccessExpression:
5667                return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
5668            case SyntaxKind.PropertyAssignment: {
5669                const parentAccess = accessKind(parent.parent);
5670                // In `({ x: varname }) = { x: 1 }`, the left `x` is a read, the right `x` is a write.
5671                return node === (parent as PropertyAssignment).name ? reverseAccessKind(parentAccess) : parentAccess;
5672            }
5673            case SyntaxKind.ShorthandPropertyAssignment:
5674                // Assume it's the local variable being accessed, since we don't check public properties for --noUnusedLocals.
5675                return node === (parent as ShorthandPropertyAssignment).objectAssignmentInitializer ? AccessKind.Read : accessKind(parent.parent);
5676            case SyntaxKind.ArrayLiteralExpression:
5677                return accessKind(parent);
5678            default:
5679                return AccessKind.Read;
5680        }
5681
5682        function writeOrReadWrite(): AccessKind {
5683            // If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
5684            return parent.parent && walkUpParenthesizedExpressions(parent.parent).kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
5685        }
5686    }
5687    function reverseAccessKind(a: AccessKind): AccessKind {
5688        switch (a) {
5689            case AccessKind.Read:
5690                return AccessKind.Write;
5691            case AccessKind.Write:
5692                return AccessKind.Read;
5693            case AccessKind.ReadWrite:
5694                return AccessKind.ReadWrite;
5695            default:
5696                return Debug.assertNever(a);
5697        }
5698    }
5699
5700    export function compareDataObjects(dst: any, src: any): boolean {
5701        if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) {
5702            return false;
5703        }
5704
5705        for (const e in dst) {
5706            if (typeof dst[e] === "object") {
5707                if (!compareDataObjects(dst[e], src[e])) {
5708                    return false;
5709                }
5710            }
5711            else if (typeof dst[e] !== "function") {
5712                if (dst[e] !== src[e]) {
5713                    return false;
5714                }
5715            }
5716        }
5717        return true;
5718    }
5719
5720    /**
5721     * clears already present map by calling onDeleteExistingValue callback before deleting that key/value
5722     */
5723    export function clearMap<K, T>(map: { forEach: ESMap<K, T>["forEach"]; clear: ESMap<K, T>["clear"]; }, onDeleteValue: (valueInMap: T, key: K) => void) {
5724        // Remove all
5725        map.forEach(onDeleteValue);
5726        map.clear();
5727    }
5728
5729    export interface MutateMapSkippingNewValuesOptions<K, T, U> {
5730        onDeleteValue(existingValue: T, key: K): void;
5731
5732        /**
5733         * If present this is called with the key when there is value for that key both in new map as well as existing map provided
5734         * Caller can then decide to update or remove this key.
5735         * If the key is removed, caller will get callback of createNewValue for that key.
5736         * If this callback is not provided, the value of such keys is not updated.
5737         */
5738        onExistingValue?(existingValue: T, valueInNewMap: U, key: K): void;
5739    }
5740
5741    /**
5742     * Mutates the map with newMap such that keys in map will be same as newMap.
5743     */
5744    export function mutateMapSkippingNewValues<K, T, U>(
5745        map: ESMap<K, T>,
5746        newMap: ReadonlyESMap<K, U>,
5747        options: MutateMapSkippingNewValuesOptions<K, T, U>
5748    ) {
5749        const { onDeleteValue, onExistingValue } = options;
5750        // Needs update
5751        map.forEach((existingValue, key) => {
5752            const valueInNewMap = newMap.get(key);
5753            // Not present any more in new map, remove it
5754            if (valueInNewMap === undefined) {
5755                map.delete(key);
5756                onDeleteValue(existingValue, key);
5757            }
5758            // If present notify about existing values
5759            else if (onExistingValue) {
5760                onExistingValue(existingValue, valueInNewMap, key);
5761            }
5762        });
5763    }
5764
5765    export interface MutateMapOptions<K, T, U> extends MutateMapSkippingNewValuesOptions<K, T, U> {
5766        createNewValue(key: K, valueInNewMap: U): T;
5767    }
5768
5769    /**
5770     * Mutates the map with newMap such that keys in map will be same as newMap.
5771     */
5772    export function mutateMap<K, T, U>(map: ESMap<K, T>, newMap: ReadonlyESMap<K, U>, options: MutateMapOptions<K, T, U>) {
5773        // Needs update
5774        mutateMapSkippingNewValues(map, newMap, options);
5775
5776        const { createNewValue } = options;
5777        // Add new values that are not already present
5778        newMap.forEach((valueInNewMap, key) => {
5779            if (!map.has(key)) {
5780                // New values
5781                map.set(key, createNewValue(key, valueInNewMap));
5782            }
5783        });
5784    }
5785
5786    export function isAbstractConstructorSymbol(symbol: Symbol): boolean {
5787        if (symbol.flags & SymbolFlags.Class) {
5788            const declaration = getClassLikeDeclarationOfSymbol(symbol);
5789            return !!declaration && hasSyntacticModifier(declaration, ModifierFlags.Abstract);
5790        }
5791        return false;
5792    }
5793
5794    export function getClassLikeDeclarationOfSymbol(symbol: Symbol): ClassLikeDeclaration | undefined {
5795        return symbol.declarations?.find(isClassLike);
5796    }
5797
5798    export function getObjectFlags(type: Type): ObjectFlags {
5799        return type.flags & TypeFlags.ObjectFlagsType ? (type as ObjectFlagsType).objectFlags : 0;
5800    }
5801
5802    export function typeHasCallOrConstructSignatures(type: Type, checker: TypeChecker) {
5803        return checker.getSignaturesOfType(type, SignatureKind.Call).length !== 0 || checker.getSignaturesOfType(type, SignatureKind.Construct).length !== 0;
5804    }
5805
5806    export function forSomeAncestorDirectory(directory: string, callback: (directory: string) => boolean): boolean {
5807        return !!forEachAncestorDirectory(directory, d => callback(d) ? true : undefined);
5808    }
5809
5810    export function isUMDExportSymbol(symbol: Symbol | undefined): boolean {
5811        return !!symbol && !!symbol.declarations && !!symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
5812    }
5813
5814    export function showModuleSpecifier({ moduleSpecifier }: ImportDeclaration): string {
5815        return isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : getTextOfNode(moduleSpecifier);
5816    }
5817
5818    export function getLastChild(node: Node): Node | undefined {
5819        let lastChild: Node | undefined;
5820        forEachChild(node,
5821            child => {
5822                if (nodeIsPresent(child)) lastChild = child;
5823            },
5824            children => {
5825                // As an optimization, jump straight to the end of the list.
5826                for (let i = children.length - 1; i >= 0; i--) {
5827                    if (nodeIsPresent(children[i])) {
5828                        lastChild = children[i];
5829                        break;
5830                    }
5831                }
5832            });
5833        return lastChild;
5834    }
5835
5836    /** Add a value to a set, and return true if it wasn't already present. */
5837    export function addToSeen<K>(seen: ESMap<K, true>, key: K): boolean;
5838    export function addToSeen<K, T>(seen: ESMap<K, T>, key: K, value: T): boolean;
5839    export function addToSeen<K, T>(seen: ESMap<K, T>, key: K, value: T = true as any): boolean {
5840        if (seen.has(key)) {
5841            return false;
5842        }
5843        seen.set(key, value);
5844        return true;
5845    }
5846
5847    export function isObjectTypeDeclaration(node: Node): node is ObjectTypeDeclaration {
5848        return isClassLike(node) || isInterfaceDeclaration(node) || isTypeLiteralNode(node);
5849    }
5850
5851    export function isTypeNodeKind(kind: SyntaxKind): kind is TypeNodeSyntaxKind {
5852        return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode)
5853            || kind === SyntaxKind.AnyKeyword
5854            || kind === SyntaxKind.UnknownKeyword
5855            || kind === SyntaxKind.NumberKeyword
5856            || kind === SyntaxKind.BigIntKeyword
5857            || kind === SyntaxKind.ObjectKeyword
5858            || kind === SyntaxKind.BooleanKeyword
5859            || kind === SyntaxKind.StringKeyword
5860            || kind === SyntaxKind.SymbolKeyword
5861            || kind === SyntaxKind.VoidKeyword
5862            || kind === SyntaxKind.UndefinedKeyword
5863            || kind === SyntaxKind.NeverKeyword
5864            || kind === SyntaxKind.ExpressionWithTypeArguments
5865            || kind === SyntaxKind.JSDocAllType
5866            || kind === SyntaxKind.JSDocUnknownType
5867            || kind === SyntaxKind.JSDocNullableType
5868            || kind === SyntaxKind.JSDocNonNullableType
5869            || kind === SyntaxKind.JSDocOptionalType
5870            || kind === SyntaxKind.JSDocFunctionType
5871            || kind === SyntaxKind.JSDocVariadicType;
5872    }
5873
5874    export function isAccessExpression(node: Node): node is AccessExpression {
5875        return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression;
5876    }
5877
5878    export function getNameOfAccessExpression(node: AccessExpression) {
5879        if (node.kind === SyntaxKind.PropertyAccessExpression) {
5880            return node.name;
5881        }
5882        Debug.assert(node.kind === SyntaxKind.ElementAccessExpression);
5883        return node.argumentExpression;
5884    }
5885
5886    export function isBundleFileTextLike(section: BundleFileSection): section is BundleFileTextLike {
5887        switch (section.kind) {
5888            case BundleFileSectionKind.Text:
5889            case BundleFileSectionKind.Internal:
5890                return true;
5891            default:
5892                return false;
5893        }
5894    }
5895
5896    export function isNamedImportsOrExports(node: Node): node is NamedImportsOrExports {
5897        return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports;
5898    }
5899
5900    export function getLeftmostAccessExpression(expr: Expression): Expression {
5901        while (isAccessExpression(expr)) {
5902            expr = expr.expression;
5903        }
5904        return expr;
5905    }
5906
5907    export function forEachNameInAccessChainWalkingLeft<T>(name: MemberName | StringLiteralLike, action: (name: MemberName | StringLiteralLike) => T | undefined): T | undefined {
5908        if (isAccessExpression(name.parent) && isRightSideOfAccessExpression(name)) {
5909            return walkAccessExpression(name.parent);
5910        }
5911
5912        function walkAccessExpression(access: AccessExpression): T | undefined {
5913            if (access.kind === SyntaxKind.PropertyAccessExpression) {
5914                const res = action(access.name);
5915                if (res !== undefined) {
5916                    return res;
5917                }
5918            }
5919            else if (access.kind === SyntaxKind.ElementAccessExpression) {
5920                if (isIdentifier(access.argumentExpression) || isStringLiteralLike(access.argumentExpression)) {
5921                    const res = action(access.argumentExpression);
5922                    if (res !== undefined) {
5923                        return res;
5924                    }
5925                }
5926                else {
5927                    // Chain interrupted by non-static-name access 'x[expr()].y.z'
5928                    return undefined;
5929                }
5930            }
5931
5932            if (isAccessExpression(access.expression)) {
5933                return walkAccessExpression(access.expression);
5934            }
5935            if (isIdentifier(access.expression)) {
5936                // End of chain at Identifier 'x.y.z'
5937                return action(access.expression);
5938            }
5939            // End of chain at non-Identifier 'x().y.z'
5940            return undefined;
5941        }
5942    }
5943
5944
5945
5946    export function getLeftmostExpression(node: Expression, stopAtCallExpressions: boolean) {
5947        while (true) {
5948            switch (node.kind) {
5949                case SyntaxKind.PostfixUnaryExpression:
5950                    node = (node as PostfixUnaryExpression).operand;
5951                    continue;
5952
5953                case SyntaxKind.BinaryExpression:
5954                    node = (node as BinaryExpression).left;
5955                    continue;
5956
5957                case SyntaxKind.ConditionalExpression:
5958                    node = (node as ConditionalExpression).condition;
5959                    continue;
5960
5961                case SyntaxKind.TaggedTemplateExpression:
5962                    node = (node as TaggedTemplateExpression).tag;
5963                    continue;
5964
5965                case SyntaxKind.CallExpression:
5966                    if (stopAtCallExpressions) {
5967                        return node;
5968                    }
5969                    // falls through
5970                case SyntaxKind.AsExpression:
5971                case SyntaxKind.ElementAccessExpression:
5972                case SyntaxKind.PropertyAccessExpression:
5973                case SyntaxKind.NonNullExpression:
5974                case SyntaxKind.PartiallyEmittedExpression:
5975                case SyntaxKind.SatisfiesExpression:
5976                    node = (node as CallExpression | PropertyAccessExpression | ElementAccessExpression | AsExpression | NonNullExpression | PartiallyEmittedExpression | SatisfiesExpression).expression;
5977                    continue;
5978            }
5979
5980            return node;
5981        }
5982    }
5983
5984    export interface ObjectAllocator {
5985        getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node;
5986        getTokenConstructor(): new <TKind extends SyntaxKind>(kind: TKind, pos?: number, end?: number) => Token<TKind>;
5987        getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos?: number, end?: number) => Identifier;
5988        getPrivateIdentifierConstructor(): new (kind: SyntaxKind.PrivateIdentifier, pos?: number, end?: number) => PrivateIdentifier;
5989        getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos?: number, end?: number) => SourceFile;
5990        getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol;
5991        getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type;
5992        getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature;
5993        getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource;
5994    }
5995
5996    function Symbol(this: Symbol, flags: SymbolFlags, name: __String) {
5997        this.flags = flags;
5998        this.escapedName = name;
5999        this.declarations = undefined;
6000        this.valueDeclaration = undefined;
6001        this.id = undefined;
6002        this.mergeId = undefined;
6003        this.parent = undefined;
6004    }
6005
6006    function Type(this: Type, checker: TypeChecker, flags: TypeFlags) {
6007        this.flags = flags;
6008        if (Debug.isDebugging || tracing) {
6009            this.checker = checker;
6010        }
6011    }
6012
6013    function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) {
6014        this.flags = flags;
6015        if (Debug.isDebugging) {
6016            this.checker = checker;
6017        }
6018    }
6019
6020    function Node(this: Mutable<Node>, kind: SyntaxKind, pos: number, end: number) {
6021        this.pos = pos;
6022        this.end = end;
6023        this.kind = kind;
6024        this.id = 0;
6025        this.flags = NodeFlags.None;
6026        this.modifierFlagsCache = ModifierFlags.None;
6027        this.transformFlags = TransformFlags.None;
6028        this.parent = undefined!;
6029        this.original = undefined;
6030    }
6031
6032    function Token(this: Mutable<Node>, kind: SyntaxKind, pos: number, end: number) {
6033        this.pos = pos;
6034        this.end = end;
6035        this.kind = kind;
6036        this.id = 0;
6037        this.flags = NodeFlags.None;
6038        this.transformFlags = TransformFlags.None;
6039        this.parent = undefined!;
6040    }
6041
6042    function Identifier(this: Mutable<Node>, kind: SyntaxKind, pos: number, end: number) {
6043        this.pos = pos;
6044        this.end = end;
6045        this.kind = kind;
6046        this.id = 0;
6047        this.flags = NodeFlags.None;
6048        this.transformFlags = TransformFlags.None;
6049        this.parent = undefined!;
6050        this.original = undefined;
6051        this.flowNode = undefined;
6052    }
6053
6054    function SourceMapSource(this: SourceMapSource, fileName: string, text: string, skipTrivia?: (pos: number) => number) {
6055        this.fileName = fileName;
6056        this.text = text;
6057        this.skipTrivia = skipTrivia || (pos => pos);
6058    }
6059
6060    // eslint-disable-next-line prefer-const
6061    export const objectAllocator: ObjectAllocator = {
6062        getNodeConstructor: () => Node as any,
6063        getTokenConstructor: () => Token as any,
6064        getIdentifierConstructor: () => Identifier as any,
6065        getPrivateIdentifierConstructor: () => Node as any,
6066        getSourceFileConstructor: () => Node as any,
6067        getSymbolConstructor: () => Symbol as any,
6068        getTypeConstructor: () => Type as any,
6069        getSignatureConstructor: () => Signature as any,
6070        getSourceMapSourceConstructor: () => SourceMapSource as any,
6071    };
6072
6073    export function setObjectAllocator(alloc: ObjectAllocator) {
6074        Object.assign(objectAllocator, alloc);
6075    }
6076
6077    export function formatStringFromArgs(text: string, args: ArrayLike<string | number>, baseIndex = 0): string {
6078        return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index + baseIndex]));
6079    }
6080
6081    let localizedDiagnosticMessages: MapLike<string> | undefined;
6082
6083    /* @internal */
6084    export function setLocalizedDiagnosticMessages(messages: typeof localizedDiagnosticMessages) {
6085        localizedDiagnosticMessages = messages;
6086    }
6087
6088    /* @internal */
6089    // If the localized messages json is unset, and if given function use it to set the json
6090
6091    export function maybeSetLocalizedDiagnosticMessages(getMessages: undefined | (() => typeof localizedDiagnosticMessages)) {
6092        if (!localizedDiagnosticMessages && getMessages) {
6093            localizedDiagnosticMessages = getMessages();
6094        }
6095    }
6096
6097    export function getLocaleSpecificMessage(message: DiagnosticMessage) {
6098        return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message;
6099    }
6100
6101    export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithDetachedLocation;
6102    export function createDetachedDiagnostic(fileName: string, start: number, length: number, message: DiagnosticMessage): DiagnosticWithDetachedLocation {
6103        assertDiagnosticLocation(/*file*/ undefined, start, length);
6104        let text = getLocaleSpecificMessage(message);
6105
6106        if (arguments.length > 4) {
6107            text = formatStringFromArgs(text, arguments, 4);
6108        }
6109
6110        return {
6111            file: undefined,
6112            start,
6113            length,
6114
6115            messageText: text,
6116            category: message.category,
6117            code: message.code,
6118            reportsUnnecessary: message.reportsUnnecessary,
6119            fileName,
6120        };
6121    }
6122
6123    function isDiagnosticWithDetachedLocation(diagnostic: DiagnosticRelatedInformation | DiagnosticWithDetachedLocation): diagnostic is DiagnosticWithDetachedLocation {
6124        return diagnostic.file === undefined
6125            && diagnostic.start !== undefined
6126            && diagnostic.length !== undefined
6127            && typeof (diagnostic as DiagnosticWithDetachedLocation).fileName === "string";
6128    }
6129
6130    function attachFileToDiagnostic(diagnostic: DiagnosticWithDetachedLocation, file: SourceFile): DiagnosticWithLocation {
6131        const fileName = file.fileName || "";
6132        const length = file.text.length;
6133        Debug.assertEqual(diagnostic.fileName, fileName);
6134        Debug.assertLessThanOrEqual(diagnostic.start, length);
6135        Debug.assertLessThanOrEqual(diagnostic.start + diagnostic.length, length);
6136        const diagnosticWithLocation: DiagnosticWithLocation = {
6137            file,
6138            start: diagnostic.start,
6139            length: diagnostic.length,
6140            messageText: diagnostic.messageText,
6141            category: diagnostic.category,
6142            code: diagnostic.code,
6143            reportsUnnecessary: diagnostic.reportsUnnecessary
6144        };
6145        if (diagnostic.relatedInformation) {
6146            diagnosticWithLocation.relatedInformation = [];
6147            for (const related of diagnostic.relatedInformation) {
6148                if (isDiagnosticWithDetachedLocation(related) && related.fileName === fileName) {
6149                    Debug.assertLessThanOrEqual(related.start, length);
6150                    Debug.assertLessThanOrEqual(related.start + related.length, length);
6151                    diagnosticWithLocation.relatedInformation.push(attachFileToDiagnostic(related, file));
6152                }
6153                else {
6154                    diagnosticWithLocation.relatedInformation.push(related);
6155                }
6156            }
6157        }
6158        return diagnosticWithLocation;
6159    }
6160
6161    export function attachFileToDiagnostics(diagnostics: DiagnosticWithDetachedLocation[], file: SourceFile): DiagnosticWithLocation[] {
6162        const diagnosticsWithLocation: DiagnosticWithLocation[] = [];
6163        for (const diagnostic of diagnostics) {
6164            diagnosticsWithLocation.push(attachFileToDiagnostic(diagnostic, file));
6165        }
6166        return diagnosticsWithLocation;
6167    }
6168
6169    export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticWithLocation;
6170    export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation {
6171        assertDiagnosticLocation(file, start, length);
6172
6173        let text = getLocaleSpecificMessage(message);
6174
6175        if (arguments.length > 4) {
6176            text = formatStringFromArgs(text, arguments, 4);
6177        }
6178
6179        return {
6180            file,
6181            start,
6182            length,
6183
6184            messageText: text,
6185            category: message.category,
6186            code: message.code,
6187            reportsUnnecessary: message.reportsUnnecessary,
6188            reportsDeprecated: message.reportsDeprecated
6189        };
6190    }
6191
6192    export function formatMessage(_dummy: any, message: DiagnosticMessage, ...args: (string | number | undefined)[]): string;
6193    export function formatMessage(_dummy: any, message: DiagnosticMessage): string {
6194        let text = getLocaleSpecificMessage(message);
6195
6196        if (arguments.length > 2) {
6197            text = formatStringFromArgs(text, arguments, 2);
6198        }
6199
6200        return text;
6201    }
6202
6203    export function createCompilerDiagnostic(message: DiagnosticMessage, ...args: (string | number | undefined)[]): Diagnostic;
6204    export function createCompilerDiagnostic(message: DiagnosticMessage): Diagnostic {
6205        let text = getLocaleSpecificMessage(message);
6206
6207        if (arguments.length > 1) {
6208            text = formatStringFromArgs(text, arguments, 1);
6209        }
6210
6211        return {
6212            file: undefined,
6213            start: undefined,
6214            length: undefined,
6215
6216            messageText: text,
6217            category: message.category,
6218            code: message.code,
6219            reportsUnnecessary: message.reportsUnnecessary,
6220            reportsDeprecated: message.reportsDeprecated
6221        };
6222    }
6223
6224    export function createCompilerDiagnosticFromMessageChain(chain: DiagnosticMessageChain, relatedInformation?: DiagnosticRelatedInformation[]): Diagnostic {
6225        return {
6226            file: undefined,
6227            start: undefined,
6228            length: undefined,
6229
6230            code: chain.code,
6231            category: chain.category,
6232            messageText: chain.next ? chain : chain.messageText,
6233            relatedInformation
6234        };
6235    }
6236
6237    export function chainDiagnosticMessages(details: DiagnosticMessageChain | DiagnosticMessageChain[] | undefined, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticMessageChain;
6238    export function chainDiagnosticMessages(details: DiagnosticMessageChain | DiagnosticMessageChain[] | undefined, message: DiagnosticMessage): DiagnosticMessageChain {
6239        let text = getLocaleSpecificMessage(message);
6240
6241        if (arguments.length > 2) {
6242            text = formatStringFromArgs(text, arguments, 2);
6243        }
6244        return {
6245            messageText: text,
6246            category: message.category,
6247            code: message.code,
6248
6249            next: details === undefined || Array.isArray(details) ? details : [details]
6250        };
6251    }
6252
6253    export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): void {
6254        let lastChain = headChain;
6255        while (lastChain.next) {
6256            lastChain = lastChain.next[0];
6257        }
6258
6259        lastChain.next = [tailChain];
6260    }
6261
6262    function getDiagnosticFilePath(diagnostic: Diagnostic): string | undefined {
6263        return diagnostic.file ? diagnostic.file.path : undefined;
6264    }
6265
6266    export function compareDiagnostics(d1: Diagnostic, d2: Diagnostic): Comparison {
6267        return compareDiagnosticsSkipRelatedInformation(d1, d2) ||
6268            compareRelatedInformation(d1, d2) ||
6269            Comparison.EqualTo;
6270    }
6271
6272    export function compareDiagnosticsSkipRelatedInformation(d1: Diagnostic, d2: Diagnostic): Comparison {
6273        return compareStringsCaseSensitive(getDiagnosticFilePath(d1), getDiagnosticFilePath(d2)) ||
6274            compareValues(d1.start, d2.start) ||
6275            compareValues(d1.length, d2.length) ||
6276            compareValues(d1.code, d2.code) ||
6277            compareMessageText(d1.messageText, d2.messageText) ||
6278            Comparison.EqualTo;
6279    }
6280
6281    function compareRelatedInformation(d1: Diagnostic, d2: Diagnostic): Comparison {
6282        if (!d1.relatedInformation && !d2.relatedInformation) {
6283            return Comparison.EqualTo;
6284        }
6285        if (d1.relatedInformation && d2.relatedInformation) {
6286            return compareValues(d1.relatedInformation.length, d2.relatedInformation.length) || forEach(d1.relatedInformation, (d1i, index) => {
6287                const d2i = d2.relatedInformation![index];
6288                return compareDiagnostics(d1i, d2i); // EqualTo is 0, so falsy, and will cause the next item to be compared
6289            }) || Comparison.EqualTo;
6290        }
6291        return d1.relatedInformation ? Comparison.LessThan : Comparison.GreaterThan;
6292    }
6293
6294    function compareMessageText(t1: string | DiagnosticMessageChain, t2: string | DiagnosticMessageChain): Comparison {
6295        if (typeof t1 === "string" && typeof t2 === "string") {
6296            return compareStringsCaseSensitive(t1, t2);
6297        }
6298        else if (typeof t1 === "string") {
6299            return Comparison.LessThan;
6300        }
6301        else if (typeof t2 === "string") {
6302            return Comparison.GreaterThan;
6303        }
6304        let res = compareStringsCaseSensitive(t1.messageText, t2.messageText);
6305        if (res) {
6306            return res;
6307        }
6308        if (!t1.next && !t2.next) {
6309            return Comparison.EqualTo;
6310        }
6311        if (!t1.next) {
6312            return Comparison.LessThan;
6313        }
6314        if (!t2.next) {
6315            return Comparison.GreaterThan;
6316        }
6317        const len = Math.min(t1.next.length, t2.next.length);
6318        for (let i = 0; i < len; i++) {
6319            res = compareMessageText(t1.next[i], t2.next[i]);
6320            if (res) {
6321                return res;
6322            }
6323        }
6324        if (t1.next.length < t2.next.length) {
6325            return Comparison.LessThan;
6326        }
6327        else if (t1.next.length > t2.next.length) {
6328            return Comparison.GreaterThan;
6329        }
6330        return Comparison.EqualTo;
6331    }
6332
6333    export function getLanguageVariant(scriptKind: ScriptKind) {
6334        // .tsx and .jsx files are treated as jsx language variant.
6335        return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSON ? LanguageVariant.JSX : LanguageVariant.Standard;
6336    }
6337
6338    /**
6339     * This is a somewhat unavoidable full tree walk to locate a JSX tag - `import.meta` requires the same,
6340     * but we avoid that walk (or parts of it) if at all possible using the `PossiblyContainsImportMeta` node flag.
6341     * Unfortunately, there's no `NodeFlag` space to do the same for JSX.
6342     */
6343    function walkTreeForJSXTags(node: Node): Node | undefined {
6344        if (!(node.transformFlags & TransformFlags.ContainsJsx)) return undefined;
6345        return isJsxOpeningLikeElement(node) || isJsxFragment(node) ? node : forEachChild(node, walkTreeForJSXTags);
6346    }
6347
6348    function isFileModuleFromUsingJSXTag(file: SourceFile): Node | undefined {
6349        // Excludes declaration files - they still require an explicit `export {}` or the like
6350        // for back compat purposes. (not that declaration files should contain JSX tags!)
6351        return !file.isDeclarationFile ? walkTreeForJSXTags(file) : undefined;
6352    }
6353
6354    /**
6355     * Note that this requires file.impliedNodeFormat be set already; meaning it must be set very early on
6356     * in SourceFile construction.
6357     */
6358    function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined {
6359        // Excludes declaration files - they still require an explicit `export {}` or the like
6360        // for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files
6361        // that aren't esm-mode (meaning not in a `type: module` scope).
6362        return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts, Extension.Mjs, Extension.Mts]))) && !file.isDeclarationFile ? true : undefined;
6363    }
6364
6365    export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void {
6366        // TODO: Should this callback be cached?
6367        switch (getEmitModuleDetectionKind(options)) {
6368            case ModuleDetectionKind.Force:
6369                // All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule
6370                return (file: SourceFile) => {
6371                    file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined;
6372                };
6373            case ModuleDetectionKind.Legacy:
6374                // Files are modules if they have imports, exports, or import.meta
6375                return (file: SourceFile) => {
6376                    file.externalModuleIndicator = isFileProbablyExternalModule(file);
6377                };
6378            case ModuleDetectionKind.Auto:
6379                // If module is nodenext or node16, all esm format files are modules
6380                // If jsx is react-jsx or react-jsxdev then jsx tags force module-ness
6381                // otherwise, the presence of import or export statments (or import.meta) implies module-ness
6382                const checks: ((file: SourceFile) => Node | true | undefined)[] = [isFileProbablyExternalModule];
6383                if (options.jsx === JsxEmit.ReactJSX || options.jsx === JsxEmit.ReactJSXDev) {
6384                    checks.push(isFileModuleFromUsingJSXTag);
6385                }
6386                checks.push(isFileForcedToBeModuleByFormat);
6387                const combined = or(...checks);
6388                const callback = (file: SourceFile) => void (file.externalModuleIndicator = combined(file));
6389                return callback;
6390        }
6391    }
6392
6393    export function getEmitScriptTarget(compilerOptions: {module?: CompilerOptions["module"], target?: CompilerOptions["target"]}) {
6394        return compilerOptions.target ||
6395            (compilerOptions.module === ModuleKind.Node16 && ScriptTarget.ES2022) ||
6396            (compilerOptions.module === ModuleKind.NodeNext && ScriptTarget.ESNext) ||
6397            ScriptTarget.ES3;
6398    }
6399
6400    export function getEmitModuleKind(compilerOptions: {module?: CompilerOptions["module"], target?: CompilerOptions["target"]}) {
6401        return typeof compilerOptions.module === "number" ?
6402            compilerOptions.module :
6403            getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2015 ? ModuleKind.ES2015 : ModuleKind.CommonJS;
6404    }
6405
6406    export function getEmitModuleResolutionKind(compilerOptions: CompilerOptions) {
6407        let moduleResolution = compilerOptions.moduleResolution;
6408        if (moduleResolution === undefined) {
6409            switch (getEmitModuleKind(compilerOptions)) {
6410                case ModuleKind.CommonJS:
6411                    moduleResolution = ModuleResolutionKind.NodeJs;
6412                    break;
6413                case ModuleKind.Node16:
6414                    moduleResolution = ModuleResolutionKind.Node16;
6415                    break;
6416                case ModuleKind.NodeNext:
6417                    moduleResolution = ModuleResolutionKind.NodeNext;
6418                    break;
6419                default:
6420                    moduleResolution = ModuleResolutionKind.Classic;
6421                    break;
6422            }
6423        }
6424        return moduleResolution;
6425    }
6426
6427    export function getEmitModuleDetectionKind(options: CompilerOptions) {
6428        return options.moduleDetection ||
6429            (getEmitModuleKind(options) === ModuleKind.Node16 || getEmitModuleKind(options) === ModuleKind.NodeNext ? ModuleDetectionKind.Force : ModuleDetectionKind.Auto);
6430    }
6431
6432    export function hasJsonModuleEmitEnabled(options: CompilerOptions) {
6433        switch (getEmitModuleKind(options)) {
6434            case ModuleKind.CommonJS:
6435            case ModuleKind.AMD:
6436            case ModuleKind.ES2015:
6437            case ModuleKind.ES2020:
6438            case ModuleKind.ES2022:
6439            case ModuleKind.ESNext:
6440            case ModuleKind.Node16:
6441            case ModuleKind.NodeNext:
6442                return true;
6443            default:
6444                return false;
6445        }
6446    }
6447
6448    export function unreachableCodeIsError(options: CompilerOptions): boolean {
6449        return options.allowUnreachableCode === false;
6450    }
6451
6452    export function unusedLabelIsError(options: CompilerOptions): boolean {
6453        return options.allowUnusedLabels === false;
6454    }
6455
6456    export function getAreDeclarationMapsEnabled(options: CompilerOptions) {
6457        return !!(getEmitDeclarations(options) && options.declarationMap);
6458    }
6459
6460    export function getESModuleInterop(compilerOptions: CompilerOptions) {
6461        if (compilerOptions.esModuleInterop !== undefined) {
6462            return compilerOptions.esModuleInterop;
6463        }
6464        switch (getEmitModuleKind(compilerOptions)) {
6465            case ModuleKind.Node16:
6466            case ModuleKind.NodeNext:
6467                return true;
6468        }
6469        return undefined;
6470    }
6471
6472    export function getAllowSyntheticDefaultImports(compilerOptions: CompilerOptions) {
6473        const moduleKind = getEmitModuleKind(compilerOptions);
6474        return compilerOptions.allowSyntheticDefaultImports !== undefined
6475            ? compilerOptions.allowSyntheticDefaultImports
6476            : getESModuleInterop(compilerOptions) ||
6477            moduleKind === ModuleKind.System;
6478    }
6479
6480    export function getEmitDeclarations(compilerOptions: CompilerOptions): boolean {
6481        return !!(compilerOptions.declaration || compilerOptions.composite);
6482    }
6483
6484    export function shouldPreserveConstEnums(compilerOptions: CompilerOptions): boolean {
6485        return !!(compilerOptions.preserveConstEnums || compilerOptions.isolatedModules);
6486    }
6487
6488    export function isIncrementalCompilation(options: CompilerOptions) {
6489        return !!(options.incremental || options.composite);
6490    }
6491
6492    export type StrictOptionName =
6493        | "noImplicitAny"
6494        | "noImplicitThis"
6495        | "strictNullChecks"
6496        | "strictFunctionTypes"
6497        | "strictBindCallApply"
6498        | "strictPropertyInitialization"
6499        | "alwaysStrict"
6500        | "useUnknownInCatchVariables"
6501        ;
6502
6503    export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean {
6504        return compilerOptions[flag] === undefined ? !!compilerOptions.strict : !!compilerOptions[flag];
6505    }
6506
6507    export function getAllowJSCompilerOption(compilerOptions: CompilerOptions): boolean {
6508        return compilerOptions.allowJs === undefined ? !!compilerOptions.checkJs : compilerOptions.allowJs;
6509    }
6510
6511    export function getUseDefineForClassFields(compilerOptions: CompilerOptions): boolean {
6512        return compilerOptions.useDefineForClassFields === undefined ? getEmitScriptTarget(compilerOptions) >= ScriptTarget.ES2022 : compilerOptions.useDefineForClassFields;
6513    }
6514
6515    export function compilerOptionsAffectSemanticDiagnostics(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean {
6516        return optionsHaveChanges(oldOptions, newOptions, semanticDiagnosticsOptionDeclarations);
6517    }
6518
6519    export function compilerOptionsAffectEmit(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean {
6520        return optionsHaveChanges(oldOptions, newOptions, affectsEmitOptionDeclarations);
6521    }
6522
6523    export function compilerOptionsAffectDeclarationPath(newOptions: CompilerOptions, oldOptions: CompilerOptions): boolean {
6524        return optionsHaveChanges(oldOptions, newOptions, affectsDeclarationPathOptionDeclarations);
6525    }
6526
6527    export function getCompilerOptionValue(options: CompilerOptions, option: CommandLineOption): unknown {
6528        return option.strictFlag ? getStrictOptionValue(options, option.name as StrictOptionName) : options[option.name];
6529    }
6530
6531    export function getJSXTransformEnabled(options: CompilerOptions): boolean {
6532        const jsx = options.jsx;
6533        return jsx === JsxEmit.React || jsx === JsxEmit.ReactJSX || jsx === JsxEmit.ReactJSXDev;
6534    }
6535
6536    export function getJSXImplicitImportBase(compilerOptions: CompilerOptions, file?: SourceFile): string | undefined {
6537        const jsxImportSourcePragmas = file?.pragmas.get("jsximportsource");
6538        const jsxImportSourcePragma = isArray(jsxImportSourcePragmas) ? jsxImportSourcePragmas[jsxImportSourcePragmas.length - 1] : jsxImportSourcePragmas;
6539        return compilerOptions.jsx === JsxEmit.ReactJSX ||
6540            compilerOptions.jsx === JsxEmit.ReactJSXDev ||
6541            compilerOptions.jsxImportSource ||
6542            jsxImportSourcePragma ?
6543                jsxImportSourcePragma?.arguments.factory || compilerOptions.jsxImportSource || "react" :
6544                undefined;
6545    }
6546
6547    export function getJSXRuntimeImport(base: string | undefined, options: CompilerOptions) {
6548        return base ? `${base}/${options.jsx === JsxEmit.ReactJSXDev ? "jsx-dev-runtime" : "jsx-runtime"}` : undefined;
6549    }
6550
6551    export function hasZeroOrOneAsteriskCharacter(str: string): boolean {
6552        let seenAsterisk = false;
6553        for (let i = 0; i < str.length; i++) {
6554            if (str.charCodeAt(i) === CharacterCodes.asterisk) {
6555                if (!seenAsterisk) {
6556                    seenAsterisk = true;
6557                }
6558                else {
6559                    // have already seen asterisk
6560                    return false;
6561                }
6562            }
6563        }
6564        return true;
6565    }
6566
6567    export interface SymlinkedDirectory {
6568        /** Matches the casing returned by `realpath`.  Used to compute the `realpath` of children. */
6569        real: string;
6570        /** toPath(real).  Stored to avoid repeated recomputation. */
6571        realPath: Path;
6572    }
6573
6574    export interface SymlinkCache {
6575        /** Gets a map from symlink to realpath. Keys have trailing directory separators. */
6576        getSymlinkedDirectories(): ReadonlyESMap<Path, SymlinkedDirectory | false> | undefined;
6577        /** Gets a map from realpath to symlinks. Keys have trailing directory separators. */
6578        getSymlinkedDirectoriesByRealpath(): MultiMap<Path, string> | undefined;
6579        /** Gets a map from symlink to realpath */
6580        getSymlinkedFiles(): ReadonlyESMap<Path, string> | undefined;
6581        setSymlinkedDirectory(symlink: string, real: SymlinkedDirectory | false): void;
6582        setSymlinkedFile(symlinkPath: Path, real: string): void;
6583        /**
6584         * @internal
6585         * Uses resolvedTypeReferenceDirectives from program instead of from files, since files
6586         * don't include automatic type reference directives. Must be called only when
6587         * `hasProcessedResolutions` returns false (once per cache instance).
6588         */
6589        setSymlinksFromResolutions(files: readonly SourceFile[], typeReferenceDirectives: ModeAwareCache<ResolvedTypeReferenceDirective | undefined> | undefined): void;
6590        /**
6591         * @internal
6592         * Whether `setSymlinksFromResolutions` has already been called.
6593         */
6594        hasProcessedResolutions(): boolean;
6595    }
6596
6597    export function createSymlinkCache(cwd: string, getCanonicalFileName: GetCanonicalFileName, isOHModules?: boolean): SymlinkCache {
6598        let symlinkedDirectories: ESMap<Path, SymlinkedDirectory | false> | undefined;
6599        let symlinkedDirectoriesByRealpath: MultiMap<Path, string> | undefined;
6600        let symlinkedFiles: ESMap<Path, string> | undefined;
6601        let hasProcessedResolutions = false;
6602        return {
6603            getSymlinkedFiles: () => symlinkedFiles,
6604            getSymlinkedDirectories: () => symlinkedDirectories,
6605            getSymlinkedDirectoriesByRealpath: () => symlinkedDirectoriesByRealpath,
6606            setSymlinkedFile: (path, real) => (symlinkedFiles || (symlinkedFiles = new Map())).set(path, real),
6607            setSymlinkedDirectory: (symlink, real) => {
6608                // Large, interconnected dependency graphs in pnpm will have a huge number of symlinks
6609                // where both the realpath and the symlink path are inside node_modules/.pnpm. Since
6610                // this path is never a candidate for a module specifier, we can ignore it entirely.
6611                let symlinkPath = toPath(symlink, cwd, getCanonicalFileName);
6612                if (!containsIgnoredPath(symlinkPath)) {
6613                    symlinkPath = ensureTrailingDirectorySeparator(symlinkPath);
6614                    if (real !== false && !symlinkedDirectories?.has(symlinkPath)) {
6615                        (symlinkedDirectoriesByRealpath ||= createMultiMap()).add(ensureTrailingDirectorySeparator(real.realPath), symlink);
6616                    }
6617                    (symlinkedDirectories || (symlinkedDirectories = new Map())).set(symlinkPath, real);
6618                }
6619            },
6620            setSymlinksFromResolutions(files, typeReferenceDirectives) {
6621                Debug.assert(!hasProcessedResolutions);
6622                hasProcessedResolutions = true;
6623                for (const file of files) {
6624                    file.resolvedModules?.forEach(resolution => processResolution(this, resolution, isOHModules));
6625                }
6626                typeReferenceDirectives?.forEach(resolution => processResolution(this, resolution, isOHModules));
6627            },
6628            hasProcessedResolutions: () => hasProcessedResolutions,
6629        };
6630
6631        function processResolution(cache: SymlinkCache, resolution: ResolvedModuleFull | ResolvedTypeReferenceDirective | undefined, isOHModules?: boolean) {
6632            if (!resolution || !resolution.originalPath || !resolution.resolvedFileName) return;
6633            const { resolvedFileName, originalPath } = resolution;
6634            cache.setSymlinkedFile(toPath(originalPath, cwd, getCanonicalFileName), resolvedFileName);
6635            const [commonResolved, commonOriginal] = guessDirectorySymlink(resolvedFileName, originalPath, cwd, getCanonicalFileName, isOHModules) || emptyArray;
6636            if (commonResolved && commonOriginal) {
6637                cache.setSymlinkedDirectory(
6638                    commonOriginal,
6639                    { real: commonResolved, realPath: toPath(commonResolved, cwd, getCanonicalFileName) });
6640            }
6641        }
6642    }
6643
6644    function guessDirectorySymlink(a: string, b: string, cwd: string, getCanonicalFileName: GetCanonicalFileName, isOHModules?: boolean): [string, string] | undefined {
6645        const aParts = getPathComponents(getNormalizedAbsolutePath(a, cwd));
6646        const bParts = getPathComponents(getNormalizedAbsolutePath(b, cwd));
6647        let isDirectory = false;
6648        while (
6649            aParts.length >= 2 && bParts.length >= 2 &&
6650            !isNodeModulesOrScopedPackageDirectory(aParts[aParts.length - 2], getCanonicalFileName, isOHModules) &&
6651            !isNodeModulesOrScopedPackageDirectory(bParts[bParts.length - 2], getCanonicalFileName, isOHModules) &&
6652            getCanonicalFileName(aParts[aParts.length - 1]) === getCanonicalFileName(bParts[bParts.length - 1])
6653        ) {
6654            aParts.pop();
6655            bParts.pop();
6656            isDirectory = true;
6657        }
6658        return isDirectory ? [getPathFromPathComponents(aParts), getPathFromPathComponents(bParts)] : undefined;
6659    }
6660
6661    // KLUDGE: Don't assume one 'node_modules' links to another. More likely a single directory inside the node_modules is the symlink.
6662    // ALso, don't assume that an `@foo` directory is linked. More likely the contents of that are linked.
6663    function isNodeModulesOrScopedPackageDirectory(s: string | undefined, getCanonicalFileName: GetCanonicalFileName, isOHModules?: boolean): boolean {
6664        return s !== undefined && (getCanonicalFileName(s) === "node_modules" || (isOHModules && getCanonicalFileName(s) === "oh_modules") || startsWith(s, "@"));
6665    }
6666
6667    function stripLeadingDirectorySeparator(s: string): string | undefined {
6668        return isAnyDirectorySeparator(s.charCodeAt(0)) ? s.slice(1) : undefined;
6669    }
6670
6671    export function tryRemoveDirectoryPrefix(path: string, dirPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
6672        const withoutPrefix = tryRemovePrefix(path, dirPath, getCanonicalFileName);
6673        return withoutPrefix === undefined ? undefined : stripLeadingDirectorySeparator(withoutPrefix);
6674    }
6675
6676    // Reserved characters, forces escaping of any non-word (or digit), non-whitespace character.
6677    // It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future
6678    // proof.
6679    const reservedCharacterPattern = /[^\w\s\/]/g;
6680
6681    export function regExpEscape(text: string) {
6682        return text.replace(reservedCharacterPattern, escapeRegExpCharacter);
6683    }
6684
6685    function escapeRegExpCharacter(match: string) {
6686        return "\\" + match;
6687    }
6688
6689    const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
6690
6691    export const commonPackageFolders: readonly string[] = ["node_modules", "oh_modules", "bower_components", "jspm_packages"];
6692
6693    const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
6694
6695    interface WildcardMatcher {
6696        singleAsteriskRegexFragment: string;
6697        doubleAsteriskRegexFragment: string;
6698        replaceWildcardCharacter: (match: string) => string;
6699    }
6700
6701    const filesMatcher: WildcardMatcher = {
6702        /**
6703         * Matches any single directory segment unless it is the last segment and a .min.js file
6704         * Breakdown:
6705         *  [^./]                   # matches everything up to the first . character (excluding directory separators)
6706         *  (\\.(?!min\\.js$))?     # matches . characters but not if they are part of the .min.js file extension
6707         */
6708        singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*",
6709        /**
6710         * Regex for the ** wildcard. Matches any number of subdirectories. When used for including
6711         * files or directories, does not match subdirectories that start with a . character
6712         */
6713        doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
6714        replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment)
6715    };
6716
6717    const directoriesMatcher: WildcardMatcher = {
6718        singleAsteriskRegexFragment: "[^/]*",
6719        /**
6720         * Regex for the ** wildcard. Matches any number of subdirectories. When used for including
6721         * files or directories, does not match subdirectories that start with a . character
6722         */
6723        doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
6724        replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment)
6725    };
6726
6727    const excludeMatcher: WildcardMatcher = {
6728        singleAsteriskRegexFragment: "[^/]*",
6729        doubleAsteriskRegexFragment: "(/.+?)?",
6730        replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment)
6731    };
6732
6733    const wildcardMatchers = {
6734        files: filesMatcher,
6735        directories: directoriesMatcher,
6736        exclude: excludeMatcher
6737    };
6738
6739    export function getRegularExpressionForWildcard(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
6740        const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
6741        if (!patterns || !patterns.length) {
6742            return undefined;
6743        }
6744
6745        const pattern = patterns.map(pattern => `(${pattern})`).join("|");
6746        // If excluding, match "foo/bar/baz...", but if including, only allow "foo".
6747        const terminator = usage === "exclude" ? "($|/)" : "$";
6748        return `^(${pattern})${terminator}`;
6749    }
6750
6751    export function getRegularExpressionsForWildcards(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): readonly string[] | undefined {
6752        if (specs === undefined || specs.length === 0) {
6753            return undefined;
6754        }
6755
6756        return flatMap(specs, spec =>
6757            spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]));
6758    }
6759
6760    /**
6761     * An "includes" path "foo" is implicitly a glob "foo/** /*" (without the space) if its last component has no extension,
6762     * and does not contain any glob characters itself.
6763     */
6764    export function isImplicitGlob(lastPathComponent: string): boolean {
6765        return !/[.*?]/.test(lastPathComponent);
6766    }
6767
6768    export function getPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude") {
6769        const pattern = spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]);
6770        return pattern && `^(${pattern})${usage === "exclude" ? "($|/)" : "$"}`;
6771    }
6772
6773    function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined {
6774        let subpattern = "";
6775        let hasWrittenComponent = false;
6776        const components = getNormalizedPathComponents(spec, basePath);
6777        const lastComponent = last(components);
6778        if (usage !== "exclude" && lastComponent === "**") {
6779            return undefined;
6780        }
6781
6782        // getNormalizedPathComponents includes the separator for the root component.
6783        // We need to remove to create our regex correctly.
6784        components[0] = removeTrailingDirectorySeparator(components[0]);
6785
6786        if (isImplicitGlob(lastComponent)) {
6787            components.push("**", "*");
6788        }
6789
6790        let optionalCount = 0;
6791        for (let component of components) {
6792            if (component === "**") {
6793                subpattern += doubleAsteriskRegexFragment;
6794            }
6795            else {
6796                if (usage === "directories") {
6797                    subpattern += "(";
6798                    optionalCount++;
6799                }
6800
6801                if (hasWrittenComponent) {
6802                    subpattern += directorySeparator;
6803                }
6804
6805                if (usage !== "exclude") {
6806                    let componentPattern = "";
6807                    // The * and ? wildcards should not match directories or files that start with . if they
6808                    // appear first in a component. Dotted directories and files can be included explicitly
6809                    // like so: **/.*/.*
6810                    if (component.charCodeAt(0) === CharacterCodes.asterisk) {
6811                        componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?";
6812                        component = component.substr(1);
6813                    }
6814                    else if (component.charCodeAt(0) === CharacterCodes.question) {
6815                        componentPattern += "[^./]";
6816                        component = component.substr(1);
6817                    }
6818
6819                    componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
6820
6821                    // Patterns should not include subfolders like node_modules unless they are
6822                    // explicitly included as part of the path.
6823                    //
6824                    // As an optimization, if the component pattern is the same as the component,
6825                    // then there definitely were no wildcard characters and we do not need to
6826                    // add the exclusion pattern.
6827                    if (componentPattern !== component) {
6828                        subpattern += implicitExcludePathRegexPattern;
6829                    }
6830
6831                    subpattern += componentPattern;
6832                }
6833                else {
6834                    subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
6835                }
6836            }
6837
6838            hasWrittenComponent = true;
6839        }
6840
6841        while (optionalCount > 0) {
6842            subpattern += ")?";
6843            optionalCount--;
6844        }
6845
6846        return subpattern;
6847    }
6848
6849    function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) {
6850        return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match;
6851    }
6852
6853    export interface FileSystemEntries {
6854        readonly files: readonly string[];
6855        readonly directories: readonly string[];
6856    }
6857
6858    export interface FileMatcherPatterns {
6859        /** One pattern for each "include" spec. */
6860        includeFilePatterns: readonly string[] | undefined;
6861        /** One pattern matching one of any of the "include" specs. */
6862        includeFilePattern: string | undefined;
6863        includeDirectoryPattern: string | undefined;
6864        excludePattern: string | undefined;
6865        basePaths: readonly string[];
6866    }
6867
6868    /** @param path directory of the tsconfig.json */
6869    export function getFileMatcherPatterns(path: string, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns {
6870        path = normalizePath(path);
6871        currentDirectory = normalizePath(currentDirectory);
6872        const absolutePath = combinePaths(currentDirectory, path);
6873
6874        return {
6875            includeFilePatterns: map(getRegularExpressionsForWildcards(includes, absolutePath, "files"), pattern => `^${pattern}$`),
6876            includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"),
6877            includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"),
6878            excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"),
6879            basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames)
6880        };
6881    }
6882
6883    export function getRegexFromPattern(pattern: string, useCaseSensitiveFileNames: boolean): RegExp {
6884        return new RegExp(pattern, useCaseSensitiveFileNames ? "" : "i");
6885    }
6886
6887    /** @param path directory of the tsconfig.json */
6888    export function matchFiles(path: string, extensions: readonly string[] | undefined, excludes: readonly string[] | undefined, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries, realpath: (path: string) => string): string[] {
6889        path = normalizePath(path);
6890        currentDirectory = normalizePath(currentDirectory);
6891
6892        const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory);
6893
6894        const includeFileRegexes = patterns.includeFilePatterns && patterns.includeFilePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames));
6895        const includeDirectoryRegex = patterns.includeDirectoryPattern && getRegexFromPattern(patterns.includeDirectoryPattern, useCaseSensitiveFileNames);
6896        const excludeRegex = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, useCaseSensitiveFileNames);
6897
6898        // Associate an array of results with each include regex. This keeps results in order of the "include" order.
6899        // If there are no "includes", then just put everything in results[0].
6900        const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]];
6901        const visited = new Map<string, true>();
6902        const toCanonical = createGetCanonicalFileName(useCaseSensitiveFileNames);
6903        for (const basePath of patterns.basePaths) {
6904            visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
6905        }
6906
6907        return flatten(results);
6908
6909        function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
6910            const canonicalPath = toCanonical(realpath(absolutePath));
6911            if (visited.has(canonicalPath)) return;
6912            visited.set(canonicalPath, true);
6913            const { files, directories } = getFileSystemEntries(path);
6914
6915            for (const current of sort<string>(files, compareStringsCaseSensitive)) {
6916                const name = combinePaths(path, current);
6917                const absoluteName = combinePaths(absolutePath, current);
6918                if (extensions && !fileExtensionIsOneOf(name, extensions)) continue;
6919                if (excludeRegex && excludeRegex.test(absoluteName)) continue;
6920                if (!includeFileRegexes) {
6921                    results[0].push(name);
6922                }
6923                else {
6924                    const includeIndex = findIndex(includeFileRegexes, re => re.test(absoluteName));
6925                    if (includeIndex !== -1) {
6926                        results[includeIndex].push(name);
6927                    }
6928                }
6929            }
6930
6931            if (depth !== undefined) {
6932                depth--;
6933                if (depth === 0) {
6934                    return;
6935                }
6936            }
6937
6938            for (const current of sort<string>(directories, compareStringsCaseSensitive)) {
6939                const name = combinePaths(path, current);
6940                const absoluteName = combinePaths(absolutePath, current);
6941                if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&
6942                    (!excludeRegex || !excludeRegex.test(absoluteName))) {
6943                    visitDirectory(name, absoluteName, depth);
6944                }
6945            }
6946        }
6947    }
6948
6949    /**
6950     * Computes the unique non-wildcard base paths amongst the provided include patterns.
6951     */
6952    function getBasePaths(path: string, includes: readonly string[] | undefined, useCaseSensitiveFileNames: boolean): string[] {
6953        // Storage for our results in the form of literal paths (e.g. the paths as written by the user).
6954        const basePaths: string[] = [path];
6955
6956        if (includes) {
6957            // Storage for literal base paths amongst the include patterns.
6958            const includeBasePaths: string[] = [];
6959            for (const include of includes) {
6960                // We also need to check the relative paths by converting them to absolute and normalizing
6961                // in case they escape the base path (e.g "..\somedirectory")
6962                const absolute: string = isRootedDiskPath(include) ? include : normalizePath(combinePaths(path, include));
6963                // Append the literal and canonical candidate base paths.
6964                includeBasePaths.push(getIncludeBasePath(absolute));
6965            }
6966
6967            // Sort the offsets array using either the literal or canonical path representations.
6968            includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames));
6969
6970            // Iterate over each include base path and include unique base paths that are not a
6971            // subpath of an existing base path
6972            for (const includeBasePath of includeBasePaths) {
6973                if (every(basePaths, basePath => !containsPath(basePath, includeBasePath, path, !useCaseSensitiveFileNames))) {
6974                    basePaths.push(includeBasePath);
6975                }
6976            }
6977        }
6978
6979        return basePaths;
6980    }
6981
6982    function getIncludeBasePath(absolute: string): string {
6983        const wildcardOffset = indexOfAnyCharCode(absolute, wildcardCharCodes);
6984        if (wildcardOffset < 0) {
6985            // No "*" or "?" in the path
6986            return !hasExtension(absolute)
6987                ? absolute
6988                : removeTrailingDirectorySeparator(getDirectoryPath(absolute));
6989        }
6990        return absolute.substring(0, absolute.lastIndexOf(directorySeparator, wildcardOffset));
6991    }
6992
6993    export function ensureScriptKind(fileName: string, scriptKind: ScriptKind | undefined): ScriptKind {
6994        // Using scriptKind as a condition handles both:
6995        // - 'scriptKind' is unspecified and thus it is `undefined`
6996        // - 'scriptKind' is set and it is `Unknown` (0)
6997        // If the 'scriptKind' is 'undefined' or 'Unknown' then we attempt
6998        // to get the ScriptKind from the file name. If it cannot be resolved
6999        // from the file name then the default 'TS' script kind is returned.
7000        return scriptKind || getScriptKindFromFileName(fileName) || ScriptKind.TS;
7001    }
7002
7003    export function getScriptKindFromFileName(fileName: string): ScriptKind {
7004        const ext = fileName.substr(fileName.lastIndexOf("."));
7005        switch (ext.toLowerCase()) {
7006            case Extension.Js:
7007            case Extension.Cjs:
7008            case Extension.Mjs:
7009                return ScriptKind.JS;
7010            case Extension.Jsx:
7011                return ScriptKind.JSX;
7012            case Extension.Ts:
7013            case Extension.Cts:
7014            case Extension.Mts:
7015                return ScriptKind.TS;
7016            case Extension.Tsx:
7017                return ScriptKind.TSX;
7018            case Extension.Json:
7019                return ScriptKind.JSON;
7020            case Extension.Ets:
7021                return ScriptKind.ETS;
7022            default:
7023                return ScriptKind.Unknown;
7024        }
7025    }
7026
7027    /**
7028     *  Groups of supported extensions in order of file resolution precedence. (eg, TS > TSX > DTS and seperately, CTS > DCTS)
7029     */
7030    export const supportedTSExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts, Extension.Ets, Extension.Dets], [Extension.Cts, Extension.Dcts], [Extension.Mts, Extension.Dmts]];
7031    export const supportedTSExtensionsFlat: readonly Extension[] = flatten(supportedTSExtensions);
7032    const supportedTSExtensionsWithJson: readonly Extension[][] = [...supportedTSExtensions, [Extension.Json]];
7033    /** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
7034    const supportedTSExtensionsForExtractExtension: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Dets, Extension.Cts, Extension.Mts, Extension.Ts, Extension.Tsx, Extension.Cts, Extension.Mts, Extension.Ets];
7035    export const supportedJSExtensions: readonly Extension[][] = [[Extension.Js, Extension.Jsx], [Extension.Mjs], [Extension.Cjs]];
7036    export const supportedJSExtensionsFlat: readonly Extension[] = flatten(supportedJSExtensions);
7037    const allSupportedExtensions: readonly Extension[][] = [[Extension.Ts, Extension.Tsx, Extension.Dts, Extension.Ets, Extension.Dets, Extension.Js, Extension.Jsx], [Extension.Cts, Extension.Dcts, Extension.Cjs], [Extension.Mts, Extension.Dmts, Extension.Mjs]];
7038    const allSupportedExtensionsWithJson: readonly Extension[][] = [...allSupportedExtensions, [Extension.Json]];
7039    export const supportedDeclarationExtensions: readonly Extension[] = [Extension.Dts, Extension.Dcts, Extension.Dmts, Extension.Dets];
7040
7041    export function getSupportedExtensions(options?: CompilerOptions): readonly Extension[][];
7042    export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][];
7043    export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]): readonly string[][] {
7044        const needJsExtensions = options && getAllowJSCompilerOption(options);
7045
7046        if (!extraFileExtensions || extraFileExtensions.length === 0) {
7047            return needJsExtensions ? allSupportedExtensions : supportedTSExtensions;
7048        }
7049
7050        const builtins = needJsExtensions ? allSupportedExtensions : supportedTSExtensions;
7051        const flatBuiltins = flatten(builtins);
7052        const extensions = [
7053            ...builtins,
7054            ...mapDefined(extraFileExtensions, x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJSLike(x.scriptKind) && flatBuiltins.indexOf(x.extension as Extension) === -1 ? [x.extension] : undefined)
7055        ];
7056
7057        return extensions;
7058    }
7059
7060    export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly Extension[][]): readonly Extension[][];
7061    export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][];
7062    export function getSupportedExtensionsWithJsonIfResolveJsonModule(options: CompilerOptions | undefined, supportedExtensions: readonly string[][]): readonly string[][] {
7063        if (!options || !options.resolveJsonModule) return supportedExtensions;
7064        if (supportedExtensions === allSupportedExtensions) return allSupportedExtensionsWithJson;
7065        if (supportedExtensions === supportedTSExtensions) return supportedTSExtensionsWithJson;
7066        return [...supportedExtensions, [Extension.Json]];
7067    }
7068
7069    function isJSLike(scriptKind: ScriptKind | undefined): boolean {
7070        return scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX;
7071    }
7072
7073    export function hasJSFileExtension(fileName: string): boolean {
7074        return some(supportedJSExtensionsFlat, extension => fileExtensionIs(fileName, extension));
7075    }
7076
7077    export function hasTSFileExtension(fileName: string): boolean {
7078        return some(supportedTSExtensionsFlat, extension => fileExtensionIs(fileName, extension));
7079    }
7080
7081    export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: readonly FileExtensionInfo[]) {
7082        if (!fileName) return false;
7083
7084        const supportedExtensions = getSupportedExtensions(compilerOptions, extraFileExtensions);
7085        for (const extension of flatten(getSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions))) {
7086            if (fileExtensionIs(fileName, extension)) {
7087                return true;
7088            }
7089        }
7090        return false;
7091    }
7092
7093    function numberOfDirectorySeparators(str: string) {
7094        const match = str.match(/\//g);
7095        return match ? match.length : 0;
7096    }
7097
7098    export function compareNumberOfDirectorySeparators(path1: string, path2: string) {
7099        return compareValues(
7100            numberOfDirectorySeparators(path1),
7101            numberOfDirectorySeparators(path2)
7102        );
7103    }
7104
7105    const extensionsToRemove = [Extension.Dts, Extension.Dets, Extension.Dmts, Extension.Dcts, Extension.Mjs, Extension.Mts, Extension.Cjs, Extension.Cts, Extension.Ts, Extension.Js, Extension.Tsx, Extension.Jsx, Extension.Json, Extension.Ets];
7106    export function removeFileExtension(path: string): string {
7107        for (const ext of extensionsToRemove) {
7108            const extensionless = tryRemoveExtension(path, ext);
7109            if (extensionless !== undefined) {
7110                return extensionless;
7111            }
7112        }
7113        return path;
7114    }
7115
7116    export function tryRemoveExtension(path: string, extension: string): string | undefined {
7117        return fileExtensionIs(path, extension) ? removeExtension(path, extension) : undefined;
7118    }
7119
7120    export function removeExtension(path: string, extension: string): string {
7121        return path.substring(0, path.length - extension.length);
7122    }
7123
7124    export function changeExtension<T extends string | Path>(path: T, newExtension: string): T {
7125        return changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false) as T;
7126    }
7127
7128    /**
7129     * Returns the input if there are no stars, a pattern if there is exactly one,
7130     * and undefined if there are more.
7131     */
7132    export function tryParsePattern(pattern: string): string | Pattern | undefined {
7133        const indexOfStar = pattern.indexOf("*");
7134        if (indexOfStar === -1) {
7135            return pattern;
7136        }
7137        return pattern.indexOf("*", indexOfStar + 1) !== -1
7138            ? undefined
7139            : {
7140                prefix: pattern.substr(0, indexOfStar),
7141                suffix: pattern.substr(indexOfStar + 1)
7142            };
7143    }
7144
7145    export function tryParsePatterns(paths: MapLike<string[]>): (string | Pattern)[] {
7146        return mapDefined(getOwnKeys(paths), path => tryParsePattern(path));
7147    }
7148
7149    export function positionIsSynthesized(pos: number): boolean {
7150        // This is a fast way of testing the following conditions:
7151        //  pos === undefined || pos === null || isNaN(pos) || pos < 0;
7152        return !(pos >= 0);
7153    }
7154
7155    /** True if an extension is one of the supported TypeScript extensions. */
7156    export function extensionIsTS(ext: Extension): boolean {
7157        return ext === Extension.Ts || ext === Extension.Tsx || ext === Extension.Dts || ext === Extension.Cts || ext === Extension.Mts || ext === Extension.Dmts || ext === Extension.Dcts || ext === Extension.Ets || ext === Extension.Dets;
7158    }
7159
7160    export function resolutionExtensionIsTSOrJson(ext: Extension) {
7161        return extensionIsTS(ext) || ext === Extension.Json;
7162    }
7163
7164    /**
7165     * Gets the extension from a path.
7166     * Path must have a valid extension.
7167     */
7168    export function extensionFromPath(path: string): Extension {
7169        const ext = tryGetExtensionFromPath(path);
7170        return ext !== undefined ? ext : Debug.fail(`File ${path} has unknown extension.`);
7171    }
7172
7173    export function isAnySupportedFileExtension(path: string): boolean {
7174        return tryGetExtensionFromPath(path) !== undefined;
7175    }
7176
7177    export function tryGetExtensionFromPath(path: string): Extension | undefined {
7178        if (fileExtensionIs(path, Extension.Ets)) {
7179            return Extension.Ets;
7180        }
7181        return find<Extension>(extensionsToRemove, e => fileExtensionIs(path, e));
7182    }
7183
7184    export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) {
7185        return sourceFile.checkJsDirective ? sourceFile.checkJsDirective.enabled : compilerOptions.checkJs;
7186    }
7187
7188    export const emptyFileSystemEntries: FileSystemEntries = {
7189        files: emptyArray,
7190        directories: emptyArray
7191    };
7192
7193
7194    /**
7195     * patternOrStrings contains both patterns (containing "*") and regular strings.
7196     * Return an exact match if possible, or a pattern match, or undefined.
7197     * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
7198     */
7199    export function matchPatternOrExact(patternOrStrings: readonly (string | Pattern)[], candidate: string): string | Pattern | undefined {
7200        const patterns: Pattern[] = [];
7201        for (const patternOrString of patternOrStrings) {
7202            if (patternOrString === candidate) {
7203                return candidate;
7204            }
7205
7206            if (!isString(patternOrString)) {
7207                patterns.push(patternOrString);
7208            }
7209        }
7210
7211        return findBestPatternMatch(patterns, _ => _, candidate);
7212    }
7213
7214    export type Mutable<T extends object> = { -readonly [K in keyof T]: T[K] };
7215
7216    export function sliceAfter<T>(arr: readonly T[], value: T): readonly T[] {
7217        const index = arr.indexOf(value);
7218        Debug.assert(index !== -1);
7219        return arr.slice(index);
7220    }
7221
7222    export function addRelatedInfo<T extends Diagnostic>(diagnostic: T, ...relatedInformation: DiagnosticRelatedInformation[]): T {
7223        if (!relatedInformation.length) {
7224            return diagnostic;
7225        }
7226        if (!diagnostic.relatedInformation) {
7227            diagnostic.relatedInformation = [];
7228        }
7229        Debug.assert(diagnostic.relatedInformation !== emptyArray, "Diagnostic had empty array singleton for related info, but is still being constructed!");
7230        diagnostic.relatedInformation.push(...relatedInformation);
7231        return diagnostic;
7232    }
7233
7234    export function minAndMax<T>(arr: readonly T[], getValue: (value: T) => number): { readonly min: number, readonly max: number } {
7235        Debug.assert(arr.length !== 0);
7236        let min = getValue(arr[0]);
7237        let max = min;
7238        for (let i = 1; i < arr.length; i++) {
7239            const value = getValue(arr[i]);
7240            if (value < min) {
7241                min = value;
7242            }
7243            else if (value > max) {
7244                max = value;
7245            }
7246        }
7247        return { min, max };
7248    }
7249
7250    export function rangeOfNode(node: Node): TextRange {
7251        return { pos: getTokenPosOfNode(node), end: node.end };
7252    }
7253
7254    export function rangeOfTypeParameters(sourceFile: SourceFile, typeParameters: NodeArray<TypeParameterDeclaration>): TextRange {
7255        // Include the `<>`
7256        const pos = typeParameters.pos - 1;
7257        const end = skipTrivia(sourceFile.text, typeParameters.end) + 1;
7258        return { pos, end };
7259    }
7260
7261    export interface HostWithIsSourceOfProjectReferenceRedirect {
7262        isSourceOfProjectReferenceRedirect(fileName: string): boolean;
7263    }
7264    export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions, host: HostWithIsSourceOfProjectReferenceRedirect) {
7265        // If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
7266        // If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
7267        // '/// <reference no-default-lib="true"/>' directive.
7268        return (options.skipLibCheck && sourceFile.isDeclarationFile ||
7269            options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
7270            host.isSourceOfProjectReferenceRedirect(sourceFile.fileName);
7271    }
7272
7273    export function isJsonEqual(a: unknown, b: unknown): boolean {
7274        // eslint-disable-next-line no-null/no-null
7275        return a === b || typeof a === "object" && a !== null && typeof b === "object" && b !== null && equalOwnProperties(a as MapLike<unknown>, b as MapLike<unknown>, isJsonEqual);
7276    }
7277
7278    /**
7279     * Converts a bigint literal string, e.g. `0x1234n`,
7280     * to its decimal string representation, e.g. `4660`.
7281     */
7282    export function parsePseudoBigInt(stringValue: string): string {
7283        let log2Base: number;
7284        switch (stringValue.charCodeAt(1)) { // "x" in "0x123"
7285            case CharacterCodes.b:
7286            case CharacterCodes.B: // 0b or 0B
7287                log2Base = 1;
7288                break;
7289            case CharacterCodes.o:
7290            case CharacterCodes.O: // 0o or 0O
7291                log2Base = 3;
7292                break;
7293            case CharacterCodes.x:
7294            case CharacterCodes.X: // 0x or 0X
7295                log2Base = 4;
7296                break;
7297            default: // already in decimal; omit trailing "n"
7298                const nIndex = stringValue.length - 1;
7299                // Skip leading 0s
7300                let nonZeroStart = 0;
7301                while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) {
7302                    nonZeroStart++;
7303                }
7304                return stringValue.slice(nonZeroStart, nIndex) || "0";
7305        }
7306
7307        // Omit leading "0b", "0o", or "0x", and trailing "n"
7308        const startIndex = 2, endIndex = stringValue.length - 1;
7309        const bitsNeeded = (endIndex - startIndex) * log2Base;
7310        // Stores the value specified by the string as a LE array of 16-bit integers
7311        // using Uint16 instead of Uint32 so combining steps can use bitwise operators
7312        const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0));
7313        // Add the digits, one at a time
7314        for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) {
7315            const segment = bitOffset >>> 4;
7316            const digitChar = stringValue.charCodeAt(i);
7317            // Find character range: 0-9 < A-F < a-f
7318            const digit = digitChar <= CharacterCodes._9
7319                ? digitChar - CharacterCodes._0
7320                : 10 + digitChar -
7321                    (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a);
7322            const shiftedDigit = digit << (bitOffset & 15);
7323            segments[segment] |= shiftedDigit;
7324            const residual = shiftedDigit >>> 16;
7325            if (residual) segments[segment + 1] |= residual; // overflows segment
7326        }
7327        // Repeatedly divide segments by 10 and add remainder to base10Value
7328        let base10Value = "";
7329        let firstNonzeroSegment = segments.length - 1;
7330        let segmentsRemaining = true;
7331        while (segmentsRemaining) {
7332            let mod10 = 0;
7333            segmentsRemaining = false;
7334            for (let segment = firstNonzeroSegment; segment >= 0; segment--) {
7335                const newSegment = mod10 << 16 | segments[segment];
7336                const segmentValue = (newSegment / 10) | 0;
7337                segments[segment] = segmentValue;
7338                mod10 = newSegment - segmentValue * 10;
7339                if (segmentValue && !segmentsRemaining) {
7340                    firstNonzeroSegment = segment;
7341                    segmentsRemaining = true;
7342                }
7343            }
7344            base10Value = mod10 + base10Value;
7345        }
7346        return base10Value;
7347    }
7348
7349    export function pseudoBigIntToString({negative, base10Value}: PseudoBigInt): string {
7350        return (negative && base10Value !== "0" ? "-" : "") + base10Value;
7351    }
7352
7353    export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean {
7354        return !!(useSite.flags & NodeFlags.Ambient)
7355            || isPartOfTypeQuery(useSite)
7356            || isIdentifierInNonEmittingHeritageClause(useSite)
7357            || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite)
7358            || !(isExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite));
7359    }
7360
7361    function isShorthandPropertyNameUseSite(useSite: Node) {
7362        return isIdentifier(useSite) && isShorthandPropertyAssignment(useSite.parent) && useSite.parent.name === useSite;
7363    }
7364
7365    function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node) {
7366        while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) {
7367            node = node.parent;
7368        }
7369        if (node.kind !== SyntaxKind.ComputedPropertyName) {
7370            return false;
7371        }
7372        if (hasSyntacticModifier(node.parent, ModifierFlags.Abstract)) {
7373            return true;
7374        }
7375        const containerKind = node.parent.parent.kind;
7376        return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral;
7377    }
7378
7379    /** Returns true for an identifier in 1) an `implements` clause, and 2) an `extends` clause of an interface. */
7380    function isIdentifierInNonEmittingHeritageClause(node: Node): boolean {
7381        if (node.kind !== SyntaxKind.Identifier) return false;
7382        const heritageClause = findAncestor(node.parent, parent => {
7383            switch (parent.kind) {
7384                case SyntaxKind.HeritageClause:
7385                    return true;
7386                case SyntaxKind.PropertyAccessExpression:
7387                case SyntaxKind.ExpressionWithTypeArguments:
7388                    return false;
7389                default:
7390                    return "quit";
7391            }
7392        }) as HeritageClause | undefined;
7393        return heritageClause?.token === SyntaxKind.ImplementsKeyword || heritageClause?.parent.kind === SyntaxKind.InterfaceDeclaration;
7394    }
7395
7396    export function isIdentifierTypeReference(node: Node): node is TypeReferenceNode & { typeName: Identifier } {
7397        return isTypeReferenceNode(node) && isIdentifier(node.typeName);
7398    }
7399
7400    export function arrayIsHomogeneous<T>(array: readonly T[], comparer: EqualityComparer<T> = equateValues) {
7401        if (array.length < 2) return true;
7402        const first = array[0];
7403        for (let i = 1, length = array.length; i < length; i++) {
7404            const target = array[i];
7405            if (!comparer(first, target)) return false;
7406        }
7407        return true;
7408    }
7409
7410    /**
7411     * Bypasses immutability and directly sets the `pos` property of a `TextRange` or `Node`.
7412     */
7413    /* @internal */
7414    export function setTextRangePos<T extends ReadonlyTextRange>(range: T, pos: number) {
7415        (range as TextRange).pos = pos;
7416        return range;
7417    }
7418
7419    /**
7420     * Bypasses immutability and directly sets the `end` property of a `TextRange` or `Node`.
7421     */
7422    /* @internal */
7423    export function setTextRangeEnd<T extends ReadonlyTextRange>(range: T, end: number) {
7424        (range as TextRange).end = end;
7425        return range;
7426    }
7427
7428    /**
7429     * Bypasses immutability and directly sets the `pos` and `end` properties of a `TextRange` or `Node`.
7430     */
7431    /* @internal */
7432    export function setTextRangePosEnd<T extends ReadonlyTextRange>(range: T, pos: number, end: number) {
7433        return setTextRangeEnd(setTextRangePos(range, pos), end);
7434    }
7435
7436    /**
7437     * Bypasses immutability and directly sets the `pos` and `end` properties of a `TextRange` or `Node` from the
7438     * provided position and width.
7439     */
7440    /* @internal */
7441    export function setTextRangePosWidth<T extends ReadonlyTextRange>(range: T, pos: number, width: number) {
7442        return setTextRangePosEnd(range, pos, pos + width);
7443    }
7444
7445    /**
7446     * Bypasses immutability and directly sets the `flags` property of a `Node`.
7447     */
7448    /* @internal */
7449    export function setNodeFlags<T extends Node>(node: T, newFlags: NodeFlags): T;
7450    /* @internal */
7451    export function setNodeFlags<T extends Node>(node: T | undefined, newFlags: NodeFlags): T | undefined;
7452    export function setNodeFlags<T extends Node>(node: T | undefined, newFlags: NodeFlags): T | undefined {
7453        if (node) {
7454            (node as Mutable<T>).flags = newFlags;
7455        }
7456        return node;
7457    }
7458
7459    /**
7460     * Bypasses immutability and directly sets the `parent` property of a `Node`.
7461     */
7462    /* @internal */
7463    export function setParent<T extends Node>(child: T, parent: T["parent"] | undefined): T;
7464    /* @internal */
7465    export function setParent<T extends Node>(child: T | undefined, parent: T["parent"] | undefined): T | undefined;
7466    export function setParent<T extends Node>(child: T | undefined, parent: T["parent"] | undefined): T | undefined {
7467        if (child && parent) {
7468            (child as Mutable<T>).parent = parent;
7469        }
7470        return child;
7471    }
7472
7473    /**
7474     * Bypasses immutability and directly sets the `parent` property of each `Node` in an array of nodes, if is not already set.
7475     */
7476    /* @internal */
7477    export function setEachParent<T extends readonly Node[]>(children: T, parent: T[number]["parent"]): T;
7478    /* @internal */
7479    export function setEachParent<T extends readonly Node[]>(children: T | undefined, parent: T[number]["parent"]): T | undefined;
7480    export function setEachParent<T extends readonly Node[]>(children: T | undefined, parent: T[number]["parent"]): T | undefined {
7481        if (children) {
7482            for (const child of children) {
7483                setParent(child, parent);
7484            }
7485        }
7486        return children;
7487    }
7488
7489    function isPackedElement(node: Expression) {
7490        return !isOmittedExpression(node);
7491    }
7492
7493    /**
7494     * Determines whether the provided node is an ArrayLiteralExpression that contains no missing elements.
7495     */
7496    export function isPackedArrayLiteral(node: Expression) {
7497        return isArrayLiteralExpression(node) && every(node.elements, isPackedElement);
7498    }
7499
7500    /**
7501     * Indicates whether the result of an `Expression` will be unused.
7502     *
7503     * NOTE: This requires a node with a valid `parent` pointer.
7504     */
7505    export function expressionResultIsUnused(node: Expression): boolean {
7506        Debug.assertIsDefined(node.parent);
7507        while (true) {
7508            const parent: Node = node.parent;
7509            // walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression
7510            if (isParenthesizedExpression(parent)) {
7511                node = parent;
7512                continue;
7513            }
7514            // result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop
7515            if (isExpressionStatement(parent) ||
7516                isVoidExpression(parent) ||
7517                isForStatement(parent) && (parent.initializer === node || parent.incrementor === node)) {
7518                return true;
7519            }
7520            if (isCommaListExpression(parent)) {
7521                // left side of comma is always unused
7522                if (node !== last(parent.elements)) return true;
7523                // right side of comma is unused if parent is unused
7524                node = parent;
7525                continue;
7526            }
7527            if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.CommaToken) {
7528                // left side of comma is always unused
7529                if (node === parent.left) return true;
7530                // right side of comma is unused if parent is unused
7531                node = parent;
7532                continue;
7533            }
7534            return false;
7535        }
7536    }
7537
7538    export function containsIgnoredPath(path: string) {
7539        return some(ignoredPaths, p => stringContains(path, p));
7540    }
7541
7542    export function getContainingNodeArray(node: Node): NodeArray<Node> | undefined {
7543        if (!node.parent) return undefined;
7544        switch (node.kind) {
7545            case SyntaxKind.TypeParameter:
7546                const { parent } = node as TypeParameterDeclaration;
7547                return parent.kind === SyntaxKind.InferType ? undefined : parent.typeParameters;
7548            case SyntaxKind.Parameter:
7549                return (node as ParameterDeclaration).parent.parameters;
7550            case SyntaxKind.TemplateLiteralTypeSpan:
7551                return (node as TemplateLiteralTypeSpan).parent.templateSpans;
7552            case SyntaxKind.TemplateSpan:
7553                return (node as TemplateSpan).parent.templateSpans;
7554            case SyntaxKind.Decorator: {
7555                const { parent } = node as Decorator;
7556                return canHaveDecorators(parent) ? parent.modifiers :
7557                    canHaveIllegalDecorators(parent) ? parent.illegalDecorators :
7558                    undefined;
7559            }
7560            case SyntaxKind.HeritageClause:
7561                return (node as HeritageClause).parent.heritageClauses;
7562        }
7563
7564        const { parent } = node;
7565        if (isJSDocTag(node)) {
7566            return isJSDocTypeLiteral(node.parent) ? undefined : node.parent.tags;
7567        }
7568
7569        switch (parent.kind) {
7570            case SyntaxKind.TypeLiteral:
7571            case SyntaxKind.InterfaceDeclaration:
7572                return isTypeElement(node) ? (parent as TypeLiteralNode | InterfaceDeclaration).members : undefined;
7573            case SyntaxKind.UnionType:
7574            case SyntaxKind.IntersectionType:
7575                return (parent as UnionOrIntersectionTypeNode).types;
7576            case SyntaxKind.TupleType:
7577            case SyntaxKind.ArrayLiteralExpression:
7578            case SyntaxKind.CommaListExpression:
7579            case SyntaxKind.NamedImports:
7580            case SyntaxKind.NamedExports:
7581                return (parent as TupleTypeNode | ArrayLiteralExpression | CommaListExpression | NamedImports | NamedExports).elements;
7582            case SyntaxKind.ObjectLiteralExpression:
7583            case SyntaxKind.JsxAttributes:
7584                return (parent as ObjectLiteralExpressionBase<ObjectLiteralElement>).properties;
7585            case SyntaxKind.CallExpression:
7586            case SyntaxKind.NewExpression:
7587                return isTypeNode(node) ? (parent as CallExpression | NewExpression).typeArguments :
7588                    (parent as CallExpression | NewExpression).expression === node ? undefined :
7589                    (parent as CallExpression | NewExpression).arguments;
7590            case SyntaxKind.JsxElement:
7591            case SyntaxKind.JsxFragment:
7592                return isJsxChild(node) ? (parent as JsxElement | JsxFragment).children : undefined;
7593            case SyntaxKind.JsxOpeningElement:
7594            case SyntaxKind.JsxSelfClosingElement:
7595                return isTypeNode(node) ? (parent as JsxOpeningElement | JsxSelfClosingElement).typeArguments : undefined;
7596            case SyntaxKind.Block:
7597            case SyntaxKind.CaseClause:
7598            case SyntaxKind.DefaultClause:
7599            case SyntaxKind.ModuleBlock:
7600                return (parent as Block | CaseOrDefaultClause | ModuleBlock).statements;
7601            case SyntaxKind.CaseBlock:
7602                return (parent as CaseBlock).clauses;
7603            case SyntaxKind.ClassDeclaration:
7604            case SyntaxKind.ClassExpression:
7605                return isClassElement(node) ? (parent as ClassLikeDeclaration).members : undefined;
7606            case SyntaxKind.EnumDeclaration:
7607                return isEnumMember(node) ? (parent as EnumDeclaration).members : undefined;
7608            case SyntaxKind.SourceFile:
7609                return (parent as SourceFile).statements;
7610        }
7611    }
7612
7613    export function hasContextSensitiveParameters(node: FunctionLikeDeclaration) {
7614        // Functions with type parameters are not context sensitive.
7615        if (!node.typeParameters) {
7616            // Functions with any parameters that lack type annotations are context sensitive.
7617            if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) {
7618                return true;
7619            }
7620            if (node.kind !== SyntaxKind.ArrowFunction) {
7621                // If the first parameter is not an explicit 'this' parameter, then the function has
7622                // an implicit 'this' parameter which is subject to contextual typing.
7623                const parameter = firstOrUndefined(node.parameters);
7624                if (!(parameter && parameterIsThisKeyword(parameter))) {
7625                    return true;
7626                }
7627            }
7628        }
7629        return false;
7630    }
7631
7632    /* @internal */
7633    export function isInfinityOrNaNString(name: string | __String): boolean {
7634        return name === "Infinity" || name === "-Infinity" || name === "NaN";
7635    }
7636
7637    export function isCatchClauseVariableDeclaration(node: Node) {
7638        return node.kind === SyntaxKind.VariableDeclaration && node.parent.kind === SyntaxKind.CatchClause;
7639    }
7640
7641    export function isParameterOrCatchClauseVariable(symbol: Symbol) {
7642        const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration);
7643        return !!declaration && (isParameter(declaration) || isCatchClauseVariableDeclaration(declaration));
7644    }
7645
7646    export function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction {
7647        return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction;
7648    }
7649
7650    export function escapeSnippetText(text: string): string {
7651        return text.replace(/\$/gm, () => "\\$");
7652    }
7653
7654    export function isNumericLiteralName(name: string | __String) {
7655        // The intent of numeric names is that
7656        //     - they are names with text in a numeric form, and that
7657        //     - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit',
7658        //         acquired by applying the abstract 'ToNumber' operation on the name's text.
7659        //
7660        // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name.
7661        // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold.
7662        //
7663        // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)'
7664        // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'.
7665        // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names
7666        // because their 'ToString' representation is not equal to their original text.
7667        // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1.
7668        //
7669        // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'.
7670        // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation.
7671        // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number.
7672        //
7673        // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
7674        // This is desired behavior, because when indexing with them as numeric entities, you are indexing
7675        // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
7676        return (+name).toString() === name;
7677    }
7678
7679    export function createPropertyNameNodeForIdentifierOrLiteral(name: string, target: ScriptTarget, singleQuote?: boolean, stringNamed?: boolean) {
7680        return isIdentifierText(name, target) ? factory.createIdentifier(name) :
7681            !stringNamed && isNumericLiteralName(name) && +name >= 0 ? factory.createNumericLiteral(+name) :
7682            factory.createStringLiteral(name, !!singleQuote);
7683    }
7684
7685    export function isThisTypeParameter(type: Type): boolean {
7686        return !!(type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType);
7687    }
7688
7689    export interface NodeModulePathParts {
7690        readonly topLevelNodeModulesIndex: number;
7691        readonly topLevelPackageNameIndex: number;
7692        readonly packageRootIndex: number;
7693        readonly fileNameIndex: number;
7694    }
7695    export function getNodeModulePathParts(fullPath: string, modulePathPart: string): NodeModulePathParts | undefined {
7696        // If fullPath can't be valid module file within node_modules, returns undefined.
7697        // Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js
7698        // Returns indices:                       ^            ^                                                      ^             ^
7699
7700        let topLevelNodeModulesIndex = 0;
7701        let topLevelPackageNameIndex = 0;
7702        let packageRootIndex = 0;
7703        let fileNameIndex = 0;
7704
7705        const enum States {
7706            BeforeNodeModules,
7707            NodeModules,
7708            Scope,
7709            PackageContent
7710        }
7711
7712        let partStart = 0;
7713        let partEnd = 0;
7714        let state = States.BeforeNodeModules;
7715
7716        while (partEnd >= 0) {
7717            partStart = partEnd;
7718            partEnd = fullPath.indexOf("/", partStart + 1);
7719            switch (state) {
7720                case States.BeforeNodeModules:
7721                    if (fullPath.indexOf(modulePathPart, partStart) === partStart) {
7722                        topLevelNodeModulesIndex = partStart;
7723                        topLevelPackageNameIndex = partEnd;
7724                        state = States.NodeModules;
7725                    }
7726                    break;
7727                case States.NodeModules:
7728                case States.Scope:
7729                    if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") {
7730                        state = States.Scope;
7731                    }
7732                    else {
7733                        packageRootIndex = partEnd;
7734                        state = States.PackageContent;
7735                    }
7736                    break;
7737                case States.PackageContent:
7738                    if (fullPath.indexOf(modulePathPart, partStart) === partStart) {
7739                        state = States.NodeModules;
7740                    }
7741                    else {
7742                        state = States.PackageContent;
7743                    }
7744                    break;
7745            }
7746        }
7747
7748        fileNameIndex = partStart;
7749
7750        return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined;
7751    }
7752
7753    export function getParameterTypeNode(parameter: ParameterDeclaration | JSDocParameterTag) {
7754        return parameter.kind === SyntaxKind.JSDocParameterTag ? parameter.typeExpression?.type : parameter.type;
7755    }
7756
7757    export function isTypeDeclaration(node: Node): node is TypeParameterDeclaration | ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTypedefTag | JSDocCallbackTag | JSDocEnumTag | EnumDeclaration | ImportClause | ImportSpecifier | ExportSpecifier {
7758        switch (node.kind) {
7759            case SyntaxKind.TypeParameter:
7760            case SyntaxKind.ClassDeclaration:
7761            case SyntaxKind.InterfaceDeclaration:
7762            case SyntaxKind.TypeAliasDeclaration:
7763            case SyntaxKind.EnumDeclaration:
7764            case SyntaxKind.JSDocTypedefTag:
7765            case SyntaxKind.JSDocCallbackTag:
7766            case SyntaxKind.JSDocEnumTag:
7767                return true;
7768            case SyntaxKind.ImportClause:
7769                return (node as ImportClause).isTypeOnly;
7770            case SyntaxKind.ImportSpecifier:
7771            case SyntaxKind.ExportSpecifier:
7772                return (node as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly;
7773            default:
7774                return false;
7775        }
7776    }
7777
7778    export function canHaveExportModifier(node: Node): node is Extract<HasModifiers, Statement> {
7779        return isEnumDeclaration(node) || isVariableStatement(node) || isFunctionDeclaration(node) || isClassDeclaration(node)
7780            || isInterfaceDeclaration(node) || isTypeDeclaration(node) || (isModuleDeclaration(node) && !isExternalModuleAugmentation(node) && !isGlobalScopeAugmentation(node));
7781    }
7782
7783    export function isCalledStructDeclaration(declarations: Declaration[] | undefined): boolean {
7784        if (!declarations) {
7785            return false;
7786        }
7787
7788        return declarations.some(declaration => declaration.kind === SyntaxKind.StructDeclaration);
7789    }
7790
7791    export function getNameOfDecorator(node: Decorator): string | undefined {
7792        const expression = node.expression;
7793
7794        if (isIdentifier(expression)) {
7795            return expression.escapedText.toString();
7796        }
7797
7798        if (isCallExpression(expression) && isIdentifier(expression.expression)) {
7799            return expression.expression.escapedText.toString();
7800        }
7801
7802        return undefined;
7803    }
7804}
7805
7806namespace ts {
7807    export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile) {
7808        return node.kind !== SyntaxKind.JsxText ? getLeadingCommentRanges(sourceFileOfNode.text, node.pos) : undefined;
7809    }
7810
7811    export function createTextWriter(newLine: string): EmitTextWriter {
7812        let output: string;
7813        let indent: number;
7814        let lineStart: boolean;
7815        let lineCount: number;
7816        let linePos: number;
7817        let hasTrailingComment = false;
7818
7819        function updateLineCountAndPosFor(s: string) {
7820            const lineStartsOfS = computeLineStarts(s);
7821            if (lineStartsOfS.length > 1) {
7822                lineCount = lineCount + lineStartsOfS.length - 1;
7823                linePos = output.length - s.length + last(lineStartsOfS);
7824                lineStart = (linePos - output.length) === 0;
7825            }
7826            else {
7827                lineStart = false;
7828            }
7829        }
7830
7831        function writeText(s: string) {
7832            if (s && s.length) {
7833                if (lineStart) {
7834                    s = getIndentString(indent) + s;
7835                    lineStart = false;
7836                }
7837                output += s;
7838                updateLineCountAndPosFor(s);
7839            }
7840        }
7841
7842        function write(s: string) {
7843            if (s) hasTrailingComment = false;
7844            writeText(s);
7845        }
7846
7847        function writeComment(s: string) {
7848            if (s) hasTrailingComment = true;
7849            writeText(s);
7850        }
7851
7852        function reset(): void {
7853            output = "";
7854            indent = 0;
7855            lineStart = true;
7856            lineCount = 0;
7857            linePos = 0;
7858            hasTrailingComment = false;
7859        }
7860
7861        function rawWrite(s: string) {
7862            if (s !== undefined) {
7863                output += s;
7864                updateLineCountAndPosFor(s);
7865                hasTrailingComment = false;
7866            }
7867        }
7868
7869        function writeLiteral(s: string) {
7870            if (s && s.length) {
7871                write(s);
7872            }
7873        }
7874
7875        function writeLine(force?: boolean) {
7876            if (!lineStart || force) {
7877                output += newLine;
7878                lineCount++;
7879                linePos = output.length;
7880                lineStart = true;
7881                hasTrailingComment = false;
7882            }
7883        }
7884
7885        function getTextPosWithWriteLine() {
7886            return lineStart ? output.length : (output.length + newLine.length);
7887        }
7888
7889        reset();
7890
7891        return {
7892            write,
7893            rawWrite,
7894            writeLiteral,
7895            writeLine,
7896            increaseIndent: () => { indent++; },
7897            decreaseIndent: () => { indent--; },
7898            getIndent: () => indent,
7899            getTextPos: () => output.length,
7900            getLine: () => lineCount,
7901            getColumn: () => lineStart ? indent * getIndentSize() : output.length - linePos,
7902            getText: () => output,
7903            isAtStartOfLine: () => lineStart,
7904            hasTrailingComment: () => hasTrailingComment,
7905            hasTrailingWhitespace: () => !!output.length && isWhiteSpaceLike(output.charCodeAt(output.length - 1)),
7906            clear: reset,
7907            reportInaccessibleThisError: noop,
7908            reportPrivateInBaseOfClassExpression: noop,
7909            reportInaccessibleUniqueSymbolError: noop,
7910            trackSymbol: () => false,
7911            writeKeyword: write,
7912            writeOperator: write,
7913            writeParameter: write,
7914            writeProperty: write,
7915            writePunctuation: write,
7916            writeSpace: write,
7917            writeStringLiteral: write,
7918            writeSymbol: (s, _) => write(s),
7919            writeTrailingSemicolon: write,
7920            writeComment,
7921            getTextPosWithWriteLine
7922        };
7923    }
7924
7925    /**
7926     * Bypasses immutability and directly sets the `parent` property of each `Node` recursively.
7927     * @param rootNode The root node from which to start the recursion.
7928     * @param incremental When `true`, only recursively descends through nodes whose `parent` pointers are incorrect.
7929     * This allows us to quickly bail out of setting `parent` for subtrees during incremental parsing.
7930     */
7931    export function setParentRecursive<T extends Node>(rootNode: T, incremental: boolean): T;
7932    export function setParentRecursive<T extends Node>(rootNode: T | undefined, incremental: boolean): T | undefined;
7933    export function setParentRecursive<T extends Node>(rootNode: T | undefined, incremental: boolean): T | undefined {
7934        if (!rootNode) return rootNode;
7935        forEachChildRecursively(rootNode, isJSDocNode(rootNode) ? bindParentToChildIgnoringJSDoc : bindParentToChild);
7936        return rootNode;
7937
7938        function bindParentToChildIgnoringJSDoc(child: Node, parent: Node): void | "skip" {
7939            if (incremental && child.parent === parent) {
7940                return "skip";
7941            }
7942            setParent(child, parent);
7943        }
7944
7945        function bindJSDoc(child: Node) {
7946            if (hasJSDocNodes(child)) {
7947                for (const doc of child.jsDoc!) {
7948                    bindParentToChildIgnoringJSDoc(doc, child);
7949                    forEachChildRecursively(doc, bindParentToChildIgnoringJSDoc);
7950                }
7951            }
7952        }
7953
7954        function bindParentToChild(child: Node, parent: Node) {
7955            return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child);
7956        }
7957    }
7958}