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