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