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