• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    __String, AccessExpression, AccessorDeclaration, AnnotationElement, ArrayBindingElement, ArrayBindingOrAssignmentPattern,
3    AssertionExpression, AssertionKey, AssignmentDeclarationKind, AssignmentPattern, AutoAccessorPropertyDeclaration,
4    BinaryExpression, BindableObjectDefinePropertyCall, BindingElement, BindingName, BindingOrAssignmentElement,
5    BindingOrAssignmentElementTarget, BindingOrAssignmentPattern, BindingPattern, Block, BooleanLiteral,
6    BreakOrContinueStatement, CallChain, CallExpression, CallLikeExpression, canHaveIllegalTypeParameters,
7    CaseOrDefaultClause, CharacterCodes, ClassElement, ClassLikeDeclaration, ClassStaticBlockDeclaration, combinePaths,
8    compareDiagnostics, CompilerOptions, ConciseBody, ConstructorDeclaration, ConstructorTypeNode, contains,
9    createCompilerDiagnostic, Debug, Declaration, DeclarationName, DeclarationStatement, DeclarationWithTypeParameters,
10    Decorator, Diagnostic, Diagnostics, ElementAccessChain, ElementAccessExpression, EmitResolver, emptyArray, EntityName,
11    entityNameToString, EnumDeclaration, every, ExportAssignment, ExportSpecifier, Expression, filter, find, flatMap,
12    ForInitializer, ForInOrOfStatement, FunctionBody, FunctionLikeDeclaration, FunctionTypeNode, GeneratedIdentifier,
13    GeneratedIdentifierFlags, GeneratedPrivateIdentifier, GetAccessorDeclaration, getAssignmentDeclarationKind,
14    getDirectoryPath, getEffectiveModifierFlags, getEffectiveModifierFlagsAlwaysIncludeJSDoc,
15    getElementOrPropertyAccessArgumentExpressionOrName, getEmitScriptTarget, getJSDocCommentsAndTags,
16    getJSDocTypeParameterDeclarations, hasAccessorModifier, hasAnnotations, hasDecorators, HasDecorators, HasExpressionInitializer,
17    hasIllegalDecorators, HasIllegalDecorators, HasInitializer, HasJSDoc, HasModifiers, hasProperty,
18    hasSyntacticModifier, HasType, Identifier, ImportClause, ImportEqualsDeclaration, ImportOrExportSpecifier,
19    ImportSpecifier, ImportTypeNode, isAccessExpression, isAmbientModule, isAnnotation, isAnnotationDeclaration, isAnyImportOrReExport, isArrowFunction,
20    isBinaryExpression, isBindableStaticElementAccessExpression, isBindingElement, isBlock, isCallExpression,
21    isCallSignatureDeclaration, isClassExpression, isClassStaticBlockDeclaration, isDecorator, isDecoratorOrAnnotation,
22    isElementAccessExpression, isExportAssignment, isExportDeclaration, isExportSpecifier, isFunctionBlock,
23    isFunctionExpression, isFunctionTypeNode, isIdentifier, isImportDeclaration, isImportSpecifier, isInEtsFile, isInJSFile, isJSDoc, isJSDocAugmentsTag,
24    isJSDocClassTag, isJSDocDeprecatedTag, isJSDocEnumTag, isJSDocFunctionType, isJSDocImplementsTag,
25    isJSDocOverrideTag, isJSDocParameterTag, isJSDocPrivateTag, isJSDocProtectedTag, isJSDocPublicTag,
26    isJSDocReadonlyTag, isJSDocReturnTag, isJSDocSignature, isJSDocTemplateTag, isJSDocThisTag, isJSDocTypeAlias,
27    isJSDocTypeLiteral, isJSDocTypeTag, isModuleBlock, isNamedImports, isNamedExports, isNonNullExpression, isNotEmittedStatement, isOmittedExpression,
28    isParameter, isPartiallyEmittedExpression, isPrivateIdentifier, isPropertyAccessExpression, isPropertyAssignment,
29    isPropertyDeclaration, isRootedDiskPath, isSourceFile, isStringLiteral, isTypeLiteralNode, isTypeNodeKind,
30    isTypeReferenceNode, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, isWhiteSpaceLike,
31    IterationStatement, JSDocAugmentsTag, JSDocClassTag, JSDocComment, JSDocContainer, JSDocDeprecatedTag, JSDocEnumTag,
32    JSDocImplementsTag, JSDocLink, JSDocLinkCode, JSDocLinkPlain, JSDocNamespaceBody, JSDocOverrideTag,
33    JSDocParameterTag, JSDocPrivateTag, JSDocPropertyLikeTag, JSDocProtectedTag, JSDocPublicTag, JSDocReadonlyTag,
34    JSDocReturnTag, JSDocSignature, JSDocTag, JSDocTemplateTag, JSDocThisTag, JSDocTypedefTag, JSDocTypeTag,
35    JsxAttributeLike, JsxChild, JsxExpression, JsxOpeningLikeElement, JsxTagNameExpression, LabeledStatement,
36    lastOrUndefined, LeftHandSideExpression, LiteralExpression, LiteralToken, mapDefined, MemberName, MethodDeclaration, Modifier,
37    ModifierFlags, ModifierLike, modifierToFlag, ModuleBody, ModuleDeclaration, ModuleReference, NamedDeclaration,
38    NamedExportBindings, NamedImportBindings, NamespaceBody, NamespaceImport, NewExpression, Node, NodeArray, NodeFlags,
39    NonNullChain, normalizePath, NotEmittedStatement, ObjectBindingOrAssignmentElement,
40    ObjectBindingOrAssignmentPattern, ObjectLiteralElement, ObjectLiteralElementLike, OptionalChain, OptionalChainRoot,
41    OuterExpressionKinds, ParameterDeclaration, PartiallyEmittedExpression, pathIsRelative, PostfixUnaryExpression,
42    PrefixUnaryExpression, PrivateClassElementDeclaration, PrivateIdentifier, PrivateIdentifierPropertyAccessExpression,
43    PropertyAccessChain, PropertyAccessExpression, PropertyDeclaration, PropertyName, Push, QualifiedName, ScriptTarget,
44    SetAccessorDeclaration, setLocalizedDiagnosticMessages, setUILocale, SignatureDeclaration, skipOuterExpressions,
45    some, sortAndDeduplicate, SortedReadonlyArray, SourceFile, Statement, StringLiteral, StringLiteralLike, StructDeclaration,
46    Symbol, SyntaxKind, TemplateLiteral, TemplateLiteralToken, TemplateMiddle, TemplateTail, TextChangeRange, TextRange,
47    TextSpan, TypeElement, TypeNode, TypeOnlyAliasDeclaration, TypeParameterDeclaration, TypeReferenceType,
48    UnaryExpression, UnparsedNode, UnparsedTextLike, VariableDeclaration,
49} from "./_namespaces/ts";
50
51export function isExternalModuleNameRelative(moduleName: string): boolean {
52    // TypeScript 1.0 spec (April 2014): 11.2.1
53    // An external module name is "relative" if the first term is "." or "..".
54    // Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules`, `oh_modules` or treat it as an ambient module.
55    return pathIsRelative(moduleName) || isRootedDiskPath(moduleName);
56}
57
58export function sortAndDeduplicateDiagnostics<T extends Diagnostic>(diagnostics: readonly T[]): SortedReadonlyArray<T> {
59    return sortAndDeduplicate<T>(diagnostics, compareDiagnostics);
60}
61
62export function getDefaultLibFileName(options: CompilerOptions): string {
63    switch (getEmitScriptTarget(options)) {
64        case ScriptTarget.ESNext:
65            return "lib.esnext.full.d.ts";
66        case ScriptTarget.ES2022:
67            return "lib.es2022.full.d.ts";
68        case ScriptTarget.ES2021:
69            return "lib.es2021.full.d.ts";
70        case ScriptTarget.ES2020:
71            return "lib.es2020.full.d.ts";
72        case ScriptTarget.ES2019:
73            return "lib.es2019.full.d.ts";
74        case ScriptTarget.ES2018:
75            return "lib.es2018.full.d.ts";
76        case ScriptTarget.ES2017:
77            return "lib.es2017.full.d.ts";
78        case ScriptTarget.ES2016:
79            return "lib.es2016.full.d.ts";
80        case ScriptTarget.ES2015:
81            return "lib.es6.d.ts";  // We don't use lib.es2015.full.d.ts due to breaking change.
82        default:
83            return "lib.d.ts";
84    }
85}
86
87export function textSpanEnd(span: TextSpan) {
88    return span.start + span.length;
89}
90
91export function textSpanIsEmpty(span: TextSpan) {
92    return span.length === 0;
93}
94
95export function textSpanContainsPosition(span: TextSpan, position: number) {
96    return position >= span.start && position < textSpanEnd(span);
97}
98
99/** @internal */
100export function textRangeContainsPositionInclusive(span: TextRange, position: number): boolean {
101    return position >= span.pos && position <= span.end;
102}
103
104// Returns true if 'span' contains 'other'.
105export function textSpanContainsTextSpan(span: TextSpan, other: TextSpan) {
106    return other.start >= span.start && textSpanEnd(other) <= textSpanEnd(span);
107}
108
109export function textSpanOverlapsWith(span: TextSpan, other: TextSpan) {
110    return textSpanOverlap(span, other) !== undefined;
111}
112
113export function textSpanOverlap(span1: TextSpan, span2: TextSpan): TextSpan | undefined {
114    const overlap = textSpanIntersection(span1, span2);
115    return overlap && overlap.length === 0 ? undefined : overlap;
116}
117
118export function textSpanIntersectsWithTextSpan(span: TextSpan, other: TextSpan) {
119    return decodedTextSpanIntersectsWith(span.start, span.length, other.start, other.length);
120}
121
122export function textSpanIntersectsWith(span: TextSpan, start: number, length: number) {
123    return decodedTextSpanIntersectsWith(span.start, span.length, start, length);
124}
125
126export function decodedTextSpanIntersectsWith(start1: number, length1: number, start2: number, length2: number) {
127    const end1 = start1 + length1;
128    const end2 = start2 + length2;
129    return start2 <= end1 && end2 >= start1;
130}
131
132export function textSpanIntersectsWithPosition(span: TextSpan, position: number) {
133    return position <= textSpanEnd(span) && position >= span.start;
134}
135
136export function textSpanIntersection(span1: TextSpan, span2: TextSpan): TextSpan | undefined {
137    const start = Math.max(span1.start, span2.start);
138    const end = Math.min(textSpanEnd(span1), textSpanEnd(span2));
139    return start <= end ? createTextSpanFromBounds(start, end) : undefined;
140}
141
142export function createTextSpan(start: number, length: number): TextSpan {
143    if (start < 0) {
144        throw new Error("start < 0");
145    }
146    if (length < 0) {
147        throw new Error("length < 0");
148    }
149
150    return { start, length };
151}
152
153export function createTextSpanFromBounds(start: number, end: number) {
154    return createTextSpan(start, end - start);
155}
156
157export function textChangeRangeNewSpan(range: TextChangeRange) {
158    return createTextSpan(range.span.start, range.newLength);
159}
160
161export function textChangeRangeIsUnchanged(range: TextChangeRange) {
162    return textSpanIsEmpty(range.span) && range.newLength === 0;
163}
164
165export function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange {
166    if (newLength < 0) {
167        throw new Error("newLength < 0");
168    }
169
170    return { span, newLength };
171}
172
173export let unchangedTextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0); // eslint-disable-line prefer-const
174
175/**
176 * Called to merge all the changes that occurred across several versions of a script snapshot
177 * into a single change.  i.e. if a user keeps making successive edits to a script we will
178 * have a text change from V1 to V2, V2 to V3, ..., Vn.
179 *
180 * This function will then merge those changes into a single change range valid between V1 and
181 * Vn.
182 */
183export function collapseTextChangeRangesAcrossMultipleVersions(changes: readonly TextChangeRange[]): TextChangeRange {
184    if (changes.length === 0) {
185        return unchangedTextChangeRange;
186    }
187
188    if (changes.length === 1) {
189        return changes[0];
190    }
191
192    // We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd }
193    // as it makes things much easier to reason about.
194    const change0 = changes[0];
195
196    let oldStartN = change0.span.start;
197    let oldEndN = textSpanEnd(change0.span);
198    let newEndN = oldStartN + change0.newLength;
199
200    for (let i = 1; i < changes.length; i++) {
201        const nextChange = changes[i];
202
203        // Consider the following case:
204        // i.e. two edits.  The first represents the text change range { { 10, 50 }, 30 }.  i.e. The span starting
205        // at 10, with length 50 is reduced to length 30.  The second represents the text change range { { 30, 30 }, 40 }.
206        // i.e. the span starting at 30 with length 30 is increased to length 40.
207        //
208        //      0         10        20        30        40        50        60        70        80        90        100
209        //      -------------------------------------------------------------------------------------------------------
210        //                |                                                 /
211        //                |                                            /----
212        //  T1            |                                       /----
213        //                |                                  /----
214        //                |                             /----
215        //      -------------------------------------------------------------------------------------------------------
216        //                                     |                            \
217        //                                     |                               \
218        //   T2                                |                                 \
219        //                                     |                                   \
220        //                                     |                                      \
221        //      -------------------------------------------------------------------------------------------------------
222        //
223        // Merging these turns out to not be too difficult.  First, determining the new start of the change is trivial
224        // it's just the min of the old and new starts.  i.e.:
225        //
226        //      0         10        20        30        40        50        60        70        80        90        100
227        //      ------------------------------------------------------------*------------------------------------------
228        //                |                                                 /
229        //                |                                            /----
230        //  T1            |                                       /----
231        //                |                                  /----
232        //                |                             /----
233        //      ----------------------------------------$-------------------$------------------------------------------
234        //                .                    |                            \
235        //                .                    |                               \
236        //   T2           .                    |                                 \
237        //                .                    |                                   \
238        //                .                    |                                      \
239        //      ----------------------------------------------------------------------*--------------------------------
240        //
241        // (Note the dots represent the newly inferred start.
242        // Determining the new and old end is also pretty simple.  Basically it boils down to paying attention to the
243        // absolute positions at the asterisks, and the relative change between the dollar signs. Basically, we see
244        // which if the two $'s precedes the other, and we move that one forward until they line up.  in this case that
245        // means:
246        //
247        //      0         10        20        30        40        50        60        70        80        90        100
248        //      --------------------------------------------------------------------------------*----------------------
249        //                |                                                                     /
250        //                |                                                                /----
251        //  T1            |                                                           /----
252        //                |                                                      /----
253        //                |                                                 /----
254        //      ------------------------------------------------------------$------------------------------------------
255        //                .                    |                            \
256        //                .                    |                               \
257        //   T2           .                    |                                 \
258        //                .                    |                                   \
259        //                .                    |                                      \
260        //      ----------------------------------------------------------------------*--------------------------------
261        //
262        // In other words (in this case), we're recognizing that the second edit happened after where the first edit
263        // ended with a delta of 20 characters (60 - 40).  Thus, if we go back in time to where the first edit started
264        // that's the same as if we started at char 80 instead of 60.
265        //
266        // As it so happens, the same logic applies if the second edit precedes the first edit.  In that case rather
267        // than pushing the first edit forward to match the second, we'll push the second edit forward to match the
268        // first.
269        //
270        // In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange
271        // semantics: { { start: 10, length: 70 }, newLength: 60 }
272        //
273        // The math then works out as follows.
274        // If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the
275        // final result like so:
276        //
277        // {
278        //      oldStart3: Min(oldStart1, oldStart2),
279        //      oldEnd3: Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)),
280        //      newEnd3: Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2))
281        // }
282
283        const oldStart1 = oldStartN;
284        const oldEnd1 = oldEndN;
285        const newEnd1 = newEndN;
286
287        const oldStart2 = nextChange.span.start;
288        const oldEnd2 = textSpanEnd(nextChange.span);
289        const newEnd2 = oldStart2 + nextChange.newLength;
290
291        oldStartN = Math.min(oldStart1, oldStart2);
292        oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1));
293        newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2));
294    }
295
296    return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength*/ newEndN - oldStartN);
297}
298
299export function getTypeParameterOwner(d: Declaration): Declaration | undefined {
300    if (d && d.kind === SyntaxKind.TypeParameter) {
301        for (let current: Node = d; current; current = current.parent) {
302            if (isFunctionLike(current) || isClassLike(current) || current.kind === SyntaxKind.InterfaceDeclaration) {
303                return current as Declaration;
304            }
305        }
306    }
307}
308
309export type ParameterPropertyDeclaration = ParameterDeclaration & { parent: ConstructorDeclaration, name: Identifier };
310export function isParameterPropertyDeclaration(node: Node, parent: Node): node is ParameterPropertyDeclaration {
311    return hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier) && parent.kind === SyntaxKind.Constructor;
312}
313
314export function isEmptyBindingPattern(node: BindingName): node is BindingPattern {
315    if (isBindingPattern(node)) {
316        return every(node.elements, isEmptyBindingElement);
317    }
318    return false;
319}
320
321export function isEmptyBindingElement(node: BindingElement): boolean {
322    if (isOmittedExpression(node)) {
323        return true;
324    }
325    return isEmptyBindingPattern(node.name);
326}
327
328export function walkUpBindingElementsAndPatterns(binding: BindingElement): VariableDeclaration | ParameterDeclaration {
329    let node = binding.parent;
330    while (isBindingElement(node.parent)) {
331        node = node.parent.parent;
332    }
333    return node.parent;
334}
335
336function getCombinedFlags(node: Node, getFlags: (n: Node) => number): number {
337    if (isBindingElement(node)) {
338        node = walkUpBindingElementsAndPatterns(node);
339    }
340    let flags = getFlags(node);
341    if (node.kind === SyntaxKind.VariableDeclaration) {
342        node = node.parent;
343    }
344    if (node && node.kind === SyntaxKind.VariableDeclarationList) {
345        flags |= getFlags(node);
346        node = node.parent;
347    }
348    if (node && node.kind === SyntaxKind.VariableStatement) {
349        flags |= getFlags(node);
350    }
351    return flags;
352}
353
354export function getCombinedModifierFlags(node: Declaration): ModifierFlags {
355    return getCombinedFlags(node, getEffectiveModifierFlags);
356}
357
358/** @internal */
359export function getCombinedNodeFlagsAlwaysIncludeJSDoc(node: Declaration): ModifierFlags {
360    return getCombinedFlags(node, getEffectiveModifierFlagsAlwaysIncludeJSDoc);
361}
362
363// Returns the node flags for this node and all relevant parent nodes.  This is done so that
364// nodes like variable declarations and binding elements can returned a view of their flags
365// that includes the modifiers from their container.  i.e. flags like export/declare aren't
366// stored on the variable declaration directly, but on the containing variable statement
367// (if it has one).  Similarly, flags for let/const are stored on the variable declaration
368// list.  By calling this function, all those flags are combined so that the client can treat
369// the node as if it actually had those flags.
370export function getCombinedNodeFlags(node: Node): NodeFlags {
371    return getCombinedFlags(node, n => n.flags);
372}
373
374/** @internal */
375export const supportedLocaleDirectories = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-br", "ru", "tr", "zh-cn", "zh-tw"];
376
377/**
378 * Checks to see if the locale is in the appropriate format,
379 * and if it is, attempts to set the appropriate language.
380 */
381export function validateLocaleAndSetLanguage(
382    locale: string,
383    sys: { getExecutingFilePath(): string, resolvePath(path: string): string, fileExists(fileName: string): boolean, readFile(fileName: string): string | undefined },
384    errors?: Push<Diagnostic>) {
385    const lowerCaseLocale = locale.toLowerCase();
386    const matchResult = /^([a-z]+)([_\-]([a-z]+))?$/.exec(lowerCaseLocale);
387
388    if (!matchResult) {
389        if (errors) {
390            errors.push(createCompilerDiagnostic(Diagnostics.Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1, "en", "ja-jp"));
391        }
392        return;
393    }
394
395    const language = matchResult[1];
396    const territory = matchResult[3];
397
398    // First try the entire locale, then fall back to just language if that's all we have.
399    // Either ways do not fail, and fallback to the English diagnostic strings.
400    if (contains(supportedLocaleDirectories, lowerCaseLocale) && !trySetLanguageAndTerritory(language, territory, errors)) {
401        trySetLanguageAndTerritory(language, /*territory*/ undefined, errors);
402    }
403
404    // Set the UI locale for string collation
405    setUILocale(locale);
406
407    function trySetLanguageAndTerritory(language: string, territory: string | undefined, errors?: Push<Diagnostic>): boolean {
408        const compilerFilePath = normalizePath(sys.getExecutingFilePath());
409        const containingDirectoryPath = getDirectoryPath(compilerFilePath);
410
411        let filePath = combinePaths(containingDirectoryPath, language);
412
413        if (territory) {
414            filePath = filePath + "-" + territory;
415        }
416
417        filePath = sys.resolvePath(combinePaths(filePath, "diagnosticMessages.generated.json"));
418
419        if (!sys.fileExists(filePath)) {
420            return false;
421        }
422
423        // TODO: Add codePage support for readFile?
424        let fileContents: string | undefined = "";
425        try {
426            fileContents = sys.readFile(filePath);
427        }
428        catch (e) {
429            if (errors) {
430                errors.push(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, filePath));
431            }
432            return false;
433        }
434        try {
435            // this is a global mutation (or live binding update)!
436            setLocalizedDiagnosticMessages(JSON.parse(fileContents!));
437        }
438        catch {
439            if (errors) {
440                errors.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath));
441            }
442            return false;
443        }
444
445        return true;
446    }
447}
448
449export function getOriginalNode(node: Node): Node;
450export function getOriginalNode<T extends Node>(node: Node, nodeTest: (node: Node) => node is T): T;
451export function getOriginalNode(node: Node | undefined): Node | undefined;
452export function getOriginalNode<T extends Node>(node: Node | undefined, nodeTest: (node: Node | undefined) => node is T): T | undefined;
453export function getOriginalNode(node: Node | undefined, nodeTest?: (node: Node | undefined) => boolean): Node | undefined {
454    if (node) {
455        while (node.original !== undefined) {
456            node = node.original;
457        }
458    }
459
460    return !nodeTest || nodeTest(node) ? node : undefined;
461}
462
463/**
464 * Iterates through the parent chain of a node and performs the callback on each parent until the callback
465 * returns a truthy value, then returns that value.
466 * If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns "quit"
467 * At that point findAncestor returns undefined.
468 */
469export function findAncestor<T extends Node>(node: Node | undefined, callback: (element: Node) => element is T): T | undefined;
470export function findAncestor(node: Node | undefined, callback: (element: Node) => boolean | "quit"): Node | undefined;
471export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node | undefined {
472    while (node) {
473        const result = callback(node);
474        if (result === "quit") {
475            return undefined;
476        }
477        else if (result) {
478            return node;
479        }
480        node = node.parent;
481    }
482    return undefined;
483}
484
485/**
486 * Gets a value indicating whether a node originated in the parse tree.
487 *
488 * @param node The node to test.
489 */
490export function isParseTreeNode(node: Node): boolean {
491    return (node.flags & NodeFlags.Synthesized) === 0;
492}
493
494/**
495 * Gets the original parse tree node for a node.
496 *
497 * @param node The original node.
498 * @returns The original parse tree node if found; otherwise, undefined.
499 */
500export function getParseTreeNode(node: Node | undefined): Node | undefined;
501
502/**
503 * Gets the original parse tree node for a node.
504 *
505 * @param node The original node.
506 * @param nodeTest A callback used to ensure the correct type of parse tree node is returned.
507 * @returns The original parse tree node if found; otherwise, undefined.
508 */
509export function getParseTreeNode<T extends Node>(node: T | undefined, nodeTest?: (node: Node) => node is T): T | undefined;
510export function getParseTreeNode(node: Node | undefined, nodeTest?: (node: Node) => boolean): Node | undefined {
511    if (node === undefined || isParseTreeNode(node)) {
512        return node;
513    }
514
515    node = node.original;
516    while (node) {
517        if (isParseTreeNode(node)) {
518            return !nodeTest || nodeTest(node) ? node : undefined;
519        }
520        node = node.original;
521    }
522}
523
524/** Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' */
525export function escapeLeadingUnderscores(identifier: string): __String {
526    return (identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier) as __String;
527}
528
529/**
530 * Remove extra underscore from escaped identifier text content.
531 *
532 * @param identifier The escaped identifier text.
533 * @returns The unescaped identifier text.
534 */
535export function unescapeLeadingUnderscores(identifier: __String): string {
536    const id = identifier as string;
537    return id.length >= 3 && id.charCodeAt(0) === CharacterCodes._ && id.charCodeAt(1) === CharacterCodes._ && id.charCodeAt(2) === CharacterCodes._ ? id.substr(1) : id;
538}
539
540export function idText(identifierOrPrivateName: Identifier | PrivateIdentifier): string {
541    return unescapeLeadingUnderscores(identifierOrPrivateName.escapedText);
542}
543export function symbolName(symbol: Symbol): string {
544    if (symbol.valueDeclaration && isPrivateIdentifierClassElementDeclaration(symbol.valueDeclaration)) {
545        return idText(symbol.valueDeclaration.name);
546    }
547    return unescapeLeadingUnderscores(symbol.escapedName);
548}
549
550/**
551 * A JSDocTypedef tag has an _optional_ name field - if a name is not directly present, we should
552 * attempt to draw the name from the node the declaration is on (as that declaration is what its' symbol
553 * will be merged with)
554 */
555function nameForNamelessJSDocTypedef(declaration: JSDocTypedefTag | JSDocEnumTag): Identifier | PrivateIdentifier | undefined {
556    const hostNode = declaration.parent.parent;
557    if (!hostNode) {
558        return undefined;
559    }
560    // Covers classes, functions - any named declaration host node
561    if (isDeclaration(hostNode)) {
562        return getDeclarationIdentifier(hostNode);
563    }
564    // Covers remaining cases (returning undefined if none match).
565    switch (hostNode.kind) {
566        case SyntaxKind.VariableStatement:
567            if (hostNode.declarationList && hostNode.declarationList.declarations[0]) {
568                return getDeclarationIdentifier(hostNode.declarationList.declarations[0]);
569            }
570            break;
571        case SyntaxKind.ExpressionStatement:
572            let expr = hostNode.expression;
573            if (expr.kind === SyntaxKind.BinaryExpression && (expr as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
574                expr = (expr as BinaryExpression).left;
575            }
576            switch (expr.kind) {
577                case SyntaxKind.PropertyAccessExpression:
578                    return (expr as PropertyAccessExpression).name;
579                case SyntaxKind.ElementAccessExpression:
580                    const arg = (expr as ElementAccessExpression).argumentExpression;
581                    if (isIdentifier(arg)) {
582                        return arg;
583                    }
584            }
585            break;
586        case SyntaxKind.ParenthesizedExpression: {
587            return getDeclarationIdentifier(hostNode.expression);
588        }
589        case SyntaxKind.LabeledStatement: {
590            if (isDeclaration(hostNode.statement) || isExpression(hostNode.statement)) {
591                return getDeclarationIdentifier(hostNode.statement);
592            }
593            break;
594        }
595    }
596}
597
598function getDeclarationIdentifier(node: Declaration | Expression): Identifier | undefined {
599    const name = getNameOfDeclaration(node);
600    return name && isIdentifier(name) ? name : undefined;
601}
602
603/** @internal */
604export function nodeHasName(statement: Node, name: Identifier) {
605    if (isNamedDeclaration(statement) && isIdentifier(statement.name) && idText(statement.name as Identifier) === idText(name)) {
606        return true;
607    }
608    if (isVariableStatement(statement) && some(statement.declarationList.declarations, d => nodeHasName(d, name))) {
609        return true;
610    }
611    return false;
612}
613
614export function getNameOfJSDocTypedef(declaration: JSDocTypedefTag): Identifier | PrivateIdentifier | undefined {
615    return declaration.name || nameForNamelessJSDocTypedef(declaration);
616}
617
618/** @internal */
619export function isNamedDeclaration(node: Node): node is NamedDeclaration & { name: DeclarationName } {
620    return !!(node as NamedDeclaration).name; // A 'name' property should always be a DeclarationName.
621}
622
623/** @internal */
624export function getNonAssignedNameOfDeclaration(declaration: Declaration | Expression): DeclarationName | undefined {
625    switch (declaration.kind) {
626        case SyntaxKind.Identifier:
627            return declaration as Identifier;
628        case SyntaxKind.JSDocPropertyTag:
629        case SyntaxKind.JSDocParameterTag: {
630            const { name } = declaration as JSDocPropertyLikeTag;
631            if (name.kind === SyntaxKind.QualifiedName) {
632                return name.right;
633            }
634            break;
635        }
636        case SyntaxKind.CallExpression:
637        case SyntaxKind.BinaryExpression: {
638            const expr = declaration as BinaryExpression | CallExpression;
639            switch (getAssignmentDeclarationKind(expr)) {
640                case AssignmentDeclarationKind.ExportsProperty:
641                case AssignmentDeclarationKind.ThisProperty:
642                case AssignmentDeclarationKind.Property:
643                case AssignmentDeclarationKind.PrototypeProperty:
644                    return getElementOrPropertyAccessArgumentExpressionOrName((expr as BinaryExpression).left as AccessExpression);
645                case AssignmentDeclarationKind.ObjectDefinePropertyValue:
646                case AssignmentDeclarationKind.ObjectDefinePropertyExports:
647                case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
648                    return (expr as BindableObjectDefinePropertyCall).arguments[1];
649                default:
650                    return undefined;
651            }
652        }
653        case SyntaxKind.JSDocTypedefTag:
654            return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
655        case SyntaxKind.JSDocEnumTag:
656            return nameForNamelessJSDocTypedef(declaration as JSDocEnumTag);
657        case SyntaxKind.ExportAssignment: {
658            const { expression } = declaration as ExportAssignment;
659            return isIdentifier(expression) ? expression : undefined;
660        }
661        case SyntaxKind.ElementAccessExpression:
662            const expr = declaration as ElementAccessExpression;
663            if (isBindableStaticElementAccessExpression(expr)) {
664                return expr.argumentExpression;
665            }
666    }
667    return (declaration as NamedDeclaration).name;
668}
669
670export function getNameOfDeclaration(declaration: Declaration | Expression | undefined): DeclarationName | undefined {
671    if (declaration === undefined) return undefined;
672    return getNonAssignedNameOfDeclaration(declaration) ||
673        (isFunctionExpression(declaration) || isArrowFunction(declaration) || isClassExpression(declaration) ? getAssignedName(declaration) : undefined);
674}
675
676/** @internal */
677export function getAssignedName(node: Node): DeclarationName | undefined {
678    if (!node.parent) {
679        return undefined;
680    }
681    else if (isPropertyAssignment(node.parent) || isBindingElement(node.parent)) {
682        return node.parent.name;
683    }
684    else if (isBinaryExpression(node.parent) && node === node.parent.right) {
685        if (isIdentifier(node.parent.left)) {
686            return node.parent.left;
687        }
688        else if (isAccessExpression(node.parent.left)) {
689            return getElementOrPropertyAccessArgumentExpressionOrName(node.parent.left);
690        }
691    }
692    else if (isVariableDeclaration(node.parent) && isIdentifier(node.parent.name)) {
693        return node.parent.name;
694    }
695}
696
697export function getDecorators(node: HasDecorators): readonly Decorator[] | undefined {
698    if (hasDecorators(node)) {
699        return filter(node.modifiers, isDecorator);
700    }
701    return undefined;
702}
703
704export function getAnnotations(node: HasDecorators): readonly Decorator[] | undefined {
705    if (hasAnnotations(node)) {
706        return filter(node.modifiers, isAnnotation);
707    }
708    return undefined;
709}
710
711export function getModifiers(node: HasModifiers): readonly Modifier[] | undefined {
712    if (hasSyntacticModifier(node, ModifierFlags.Modifier)) {
713        return filter(node.modifiers, isModifier);
714    }
715    return undefined;
716}
717
718export function getAllDecorators(node: Node | undefined): readonly Decorator[] {
719    const allDecorators: Decorator[] = [];
720    if (!node) {
721        return allDecorators;
722    }
723    const decorators = getDecorators(node as HasDecorators);
724    if (decorators) {
725        allDecorators.push(...decorators);
726    }
727    const illegalDecorators = getIllegalDecorators(node as HasIllegalDecorators);
728    if (illegalDecorators) {
729        allDecorators.push(...illegalDecorators);
730    }
731    return allDecorators;
732}
733
734export function getIllegalDecorators(node: HasIllegalDecorators): readonly Decorator[] | undefined {
735    if (hasIllegalDecorators(node)) {
736        return node.illegalDecorators;
737    }
738}
739
740function getJSDocParameterTagsWorker(param: ParameterDeclaration, noCache?: boolean): readonly JSDocParameterTag[] {
741    if (param.name) {
742        if (isIdentifier(param.name)) {
743            const name = param.name.escapedText;
744            return getJSDocTagsWorker(param.parent, noCache).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name);
745        }
746        else {
747            const i = param.parent.parameters.indexOf(param);
748            Debug.assert(i > -1, "Parameters should always be in their parents' parameter list");
749            const paramTags = getJSDocTagsWorker(param.parent, noCache).filter(isJSDocParameterTag);
750            if (i < paramTags.length) {
751                return [paramTags[i]];
752            }
753        }
754    }
755    // return empty array for: out-of-order binding patterns and JSDoc function syntax, which has un-named parameters
756    return emptyArray;
757}
758
759/**
760 * Gets the JSDoc parameter tags for the node if present.
761 *
762 * @remarks Returns any JSDoc param tag whose name matches the provided
763 * parameter, whether a param tag on a containing function
764 * expression, or a param tag on a variable declaration whose
765 * initializer is the containing function. The tags closest to the
766 * node are returned first, so in the previous example, the param
767 * tag on the containing function expression would be first.
768 *
769 * For binding patterns, parameter tags are matched by position.
770 */
771export function getJSDocParameterTags(param: ParameterDeclaration): readonly JSDocParameterTag[] {
772    return getJSDocParameterTagsWorker(param, /*noCache*/ false);
773}
774
775/** @internal */
776export function getJSDocParameterTagsNoCache(param: ParameterDeclaration): readonly JSDocParameterTag[] {
777    return getJSDocParameterTagsWorker(param, /*noCache*/ true);
778}
779
780function getJSDocTypeParameterTagsWorker(param: TypeParameterDeclaration, noCache?: boolean): readonly JSDocTemplateTag[] {
781    const name = param.name.escapedText;
782    return getJSDocTagsWorker(param.parent, noCache).filter((tag): tag is JSDocTemplateTag =>
783        isJSDocTemplateTag(tag) && tag.typeParameters.some(tp => tp.name.escapedText === name));
784}
785
786/**
787 * Gets the JSDoc type parameter tags for the node if present.
788 *
789 * @remarks Returns any JSDoc template tag whose names match the provided
790 * parameter, whether a template tag on a containing function
791 * expression, or a template tag on a variable declaration whose
792 * initializer is the containing function. The tags closest to the
793 * node are returned first, so in the previous example, the template
794 * tag on the containing function expression would be first.
795 */
796export function getJSDocTypeParameterTags(param: TypeParameterDeclaration): readonly JSDocTemplateTag[] {
797    return getJSDocTypeParameterTagsWorker(param, /*noCache*/ false);
798}
799
800/** @internal */
801export function getJSDocTypeParameterTagsNoCache(param: TypeParameterDeclaration): readonly JSDocTemplateTag[] {
802    return getJSDocTypeParameterTagsWorker(param, /*noCache*/ true);
803}
804
805/**
806 * Return true if the node has JSDoc parameter tags.
807 *
808 * @remarks Includes parameter tags that are not directly on the node,
809 * for example on a variable declaration whose initializer is a function expression.
810 */
811export function hasJSDocParameterTags(node: FunctionLikeDeclaration | SignatureDeclaration): boolean {
812    return !!getFirstJSDocTag(node, isJSDocParameterTag);
813}
814
815/** Gets the JSDoc augments tag for the node if present */
816export function getJSDocAugmentsTag(node: Node): JSDocAugmentsTag | undefined {
817    return getFirstJSDocTag(node, isJSDocAugmentsTag);
818}
819
820/** Gets the JSDoc implements tags for the node if present */
821export function getJSDocImplementsTags(node: Node): readonly JSDocImplementsTag[] {
822    return getAllJSDocTags(node, isJSDocImplementsTag);
823}
824
825/** Gets the JSDoc class tag for the node if present */
826export function getJSDocClassTag(node: Node): JSDocClassTag | undefined {
827    return getFirstJSDocTag(node, isJSDocClassTag);
828}
829
830/** Gets the JSDoc public tag for the node if present */
831export function getJSDocPublicTag(node: Node): JSDocPublicTag | undefined {
832    return getFirstJSDocTag(node, isJSDocPublicTag);
833}
834
835/** @internal */
836export function getJSDocPublicTagNoCache(node: Node): JSDocPublicTag | undefined {
837    return getFirstJSDocTag(node, isJSDocPublicTag, /*noCache*/ true);
838}
839
840/** Gets the JSDoc private tag for the node if present */
841export function getJSDocPrivateTag(node: Node): JSDocPrivateTag | undefined {
842    return getFirstJSDocTag(node, isJSDocPrivateTag);
843}
844
845/** @internal */
846export function getJSDocPrivateTagNoCache(node: Node): JSDocPrivateTag | undefined {
847    return getFirstJSDocTag(node, isJSDocPrivateTag, /*noCache*/ true);
848}
849
850/** Gets the JSDoc protected tag for the node if present */
851export function getJSDocProtectedTag(node: Node): JSDocProtectedTag | undefined {
852    return getFirstJSDocTag(node, isJSDocProtectedTag);
853}
854
855/** @internal */
856export function getJSDocProtectedTagNoCache(node: Node): JSDocProtectedTag | undefined {
857    return getFirstJSDocTag(node, isJSDocProtectedTag, /*noCache*/ true);
858}
859
860/** Gets the JSDoc protected tag for the node if present */
861export function getJSDocReadonlyTag(node: Node): JSDocReadonlyTag | undefined {
862    return getFirstJSDocTag(node, isJSDocReadonlyTag);
863}
864
865/** @internal */
866export function getJSDocReadonlyTagNoCache(node: Node): JSDocReadonlyTag | undefined {
867    return getFirstJSDocTag(node, isJSDocReadonlyTag, /*noCache*/ true);
868}
869
870export function getJSDocOverrideTagNoCache(node: Node): JSDocOverrideTag | undefined {
871    return getFirstJSDocTag(node, isJSDocOverrideTag, /*noCache*/ true);
872}
873
874/** Gets the JSDoc deprecated tag for the node if present */
875export function getJSDocDeprecatedTag(node: Node): JSDocDeprecatedTag | undefined {
876    return getFirstJSDocTag(node, isJSDocDeprecatedTag);
877}
878
879/** @internal */
880export function getJSDocDeprecatedTagNoCache(node: Node): JSDocDeprecatedTag | undefined {
881    return getFirstJSDocTag(node, isJSDocDeprecatedTag, /*noCache*/ true);
882}
883
884/** Gets the JSDoc enum tag for the node if present */
885export function getJSDocEnumTag(node: Node): JSDocEnumTag | undefined {
886    return getFirstJSDocTag(node, isJSDocEnumTag);
887}
888
889/** Gets the JSDoc this tag for the node if present */
890export function getJSDocThisTag(node: Node): JSDocThisTag | undefined {
891    return getFirstJSDocTag(node, isJSDocThisTag);
892}
893
894/** Gets the JSDoc return tag for the node if present */
895export function getJSDocReturnTag(node: Node): JSDocReturnTag | undefined {
896    return getFirstJSDocTag(node, isJSDocReturnTag);
897}
898
899/** Gets the JSDoc template tag for the node if present */
900export function getJSDocTemplateTag(node: Node): JSDocTemplateTag | undefined {
901    return getFirstJSDocTag(node, isJSDocTemplateTag);
902}
903
904/** Gets the JSDoc type tag for the node if present and valid */
905export function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined {
906    // We should have already issued an error if there were multiple type jsdocs, so just use the first one.
907    const tag = getFirstJSDocTag(node, isJSDocTypeTag);
908    if (tag && tag.typeExpression && tag.typeExpression.type) {
909        return tag;
910    }
911    return undefined;
912}
913
914/**
915 * Gets the type node for the node if provided via JSDoc.
916 *
917 * @remarks The search includes any JSDoc param tag that relates
918 * to the provided parameter, for example a type tag on the
919 * parameter itself, or a param tag on a containing function
920 * expression, or a param tag on a variable declaration whose
921 * initializer is the containing function. The tags closest to the
922 * node are examined first, so in the previous example, the type
923 * tag directly on the node would be returned.
924 */
925export function getJSDocType(node: Node): TypeNode | undefined {
926    let tag: JSDocTypeTag | JSDocParameterTag | undefined = getFirstJSDocTag(node, isJSDocTypeTag);
927    if (!tag && isParameter(node)) {
928        tag = find(getJSDocParameterTags(node), tag => !!tag.typeExpression);
929    }
930
931    return tag && tag.typeExpression && tag.typeExpression.type;
932}
933
934/**
935 * Gets the return type node for the node if provided via JSDoc return tag or type tag.
936 *
937 * @remarks `getJSDocReturnTag` just gets the whole JSDoc tag. This function
938 * gets the type from inside the braces, after the fat arrow, etc.
939 */
940export function getJSDocReturnType(node: Node): TypeNode | undefined {
941    const returnTag = getJSDocReturnTag(node);
942    if (returnTag && returnTag.typeExpression) {
943        return returnTag.typeExpression.type;
944    }
945    const typeTag = getJSDocTypeTag(node);
946    if (typeTag && typeTag.typeExpression) {
947        const type = typeTag.typeExpression.type;
948        if (isTypeLiteralNode(type)) {
949            const sig = find(type.members, isCallSignatureDeclaration);
950            return sig && sig.type;
951        }
952        if (isFunctionTypeNode(type) || isJSDocFunctionType(type)) {
953            return type.type;
954        }
955    }
956}
957
958function getJSDocTagsWorker(node: Node, noCache?: boolean): readonly JSDocTag[] {
959    let tags = (node as JSDocContainer).jsDocCache;
960    // If cache is 'null', that means we did the work of searching for JSDoc tags and came up with nothing.
961    if (tags === undefined || noCache) {
962        const comments = getJSDocCommentsAndTags(node, noCache);
963        Debug.assert(comments.length < 2 || comments[0] !== comments[1]);
964        tags = flatMap(comments, j => isJSDoc(j) ? j.tags : j);
965        if (!noCache) {
966            (node as JSDocContainer).jsDocCache = tags;
967        }
968    }
969    return tags;
970}
971
972/** Get all JSDoc tags related to a node, including those on parent nodes. */
973export function getJSDocTags(node: Node): readonly JSDocTag[] {
974    return getJSDocTagsWorker(node, /*noCache*/ false);
975}
976
977/** @internal */
978export function getJSDocTagsNoCache(node: Node): readonly JSDocTag[] {
979    return getJSDocTagsWorker(node, /*noCache*/ true);
980}
981
982/** Get the first JSDoc tag of a specified kind, or undefined if not present. */
983function getFirstJSDocTag<T extends JSDocTag>(node: Node, predicate: (tag: JSDocTag) => tag is T, noCache?: boolean): T | undefined {
984    return find(getJSDocTagsWorker(node, noCache), predicate);
985}
986
987/** Gets all JSDoc tags that match a specified predicate */
988export function getAllJSDocTags<T extends JSDocTag>(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[] {
989    return getJSDocTags(node).filter(predicate);
990}
991
992/** Gets all JSDoc tags of a specified kind */
993export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[] {
994    return getJSDocTags(node).filter(doc => doc.kind === kind);
995}
996
997/** Gets the text of a jsdoc comment, flattening links to their text. */
998export function getTextOfJSDocComment(comment?: string | NodeArray<JSDocComment>) {
999    return typeof comment === "string" ? comment
1000        : comment?.map(c => c.kind === SyntaxKind.JSDocText ? c.text : formatJSDocLink(c)).join("");
1001}
1002
1003function formatJSDocLink(link: JSDocLink | JSDocLinkCode | JSDocLinkPlain) {
1004    const kind = link.kind === SyntaxKind.JSDocLink ? "link"
1005        : link.kind === SyntaxKind.JSDocLinkCode ? "linkcode"
1006        : "linkplain";
1007    const name = link.name ? entityNameToString(link.name) : "";
1008    const space = link.name && link.text.startsWith("://") ? "" : " ";
1009    return `{@${kind} ${name}${space}${link.text}}`;
1010}
1011
1012/**
1013 * Gets the effective type parameters. If the node was parsed in a
1014 * JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
1015 *
1016 * This does *not* return type parameters from a jsdoc reference to a generic type, eg
1017 *
1018 * type Id = <T>(x: T) => T
1019 * /** @type {Id} /
1020 * function id(x) { return x }
1021 */
1022export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): readonly TypeParameterDeclaration[] {
1023    if (isJSDocSignature(node)) {
1024        return emptyArray;
1025    }
1026    if (isJSDocTypeAlias(node)) {
1027        Debug.assert(node.parent.kind === SyntaxKind.JSDoc);
1028        return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined);
1029    }
1030    if (node.typeParameters) {
1031        return node.typeParameters;
1032    }
1033    if (canHaveIllegalTypeParameters(node) && node.typeParameters) {
1034        return node.typeParameters;
1035    }
1036    if (isInJSFile(node)) {
1037        const decls = getJSDocTypeParameterDeclarations(node);
1038        if (decls.length) {
1039            return decls;
1040        }
1041        const typeTag = getJSDocType(node);
1042        if (typeTag && isFunctionTypeNode(typeTag) && typeTag.typeParameters) {
1043            return typeTag.typeParameters;
1044        }
1045    }
1046    return emptyArray;
1047}
1048
1049export function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined {
1050    return node.constraint ? node.constraint :
1051        isJSDocTemplateTag(node.parent) && node === node.parent.typeParameters[0] ? node.parent.constraint :
1052        undefined;
1053}
1054
1055// #region
1056
1057export function isMemberName(node: Node): node is MemberName {
1058    return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier;
1059}
1060
1061/** @internal */
1062export function isGetOrSetAccessorDeclaration(node: Node): node is AccessorDeclaration {
1063    return node.kind === SyntaxKind.SetAccessor || node.kind === SyntaxKind.GetAccessor;
1064}
1065
1066export function isPropertyAccessChain(node: Node): node is PropertyAccessChain {
1067    return isPropertyAccessExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
1068}
1069
1070export function isElementAccessChain(node: Node): node is ElementAccessChain {
1071    return isElementAccessExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
1072}
1073
1074export function isCallChain(node: Node): node is CallChain {
1075    return isCallExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
1076}
1077
1078export function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain | NonNullChain {
1079    const kind = node.kind;
1080    return !!(node.flags & NodeFlags.OptionalChain) &&
1081        (kind === SyntaxKind.PropertyAccessExpression
1082            || kind === SyntaxKind.ElementAccessExpression
1083            || kind === SyntaxKind.CallExpression
1084            || kind === SyntaxKind.NonNullExpression);
1085}
1086
1087/** @internal */
1088export function isOptionalChainRoot(node: Node): node is OptionalChainRoot {
1089    return isOptionalChain(node) && !isNonNullExpression(node) && !!node.questionDotToken;
1090}
1091
1092/**
1093 * Determines whether a node is the expression preceding an optional chain (i.e. `a` in `a?.b`).
1094 *
1095 * @internal
1096 */
1097export function isExpressionOfOptionalChainRoot(node: Node): node is Expression & { parent: OptionalChainRoot } {
1098    return isOptionalChainRoot(node.parent) && node.parent.expression === node;
1099}
1100
1101/**
1102 * Determines whether a node is the outermost `OptionalChain` in an ECMAScript `OptionalExpression`:
1103 *
1104 * 1. For `a?.b.c`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.`)
1105 * 2. For `a?.b!`, the outermost chain is `a?.b` (`b` is the end of the chain starting at `a?.`)
1106 * 3. For `(a?.b.c).d`, the outermost chain is `a?.b.c` (`c` is the end of the chain starting at `a?.` since parens end the chain)
1107 * 4. For `a?.b.c?.d`, both `a?.b.c` and `a?.b.c?.d` are outermost (`c` is the end of the chain starting at `a?.`, and `d` is
1108 *   the end of the chain starting at `c?.`)
1109 * 5. For `a?.(b?.c).d`, both `b?.c` and `a?.(b?.c)d` are outermost (`c` is the end of the chain starting at `b`, and `d` is
1110 *   the end of the chain starting at `a?.`)
1111 *
1112 * @internal
1113 */
1114export function isOutermostOptionalChain(node: OptionalChain) {
1115    return !isOptionalChain(node.parent) // cases 1, 2, and 3
1116        || isOptionalChainRoot(node.parent) // case 4
1117        || node !== node.parent.expression; // case 5
1118}
1119
1120export function isNullishCoalesce(node: Node) {
1121    return node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.QuestionQuestionToken;
1122}
1123
1124export function isConstTypeReference(node: Node) {
1125    return isTypeReferenceNode(node) && isIdentifier(node.typeName) &&
1126        node.typeName.escapedText === "const" && !node.typeArguments;
1127}
1128
1129export function skipPartiallyEmittedExpressions(node: Expression): Expression;
1130export function skipPartiallyEmittedExpressions(node: Node): Node;
1131export function skipPartiallyEmittedExpressions(node: Node) {
1132    return skipOuterExpressions(node, OuterExpressionKinds.PartiallyEmittedExpressions);
1133}
1134
1135export function isNonNullChain(node: Node): node is NonNullChain {
1136    return isNonNullExpression(node) && !!(node.flags & NodeFlags.OptionalChain);
1137}
1138
1139export function isBreakOrContinueStatement(node: Node): node is BreakOrContinueStatement {
1140    return node.kind === SyntaxKind.BreakStatement || node.kind === SyntaxKind.ContinueStatement;
1141}
1142
1143export function isNamedExportBindings(node: Node): node is NamedExportBindings {
1144    return node.kind === SyntaxKind.NamespaceExport || node.kind === SyntaxKind.NamedExports;
1145}
1146
1147export function isUnparsedTextLike(node: Node): node is UnparsedTextLike {
1148    switch (node.kind) {
1149        case SyntaxKind.UnparsedText:
1150        case SyntaxKind.UnparsedInternalText:
1151            return true;
1152        default:
1153            return false;
1154    }
1155}
1156
1157export function isUnparsedNode(node: Node): node is UnparsedNode {
1158    return isUnparsedTextLike(node) ||
1159        node.kind === SyntaxKind.UnparsedPrologue ||
1160        node.kind === SyntaxKind.UnparsedSyntheticReference;
1161}
1162
1163export function isJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag {
1164    return node.kind === SyntaxKind.JSDocPropertyTag || node.kind === SyntaxKind.JSDocParameterTag;
1165}
1166
1167// #endregion
1168
1169// #region
1170// Node tests
1171//
1172// All node tests in the following list should *not* reference parent pointers so that
1173// they may be used with transformations.
1174/** @internal */
1175export function isNode(node: Node) {
1176    return isNodeKind(node.kind);
1177}
1178
1179/** @internal */
1180export function isNodeKind(kind: SyntaxKind) {
1181    return kind >= SyntaxKind.FirstNode;
1182}
1183
1184/**
1185 * True if kind is of some token syntax kind.
1186 * For example, this is true for an IfKeyword but not for an IfStatement.
1187 * Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail.
1188 */
1189export function isTokenKind(kind: SyntaxKind): boolean {
1190    return kind >= SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken;
1191}
1192
1193/**
1194 * True if node is of some token syntax kind.
1195 * For example, this is true for an IfKeyword but not for an IfStatement.
1196 * Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail.
1197 */
1198export function isToken(n: Node): boolean {
1199    return isTokenKind(n.kind);
1200}
1201
1202// Node Arrays
1203
1204/** @internal */
1205export function isNodeArray<T extends Node>(array: readonly T[]): array is NodeArray<T> {
1206    return hasProperty(array, "pos") && hasProperty(array, "end");
1207}
1208
1209// Literals
1210
1211/** @internal */
1212export function isLiteralKind(kind: SyntaxKind): kind is LiteralToken["kind"] {
1213    return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken;
1214}
1215
1216export function isLiteralExpression(node: Node): node is LiteralExpression {
1217    return isLiteralKind(node.kind);
1218}
1219
1220/** @internal */
1221export function isLiteralExpressionOfObject(node: Node) {
1222    switch (node.kind) {
1223        case SyntaxKind.ObjectLiteralExpression:
1224        case SyntaxKind.ArrayLiteralExpression:
1225        case SyntaxKind.RegularExpressionLiteral:
1226        case SyntaxKind.FunctionExpression:
1227        case SyntaxKind.ClassExpression:
1228            return true;
1229    }
1230    return false;
1231}
1232
1233// Pseudo-literals
1234
1235/** @internal */
1236export function isTemplateLiteralKind(kind: SyntaxKind): kind is TemplateLiteralToken["kind"] {
1237    return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken;
1238}
1239
1240export function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken {
1241    return isTemplateLiteralKind(node.kind);
1242}
1243
1244export function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail {
1245    const kind = node.kind;
1246    return kind === SyntaxKind.TemplateMiddle
1247        || kind === SyntaxKind.TemplateTail;
1248}
1249
1250export function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier {
1251    return isImportSpecifier(node) || isExportSpecifier(node);
1252}
1253
1254export function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyAliasDeclaration {
1255    switch (node.kind) {
1256        case SyntaxKind.ImportSpecifier:
1257        case SyntaxKind.ExportSpecifier:
1258            return (node as ImportOrExportSpecifier).isTypeOnly || (node as ImportOrExportSpecifier).parent.parent.isTypeOnly;
1259        case SyntaxKind.NamespaceImport:
1260            return (node as NamespaceImport).parent.isTypeOnly;
1261        case SyntaxKind.ImportClause:
1262        case SyntaxKind.ImportEqualsDeclaration:
1263            return (node as ImportClause | ImportEqualsDeclaration).isTypeOnly;
1264        default:
1265            return false;
1266    }
1267}
1268
1269export function isAssertionKey(node: Node): node is AssertionKey {
1270    return isStringLiteral(node) || isIdentifier(node);
1271}
1272
1273export function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken {
1274    return node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind);
1275}
1276
1277// Identifiers
1278
1279/** @internal */
1280export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier {
1281    return isIdentifier(node) && (node.autoGenerateFlags! & GeneratedIdentifierFlags.KindMask) > GeneratedIdentifierFlags.None;
1282}
1283
1284/** @internal */
1285export function isGeneratedPrivateIdentifier(node: Node): node is GeneratedPrivateIdentifier {
1286    return isPrivateIdentifier(node) && (node.autoGenerateFlags! & GeneratedIdentifierFlags.KindMask) > GeneratedIdentifierFlags.None;
1287}
1288
1289// Private Identifiers
1290/** @internal */
1291export function isPrivateIdentifierClassElementDeclaration(node: Node): node is PrivateClassElementDeclaration {
1292    return (isPropertyDeclaration(node) || isMethodOrAccessor(node)) && isPrivateIdentifier(node.name);
1293}
1294
1295/** @internal */
1296export function isPrivateIdentifierPropertyAccessExpression(node: Node): node is PrivateIdentifierPropertyAccessExpression {
1297    return isPropertyAccessExpression(node) && isPrivateIdentifier(node.name);
1298}
1299
1300// Keywords
1301
1302/** @internal */
1303export function isModifierKind(token: SyntaxKind): token is Modifier["kind"] {
1304    switch (token) {
1305        case SyntaxKind.AbstractKeyword:
1306        case SyntaxKind.AccessorKeyword:
1307        case SyntaxKind.AsyncKeyword:
1308        case SyntaxKind.ConstKeyword:
1309        case SyntaxKind.DeclareKeyword:
1310        case SyntaxKind.DefaultKeyword:
1311        case SyntaxKind.ExportKeyword:
1312        case SyntaxKind.InKeyword:
1313        case SyntaxKind.PublicKeyword:
1314        case SyntaxKind.PrivateKeyword:
1315        case SyntaxKind.ProtectedKeyword:
1316        case SyntaxKind.ReadonlyKeyword:
1317        case SyntaxKind.StaticKeyword:
1318        case SyntaxKind.OutKeyword:
1319        case SyntaxKind.OverrideKeyword:
1320            return true;
1321    }
1322    return false;
1323}
1324
1325/** @internal */
1326export function isParameterPropertyModifier(kind: SyntaxKind): boolean {
1327    return !!(modifierToFlag(kind) & ModifierFlags.ParameterPropertyModifier);
1328}
1329
1330/** @internal */
1331export function isClassMemberModifier(idToken: SyntaxKind): boolean {
1332    return isParameterPropertyModifier(idToken) ||
1333        idToken === SyntaxKind.StaticKeyword ||
1334        idToken === SyntaxKind.OverrideKeyword ||
1335        idToken === SyntaxKind.AccessorKeyword;
1336}
1337
1338export function isModifier(node: Node): node is Modifier {
1339    return isModifierKind(node.kind);
1340}
1341
1342export function isEntityName(node: Node): node is EntityName {
1343    const kind = node.kind;
1344    return kind === SyntaxKind.QualifiedName
1345        || kind === SyntaxKind.Identifier;
1346}
1347
1348export function isPropertyName(node: Node): node is PropertyName {
1349    const kind = node.kind;
1350    return kind === SyntaxKind.Identifier
1351        || kind === SyntaxKind.PrivateIdentifier
1352        || kind === SyntaxKind.StringLiteral
1353        || kind === SyntaxKind.NumericLiteral
1354        || kind === SyntaxKind.ComputedPropertyName;
1355}
1356
1357export function isBindingName(node: Node): node is BindingName {
1358    const kind = node.kind;
1359    return kind === SyntaxKind.Identifier
1360        || kind === SyntaxKind.ObjectBindingPattern
1361        || kind === SyntaxKind.ArrayBindingPattern;
1362}
1363
1364// Functions
1365
1366export function isFunctionLike(node: Node | undefined): node is SignatureDeclaration {
1367    return !!node && isFunctionLikeKind(node.kind);
1368}
1369
1370/** @internal */
1371export function isFunctionLikeOrClassStaticBlockDeclaration(node: Node | undefined): node is SignatureDeclaration | ClassStaticBlockDeclaration {
1372    return !!node && (isFunctionLikeKind(node.kind) || isClassStaticBlockDeclaration(node));
1373}
1374
1375/** @internal */
1376export function isFunctionLikeDeclaration(node: Node): node is FunctionLikeDeclaration {
1377    return node && isFunctionLikeDeclarationKind(node.kind);
1378}
1379
1380/** @internal */
1381export function isBooleanLiteral(node: Node): node is BooleanLiteral {
1382    return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword;
1383}
1384
1385function isFunctionLikeDeclarationKind(kind: SyntaxKind): boolean {
1386    switch (kind) {
1387        case SyntaxKind.FunctionDeclaration:
1388        case SyntaxKind.MethodDeclaration:
1389        case SyntaxKind.Constructor:
1390        case SyntaxKind.GetAccessor:
1391        case SyntaxKind.SetAccessor:
1392        case SyntaxKind.FunctionExpression:
1393        case SyntaxKind.ArrowFunction:
1394            return true;
1395        default:
1396            return false;
1397    }
1398}
1399
1400/** @internal */
1401export function isFunctionLikeKind(kind: SyntaxKind): boolean {
1402    switch (kind) {
1403        case SyntaxKind.MethodSignature:
1404        case SyntaxKind.CallSignature:
1405        case SyntaxKind.JSDocSignature:
1406        case SyntaxKind.ConstructSignature:
1407        case SyntaxKind.IndexSignature:
1408        case SyntaxKind.FunctionType:
1409        case SyntaxKind.JSDocFunctionType:
1410        case SyntaxKind.ConstructorType:
1411            return true;
1412        default:
1413            return isFunctionLikeDeclarationKind(kind);
1414    }
1415}
1416
1417/** @internal */
1418export function isFunctionOrModuleBlock(node: Node): boolean {
1419    return isSourceFile(node) || isModuleBlock(node) || isBlock(node) && isFunctionLike(node.parent);
1420}
1421
1422export function isAnnotationElement(node: Node): node is AnnotationElement {
1423    const kind = node.kind;
1424    return kind === SyntaxKind.AnnotationPropertyDeclaration;
1425}
1426
1427// Classes
1428export function isClassElement(node: Node): node is ClassElement {
1429    const kind = node.kind;
1430    return kind === SyntaxKind.Constructor
1431        || kind === SyntaxKind.PropertyDeclaration
1432        || kind === SyntaxKind.MethodDeclaration
1433        || kind === SyntaxKind.GetAccessor
1434        || kind === SyntaxKind.SetAccessor
1435        || kind === SyntaxKind.IndexSignature
1436        || kind === SyntaxKind.ClassStaticBlockDeclaration
1437        || kind === SyntaxKind.SemicolonClassElement;
1438}
1439
1440export function isClassLike(node: Node): node is ClassLikeDeclaration {
1441    return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.StructDeclaration);
1442}
1443
1444export function isStruct(node: Node): node is StructDeclaration {
1445    return node && node.kind === SyntaxKind.StructDeclaration;
1446}
1447
1448export function isAccessor(node: Node): node is AccessorDeclaration {
1449    return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor);
1450}
1451
1452export function isAutoAccessorPropertyDeclaration(node: Node): node is AutoAccessorPropertyDeclaration {
1453    return isPropertyDeclaration(node) && hasAccessorModifier(node);
1454}
1455
1456/** @internal */
1457export function isMethodOrAccessor(node: Node): node is MethodDeclaration | AccessorDeclaration {
1458    switch (node.kind) {
1459        case SyntaxKind.MethodDeclaration:
1460        case SyntaxKind.GetAccessor:
1461        case SyntaxKind.SetAccessor:
1462            return true;
1463        default:
1464            return false;
1465    }
1466}
1467
1468/** @internal */
1469export function isNamedClassElement(node: Node): node is MethodDeclaration | AccessorDeclaration | PropertyDeclaration {
1470    switch (node.kind) {
1471        case SyntaxKind.MethodDeclaration:
1472        case SyntaxKind.GetAccessor:
1473        case SyntaxKind.SetAccessor:
1474        case SyntaxKind.PropertyDeclaration:
1475            return true;
1476        default:
1477            return false;
1478    }
1479}
1480
1481// Type members
1482
1483export function isModifierLike(node: Node): node is ModifierLike {
1484    return isModifier(node) || isDecoratorOrAnnotation(node);
1485}
1486
1487export function isTypeElement(node: Node): node is TypeElement {
1488    const kind = node.kind;
1489    return kind === SyntaxKind.ConstructSignature
1490        || kind === SyntaxKind.CallSignature
1491        || kind === SyntaxKind.PropertySignature
1492        || kind === SyntaxKind.MethodSignature
1493        || kind === SyntaxKind.IndexSignature
1494        || kind === SyntaxKind.GetAccessor
1495        || kind === SyntaxKind.SetAccessor;
1496}
1497
1498export function isClassOrTypeElement(node: Node): node is ClassElement | TypeElement {
1499    return isTypeElement(node) || isClassElement(node);
1500}
1501
1502export function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike {
1503    const kind = node.kind;
1504    return kind === SyntaxKind.PropertyAssignment
1505        || kind === SyntaxKind.ShorthandPropertyAssignment
1506        || kind === SyntaxKind.SpreadAssignment
1507        || kind === SyntaxKind.MethodDeclaration
1508        || kind === SyntaxKind.GetAccessor
1509        || kind === SyntaxKind.SetAccessor;
1510}
1511
1512// Type
1513
1514/**
1515 * Node test that determines whether a node is a valid type node.
1516 * This differs from the `isPartOfTypeNode` function which determines whether a node is *part*
1517 * of a TypeNode.
1518 */
1519export function isTypeNode(node: Node): node is TypeNode {
1520    return isTypeNodeKind(node.kind);
1521}
1522
1523export function isFunctionOrConstructorTypeNode(node: Node): node is FunctionTypeNode | ConstructorTypeNode {
1524    switch (node.kind) {
1525        case SyntaxKind.FunctionType:
1526        case SyntaxKind.ConstructorType:
1527            return true;
1528    }
1529
1530    return false;
1531}
1532
1533// Binding patterns
1534
1535/** @internal */
1536export function isBindingPattern(node: Node | undefined): node is BindingPattern {
1537    if (node) {
1538        const kind = node.kind;
1539        return kind === SyntaxKind.ArrayBindingPattern
1540            || kind === SyntaxKind.ObjectBindingPattern;
1541    }
1542
1543    return false;
1544}
1545
1546/** @internal */
1547export function isAssignmentPattern(node: Node): node is AssignmentPattern {
1548    const kind = node.kind;
1549    return kind === SyntaxKind.ArrayLiteralExpression
1550        || kind === SyntaxKind.ObjectLiteralExpression;
1551}
1552
1553
1554/** @internal */
1555export function isArrayBindingElement(node: Node): node is ArrayBindingElement {
1556    const kind = node.kind;
1557    return kind === SyntaxKind.BindingElement
1558        || kind === SyntaxKind.OmittedExpression;
1559}
1560
1561
1562/**
1563 * Determines whether the BindingOrAssignmentElement is a BindingElement-like declaration
1564 *
1565 * @internal
1566 */
1567export function isDeclarationBindingElement(bindingElement: BindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement {
1568    switch (bindingElement.kind) {
1569        case SyntaxKind.VariableDeclaration:
1570        case SyntaxKind.Parameter:
1571        case SyntaxKind.BindingElement:
1572            return true;
1573    }
1574
1575    return false;
1576}
1577
1578/**
1579 * Determines whether a node is a BindingOrAssignmentPattern
1580 *
1581 * @internal
1582 */
1583export function isBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is BindingOrAssignmentPattern {
1584    return isObjectBindingOrAssignmentPattern(node)
1585        || isArrayBindingOrAssignmentPattern(node);
1586}
1587
1588/**
1589 * Determines whether a node is an ObjectBindingOrAssignmentPattern
1590 *
1591 * @internal
1592 */
1593export function isObjectBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ObjectBindingOrAssignmentPattern {
1594    switch (node.kind) {
1595        case SyntaxKind.ObjectBindingPattern:
1596        case SyntaxKind.ObjectLiteralExpression:
1597            return true;
1598    }
1599
1600    return false;
1601}
1602
1603/** @internal */
1604export function isObjectBindingOrAssignmentElement(node: Node): node is ObjectBindingOrAssignmentElement {
1605    switch (node.kind) {
1606        case SyntaxKind.BindingElement:
1607        case SyntaxKind.PropertyAssignment: // AssignmentProperty
1608        case SyntaxKind.ShorthandPropertyAssignment: // AssignmentProperty
1609        case SyntaxKind.SpreadAssignment: // AssignmentRestProperty
1610            return true;
1611    }
1612    return false;
1613}
1614
1615/**
1616 * Determines whether a node is an ArrayBindingOrAssignmentPattern
1617 *
1618 * @internal
1619 */
1620export function isArrayBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ArrayBindingOrAssignmentPattern {
1621    switch (node.kind) {
1622        case SyntaxKind.ArrayBindingPattern:
1623        case SyntaxKind.ArrayLiteralExpression:
1624            return true;
1625    }
1626
1627    return false;
1628}
1629
1630/** @internal */
1631export function isPropertyAccessOrQualifiedNameOrImportTypeNode(node: Node): node is PropertyAccessExpression | QualifiedName | ImportTypeNode {
1632    const kind = node.kind;
1633    return kind === SyntaxKind.PropertyAccessExpression
1634        || kind === SyntaxKind.QualifiedName
1635        || kind === SyntaxKind.ImportType;
1636}
1637
1638// Expression
1639
1640export function isPropertyAccessOrQualifiedName(node: Node): node is PropertyAccessExpression | QualifiedName {
1641    const kind = node.kind;
1642    return kind === SyntaxKind.PropertyAccessExpression
1643        || kind === SyntaxKind.QualifiedName;
1644}
1645
1646export function isCallLikeExpression(node: Node): node is CallLikeExpression {
1647    switch (node.kind) {
1648        case SyntaxKind.JsxOpeningElement:
1649        case SyntaxKind.JsxSelfClosingElement:
1650        case SyntaxKind.CallExpression:
1651        case SyntaxKind.NewExpression:
1652        case SyntaxKind.TaggedTemplateExpression:
1653        case SyntaxKind.Decorator:
1654        case SyntaxKind.EtsComponentExpression:
1655            return true;
1656        default:
1657            return false;
1658    }
1659}
1660
1661export function isCallOrNewExpression(node: Node): node is CallExpression | NewExpression {
1662    return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression;
1663}
1664
1665export function isTemplateLiteral(node: Node): node is TemplateLiteral {
1666    const kind = node.kind;
1667    return kind === SyntaxKind.TemplateExpression
1668        || kind === SyntaxKind.NoSubstitutionTemplateLiteral;
1669}
1670
1671/** @internal */
1672export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression {
1673    return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node).kind);
1674}
1675
1676function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean {
1677    switch (kind) {
1678        case SyntaxKind.PropertyAccessExpression:
1679        case SyntaxKind.ElementAccessExpression:
1680        case SyntaxKind.NewExpression:
1681        case SyntaxKind.CallExpression:
1682        case SyntaxKind.JsxElement:
1683        case SyntaxKind.JsxSelfClosingElement:
1684        case SyntaxKind.JsxFragment:
1685        case SyntaxKind.TaggedTemplateExpression:
1686        case SyntaxKind.ArrayLiteralExpression:
1687        case SyntaxKind.ParenthesizedExpression:
1688        case SyntaxKind.ObjectLiteralExpression:
1689        case SyntaxKind.ClassExpression:
1690        case SyntaxKind.FunctionExpression:
1691        case SyntaxKind.EtsComponentExpression:
1692        case SyntaxKind.Identifier:
1693        case SyntaxKind.PrivateIdentifier: // technically this is only an Expression if it's in a `#field in expr` BinaryExpression
1694        case SyntaxKind.RegularExpressionLiteral:
1695        case SyntaxKind.NumericLiteral:
1696        case SyntaxKind.BigIntLiteral:
1697        case SyntaxKind.StringLiteral:
1698        case SyntaxKind.NoSubstitutionTemplateLiteral:
1699        case SyntaxKind.TemplateExpression:
1700        case SyntaxKind.FalseKeyword:
1701        case SyntaxKind.NullKeyword:
1702        case SyntaxKind.ThisKeyword:
1703        case SyntaxKind.TrueKeyword:
1704        case SyntaxKind.SuperKeyword:
1705        case SyntaxKind.NonNullExpression:
1706        case SyntaxKind.ExpressionWithTypeArguments:
1707        case SyntaxKind.MetaProperty:
1708        case SyntaxKind.ImportKeyword: // technically this is only an Expression if it's in a CallExpression
1709            return true;
1710        default:
1711            return false;
1712    }
1713}
1714
1715/** @internal */
1716export function isUnaryExpression(node: Node): node is UnaryExpression {
1717    return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind);
1718}
1719
1720function isUnaryExpressionKind(kind: SyntaxKind): boolean {
1721    switch (kind) {
1722        case SyntaxKind.PrefixUnaryExpression:
1723        case SyntaxKind.PostfixUnaryExpression:
1724        case SyntaxKind.DeleteExpression:
1725        case SyntaxKind.TypeOfExpression:
1726        case SyntaxKind.VoidExpression:
1727        case SyntaxKind.AwaitExpression:
1728        case SyntaxKind.TypeAssertionExpression:
1729            return true;
1730        default:
1731            return isLeftHandSideExpressionKind(kind);
1732    }
1733}
1734
1735/** @internal */
1736export function isUnaryExpressionWithWrite(expr: Node): expr is PrefixUnaryExpression | PostfixUnaryExpression {
1737    switch (expr.kind) {
1738        case SyntaxKind.PostfixUnaryExpression:
1739            return true;
1740        case SyntaxKind.PrefixUnaryExpression:
1741            return (expr as PrefixUnaryExpression).operator === SyntaxKind.PlusPlusToken ||
1742                (expr as PrefixUnaryExpression).operator === SyntaxKind.MinusMinusToken;
1743        default:
1744            return false;
1745    }
1746}
1747
1748/**
1749 * Determines whether a node is an expression based only on its kind.
1750 * Use `isExpressionNode` if not in transforms.
1751 *
1752 * @internal
1753 */
1754export function isExpression(node: Node): node is Expression {
1755    return isExpressionKind(skipPartiallyEmittedExpressions(node).kind);
1756}
1757
1758function isExpressionKind(kind: SyntaxKind): boolean {
1759    switch (kind) {
1760        case SyntaxKind.ConditionalExpression:
1761        case SyntaxKind.YieldExpression:
1762        case SyntaxKind.ArrowFunction:
1763        case SyntaxKind.BinaryExpression:
1764        case SyntaxKind.SpreadElement:
1765        case SyntaxKind.AsExpression:
1766        case SyntaxKind.OmittedExpression:
1767        case SyntaxKind.CommaListExpression:
1768        case SyntaxKind.PartiallyEmittedExpression:
1769        case SyntaxKind.SatisfiesExpression:
1770            return true;
1771        default:
1772            return isUnaryExpressionKind(kind);
1773    }
1774}
1775
1776export function isAssertionExpression(node: Node): node is AssertionExpression {
1777    const kind = node.kind;
1778    return kind === SyntaxKind.TypeAssertionExpression
1779        || kind === SyntaxKind.AsExpression;
1780}
1781
1782/** @internal */
1783export function isNotEmittedOrPartiallyEmittedNode(node: Node): node is NotEmittedStatement | PartiallyEmittedExpression {
1784    return isNotEmittedStatement(node)
1785        || isPartiallyEmittedExpression(node);
1786}
1787
1788// Statement
1789
1790export function isIterationStatement(node: Node, lookInLabeledStatements: false): node is IterationStatement;
1791export function isIterationStatement(node: Node, lookInLabeledStatements: boolean): node is IterationStatement | LabeledStatement;
1792export function isIterationStatement(node: Node, lookInLabeledStatements: boolean): node is IterationStatement {
1793    switch (node.kind) {
1794        case SyntaxKind.ForStatement:
1795        case SyntaxKind.ForInStatement:
1796        case SyntaxKind.ForOfStatement:
1797        case SyntaxKind.DoStatement:
1798        case SyntaxKind.WhileStatement:
1799            return true;
1800        case SyntaxKind.LabeledStatement:
1801            return lookInLabeledStatements && isIterationStatement((node as LabeledStatement).statement, lookInLabeledStatements);
1802    }
1803
1804    return false;
1805}
1806
1807/** @internal */
1808export function isScopeMarker(node: Node) {
1809    return isExportAssignment(node) || isExportDeclaration(node);
1810}
1811
1812/** @internal */
1813export function hasScopeMarker(statements: readonly Statement[]) {
1814    return some(statements, isScopeMarker);
1815}
1816
1817/** @internal */
1818export function needsScopeMarker(result: Statement) {
1819    return !isAnyImportOrReExport(result) && !isExportAssignment(result) && !hasSyntacticModifier(result, ModifierFlags.Export) && !isAmbientModule(result);
1820}
1821
1822/** @internal */
1823export function isExternalModuleIndicator(result: Statement) {
1824    // Exported top-level member indicates moduleness
1825    return isAnyImportOrReExport(result) || isExportAssignment(result) || hasSyntacticModifier(result, ModifierFlags.Export);
1826}
1827
1828/**
1829 * Semantics of annotations is not defined for JS. The presence of AnnotationDeclaration
1830 * in SourceFile shouldn't effect on resulting JS code.
1831 * But AnnotationDeclaration reuse a general mechanics for declaration importing and exporting.
1832 * It may lead to generation of boilerplate code in process of transformation into JS.
1833 * For example,
1834 * // A.ts
1835 * export @inteface Anno {}
1836 *
1837 * // A.js
1838 * exports.__esModule = true; // <-- side effect
1839 *
1840 * Following function returns true if SourceFile contains only AnnotationDeclaration
1841 * as importing or exporting entity.
1842 * @internal
1843 */
1844export function isOnlyAnnotationsAreExportedOrImported(s: SourceFile, resolver: EmitResolver): boolean {
1845    // Ingnore any files except ets
1846    if (!isInEtsFile(s)) {
1847        return false;
1848    }
1849    // SourceFile exports or imports contains only annotation declarations
1850    const exports = getExportStatements(s);
1851    const imports = s.imports;
1852    if (exports.length === 0 && imports.length === 0) {
1853        return false;
1854    }
1855    return allExportsAreAnnotations(exports, resolver) && allImportsAreAnnotations(imports, resolver);
1856}
1857
1858function getExportStatements(s: SourceFile): Statement[] {
1859    return mapDefined(s.statements, (s: Statement) => {
1860        const os = getOriginalNode(s) as Statement;
1861        return isExternalModuleIndicator(os) ? os : undefined;
1862    });
1863}
1864
1865function allExportsAreAnnotations(exports: Statement[], resolver: EmitResolver): boolean {
1866    return every(exports, e => {
1867        if (isAnnotationDeclaration(e)) {
1868            return true;
1869        }
1870        else if (isExportDeclaration(e) && e.exportClause && isNamedExports(e.exportClause)) {
1871            return e.exportClause.elements.length > 0 && every(e.exportClause.elements, e => resolver.isReferredToAnnotation(e) === true);
1872        }
1873        else if (isExportAssignment(e) && resolver.isReferredToAnnotation(e) === true) {
1874            return true;
1875        }
1876        else if (isImportDeclaration(e) && e.importClause && e.importClause.namedBindings && isNamedImports(e.importClause.namedBindings)) {
1877            return e.importClause.namedBindings.elements.length > 0 &&
1878                every(e.importClause.namedBindings.elements, e => resolver.isReferredToAnnotation(e) === true);
1879        }
1880        return false;
1881    });
1882}
1883
1884function allImportsAreAnnotations(imports: readonly StringLiteralLike[], resolver: EmitResolver): boolean {
1885    return every(imports, i => {
1886        if (isImportDeclaration(i.parent) && i.parent.importClause && i.parent.importClause.namedBindings &&
1887            isNamedImports(i.parent.importClause.namedBindings)) {
1888            return i.parent.importClause.namedBindings.elements.length > 0 &&
1889                every(i.parent.importClause.namedBindings.elements, e => resolver.isReferredToAnnotation(e) === true);
1890        }
1891        else if (isExportDeclaration(i.parent) && i.parent.exportClause && isNamedExports(i.parent.exportClause)) {
1892            return i.parent.exportClause.elements.length > 0 && every(i.parent.exportClause.elements, e => resolver.isReferredToAnnotation(e) === true);
1893        }
1894        return false;
1895    });
1896}
1897
1898/** @internal */
1899export function isForInOrOfStatement(node: Node): node is ForInOrOfStatement {
1900    return node.kind === SyntaxKind.ForInStatement || node.kind === SyntaxKind.ForOfStatement;
1901}
1902
1903// Element
1904
1905/** @internal */
1906export function isConciseBody(node: Node): node is ConciseBody {
1907    return isBlock(node)
1908        || isExpression(node);
1909}
1910
1911/** @internal */
1912export function isFunctionBody(node: Node): node is FunctionBody {
1913    return isBlock(node);
1914}
1915
1916/** @internal */
1917export function isForInitializer(node: Node): node is ForInitializer {
1918    return isVariableDeclarationList(node)
1919        || isExpression(node);
1920}
1921
1922/** @internal */
1923export function isModuleBody(node: Node): node is ModuleBody {
1924    const kind = node.kind;
1925    return kind === SyntaxKind.ModuleBlock
1926        || kind === SyntaxKind.ModuleDeclaration
1927        || kind === SyntaxKind.Identifier;
1928}
1929
1930/** @internal */
1931export function isNamespaceBody(node: Node): node is NamespaceBody {
1932    const kind = node.kind;
1933    return kind === SyntaxKind.ModuleBlock
1934        || kind === SyntaxKind.ModuleDeclaration;
1935}
1936
1937/** @internal */
1938export function isJSDocNamespaceBody(node: Node): node is JSDocNamespaceBody {
1939    const kind = node.kind;
1940    return kind === SyntaxKind.Identifier
1941        || kind === SyntaxKind.ModuleDeclaration;
1942}
1943
1944/** @internal */
1945export function isNamedImportBindings(node: Node): node is NamedImportBindings {
1946    const kind = node.kind;
1947    return kind === SyntaxKind.NamedImports
1948        || kind === SyntaxKind.NamespaceImport;
1949}
1950
1951/** @internal */
1952export function isModuleOrEnumDeclaration(node: Node): node is ModuleDeclaration | EnumDeclaration {
1953    return node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration;
1954}
1955
1956function isDeclarationKind(kind: SyntaxKind) {
1957    return kind === SyntaxKind.ArrowFunction
1958        || kind === SyntaxKind.BindingElement
1959        || kind === SyntaxKind.ClassDeclaration
1960        || kind === SyntaxKind.ClassExpression
1961        || kind === SyntaxKind.ClassStaticBlockDeclaration
1962        || kind === SyntaxKind.StructDeclaration
1963        || kind === SyntaxKind.AnnotationDeclaration
1964        || kind === SyntaxKind.Constructor
1965        || kind === SyntaxKind.EnumDeclaration
1966        || kind === SyntaxKind.EnumMember
1967        || kind === SyntaxKind.ExportSpecifier
1968        || kind === SyntaxKind.FunctionDeclaration
1969        || kind === SyntaxKind.FunctionExpression
1970        || kind === SyntaxKind.GetAccessor
1971        || kind === SyntaxKind.ImportClause
1972        || kind === SyntaxKind.ImportEqualsDeclaration
1973        || kind === SyntaxKind.ImportSpecifier
1974        || kind === SyntaxKind.InterfaceDeclaration
1975        || kind === SyntaxKind.JsxAttribute
1976        || kind === SyntaxKind.MethodDeclaration
1977        || kind === SyntaxKind.MethodSignature
1978        || kind === SyntaxKind.ModuleDeclaration
1979        || kind === SyntaxKind.NamespaceExportDeclaration
1980        || kind === SyntaxKind.NamespaceImport
1981        || kind === SyntaxKind.NamespaceExport
1982        || kind === SyntaxKind.Parameter
1983        || kind === SyntaxKind.PropertyAssignment
1984        || kind === SyntaxKind.PropertyDeclaration
1985        || kind === SyntaxKind.AnnotationPropertyDeclaration
1986        || kind === SyntaxKind.PropertySignature
1987        || kind === SyntaxKind.SetAccessor
1988        || kind === SyntaxKind.ShorthandPropertyAssignment
1989        || kind === SyntaxKind.TypeAliasDeclaration
1990        || kind === SyntaxKind.TypeParameter
1991        || kind === SyntaxKind.VariableDeclaration
1992        || kind === SyntaxKind.JSDocTypedefTag
1993        || kind === SyntaxKind.JSDocCallbackTag
1994        || kind === SyntaxKind.JSDocPropertyTag;
1995}
1996
1997function isDeclarationStatementKind(kind: SyntaxKind) {
1998    return kind === SyntaxKind.FunctionDeclaration
1999        || kind === SyntaxKind.MissingDeclaration
2000        || kind === SyntaxKind.ClassDeclaration
2001        || kind === SyntaxKind.StructDeclaration
2002        || kind === SyntaxKind.AnnotationDeclaration
2003        || kind === SyntaxKind.InterfaceDeclaration
2004        || kind === SyntaxKind.TypeAliasDeclaration
2005        || kind === SyntaxKind.EnumDeclaration
2006        || kind === SyntaxKind.ModuleDeclaration
2007        || kind === SyntaxKind.ImportDeclaration
2008        || kind === SyntaxKind.ImportEqualsDeclaration
2009        || kind === SyntaxKind.ExportDeclaration
2010        || kind === SyntaxKind.ExportAssignment
2011        || kind === SyntaxKind.NamespaceExportDeclaration;
2012}
2013
2014function isStatementKindButNotDeclarationKind(kind: SyntaxKind) {
2015    return kind === SyntaxKind.BreakStatement
2016        || kind === SyntaxKind.ContinueStatement
2017        || kind === SyntaxKind.DebuggerStatement
2018        || kind === SyntaxKind.DoStatement
2019        || kind === SyntaxKind.ExpressionStatement
2020        || kind === SyntaxKind.EmptyStatement
2021        || kind === SyntaxKind.ForInStatement
2022        || kind === SyntaxKind.ForOfStatement
2023        || kind === SyntaxKind.ForStatement
2024        || kind === SyntaxKind.IfStatement
2025        || kind === SyntaxKind.LabeledStatement
2026        || kind === SyntaxKind.ReturnStatement
2027        || kind === SyntaxKind.SwitchStatement
2028        || kind === SyntaxKind.ThrowStatement
2029        || kind === SyntaxKind.TryStatement
2030        || kind === SyntaxKind.VariableStatement
2031        || kind === SyntaxKind.WhileStatement
2032        || kind === SyntaxKind.WithStatement
2033        || kind === SyntaxKind.NotEmittedStatement
2034        || kind === SyntaxKind.EndOfDeclarationMarker
2035        || kind === SyntaxKind.MergeDeclarationMarker;
2036}
2037
2038/** @internal */
2039export function isDeclaration(node: Node): node is NamedDeclaration {
2040    if (node.kind === SyntaxKind.TypeParameter) {
2041        return (node.parent && node.parent.kind !== SyntaxKind.JSDocTemplateTag) || isInJSFile(node);
2042    }
2043
2044    return isDeclarationKind(node.kind);
2045}
2046
2047export function isDeclarationStatement(node: Node): node is DeclarationStatement {
2048    return isDeclarationStatementKind(node.kind);
2049}
2050
2051/**
2052 * Determines whether the node is a statement that is not also a declaration
2053 * @internal
2054 */
2055export function isStatementButNotDeclaration(node: Node): node is Statement {
2056    return isStatementKindButNotDeclarationKind(node.kind);
2057}
2058
2059/** @internal */
2060export function isStatement(node: Node): node is Statement {
2061    const kind = node.kind;
2062    return isStatementKindButNotDeclarationKind(kind)
2063        || isDeclarationStatementKind(kind)
2064        || isBlockStatement(node);
2065}
2066
2067function isBlockStatement(node: Node): node is Block {
2068    if (node.kind !== SyntaxKind.Block) return false;
2069    if (node.parent !== undefined) {
2070        if (node.parent.kind === SyntaxKind.TryStatement || node.parent.kind === SyntaxKind.CatchClause) {
2071            return false;
2072        }
2073    }
2074    return !isFunctionBlock(node);
2075}
2076
2077/**
2078 * NOTE: This is similar to `isStatement` but does not access parent pointers.
2079 * @internal
2080 */
2081export function isStatementOrBlock(node: Node): node is Statement | Block {
2082    const kind = node.kind;
2083    return isStatementKindButNotDeclarationKind(kind)
2084        || isDeclarationStatementKind(kind)
2085        || kind === SyntaxKind.Block;
2086}
2087
2088// Module references
2089
2090/** @internal */
2091export function isModuleReference(node: Node): node is ModuleReference {
2092    const kind = node.kind;
2093    return kind === SyntaxKind.ExternalModuleReference
2094        || kind === SyntaxKind.QualifiedName
2095        || kind === SyntaxKind.Identifier;
2096}
2097
2098// JSX
2099
2100/** @internal */
2101export function isJsxTagNameExpression(node: Node): node is JsxTagNameExpression {
2102    const kind = node.kind;
2103    return kind === SyntaxKind.ThisKeyword
2104        || kind === SyntaxKind.Identifier
2105        || kind === SyntaxKind.PropertyAccessExpression;
2106}
2107
2108/** @internal */
2109export function isJsxChild(node: Node): node is JsxChild {
2110    const kind = node.kind;
2111    return kind === SyntaxKind.JsxElement
2112        || kind === SyntaxKind.JsxExpression
2113        || kind === SyntaxKind.JsxSelfClosingElement
2114        || kind === SyntaxKind.JsxText
2115        || kind === SyntaxKind.JsxFragment;
2116}
2117
2118/** @internal */
2119export function isJsxAttributeLike(node: Node): node is JsxAttributeLike {
2120    const kind = node.kind;
2121    return kind === SyntaxKind.JsxAttribute
2122        || kind === SyntaxKind.JsxSpreadAttribute;
2123}
2124
2125/** @internal */
2126export function isStringLiteralOrJsxExpression(node: Node): node is StringLiteral | JsxExpression {
2127    const kind = node.kind;
2128    return kind === SyntaxKind.StringLiteral
2129        || kind === SyntaxKind.JsxExpression;
2130}
2131
2132export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement {
2133    const kind = node.kind;
2134    return kind === SyntaxKind.JsxOpeningElement
2135        || kind === SyntaxKind.JsxSelfClosingElement;
2136}
2137
2138// Clauses
2139
2140export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause {
2141    const kind = node.kind;
2142    return kind === SyntaxKind.CaseClause
2143        || kind === SyntaxKind.DefaultClause;
2144}
2145
2146// JSDoc
2147
2148/**
2149 * True if node is of some JSDoc syntax kind.
2150 * @internal
2151 */
2152export function isJSDocNode(node: Node): boolean {
2153    return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode;
2154}
2155
2156/** True if node is of a kind that may contain comment text. */
2157export function isJSDocCommentContainingNode(node: Node): boolean {
2158    return node.kind === SyntaxKind.JSDoc
2159        || node.kind === SyntaxKind.JSDocNamepathType
2160        || node.kind === SyntaxKind.JSDocText
2161        || isJSDocLinkLike(node)
2162        || isJSDocTag(node)
2163        || isJSDocTypeLiteral(node)
2164        || isJSDocSignature(node);
2165}
2166
2167// TODO: determine what this does before making it public.
2168/** @internal */
2169export function isJSDocTag(node: Node): node is JSDocTag {
2170    return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
2171}
2172
2173export function isSetAccessor(node: Node): node is SetAccessorDeclaration {
2174    return node.kind === SyntaxKind.SetAccessor;
2175}
2176
2177export function isGetAccessor(node: Node): node is GetAccessorDeclaration {
2178    return node.kind === SyntaxKind.GetAccessor;
2179}
2180
2181/**
2182 * True if has jsdoc nodes attached to it.
2183 * @internal
2184 */
2185// TODO: GH#19856 Would like to return `node is Node & { jsDoc: JSDoc[] }` but it causes long compile times
2186export function hasJSDocNodes(node: Node): node is HasJSDoc {
2187    const { jsDoc } = node as JSDocContainer;
2188    return !!jsDoc && jsDoc.length > 0;
2189}
2190
2191/**
2192 * True if has type node attached to it.
2193 * @internal
2194 */
2195export function hasType(node: Node): node is HasType {
2196    return !!(node as HasType).type;
2197}
2198
2199/**
2200 * True if has initializer node attached to it.
2201 * @internal
2202 */
2203export function hasInitializer(node: Node): node is HasInitializer {
2204    return !!(node as HasInitializer).initializer;
2205}
2206
2207/** True if has initializer node attached to it. */
2208export function hasOnlyExpressionInitializer(node: Node): node is HasExpressionInitializer {
2209    switch (node.kind) {
2210        case SyntaxKind.VariableDeclaration:
2211        case SyntaxKind.Parameter:
2212        case SyntaxKind.BindingElement:
2213        case SyntaxKind.PropertyDeclaration:
2214        case SyntaxKind.AnnotationPropertyDeclaration:
2215        case SyntaxKind.PropertyAssignment:
2216        case SyntaxKind.EnumMember:
2217            return true;
2218        default:
2219            return false;
2220    }
2221}
2222
2223export function isObjectLiteralElement(node: Node): node is ObjectLiteralElement {
2224    return node.kind === SyntaxKind.JsxAttribute || node.kind === SyntaxKind.JsxSpreadAttribute || isObjectLiteralElementLike(node);
2225}
2226
2227/** @internal */
2228export function isTypeReferenceType(node: Node): node is TypeReferenceType {
2229    return node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ExpressionWithTypeArguments;
2230}
2231
2232const MAX_SMI_X86 = 0x3fff_ffff;
2233/** @internal */
2234export function guessIndentation(lines: string[]) {
2235    let indentation = MAX_SMI_X86;
2236    for (const line of lines) {
2237        if (!line.length) {
2238            continue;
2239        }
2240        let i = 0;
2241        for (; i < line.length && i < indentation; i++) {
2242            if (!isWhiteSpaceLike(line.charCodeAt(i))) {
2243                break;
2244            }
2245        }
2246        if (i < indentation) {
2247            indentation = i;
2248        }
2249        if (indentation === 0) {
2250            return 0;
2251        }
2252    }
2253    return indentation === MAX_SMI_X86 ? undefined : indentation;
2254}
2255
2256export function isStringLiteralLike(node: Node): node is StringLiteralLike {
2257    return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
2258}
2259
2260export function isJSDocLinkLike(node: Node): node is JSDocLink | JSDocLinkCode | JSDocLinkPlain {
2261    return node.kind === SyntaxKind.JSDocLink || node.kind === SyntaxKind.JSDocLinkCode || node.kind === SyntaxKind.JSDocLinkPlain;
2262}
2263
2264export function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean {
2265    const last = lastOrUndefined<ParameterDeclaration | JSDocParameterTag>(s.parameters);
2266    return !!last && isRestParameter(last);
2267}
2268
2269export function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): boolean {
2270    const type = isJSDocParameterTag(node) ? (node.typeExpression && node.typeExpression.type) : node.type;
2271    return (node as ParameterDeclaration).dotDotDotToken !== undefined || !!type && type.kind === SyntaxKind.JSDocVariadicType;
2272}
2273
2274export class MemoryUtils {
2275    private static MemoryAfterGC: number = 0;
2276    private static baseMemorySize: number = 0;
2277    private static MIN_GC_THRESHOLD: number = 256 * 1024 * 1024; // 256MB
2278    private static memoryGCThreshold: number = MemoryUtils.MIN_GC_THRESHOLD;
2279    public static GC_THRESHOLD_RATIO: number = 0.4;
2280
2281    /**
2282     * Try garbage collection if the memory usage exceeds MEMORY_BASELINE.
2283     */
2284    public static tryGC(): void {
2285        if (!(global && global.gc && typeof global.gc === 'function')) {
2286            return;
2287        }
2288
2289        const currentMemory = process.memoryUsage().heapUsed;
2290        if ((currentMemory > MemoryUtils.baseMemorySize) && (currentMemory - MemoryUtils.MemoryAfterGC > MemoryUtils.memoryGCThreshold)) {
2291            global.gc();
2292            MemoryUtils.updateBaseMemory(currentMemory);
2293            return;
2294        }
2295    }
2296
2297    public static initializeBaseMemory(baseMemorySize?: number): void {
2298        const currentMemory = process.memoryUsage().heapUsed;
2299        MemoryUtils.baseMemorySize = baseMemorySize ? baseMemorySize : currentMemory;
2300        MemoryUtils.MemoryAfterGC = currentMemory;
2301    }
2302
2303    public static updateBaseMemory(MemoryBeforeGC: number): void {
2304        const currentMemory = process.memoryUsage().heapUsed;
2305        MemoryUtils.baseMemorySize = MemoryBeforeGC;
2306        MemoryUtils.MemoryAfterGC = currentMemory;
2307    }
2308}