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