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