• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    AccessorDeclaration, addEmitFlags, AdditiveOperator, AdditiveOperatorOrHigher, AssertionLevel,
3    AssignmentOperatorOrHigher, BinaryExpression, BinaryOperator, BinaryOperatorToken, BindingOrAssignmentElement,
4    BindingOrAssignmentElementRestIndicator, BindingOrAssignmentElementTarget, BindingOrAssignmentPattern,
5    BitwiseOperator, BitwiseOperatorOrHigher, Block, BooleanLiteral, CharacterCodes, CommaListExpression,
6    compareStringsCaseSensitive, CompilerOptions, Debug, Declaration, EmitFlags, EmitHelperFactory, EmitHost,
7    EmitResolver, EntityName, EqualityOperator, EqualityOperatorOrHigher, ExclamationToken, ExponentiationOperator,
8    ExportDeclaration, Expression, ExpressionStatement, externalHelpersModuleNameText, first, firstOrUndefined,
9    ForInitializer, GeneratedIdentifier, GeneratedIdentifierFlags, GeneratedNamePart, GeneratedPrivateIdentifier,
10    GetAccessorDeclaration,
11    getAllAccessorDeclarations, getEmitFlags, getEmitHelpers, getEmitModuleKind, getESModuleInterop,
12    getExternalModuleName, getExternalModuleNameFromPath, getJSDocType, getJSDocTypeTag, getModifiers,
13    getNamespaceDeclarationNode, getOrCreateEmitNode, getOriginalNode, getParseTreeNode,
14    getSourceTextOfNodeFromSourceFile, HasIllegalDecorators, HasIllegalModifiers, HasIllegalType,
15    HasIllegalTypeParameters, Identifier, idText, ImportCall, ImportDeclaration, ImportEqualsDeclaration,
16    isAssignmentExpression, isAssignmentOperator, isBlock, isComputedPropertyName, isDeclarationBindingElement,
17    isDefaultImport, isEffectiveExternalModule, isExclamationToken, isExportNamespaceAsDefaultDeclaration,
18    isFileLevelUniqueName, isGeneratedIdentifier, isGeneratedPrivateIdentifier, isIdentifier, isInJSFile,
19    isLiteralExpression, isMemberName, isMinusToken, isObjectLiteralElementLike, isParenthesizedExpression, isPlusToken,
20    isPostfixUnaryExpression, isPrefixUnaryExpression, isPrivateIdentifier, isPrologueDirective, isPropertyAssignment,
21    isPropertyName, isQualifiedName, isQuestionToken, isReadonlyKeyword, isShorthandPropertyAssignment, isSourceFile,
22    isSpreadAssignment, isSpreadElement, isStringLiteral, isThisTypeNode, isTypeNode, isTypeParameterDeclaration,
23    isVariableDeclarationList, JSDocNamespaceBody, JSDocTypeAssertion, JsxOpeningFragment, JsxOpeningLikeElement,
24    LeftHandSideExpression, LiteralExpression, LogicalOperator, LogicalOperatorOrHigher, map, MemberExpression,
25    MethodDeclaration, MinusToken, ModifiersArray, ModuleKind, ModuleName, MultiplicativeOperator,
26    MultiplicativeOperatorOrHigher, Mutable, NamedImportBindings, Node, NodeArray, NodeFactory, NullLiteral,
27    NumericLiteral, ObjectLiteralElementLike, ObjectLiteralExpression, or, OuterExpression, OuterExpressionKinds,
28    outFile, parseNodeFactory, PlusToken, PostfixUnaryExpression, PrefixUnaryExpression, PrivateIdentifier,
29    PropertyAssignment, PropertyDeclaration, PropertyName, pushIfUnique, QuestionToken, ReadonlyKeyword,
30    RelationalOperator, RelationalOperatorOrHigher, SetAccessorDeclaration, setOriginalNode, setParent, setStartsOnNewLine, setTextRange,
31    ShiftOperator, ShiftOperatorOrHigher, ShorthandPropertyAssignment, some, SourceFile, Statement, StringLiteral,
32    SyntaxKind, TextRange, ThisTypeNode, Token, TypeNode, TypeParameterDeclaration,
33} from "../_namespaces/ts";
34
35// Compound nodes
36
37/** @internal */
38export function createEmptyExports(factory: NodeFactory) {
39    return factory.createExportDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, factory.createNamedExports([]), /*moduleSpecifier*/ undefined);
40}
41
42/** @internal */
43export function createMemberAccessForPropertyName(factory: NodeFactory, target: Expression, memberName: PropertyName, location?: TextRange): MemberExpression {
44    if (isComputedPropertyName(memberName)) {
45         return setTextRange(factory.createElementAccessExpression(target, memberName.expression), location);
46    }
47    else {
48        const expression = setTextRange(
49            isMemberName(memberName)
50                ? factory.createPropertyAccessExpression(target, memberName)
51                : factory.createElementAccessExpression(target, memberName),
52            memberName
53        );
54        getOrCreateEmitNode(expression).flags |= EmitFlags.NoNestedSourceMaps;
55        return expression;
56    }
57}
58
59function createReactNamespace(reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment) {
60    // To ensure the emit resolver can properly resolve the namespace, we need to
61    // treat this identifier as if it were a source tree node by clearing the `Synthesized`
62    // flag and setting a parent node.
63    const react = parseNodeFactory.createIdentifier(reactNamespace || "React");
64    // Set the parent that is in parse tree
65    // this makes sure that parent chain is intact for checker to traverse complete scope tree
66    setParent(react, getParseTreeNode(parent));
67    return react;
68}
69
70function createJsxFactoryExpressionFromEntityName(factory: NodeFactory, jsxFactory: EntityName, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
71    if (isQualifiedName(jsxFactory)) {
72        const left = createJsxFactoryExpressionFromEntityName(factory, jsxFactory.left, parent);
73        const right = factory.createIdentifier(idText(jsxFactory.right)) as Mutable<Identifier>;
74        right.escapedText = jsxFactory.right.escapedText;
75        return factory.createPropertyAccessExpression(left, right);
76    }
77    else {
78        return createReactNamespace(idText(jsxFactory), parent);
79    }
80}
81
82/** @internal */
83export function createJsxFactoryExpression(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
84    return jsxFactoryEntity ?
85        createJsxFactoryExpressionFromEntityName(factory, jsxFactoryEntity, parent) :
86        factory.createPropertyAccessExpression(
87            createReactNamespace(reactNamespace, parent),
88            "createElement"
89        );
90}
91
92function createJsxFragmentFactoryExpression(factory: NodeFactory, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, parent: JsxOpeningLikeElement | JsxOpeningFragment): Expression {
93    return jsxFragmentFactoryEntity ?
94        createJsxFactoryExpressionFromEntityName(factory, jsxFragmentFactoryEntity, parent) :
95        factory.createPropertyAccessExpression(
96            createReactNamespace(reactNamespace, parent),
97            "Fragment"
98        );
99}
100
101/** @internal */
102export function createExpressionForJsxElement(factory: NodeFactory, callee: Expression, tagName: Expression, props: Expression | undefined, children: readonly Expression[] | undefined, location: TextRange): LeftHandSideExpression {
103    const argumentsList = [tagName];
104    if (props) {
105        argumentsList.push(props);
106    }
107
108    if (children && children.length > 0) {
109        if (!props) {
110            argumentsList.push(factory.createNull());
111        }
112
113        if (children.length > 1) {
114            for (const child of children) {
115                startOnNewLine(child);
116                argumentsList.push(child);
117            }
118        }
119        else {
120            argumentsList.push(children[0]);
121        }
122    }
123
124    return setTextRange(
125        factory.createCallExpression(
126            callee,
127            /*typeArguments*/ undefined,
128            argumentsList
129        ),
130        location
131    );
132}
133
134/** @internal */
135export function createExpressionForJsxFragment(factory: NodeFactory, jsxFactoryEntity: EntityName | undefined, jsxFragmentFactoryEntity: EntityName | undefined, reactNamespace: string, children: readonly Expression[], parentElement: JsxOpeningFragment, location: TextRange): LeftHandSideExpression {
136    const tagName = createJsxFragmentFactoryExpression(factory, jsxFragmentFactoryEntity, reactNamespace, parentElement);
137    const argumentsList = [tagName, factory.createNull()];
138
139    if (children && children.length > 0) {
140        if (children.length > 1) {
141            for (const child of children) {
142                startOnNewLine(child);
143                argumentsList.push(child);
144            }
145        }
146        else {
147            argumentsList.push(children[0]);
148        }
149    }
150
151    return setTextRange(
152        factory.createCallExpression(
153            createJsxFactoryExpression(factory, jsxFactoryEntity, reactNamespace, parentElement),
154            /*typeArguments*/ undefined,
155            argumentsList
156        ),
157        location
158    );
159}
160
161// Utilities
162
163/** @internal */
164export function createForOfBindingStatement(factory: NodeFactory, node: ForInitializer, boundValue: Expression): Statement {
165    if (isVariableDeclarationList(node)) {
166        const firstDeclaration = first(node.declarations);
167        const updatedDeclaration = factory.updateVariableDeclaration(
168            firstDeclaration,
169            firstDeclaration.name,
170            /*exclamationToken*/ undefined,
171            /*type*/ undefined,
172            boundValue
173        );
174        return setTextRange(
175            factory.createVariableStatement(
176                /*modifiers*/ undefined,
177                factory.updateVariableDeclarationList(node, [updatedDeclaration])
178            ),
179            /*location*/ node
180        );
181    }
182    else {
183        const updatedExpression = setTextRange(factory.createAssignment(node, boundValue), /*location*/ node);
184        return setTextRange(factory.createExpressionStatement(updatedExpression), /*location*/ node);
185    }
186}
187
188/** @internal */
189export function insertLeadingStatement(factory: NodeFactory, dest: Statement, source: Statement): Block {
190    if (isBlock(dest)) {
191        return factory.updateBlock(dest, setTextRange(factory.createNodeArray([source, ...dest.statements]), dest.statements));
192    }
193    else {
194        return factory.createBlock(factory.createNodeArray([dest, source]), /*multiLine*/ true);
195    }
196}
197
198/** @internal */
199export function createExpressionFromEntityName(factory: NodeFactory, node: EntityName | Expression): Expression {
200    if (isQualifiedName(node)) {
201        const left = createExpressionFromEntityName(factory, node.left);
202        // TODO(rbuckton): Does this need to be parented?
203        const right = setParent(setTextRange(factory.cloneNode(node.right), node.right), node.right.parent);
204        return setTextRange(factory.createPropertyAccessExpression(left, right), node);
205    }
206    else {
207        // TODO(rbuckton): Does this need to be parented?
208        return setParent(setTextRange(factory.cloneNode(node), node), node.parent);
209    }
210}
211
212/** @internal */
213export function createExpressionForPropertyName(factory: NodeFactory, memberName: Exclude<PropertyName, PrivateIdentifier>): Expression {
214    if (isIdentifier(memberName)) {
215        return factory.createStringLiteralFromNode(memberName);
216    }
217    else if (isComputedPropertyName(memberName)) {
218        // TODO(rbuckton): Does this need to be parented?
219        return setParent(setTextRange(factory.cloneNode(memberName.expression), memberName.expression), memberName.expression.parent);
220    }
221    else {
222        // TODO(rbuckton): Does this need to be parented?
223        return setParent(setTextRange(factory.cloneNode(memberName), memberName), memberName.parent);
224    }
225}
226
227function createExpressionForAccessorDeclaration(factory: NodeFactory, properties: NodeArray<Declaration>, property: AccessorDeclaration & { readonly name: Exclude<PropertyName, PrivateIdentifier>; }, receiver: Expression, multiLine: boolean) {
228    const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property);
229    if (property === firstAccessor) {
230        return setTextRange(
231            factory.createObjectDefinePropertyCall(
232                receiver,
233                createExpressionForPropertyName(factory, property.name),
234                factory.createPropertyDescriptor({
235                    enumerable: factory.createFalse(),
236                    configurable: true,
237                    get: getAccessor && setTextRange(
238                        setOriginalNode(
239                            factory.createFunctionExpression(
240                                getModifiers(getAccessor),
241                                /*asteriskToken*/ undefined,
242                                /*name*/ undefined,
243                                /*typeParameters*/ undefined,
244                                getAccessor.parameters,
245                                /*type*/ undefined,
246                                getAccessor.body! // TODO: GH#18217
247                            ),
248                            getAccessor
249                        ),
250                        getAccessor
251                    ),
252                    set: setAccessor && setTextRange(
253                        setOriginalNode(
254                            factory.createFunctionExpression(
255                                getModifiers(setAccessor),
256                                /*asteriskToken*/ undefined,
257                                /*name*/ undefined,
258                                /*typeParameters*/ undefined,
259                                setAccessor.parameters,
260                                /*type*/ undefined,
261                                setAccessor.body! // TODO: GH#18217
262                            ),
263                            setAccessor
264                        ),
265                        setAccessor
266                    )
267                }, !multiLine)
268            ),
269            firstAccessor
270        );
271    }
272
273    return undefined;
274}
275
276function createExpressionForPropertyAssignment(factory: NodeFactory, property: PropertyAssignment, receiver: Expression) {
277    return setOriginalNode(
278        setTextRange(
279            factory.createAssignment(
280                createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name),
281                property.initializer
282            ),
283            property
284        ),
285        property
286    );
287}
288
289function createExpressionForShorthandPropertyAssignment(factory: NodeFactory, property: ShorthandPropertyAssignment, receiver: Expression) {
290    return setOriginalNode(
291        setTextRange(
292            factory.createAssignment(
293                createMemberAccessForPropertyName(factory, receiver, property.name, /*location*/ property.name),
294                factory.cloneNode(property.name)
295            ),
296            /*location*/ property
297        ),
298        /*original*/ property
299    );
300}
301
302function createExpressionForMethodDeclaration(factory: NodeFactory, method: MethodDeclaration, receiver: Expression) {
303    return setOriginalNode(
304        setTextRange(
305            factory.createAssignment(
306                createMemberAccessForPropertyName(factory, receiver, method.name, /*location*/ method.name),
307                setOriginalNode(
308                    setTextRange(
309                        factory.createFunctionExpression(
310                            getModifiers(method),
311                            method.asteriskToken,
312                            /*name*/ undefined,
313                            /*typeParameters*/ undefined,
314                            method.parameters,
315                            /*type*/ undefined,
316                            method.body! // TODO: GH#18217
317                        ),
318                        /*location*/ method
319                    ),
320                    /*original*/ method
321                )
322            ),
323            /*location*/ method
324        ),
325        /*original*/ method
326    );
327}
328
329/** @internal */
330export function createExpressionForObjectLiteralElementLike(factory: NodeFactory, node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined {
331    if (property.name && isPrivateIdentifier(property.name)) {
332        Debug.failBadSyntaxKind(property.name, "Private identifiers are not allowed in object literals.");
333    }
334    switch (property.kind) {
335        case SyntaxKind.GetAccessor:
336        case SyntaxKind.SetAccessor:
337            return createExpressionForAccessorDeclaration(factory, node.properties, property as typeof property & { readonly name: Exclude<PropertyName, PrivateIdentifier> }, receiver, !!node.multiLine);
338        case SyntaxKind.PropertyAssignment:
339            return createExpressionForPropertyAssignment(factory, property, receiver);
340        case SyntaxKind.ShorthandPropertyAssignment:
341            return createExpressionForShorthandPropertyAssignment(factory, property, receiver);
342        case SyntaxKind.MethodDeclaration:
343            return createExpressionForMethodDeclaration(factory, property, receiver);
344    }
345}
346
347/**
348 * Expand the read and increment/decrement operations a pre- or post-increment or pre- or post-decrement expression.
349 *
350 * ```ts
351 * // input
352 * <expression>++
353 * // output (if result is not discarded)
354 * var <temp>;
355 * (<temp> = <expression>, <resultVariable> = <temp>++, <temp>)
356 * // output (if result is discarded)
357 * var <temp>;
358 * (<temp> = <expression>, <temp>++, <temp>)
359 *
360 * // input
361 * ++<expression>
362 * // output (if result is not discarded)
363 * var <temp>;
364 * (<temp> = <expression>, <resultVariable> = ++<temp>)
365 * // output (if result is discarded)
366 * var <temp>;
367 * (<temp> = <expression>, ++<temp>)
368 * ```
369 *
370 * It is up to the caller to supply a temporary variable for `<resultVariable>` if one is needed.
371 * The temporary variable `<temp>` is injected so that `++` and `--` work uniformly with `number` and `bigint`.
372 * The result of the expression is always the final result of incrementing or decrementing the expression, so that it can be used for storage.
373 *
374 * @param factory {@link NodeFactory} used to create the expanded representation.
375 * @param node The original prefix or postfix unary node.
376 * @param expression The expression to use as the value to increment or decrement
377 * @param resultVariable A temporary variable in which to store the result. Pass `undefined` if the result is discarded, or if the value of `<temp>` is the expected result.
378 *
379 * @internal
380 */
381export function expandPreOrPostfixIncrementOrDecrementExpression(factory: NodeFactory, node: PrefixUnaryExpression | PostfixUnaryExpression, expression: Expression, recordTempVariable: (node: Identifier) => void, resultVariable: Identifier | undefined) {
382    const operator = node.operator;
383    Debug.assert(operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken, "Expected 'node' to be a pre- or post-increment or pre- or post-decrement expression");
384
385    const temp = factory.createTempVariable(recordTempVariable);
386    expression = factory.createAssignment(temp, expression);
387    setTextRange(expression, node.operand);
388
389    let operation: Expression = isPrefixUnaryExpression(node) ?
390        factory.createPrefixUnaryExpression(operator, temp) :
391        factory.createPostfixUnaryExpression(temp, operator);
392    setTextRange(operation, node);
393
394    if (resultVariable) {
395        operation = factory.createAssignment(resultVariable, operation);
396        setTextRange(operation, node);
397    }
398
399    expression = factory.createComma(expression, operation);
400    setTextRange(expression, node);
401
402    if (isPostfixUnaryExpression(node)) {
403        expression = factory.createComma(expression, temp);
404        setTextRange(expression, node);
405    }
406
407    return expression;
408}
409
410/**
411 * Gets whether an identifier should only be referred to by its internal name.
412 *
413 * @internal
414 */
415export function isInternalName(node: Identifier) {
416    return (getEmitFlags(node) & EmitFlags.InternalName) !== 0;
417}
418
419/**
420 * Gets whether an identifier should only be referred to by its local name.
421 *
422 * @internal
423 */
424export function isLocalName(node: Identifier) {
425    return (getEmitFlags(node) & EmitFlags.LocalName) !== 0;
426}
427
428/**
429 * Gets whether an identifier should only be referred to by its export representation if the
430 * name points to an exported symbol.
431 *
432 * @internal
433 */
434export function isExportName(node: Identifier) {
435    return (getEmitFlags(node) & EmitFlags.ExportName) !== 0;
436}
437
438function isUseStrictPrologue(node: ExpressionStatement): boolean {
439    return isStringLiteral(node.expression) && node.expression.text === "use strict";
440}
441
442/** @internal */
443export function findUseStrictPrologue(statements: readonly Statement[]): Statement | undefined {
444    for (const statement of statements) {
445        if (isPrologueDirective(statement)) {
446            if (isUseStrictPrologue(statement)) {
447                return statement;
448            }
449        }
450        else {
451            break;
452        }
453    }
454    return undefined;
455}
456
457/** @internal */
458export function startsWithUseStrict(statements: readonly Statement[]) {
459    const firstStatement = firstOrUndefined(statements);
460    return firstStatement !== undefined
461        && isPrologueDirective(firstStatement)
462        && isUseStrictPrologue(firstStatement);
463}
464
465/** @internal */
466export function isCommaSequence(node: Expression): node is BinaryExpression & {operatorToken: Token<SyntaxKind.CommaToken>} | CommaListExpression {
467    return node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.CommaToken ||
468        node.kind === SyntaxKind.CommaListExpression;
469}
470
471/** @internal */
472export function isJSDocTypeAssertion(node: Node): node is JSDocTypeAssertion {
473    return isParenthesizedExpression(node)
474        && isInJSFile(node)
475        && !!getJSDocTypeTag(node);
476}
477
478/** @internal */
479export function getJSDocTypeAssertionType(node: JSDocTypeAssertion) {
480    const type = getJSDocType(node);
481    Debug.assertIsDefined(type);
482    return type;
483}
484
485/** @internal */
486export function isOuterExpression(node: Node, kinds = OuterExpressionKinds.All): node is OuterExpression {
487    switch (node.kind) {
488        case SyntaxKind.ParenthesizedExpression:
489            if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node)) {
490                return false;
491            }
492            return (kinds & OuterExpressionKinds.Parentheses) !== 0;
493        case SyntaxKind.TypeAssertionExpression:
494        case SyntaxKind.AsExpression:
495        case SyntaxKind.SatisfiesExpression:
496            return (kinds & OuterExpressionKinds.TypeAssertions) !== 0;
497        case SyntaxKind.NonNullExpression:
498            return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0;
499        case SyntaxKind.PartiallyEmittedExpression:
500            return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0;
501    }
502    return false;
503}
504
505/** @internal */
506export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression;
507/** @internal */
508export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node;
509/** @internal */
510export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) {
511    while (isOuterExpression(node, kinds)) {
512        node = node.expression;
513    }
514    return node;
515}
516
517/** @internal */
518export function skipAssertions(node: Expression): Expression;
519/** @internal */
520export function skipAssertions(node: Node): Node;
521/** @internal */
522export function skipAssertions(node: Node): Node {
523    return skipOuterExpressions(node, OuterExpressionKinds.Assertions);
524}
525
526/** @internal */
527export function startOnNewLine<T extends Node>(node: T): T {
528    return setStartsOnNewLine(node, /*newLine*/ true);
529}
530
531/** @internal */
532export function getExternalHelpersModuleName(node: SourceFile) {
533    const parseNode = getOriginalNode(node, isSourceFile);
534    const emitNode = parseNode && parseNode.emitNode;
535    return emitNode && emitNode.externalHelpersModuleName;
536}
537
538/** @internal */
539export function hasRecordedExternalHelpers(sourceFile: SourceFile) {
540    const parseNode = getOriginalNode(sourceFile, isSourceFile);
541    const emitNode = parseNode && parseNode.emitNode;
542    return !!emitNode && (!!emitNode.externalHelpersModuleName || !!emitNode.externalHelpers);
543}
544
545/** @internal */
546export function createExternalHelpersImportDeclarationIfNeeded(nodeFactory: NodeFactory, helperFactory: EmitHelperFactory, sourceFile: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStar?: boolean, hasImportDefault?: boolean) {
547    if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) {
548        let namedBindings: NamedImportBindings | undefined;
549        const moduleKind = getEmitModuleKind(compilerOptions);
550        if ((moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) || sourceFile.impliedNodeFormat === ModuleKind.ESNext) {
551            // use named imports
552            const helpers = getEmitHelpers(sourceFile);
553            if (helpers) {
554                const helperNames: string[] = [];
555                for (const helper of helpers) {
556                    if (!helper.scoped) {
557                        const importName = helper.importName;
558                        if (importName) {
559                            pushIfUnique(helperNames, importName);
560                        }
561                    }
562                }
563                if (some(helperNames)) {
564                    helperNames.sort(compareStringsCaseSensitive);
565                    // Alias the imports if the names are used somewhere in the file.
566                    // NOTE: We don't need to care about global import collisions as this is a module.
567                    namedBindings = nodeFactory.createNamedImports(
568                        map(helperNames, name => isFileLevelUniqueName(sourceFile, name)
569                            ? nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, nodeFactory.createIdentifier(name))
570                            : nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name))
571                        )
572                    );
573                    const parseNode = getOriginalNode(sourceFile, isSourceFile);
574                    const emitNode = getOrCreateEmitNode(parseNode);
575                    emitNode.externalHelpers = true;
576                }
577            }
578        }
579        else {
580            // use a namespace import
581            const externalHelpersModuleName = getOrCreateExternalHelpersModuleNameIfNeeded(nodeFactory, sourceFile, compilerOptions, hasExportStarsToExportValues, hasImportStar || hasImportDefault);
582            if (externalHelpersModuleName) {
583                namedBindings = nodeFactory.createNamespaceImport(externalHelpersModuleName);
584            }
585        }
586        if (namedBindings) {
587            const externalHelpersImportDeclaration = nodeFactory.createImportDeclaration(
588                /*modifiers*/ undefined,
589                nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings),
590                nodeFactory.createStringLiteral(externalHelpersModuleNameText),
591                 /*assertClause*/ undefined
592            );
593            addEmitFlags(externalHelpersImportDeclaration, EmitFlags.NeverApplyImportHelper);
594            return externalHelpersImportDeclaration;
595        }
596    }
597}
598
599/** @internal */
600export function getOrCreateExternalHelpersModuleNameIfNeeded(factory: NodeFactory, node: SourceFile, compilerOptions: CompilerOptions, hasExportStarsToExportValues?: boolean, hasImportStarOrImportDefault?: boolean) {
601    if (compilerOptions.importHelpers && isEffectiveExternalModule(node, compilerOptions)) {
602        const externalHelpersModuleName = getExternalHelpersModuleName(node);
603        if (externalHelpersModuleName) {
604            return externalHelpersModuleName;
605        }
606
607        const moduleKind = getEmitModuleKind(compilerOptions);
608        let create = (hasExportStarsToExportValues || (getESModuleInterop(compilerOptions) && hasImportStarOrImportDefault))
609            && moduleKind !== ModuleKind.System
610            && (moduleKind < ModuleKind.ES2015 || node.impliedNodeFormat === ModuleKind.CommonJS);
611        if (!create) {
612            const helpers = getEmitHelpers(node);
613            if (helpers) {
614                for (const helper of helpers) {
615                    if (!helper.scoped) {
616                        create = true;
617                        break;
618                    }
619                }
620            }
621        }
622
623        if (create) {
624            const parseNode = getOriginalNode(node, isSourceFile);
625            const emitNode = getOrCreateEmitNode(parseNode);
626            return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = factory.createUniqueName(externalHelpersModuleNameText));
627        }
628    }
629}
630
631/**
632 * Get the name of that target module from an import or export declaration
633 *
634 * @internal
635 */
636export function getLocalNameForExternalImport(factory: NodeFactory, node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration, sourceFile: SourceFile): Identifier | undefined {
637    const namespaceDeclaration = getNamespaceDeclarationNode(node);
638    if (namespaceDeclaration && !isDefaultImport(node) && !isExportNamespaceAsDefaultDeclaration(node)) {
639        const name = namespaceDeclaration.name;
640        return isGeneratedIdentifier(name) ? name : factory.createIdentifier(getSourceTextOfNodeFromSourceFile(sourceFile, name) || idText(name));
641    }
642    if (node.kind === SyntaxKind.ImportDeclaration && node.importClause) {
643        return factory.getGeneratedNameForNode(node);
644    }
645    if (node.kind === SyntaxKind.ExportDeclaration && node.moduleSpecifier) {
646        return factory.getGeneratedNameForNode(node);
647    }
648    return undefined;
649}
650
651/**
652 * Get the name of a target module from an import/export declaration as should be written in the emitted output.
653 * The emitted output name can be different from the input if:
654 *  1. The module has a /// <amd-module name="<new name>" />
655 *  2. --out or --outFile is used, making the name relative to the rootDir
656 *  3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System).
657 * Otherwise, a new StringLiteral node representing the module name will be returned.
658 *
659 * @internal
660 */
661export function getExternalModuleNameLiteral(factory: NodeFactory, importNode: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall, sourceFile: SourceFile, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) {
662    const moduleName = getExternalModuleName(importNode);
663    if (moduleName && isStringLiteral(moduleName)) {
664        return tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions)
665            || tryRenameExternalModule(factory, moduleName, sourceFile)
666            || factory.cloneNode(moduleName);
667    }
668
669    return undefined;
670}
671
672/**
673 * Some bundlers (SystemJS builder) sometimes want to rename dependencies.
674 * Here we check if alternative name was provided for a given moduleName and return it if possible.
675 */
676function tryRenameExternalModule(factory: NodeFactory, moduleName: LiteralExpression, sourceFile: SourceFile) {
677    const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text);
678    return rename ? factory.createStringLiteral(rename) : undefined;
679}
680
681/**
682 * Get the name of a module as should be written in the emitted output.
683 * The emitted output name can be different from the input if:
684 *  1. The module has a /// <amd-module name="<new name>" />
685 *  2. --out or --outFile is used, making the name relative to the rootDir
686 * Otherwise, a new StringLiteral node representing the module name will be returned.
687 *
688 * @internal
689 */
690export function tryGetModuleNameFromFile(factory: NodeFactory, file: SourceFile | undefined, host: EmitHost, options: CompilerOptions): StringLiteral | undefined {
691    if (!file) {
692        return undefined;
693    }
694    if (file.moduleName) {
695        return factory.createStringLiteral(file.moduleName);
696    }
697    if (!file.isDeclarationFile && outFile(options)) {
698        return factory.createStringLiteral(getExternalModuleNameFromPath(host, file.fileName));
699    }
700    return undefined;
701}
702
703function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall, host: EmitHost, factory: NodeFactory, resolver: EmitResolver, compilerOptions: CompilerOptions) {
704    return tryGetModuleNameFromFile(factory, resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions);
705}
706
707/**
708 * Gets the initializer of an BindingOrAssignmentElement.
709 *
710 * @internal
711 */
712export function getInitializerOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Expression | undefined {
713    if (isDeclarationBindingElement(bindingElement)) {
714        // `1` in `let { a = 1 } = ...`
715        // `1` in `let { a: b = 1 } = ...`
716        // `1` in `let { a: {b} = 1 } = ...`
717        // `1` in `let { a: [b] = 1 } = ...`
718        // `1` in `let [a = 1] = ...`
719        // `1` in `let [{a} = 1] = ...`
720        // `1` in `let [[a] = 1] = ...`
721        return bindingElement.initializer;
722    }
723
724    if (isPropertyAssignment(bindingElement)) {
725        // `1` in `({ a: b = 1 } = ...)`
726        // `1` in `({ a: {b} = 1 } = ...)`
727        // `1` in `({ a: [b] = 1 } = ...)`
728        const initializer = bindingElement.initializer;
729        return isAssignmentExpression(initializer, /*excludeCompoundAssignment*/ true)
730            ? initializer.right
731            : undefined;
732    }
733
734    if (isShorthandPropertyAssignment(bindingElement)) {
735        // `1` in `({ a = 1 } = ...)`
736        return bindingElement.objectAssignmentInitializer;
737    }
738
739    if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
740        // `1` in `[a = 1] = ...`
741        // `1` in `[{a} = 1] = ...`
742        // `1` in `[[a] = 1] = ...`
743        return bindingElement.right;
744    }
745
746    if (isSpreadElement(bindingElement)) {
747        // Recovery consistent with existing emit.
748        return getInitializerOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement);
749    }
750}
751
752/**
753 * Gets the name of an BindingOrAssignmentElement.
754 *
755 * @internal
756 */
757export function getTargetOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementTarget | undefined {
758    if (isDeclarationBindingElement(bindingElement)) {
759        // `a` in `let { a } = ...`
760        // `a` in `let { a = 1 } = ...`
761        // `b` in `let { a: b } = ...`
762        // `b` in `let { a: b = 1 } = ...`
763        // `a` in `let { ...a } = ...`
764        // `{b}` in `let { a: {b} } = ...`
765        // `{b}` in `let { a: {b} = 1 } = ...`
766        // `[b]` in `let { a: [b] } = ...`
767        // `[b]` in `let { a: [b] = 1 } = ...`
768        // `a` in `let [a] = ...`
769        // `a` in `let [a = 1] = ...`
770        // `a` in `let [...a] = ...`
771        // `{a}` in `let [{a}] = ...`
772        // `{a}` in `let [{a} = 1] = ...`
773        // `[a]` in `let [[a]] = ...`
774        // `[a]` in `let [[a] = 1] = ...`
775        return bindingElement.name;
776    }
777
778    if (isObjectLiteralElementLike(bindingElement)) {
779        switch (bindingElement.kind) {
780            case SyntaxKind.PropertyAssignment:
781                // `b` in `({ a: b } = ...)`
782                // `b` in `({ a: b = 1 } = ...)`
783                // `{b}` in `({ a: {b} } = ...)`
784                // `{b}` in `({ a: {b} = 1 } = ...)`
785                // `[b]` in `({ a: [b] } = ...)`
786                // `[b]` in `({ a: [b] = 1 } = ...)`
787                // `b.c` in `({ a: b.c } = ...)`
788                // `b.c` in `({ a: b.c = 1 } = ...)`
789                // `b[0]` in `({ a: b[0] } = ...)`
790                // `b[0]` in `({ a: b[0] = 1 } = ...)`
791                return getTargetOfBindingOrAssignmentElement(bindingElement.initializer as BindingOrAssignmentElement);
792
793            case SyntaxKind.ShorthandPropertyAssignment:
794                // `a` in `({ a } = ...)`
795                // `a` in `({ a = 1 } = ...)`
796                return bindingElement.name;
797
798            case SyntaxKind.SpreadAssignment:
799                // `a` in `({ ...a } = ...)`
800                return getTargetOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement);
801        }
802
803    }
804
805    if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
806        // `a` in `[a = 1] = ...`
807        // `{a}` in `[{a} = 1] = ...`
808        // `[a]` in `[[a] = 1] = ...`
809        // `a.b` in `[a.b = 1] = ...`
810        // `a[0]` in `[a[0] = 1] = ...`
811        return getTargetOfBindingOrAssignmentElement(bindingElement.left as BindingOrAssignmentElement);
812    }
813
814    if (isSpreadElement(bindingElement)) {
815        // `a` in `[...a] = ...`
816        return getTargetOfBindingOrAssignmentElement(bindingElement.expression as BindingOrAssignmentElement);
817    }
818
819    // `a` in `[a] = ...`
820    // `{a}` in `[{a}] = ...`
821    // `[a]` in `[[a]] = ...`
822    // `a.b` in `[a.b] = ...`
823    // `a[0]` in `[a[0]] = ...`
824    return bindingElement;
825}
826
827/**
828 * Determines whether an BindingOrAssignmentElement is a rest element.
829 *
830 * @internal
831 */
832export function getRestIndicatorOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): BindingOrAssignmentElementRestIndicator | undefined {
833    switch (bindingElement.kind) {
834        case SyntaxKind.Parameter:
835        case SyntaxKind.BindingElement:
836            // `...` in `let [...a] = ...`
837            return bindingElement.dotDotDotToken;
838
839        case SyntaxKind.SpreadElement:
840        case SyntaxKind.SpreadAssignment:
841            // `...` in `[...a] = ...`
842            return bindingElement;
843    }
844
845    return undefined;
846}
847
848/**
849 * Gets the property name of a BindingOrAssignmentElement
850 *
851 * @internal
852 */
853export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined {
854    const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement);
855    Debug.assert(!!propertyName || isSpreadAssignment(bindingElement), "Invalid property name for binding element.");
856    return propertyName;
857}
858
859/** @internal */
860export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateIdentifier> | undefined {
861    switch (bindingElement.kind) {
862        case SyntaxKind.BindingElement:
863            // `a` in `let { a: b } = ...`
864            // `[a]` in `let { [a]: b } = ...`
865            // `"a"` in `let { "a": b } = ...`
866            // `1` in `let { 1: b } = ...`
867            if (bindingElement.propertyName) {
868                const propertyName = bindingElement.propertyName;
869                if (isPrivateIdentifier(propertyName)) {
870                    return Debug.failBadSyntaxKind(propertyName);
871                }
872                return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression)
873                    ? propertyName.expression
874                    : propertyName;
875            }
876
877            break;
878
879        case SyntaxKind.PropertyAssignment:
880            // `a` in `({ a: b } = ...)`
881            // `[a]` in `({ [a]: b } = ...)`
882            // `"a"` in `({ "a": b } = ...)`
883            // `1` in `({ 1: b } = ...)`
884            if (bindingElement.name) {
885                const propertyName = bindingElement.name;
886                if (isPrivateIdentifier(propertyName)) {
887                    return Debug.failBadSyntaxKind(propertyName);
888                }
889                return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression)
890                    ? propertyName.expression
891                    : propertyName;
892            }
893
894            break;
895
896        case SyntaxKind.SpreadAssignment:
897            // `a` in `({ ...a } = ...)`
898            if (bindingElement.name && isPrivateIdentifier(bindingElement.name)) {
899                return Debug.failBadSyntaxKind(bindingElement.name);
900            }
901            return bindingElement.name;
902    }
903
904    const target = getTargetOfBindingOrAssignmentElement(bindingElement);
905    if (target && isPropertyName(target)) {
906        return target;
907    }
908}
909
910function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral {
911    const kind = node.kind;
912    return kind === SyntaxKind.StringLiteral
913        || kind === SyntaxKind.NumericLiteral;
914}
915
916/**
917 * Gets the elements of a BindingOrAssignmentPattern
918 *
919 * @internal
920 */
921export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): readonly BindingOrAssignmentElement[] {
922    switch (name.kind) {
923        case SyntaxKind.ObjectBindingPattern:
924        case SyntaxKind.ArrayBindingPattern:
925        case SyntaxKind.ArrayLiteralExpression:
926            // `a` in `{a}`
927            // `a` in `[a]`
928            return name.elements as readonly BindingOrAssignmentElement[];
929
930        case SyntaxKind.ObjectLiteralExpression:
931            // `a` in `{a}`
932            return name.properties as readonly BindingOrAssignmentElement[];
933    }
934}
935
936/** @internal */
937export function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) {
938    if (fullName) {
939        let rightNode = fullName;
940        while (true) {
941            if (isIdentifier(rightNode) || !rightNode.body) {
942                return isIdentifier(rightNode) ? rightNode : rightNode.name;
943            }
944            rightNode = rightNode.body;
945        }
946    }
947}
948
949/** @internal */
950export function canHaveIllegalType(node: Node): node is HasIllegalType {
951    const kind = node.kind;
952    return kind === SyntaxKind.Constructor
953        || kind === SyntaxKind.SetAccessor;
954}
955
956/** @internal */
957export function canHaveIllegalTypeParameters(node: Node): node is HasIllegalTypeParameters {
958    const kind = node.kind;
959    return kind === SyntaxKind.Constructor
960        || kind === SyntaxKind.GetAccessor
961        || kind === SyntaxKind.SetAccessor;
962}
963
964/** @internal */
965export function canHaveIllegalDecorators(node: Node): node is HasIllegalDecorators {
966    const kind = node.kind;
967    return kind === SyntaxKind.PropertyAssignment
968        || kind === SyntaxKind.ShorthandPropertyAssignment
969        || kind === SyntaxKind.FunctionDeclaration
970        || kind === SyntaxKind.Constructor
971        || kind === SyntaxKind.IndexSignature
972        || kind === SyntaxKind.ClassStaticBlockDeclaration
973        || kind === SyntaxKind.MissingDeclaration
974        || kind === SyntaxKind.VariableStatement
975        || kind === SyntaxKind.InterfaceDeclaration
976        || kind === SyntaxKind.TypeAliasDeclaration
977        || kind === SyntaxKind.EnumDeclaration
978        || kind === SyntaxKind.ModuleDeclaration
979        || kind === SyntaxKind.ImportEqualsDeclaration
980        || kind === SyntaxKind.ImportDeclaration
981        || kind === SyntaxKind.NamespaceExportDeclaration
982        || kind === SyntaxKind.ExportDeclaration
983        || kind === SyntaxKind.ExportAssignment;
984}
985
986/** @internal */
987export function canHaveIllegalModifiers(node: Node): node is HasIllegalModifiers {
988    const kind = node.kind;
989    return kind === SyntaxKind.ClassStaticBlockDeclaration
990        || kind === SyntaxKind.PropertyAssignment
991        || kind === SyntaxKind.ShorthandPropertyAssignment
992        || kind === SyntaxKind.FunctionType
993        || kind === SyntaxKind.MissingDeclaration
994        || kind === SyntaxKind.NamespaceExportDeclaration;
995}
996
997/** @internal */
998export const isTypeNodeOrTypeParameterDeclaration = or(isTypeNode, isTypeParameterDeclaration) as (node: Node) => node is TypeNode | TypeParameterDeclaration;
999/** @internal */
1000export const isQuestionOrExclamationToken = or(isQuestionToken, isExclamationToken) as (node: Node) => node is QuestionToken | ExclamationToken;
1001/** @internal */
1002export const isIdentifierOrThisTypeNode = or(isIdentifier, isThisTypeNode) as (node: Node) => node is Identifier | ThisTypeNode;
1003/** @internal */
1004export const isReadonlyKeywordOrPlusOrMinusToken = or(isReadonlyKeyword, isPlusToken, isMinusToken) as (node: Node) => node is ReadonlyKeyword | PlusToken | MinusToken;
1005/** @internal */
1006export const isQuestionOrPlusOrMinusToken = or(isQuestionToken, isPlusToken, isMinusToken) as (node: Node) => node is QuestionToken | PlusToken | MinusToken;
1007/** @internal */
1008export const isModuleName = or(isIdentifier, isStringLiteral) as (node: Node) => node is ModuleName;
1009
1010/** @internal */
1011export function isLiteralTypeLikeExpression(node: Node): node is NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression {
1012    const kind = node.kind;
1013    return kind === SyntaxKind.NullKeyword
1014        || kind === SyntaxKind.TrueKeyword
1015        || kind === SyntaxKind.FalseKeyword
1016        || isLiteralExpression(node)
1017        || isPrefixUnaryExpression(node);
1018}
1019
1020function isExponentiationOperator(kind: SyntaxKind): kind is ExponentiationOperator {
1021    return kind === SyntaxKind.AsteriskAsteriskToken;
1022}
1023
1024function isMultiplicativeOperator(kind: SyntaxKind): kind is MultiplicativeOperator {
1025    return kind === SyntaxKind.AsteriskToken
1026        || kind === SyntaxKind.SlashToken
1027        || kind === SyntaxKind.PercentToken;
1028}
1029
1030function isMultiplicativeOperatorOrHigher(kind: SyntaxKind): kind is MultiplicativeOperatorOrHigher {
1031    return isExponentiationOperator(kind)
1032        || isMultiplicativeOperator(kind);
1033}
1034
1035function isAdditiveOperator(kind: SyntaxKind): kind is AdditiveOperator {
1036    return kind === SyntaxKind.PlusToken
1037        || kind === SyntaxKind.MinusToken;
1038}
1039
1040function isAdditiveOperatorOrHigher(kind: SyntaxKind): kind is AdditiveOperatorOrHigher {
1041    return isAdditiveOperator(kind)
1042        || isMultiplicativeOperatorOrHigher(kind);
1043}
1044
1045function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator {
1046    return kind === SyntaxKind.LessThanLessThanToken
1047        || kind === SyntaxKind.GreaterThanGreaterThanToken
1048        || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken;
1049}
1050
1051function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher {
1052    return isShiftOperator(kind)
1053        || isAdditiveOperatorOrHigher(kind);
1054}
1055
1056function isRelationalOperator(kind: SyntaxKind): kind is RelationalOperator {
1057    return kind === SyntaxKind.LessThanToken
1058        || kind === SyntaxKind.LessThanEqualsToken
1059        || kind === SyntaxKind.GreaterThanToken
1060        || kind === SyntaxKind.GreaterThanEqualsToken
1061        || kind === SyntaxKind.InstanceOfKeyword
1062        || kind === SyntaxKind.InKeyword;
1063}
1064
1065function isRelationalOperatorOrHigher(kind: SyntaxKind): kind is RelationalOperatorOrHigher {
1066    return isRelationalOperator(kind)
1067        || isShiftOperatorOrHigher(kind);
1068}
1069
1070function isEqualityOperator(kind: SyntaxKind): kind is EqualityOperator {
1071    return kind === SyntaxKind.EqualsEqualsToken
1072        || kind === SyntaxKind.EqualsEqualsEqualsToken
1073        || kind === SyntaxKind.ExclamationEqualsToken
1074        || kind === SyntaxKind.ExclamationEqualsEqualsToken;
1075}
1076
1077function isEqualityOperatorOrHigher(kind: SyntaxKind): kind is EqualityOperatorOrHigher {
1078    return isEqualityOperator(kind)
1079        || isRelationalOperatorOrHigher(kind);
1080}
1081
1082function isBitwiseOperator(kind: SyntaxKind): kind is BitwiseOperator {
1083    return kind === SyntaxKind.AmpersandToken
1084        || kind === SyntaxKind.BarToken
1085        || kind === SyntaxKind.CaretToken;
1086}
1087
1088function isBitwiseOperatorOrHigher(kind: SyntaxKind): kind is BitwiseOperatorOrHigher {
1089    return isBitwiseOperator(kind)
1090        || isEqualityOperatorOrHigher(kind);
1091}
1092
1093// NOTE: The version in utilities includes ExclamationToken, which is not a binary operator.
1094function isLogicalOperator(kind: SyntaxKind): kind is LogicalOperator {
1095    return kind === SyntaxKind.AmpersandAmpersandToken
1096        || kind === SyntaxKind.BarBarToken;
1097}
1098
1099function isLogicalOperatorOrHigher(kind: SyntaxKind): kind is LogicalOperatorOrHigher {
1100    return isLogicalOperator(kind)
1101        || isBitwiseOperatorOrHigher(kind);
1102}
1103
1104function isAssignmentOperatorOrHigher(kind: SyntaxKind): kind is AssignmentOperatorOrHigher {
1105    return kind === SyntaxKind.QuestionQuestionToken
1106        || isLogicalOperatorOrHigher(kind)
1107        || isAssignmentOperator(kind);
1108}
1109
1110function isBinaryOperator(kind: SyntaxKind): kind is BinaryOperator {
1111    return isAssignmentOperatorOrHigher(kind)
1112        || kind === SyntaxKind.CommaToken;
1113}
1114
1115/** @internal */
1116export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken {
1117    return isBinaryOperator(node.kind);
1118}
1119
1120type BinaryExpressionState = <TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, outerState: TOuterState) => number;
1121
1122namespace BinaryExpressionState {
1123    /**
1124     * Handles walking into a `BinaryExpression`.
1125     * @param machine State machine handler functions
1126     * @param frame The current frame
1127     * @returns The new frame
1128     */
1129    export function enter<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, outerState: TOuterState): number {
1130        const prevUserState = stackIndex > 0 ? userStateStack[stackIndex - 1] : undefined;
1131        Debug.assertEqual(stateStack[stackIndex], enter);
1132        userStateStack[stackIndex] = machine.onEnter(nodeStack[stackIndex], prevUserState, outerState);
1133        stateStack[stackIndex] = nextState(machine, enter);
1134        return stackIndex;
1135    }
1136
1137    /**
1138     * Handles walking the `left` side of a `BinaryExpression`.
1139     * @param machine State machine handler functions
1140     * @param frame The current frame
1141     * @returns The new frame
1142     */
1143    export function left<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number {
1144        Debug.assertEqual(stateStack[stackIndex], left);
1145        Debug.assertIsDefined(machine.onLeft);
1146        stateStack[stackIndex] = nextState(machine, left);
1147        const nextNode = machine.onLeft(nodeStack[stackIndex].left, userStateStack[stackIndex], nodeStack[stackIndex]);
1148        if (nextNode) {
1149            checkCircularity(stackIndex, nodeStack, nextNode);
1150            return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode);
1151        }
1152        return stackIndex;
1153    }
1154
1155    /**
1156     * Handles walking the `operatorToken` of a `BinaryExpression`.
1157     * @param machine State machine handler functions
1158     * @param frame The current frame
1159     * @returns The new frame
1160     */
1161    export function operator<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number {
1162        Debug.assertEqual(stateStack[stackIndex], operator);
1163        Debug.assertIsDefined(machine.onOperator);
1164        stateStack[stackIndex] = nextState(machine, operator);
1165        machine.onOperator(nodeStack[stackIndex].operatorToken, userStateStack[stackIndex], nodeStack[stackIndex]);
1166        return stackIndex;
1167    }
1168
1169    /**
1170     * Handles walking the `right` side of a `BinaryExpression`.
1171     * @param machine State machine handler functions
1172     * @param frame The current frame
1173     * @returns The new frame
1174     */
1175    export function right<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number {
1176        Debug.assertEqual(stateStack[stackIndex], right);
1177        Debug.assertIsDefined(machine.onRight);
1178        stateStack[stackIndex] = nextState(machine, right);
1179        const nextNode = machine.onRight(nodeStack[stackIndex].right, userStateStack[stackIndex], nodeStack[stackIndex]);
1180        if (nextNode) {
1181            checkCircularity(stackIndex, nodeStack, nextNode);
1182            return pushStack(stackIndex, stateStack, nodeStack, userStateStack, nextNode);
1183        }
1184        return stackIndex;
1185    }
1186
1187    /**
1188     * Handles walking out of a `BinaryExpression`.
1189     * @param machine State machine handler functions
1190     * @param frame The current frame
1191     * @returns The new frame
1192     */
1193    export function exit<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], resultHolder: { value: TResult }, _outerState: TOuterState): number {
1194        Debug.assertEqual(stateStack[stackIndex], exit);
1195        stateStack[stackIndex] = nextState(machine, exit);
1196        const result = machine.onExit(nodeStack[stackIndex], userStateStack[stackIndex]);
1197        if (stackIndex > 0) {
1198            stackIndex--;
1199            if (machine.foldState) {
1200                const side = stateStack[stackIndex] === exit ? "right" : "left";
1201                userStateStack[stackIndex] = machine.foldState(userStateStack[stackIndex], result, side);
1202            }
1203        }
1204        else {
1205            resultHolder.value = result;
1206        }
1207        return stackIndex;
1208    }
1209
1210    /**
1211     * Handles a frame that is already done.
1212     * @returns The `done` state.
1213     */
1214    export function done<TOuterState, TState, TResult>(_machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, stackIndex: number, stateStack: BinaryExpressionState[], _nodeStack: BinaryExpression[], _userStateStack: TState[], _resultHolder: { value: TResult }, _outerState: TOuterState): number {
1215        Debug.assertEqual(stateStack[stackIndex], done);
1216        return stackIndex;
1217    }
1218
1219    export function nextState<TOuterState, TState, TResult>(machine: BinaryExpressionStateMachine<TOuterState, TState, TResult>, currentState: BinaryExpressionState) {
1220        switch (currentState) {
1221            case enter:
1222                if (machine.onLeft) return left;
1223                // falls through
1224            case left:
1225                if (machine.onOperator) return operator;
1226                // falls through
1227            case operator:
1228                if (machine.onRight) return right;
1229                // falls through
1230            case right: return exit;
1231            case exit: return done;
1232            case done: return done;
1233            default: Debug.fail("Invalid state");
1234        }
1235    }
1236
1237    function pushStack<TState>(stackIndex: number, stateStack: BinaryExpressionState[], nodeStack: BinaryExpression[], userStateStack: TState[], node: BinaryExpression) {
1238        stackIndex++;
1239        stateStack[stackIndex] = enter;
1240        nodeStack[stackIndex] = node;
1241        userStateStack[stackIndex] = undefined!;
1242        return stackIndex;
1243    }
1244
1245    function checkCircularity(stackIndex: number, nodeStack: BinaryExpression[], node: BinaryExpression) {
1246        if (Debug.shouldAssert(AssertionLevel.Aggressive)) {
1247            while (stackIndex >= 0) {
1248                Debug.assert(nodeStack[stackIndex] !== node, "Circular traversal detected.");
1249                stackIndex--;
1250            }
1251        }
1252    }
1253}
1254
1255/**
1256 * Holds state machine handler functions
1257 */
1258class BinaryExpressionStateMachine<TOuterState, TState, TResult> {
1259    constructor(
1260        readonly onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState,
1261        readonly onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1262        readonly onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined,
1263        readonly onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1264        readonly onExit: (node: BinaryExpression, userState: TState) => TResult,
1265        readonly foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined,
1266    ) {
1267    }
1268}
1269
1270/**
1271 * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree.
1272 * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking.
1273 * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side.
1274 * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node.
1275 * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame.
1276 * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent.
1277 * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node.
1278 *
1279 * @internal
1280 */
1281 export function createBinaryExpressionTrampoline<TState, TResult>(
1282    onEnter: (node: BinaryExpression, prev: TState | undefined) => TState,
1283    onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1284    onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined,
1285    onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1286    onExit: (node: BinaryExpression, userState: TState) => TResult,
1287    foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined,
1288): (node: BinaryExpression) => TResult;
1289/**
1290 * Creates a state machine that walks a `BinaryExpression` using the heap to reduce call-stack depth on a large tree.
1291 * @param onEnter Callback evaluated when entering a `BinaryExpression`. Returns new user-defined state to associate with the node while walking.
1292 * @param onLeft Callback evaluated when walking the left side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the right side.
1293 * @param onRight Callback evaluated when walking the right side of a `BinaryExpression`. Return a `BinaryExpression` to continue walking, or `void` to advance to the end of the node.
1294 * @param onExit Callback evaluated when exiting a `BinaryExpression`. The result returned will either be folded into the parent's state, or returned from the walker if at the top frame.
1295 * @param foldState Callback evaluated when the result from a nested `onExit` should be folded into the state of that node's parent.
1296 * @returns A function that walks a `BinaryExpression` node using the above callbacks, returning the result of the call to `onExit` from the outermost `BinaryExpression` node.
1297 *
1298 * @internal
1299 */
1300export function createBinaryExpressionTrampoline<TOuterState, TState, TResult>(
1301    onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState,
1302    onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1303    onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined,
1304    onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1305    onExit: (node: BinaryExpression, userState: TState) => TResult,
1306    foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined,
1307): (node: BinaryExpression, outerState: TOuterState) => TResult;
1308/** @internal */
1309export function createBinaryExpressionTrampoline<TOuterState, TState, TResult>(
1310    onEnter: (node: BinaryExpression, prev: TState | undefined, outerState: TOuterState) => TState,
1311    onLeft: ((left: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1312    onOperator: ((operatorToken: BinaryOperatorToken, userState: TState, node: BinaryExpression) => void) | undefined,
1313    onRight: ((right: Expression, userState: TState, node: BinaryExpression) => BinaryExpression | void) | undefined,
1314    onExit: (node: BinaryExpression, userState: TState) => TResult,
1315    foldState: ((userState: TState, result: TResult, side: "left" | "right") => TState) | undefined,
1316) {
1317    const machine = new BinaryExpressionStateMachine(onEnter, onLeft, onOperator, onRight, onExit, foldState);
1318    return trampoline;
1319
1320    function trampoline(node: BinaryExpression, outerState?: TOuterState) {
1321        const resultHolder: { value: TResult } = { value: undefined! };
1322        const stateStack: BinaryExpressionState[] = [BinaryExpressionState.enter];
1323        const nodeStack: BinaryExpression[] = [node];
1324        const userStateStack: TState[] = [undefined!];
1325        let stackIndex = 0;
1326        while (stateStack[stackIndex] !== BinaryExpressionState.done) {
1327            stackIndex = stateStack[stackIndex](machine, stackIndex, stateStack, nodeStack, userStateStack, resultHolder, outerState);
1328        }
1329        Debug.assertEqual(stackIndex, 0);
1330        return resultHolder.value;
1331    }
1332}
1333
1334/**
1335 * If `nodes` is not undefined, creates an empty `NodeArray` that preserves the `pos` and `end` of `nodes`.
1336 * @internal
1337 */
1338export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T>): NodeArray<T>;
1339/** @internal */
1340export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined;
1341/** @internal */
1342export function elideNodes<T extends Node>(factory: NodeFactory, nodes: NodeArray<T> | undefined): NodeArray<T> | undefined {
1343    if (nodes === undefined) return undefined;
1344    if (nodes.length === 0) return nodes;
1345    return setTextRange(factory.createNodeArray([], nodes.hasTrailingComma), nodes);
1346}
1347
1348/**
1349 * Gets the node from which a name should be generated.
1350 *
1351 * @internal
1352 */
1353export function getNodeForGeneratedName(name: GeneratedIdentifier | GeneratedPrivateIdentifier) {
1354    if (name.autoGenerateFlags & GeneratedIdentifierFlags.Node) {
1355        const autoGenerateId = name.autoGenerateId;
1356        let node = name as Node;
1357        let original = node.original;
1358        while (original) {
1359            node = original;
1360
1361            // if "node" is a different generated name (having a different "autoGenerateId"), use it and stop traversing.
1362            if (isMemberName(node)
1363                && !!(node.autoGenerateFlags! & GeneratedIdentifierFlags.Node)
1364                && node.autoGenerateId !== autoGenerateId) {
1365                break;
1366            }
1367
1368            original = node.original;
1369        }
1370        // otherwise, return the original node for the source
1371        return node;
1372    }
1373    return name;
1374}
1375
1376/**
1377 * Formats a prefix or suffix of a generated name.
1378 *
1379 * @internal
1380 */
1381export function formatGeneratedNamePart(part: string | undefined): string;
1382/**
1383 * Formats a prefix or suffix of a generated name. If the part is a {@link GeneratedNamePart}, calls {@link generateName} to format the source node.
1384 *
1385 * @internal
1386 */
1387export function formatGeneratedNamePart(part: string | GeneratedNamePart | undefined, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string;
1388/** @internal */
1389export function formatGeneratedNamePart(part: string | GeneratedNamePart | undefined, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string {
1390    return typeof part === "object" ? formatGeneratedName(/*privateName*/ false, part.prefix, part.node, part.suffix, generateName!) :
1391        typeof part === "string" ? part.length > 0 && part.charCodeAt(0) === CharacterCodes.hash ? part.slice(1) : part :
1392        "";
1393}
1394
1395function formatIdentifier(name: string | Identifier | PrivateIdentifier, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) {
1396    return typeof name === "string" ? name :
1397        formatIdentifierWorker(name, Debug.checkDefined(generateName));
1398}
1399
1400function formatIdentifierWorker(node: Identifier | PrivateIdentifier, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) {
1401    return isGeneratedPrivateIdentifier(node) ? generateName(node).slice(1) :
1402        isGeneratedIdentifier(node) ? generateName(node) :
1403        isPrivateIdentifier(node) ? (node.escapedText as string).slice(1) :
1404        idText(node);
1405}
1406
1407/**
1408 * Formats a generated name.
1409 * @param privateName When `true`, inserts a `#` character at the start of the result.
1410 * @param prefix The prefix (if any) to include before the base name.
1411 * @param baseName The base name for the generated name.
1412 * @param suffix The suffix (if any) to include after the base name.
1413 *
1414 * @internal
1415 */
1416export function formatGeneratedName(privateName: boolean, prefix: string | undefined, baseName: string, suffix: string | undefined): string;
1417/**
1418 * Formats a generated name.
1419 * @param privateName When `true`, inserts a `#` character at the start of the result.
1420 * @param prefix The prefix (if any) to include before the base name.
1421 * @param baseName The base name for the generated name.
1422 * @param suffix The suffix (if any) to include after the base name.
1423 * @param generateName Called to format the source node of {@link prefix} when it is a {@link GeneratedNamePart}.
1424 *
1425 * @internal
1426 */
1427export function formatGeneratedName(privateName: boolean, prefix: string | GeneratedNamePart | undefined, baseName: string | Identifier | PrivateIdentifier, suffix: string | GeneratedNamePart | undefined, generateName: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string): string;
1428/** @internal */
1429export function formatGeneratedName(privateName: boolean, prefix: string | GeneratedNamePart | undefined, baseName: string | Identifier | PrivateIdentifier, suffix: string | GeneratedNamePart | undefined, generateName?: (name: GeneratedIdentifier | GeneratedPrivateIdentifier) => string) {
1430    prefix = formatGeneratedNamePart(prefix, generateName!);
1431    suffix = formatGeneratedNamePart(suffix, generateName!);
1432    baseName = formatIdentifier(baseName, generateName);
1433    return `${privateName ? "#" : ""}${prefix}${baseName}${suffix}`;
1434}
1435
1436
1437/**
1438 * Creates a private backing field for an `accessor` {@link PropertyDeclaration}.
1439 *
1440 * @internal
1441 */
1442export function createAccessorPropertyBackingField(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, initializer: Expression | undefined) {
1443    return factory.updatePropertyDeclaration(
1444        node,
1445        modifiers,
1446        factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage"),
1447        /*questionOrExclamationToken*/ undefined,
1448        /*type*/ undefined,
1449        initializer
1450    );
1451}
1452
1453/**
1454 * Creates a {@link GetAccessorDeclaration} that reads from a private backing field.
1455 *
1456 * @internal
1457 */
1458export function createAccessorPropertyGetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): GetAccessorDeclaration {
1459    return factory.createGetAccessorDeclaration(
1460        modifiers,
1461        name,
1462        [],
1463        /*type*/ undefined,
1464        factory.createBlock([
1465            factory.createReturnStatement(
1466                factory.createPropertyAccessExpression(
1467                    factory.createThis(),
1468                    factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage")
1469                )
1470            )
1471        ])
1472    );
1473}
1474
1475/**
1476 * Creates a {@link SetAccessorDeclaration} that writes to a private backing field.
1477 *
1478 * @internal
1479 */
1480export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: ModifiersArray | undefined, name: PropertyName): SetAccessorDeclaration {
1481    return factory.createSetAccessorDeclaration(
1482        modifiers,
1483        name,
1484        [factory.createParameterDeclaration(
1485            /*modifiers*/ undefined,
1486            /*dotdotDotToken*/ undefined,
1487            "value"
1488        )],
1489        factory.createBlock([
1490            factory.createExpressionStatement(
1491                factory.createAssignment(
1492                    factory.createPropertyAccessExpression(
1493                        factory.createThis(),
1494                        factory.getGeneratedPrivateNameForNode(node.name, /*prefix*/ undefined, "_accessor_storage")
1495                    ),
1496                    factory.createIdentifier("value")
1497                )
1498            )
1499        ])
1500    );
1501}
1502