• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    Associativity, BinaryExpression, BinaryOperator, cast, compareValues, Comparison, ConciseBody, ESMap, Expression,
3    getExpressionAssociativity, getExpressionPrecedence, getLeftmostExpression, getOperatorAssociativity,
4    getOperatorPrecedence, identity, isBinaryExpression, isBlock, isCallExpression, isCommaSequence,
5    isConditionalTypeNode, isConstructorTypeNode, isFunctionOrConstructorTypeNode, isFunctionTypeNode, isInferTypeNode,
6    isIntersectionTypeNode, isJSDocNullableType, isLeftHandSideExpression, isLiteralKind, isNamedTupleMember,
7    isNodeArray, isOptionalChain, isTypeOperatorNode, isUnaryExpression, isUnionTypeNode, last, LeftHandSideExpression,
8    Map, NamedTupleMember, NewExpression, NodeArray, NodeFactory, OperatorPrecedence, OuterExpressionKinds,
9    ParenthesizerRules, sameMap, setTextRange, skipPartiallyEmittedExpressions, some, SyntaxKind, TypeNode,
10    UnaryExpression,
11} from "../_namespaces/ts";
12
13/** @internal */
14export function createParenthesizerRules(factory: NodeFactory): ParenthesizerRules {
15    interface BinaryPlusExpression extends BinaryExpression {
16        cachedLiteralKind: SyntaxKind;
17    }
18
19    let binaryLeftOperandParenthesizerCache: ESMap<BinaryOperator, (node: Expression) => Expression> | undefined;
20    let binaryRightOperandParenthesizerCache: ESMap<BinaryOperator, (node: Expression) => Expression> | undefined;
21
22    return {
23        getParenthesizeLeftSideOfBinaryForOperator,
24        getParenthesizeRightSideOfBinaryForOperator,
25        parenthesizeLeftSideOfBinary,
26        parenthesizeRightSideOfBinary,
27        parenthesizeExpressionOfComputedPropertyName,
28        parenthesizeConditionOfConditionalExpression,
29        parenthesizeBranchOfConditionalExpression,
30        parenthesizeExpressionOfExportDefault,
31        parenthesizeExpressionOfNew,
32        parenthesizeLeftSideOfAccess,
33        parenthesizeOperandOfPostfixUnary,
34        parenthesizeOperandOfPrefixUnary,
35        parenthesizeExpressionsOfCommaDelimitedList,
36        parenthesizeExpressionForDisallowedComma,
37        parenthesizeExpressionOfExpressionStatement,
38        parenthesizeConciseBodyOfArrowFunction,
39        parenthesizeCheckTypeOfConditionalType,
40        parenthesizeExtendsTypeOfConditionalType,
41        parenthesizeConstituentTypesOfUnionType,
42        parenthesizeConstituentTypeOfUnionType,
43        parenthesizeConstituentTypesOfIntersectionType,
44        parenthesizeConstituentTypeOfIntersectionType,
45        parenthesizeOperandOfTypeOperator,
46        parenthesizeOperandOfReadonlyTypeOperator,
47        parenthesizeNonArrayTypeOfPostfixType,
48        parenthesizeElementTypesOfTupleType,
49        parenthesizeElementTypeOfTupleType,
50        parenthesizeTypeOfOptionalType,
51        parenthesizeTypeArguments,
52        parenthesizeLeadingTypeArgument,
53    };
54
55    function getParenthesizeLeftSideOfBinaryForOperator(operatorKind: BinaryOperator) {
56        binaryLeftOperandParenthesizerCache ||= new Map();
57        let parenthesizerRule = binaryLeftOperandParenthesizerCache.get(operatorKind);
58        if (!parenthesizerRule) {
59            parenthesizerRule = node => parenthesizeLeftSideOfBinary(operatorKind, node);
60            binaryLeftOperandParenthesizerCache.set(operatorKind, parenthesizerRule);
61        }
62        return parenthesizerRule;
63    }
64
65    function getParenthesizeRightSideOfBinaryForOperator(operatorKind: BinaryOperator) {
66        binaryRightOperandParenthesizerCache ||= new Map();
67        let parenthesizerRule = binaryRightOperandParenthesizerCache.get(operatorKind);
68        if (!parenthesizerRule) {
69            parenthesizerRule = node => parenthesizeRightSideOfBinary(operatorKind, /*leftSide*/ undefined, node);
70            binaryRightOperandParenthesizerCache.set(operatorKind, parenthesizerRule);
71        }
72        return parenthesizerRule;
73    }
74
75    /**
76     * Determines whether the operand to a BinaryExpression needs to be parenthesized.
77     *
78     * @param binaryOperator The operator for the BinaryExpression.
79     * @param operand The operand for the BinaryExpression.
80     * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the
81     *                           BinaryExpression.
82     */
83    function binaryOperandNeedsParentheses(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand: Expression | undefined) {
84        // If the operand has lower precedence, then it needs to be parenthesized to preserve the
85        // intent of the expression. For example, if the operand is `a + b` and the operator is
86        // `*`, then we need to parenthesize the operand to preserve the intended order of
87        // operations: `(a + b) * x`.
88        //
89        // If the operand has higher precedence, then it does not need to be parenthesized. For
90        // example, if the operand is `a * b` and the operator is `+`, then we do not need to
91        // parenthesize to preserve the intended order of operations: `a * b + x`.
92        //
93        // If the operand has the same precedence, then we need to check the associativity of
94        // the operator based on whether this is the left or right operand of the expression.
95        //
96        // For example, if `a / d` is on the right of operator `*`, we need to parenthesize
97        // to preserve the intended order of operations: `x * (a / d)`
98        //
99        // If `a ** d` is on the left of operator `**`, we need to parenthesize to preserve
100        // the intended order of operations: `(a ** b) ** c`
101        const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator);
102        const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator);
103        const emittedOperand = skipPartiallyEmittedExpressions(operand);
104        if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > OperatorPrecedence.Assignment) {
105            // We need to parenthesize arrow functions on the right side to avoid it being
106            // parsed as parenthesized expression: `a && (() => {})`
107            return true;
108        }
109        const operandPrecedence = getExpressionPrecedence(emittedOperand);
110        switch (compareValues(operandPrecedence, binaryOperatorPrecedence)) {
111            case Comparison.LessThan:
112                // If the operand is the right side of a right-associative binary operation
113                // and is a yield expression, then we do not need parentheses.
114                if (!isLeftSideOfBinary
115                    && binaryOperatorAssociativity === Associativity.Right
116                    && operand.kind === SyntaxKind.YieldExpression) {
117                    return false;
118                }
119
120                return true;
121
122            case Comparison.GreaterThan:
123                return false;
124
125            case Comparison.EqualTo:
126                if (isLeftSideOfBinary) {
127                    // No need to parenthesize the left operand when the binary operator is
128                    // left associative:
129                    //  (a*b)/x    -> a*b/x
130                    //  (a**b)/x   -> a**b/x
131                    //
132                    // Parentheses are needed for the left operand when the binary operator is
133                    // right associative:
134                    //  (a/b)**x   -> (a/b)**x
135                    //  (a**b)**x  -> (a**b)**x
136                    return binaryOperatorAssociativity === Associativity.Right;
137                }
138                else {
139                    if (isBinaryExpression(emittedOperand)
140                        && emittedOperand.operatorToken.kind === binaryOperator) {
141                        // No need to parenthesize the right operand when the binary operator and
142                        // operand are the same and one of the following:
143                        //  x*(a*b)     => x*a*b
144                        //  x|(a|b)     => x|a|b
145                        //  x&(a&b)     => x&a&b
146                        //  x^(a^b)     => x^a^b
147                        if (operatorHasAssociativeProperty(binaryOperator)) {
148                            return false;
149                        }
150
151                        // No need to parenthesize the right operand when the binary operator
152                        // is plus (+) if both the left and right operands consist solely of either
153                        // literals of the same kind or binary plus (+) expressions for literals of
154                        // the same kind (recursively).
155                        //  "a"+(1+2)       => "a"+(1+2)
156                        //  "a"+("b"+"c")   => "a"+"b"+"c"
157                        if (binaryOperator === SyntaxKind.PlusToken) {
158                            const leftKind = leftOperand ? getLiteralKindOfBinaryPlusOperand(leftOperand) : SyntaxKind.Unknown;
159                            if (isLiteralKind(leftKind) && leftKind === getLiteralKindOfBinaryPlusOperand(emittedOperand)) {
160                                return false;
161                            }
162                        }
163                    }
164
165                    // No need to parenthesize the right operand when the operand is right
166                    // associative:
167                    //  x/(a**b)    -> x/a**b
168                    //  x**(a**b)   -> x**a**b
169                    //
170                    // Parentheses are needed for the right operand when the operand is left
171                    // associative:
172                    //  x/(a*b)     -> x/(a*b)
173                    //  x**(a/b)    -> x**(a/b)
174                    const operandAssociativity = getExpressionAssociativity(emittedOperand);
175                    return operandAssociativity === Associativity.Left;
176                }
177        }
178    }
179
180    /**
181     * Determines whether a binary operator is mathematically associative.
182     *
183     * @param binaryOperator The binary operator.
184     */
185    function operatorHasAssociativeProperty(binaryOperator: SyntaxKind) {
186        // The following operators are associative in JavaScript:
187        //  (a*b)*c     -> a*(b*c)  -> a*b*c
188        //  (a|b)|c     -> a|(b|c)  -> a|b|c
189        //  (a&b)&c     -> a&(b&c)  -> a&b&c
190        //  (a^b)^c     -> a^(b^c)  -> a^b^c
191        //  (a,b),c     -> a,(b,c)  -> a,b,c
192        //
193        // While addition is associative in mathematics, JavaScript's `+` is not
194        // guaranteed to be associative as it is overloaded with string concatenation.
195        return binaryOperator === SyntaxKind.AsteriskToken
196            || binaryOperator === SyntaxKind.BarToken
197            || binaryOperator === SyntaxKind.AmpersandToken
198            || binaryOperator === SyntaxKind.CaretToken
199            || binaryOperator === SyntaxKind.CommaToken;
200    }
201
202    /**
203     * This function determines whether an expression consists of a homogeneous set of
204     * literal expressions or binary plus expressions that all share the same literal kind.
205     * It is used to determine whether the right-hand operand of a binary plus expression can be
206     * emitted without parentheses.
207     */
208    function getLiteralKindOfBinaryPlusOperand(node: Expression): SyntaxKind {
209        node = skipPartiallyEmittedExpressions(node);
210
211        if (isLiteralKind(node.kind)) {
212            return node.kind;
213        }
214
215        if (node.kind === SyntaxKind.BinaryExpression && (node as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken) {
216            if ((node as BinaryPlusExpression).cachedLiteralKind !== undefined) {
217                return (node as BinaryPlusExpression).cachedLiteralKind;
218            }
219
220            const leftKind = getLiteralKindOfBinaryPlusOperand((node as BinaryExpression).left);
221            const literalKind = isLiteralKind(leftKind)
222                && leftKind === getLiteralKindOfBinaryPlusOperand((node as BinaryExpression).right)
223                    ? leftKind
224                    : SyntaxKind.Unknown;
225
226            (node as BinaryPlusExpression).cachedLiteralKind = literalKind;
227            return literalKind;
228        }
229
230        return SyntaxKind.Unknown;
231    }
232
233    /**
234     * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended
235     * order of operations.
236     *
237     * @param binaryOperator The operator for the BinaryExpression.
238     * @param operand The operand for the BinaryExpression.
239     * @param isLeftSideOfBinary A value indicating whether the operand is the left side of the
240     *                           BinaryExpression.
241     */
242    function parenthesizeBinaryOperand(binaryOperator: SyntaxKind, operand: Expression, isLeftSideOfBinary: boolean, leftOperand?: Expression) {
243        const skipped = skipPartiallyEmittedExpressions(operand);
244
245        // If the resulting expression is already parenthesized, we do not need to do any further processing.
246        if (skipped.kind === SyntaxKind.ParenthesizedExpression) {
247            return operand;
248        }
249
250        return binaryOperandNeedsParentheses(binaryOperator, operand, isLeftSideOfBinary, leftOperand)
251            ? factory.createParenthesizedExpression(operand)
252            : operand;
253    }
254
255
256    function parenthesizeLeftSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression): Expression {
257        return parenthesizeBinaryOperand(binaryOperator, leftSide, /*isLeftSideOfBinary*/ true);
258    }
259
260    function parenthesizeRightSideOfBinary(binaryOperator: SyntaxKind, leftSide: Expression | undefined, rightSide: Expression): Expression {
261        return parenthesizeBinaryOperand(binaryOperator, rightSide, /*isLeftSideOfBinary*/ false, leftSide);
262    }
263
264    function parenthesizeExpressionOfComputedPropertyName(expression: Expression): Expression {
265        return isCommaSequence(expression) ? factory.createParenthesizedExpression(expression) : expression;
266    }
267
268    function parenthesizeConditionOfConditionalExpression(condition: Expression): Expression {
269        const conditionalPrecedence = getOperatorPrecedence(SyntaxKind.ConditionalExpression, SyntaxKind.QuestionToken);
270        const emittedCondition = skipPartiallyEmittedExpressions(condition);
271        const conditionPrecedence = getExpressionPrecedence(emittedCondition);
272        if (compareValues(conditionPrecedence, conditionalPrecedence) !== Comparison.GreaterThan) {
273            return factory.createParenthesizedExpression(condition);
274        }
275        return condition;
276    }
277
278    function parenthesizeBranchOfConditionalExpression(branch: Expression): Expression {
279        // per ES grammar both 'whenTrue' and 'whenFalse' parts of conditional expression are assignment expressions
280        // so in case when comma expression is introduced as a part of previous transformations
281        // if should be wrapped in parens since comma operator has the lowest precedence
282        const emittedExpression = skipPartiallyEmittedExpressions(branch);
283        return isCommaSequence(emittedExpression)
284            ? factory.createParenthesizedExpression(branch)
285            : branch;
286    }
287
288    /**
289     *  [Per the spec](https://tc39.github.io/ecma262/#prod-ExportDeclaration), `export default` accepts _AssigmentExpression_ but
290     *  has a lookahead restriction for `function`, `async function`, and `class`.
291     *
292     * Basically, that means we need to parenthesize in the following cases:
293     *
294     * - BinaryExpression of CommaToken
295     * - CommaList (synthetic list of multiple comma expressions)
296     * - FunctionExpression
297     * - ClassExpression
298     */
299    function parenthesizeExpressionOfExportDefault(expression: Expression): Expression {
300        const check = skipPartiallyEmittedExpressions(expression);
301        let needsParens = isCommaSequence(check);
302        if (!needsParens) {
303            switch (getLeftmostExpression(check, /*stopAtCallExpression*/ false).kind) {
304                case SyntaxKind.ClassExpression:
305                case SyntaxKind.FunctionExpression:
306                    needsParens = true;
307            }
308        }
309        return needsParens ? factory.createParenthesizedExpression(expression) : expression;
310    }
311
312    /**
313     * Wraps an expression in parentheses if it is needed in order to use the expression
314     * as the expression of a `NewExpression` node.
315     */
316    function parenthesizeExpressionOfNew(expression: Expression): LeftHandSideExpression {
317        const leftmostExpr = getLeftmostExpression(expression, /*stopAtCallExpressions*/ true);
318        switch (leftmostExpr.kind) {
319            case SyntaxKind.CallExpression:
320                return factory.createParenthesizedExpression(expression);
321
322            case SyntaxKind.NewExpression:
323                return !(leftmostExpr as NewExpression).arguments
324                    ? factory.createParenthesizedExpression(expression)
325                    : expression as LeftHandSideExpression; // TODO(rbuckton): Verify this assertion holds
326        }
327
328        return parenthesizeLeftSideOfAccess(expression);
329    }
330
331    /**
332     * Wraps an expression in parentheses if it is needed in order to use the expression for
333     * property or element access.
334     */
335    function parenthesizeLeftSideOfAccess(expression: Expression, optionalChain?: boolean): LeftHandSideExpression {
336        // isLeftHandSideExpression is almost the correct criterion for when it is not necessary
337        // to parenthesize the expression before a dot. The known exception is:
338        //
339        //    NewExpression:
340        //       new C.x        -> not the same as (new C).x
341        //
342        const emittedExpression = skipPartiallyEmittedExpressions(expression);
343        if (isLeftHandSideExpression(emittedExpression)
344            && (emittedExpression.kind !== SyntaxKind.NewExpression || (emittedExpression as NewExpression).arguments)
345            && (optionalChain || !isOptionalChain(emittedExpression))) {
346            // TODO(rbuckton): Verify whether this assertion holds.
347            return expression as LeftHandSideExpression;
348        }
349
350        // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
351        return setTextRange(factory.createParenthesizedExpression(expression), expression);
352    }
353
354    function parenthesizeOperandOfPostfixUnary(operand: Expression): LeftHandSideExpression {
355        // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
356        return isLeftHandSideExpression(operand) ? operand : setTextRange(factory.createParenthesizedExpression(operand), operand);
357    }
358
359    function parenthesizeOperandOfPrefixUnary(operand: Expression): UnaryExpression {
360        // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
361        return isUnaryExpression(operand) ? operand : setTextRange(factory.createParenthesizedExpression(operand), operand);
362    }
363
364    function parenthesizeExpressionsOfCommaDelimitedList(elements: NodeArray<Expression>): NodeArray<Expression> {
365        const result = sameMap(elements, parenthesizeExpressionForDisallowedComma);
366        return setTextRange(factory.createNodeArray(result, elements.hasTrailingComma), elements);
367    }
368
369    function parenthesizeExpressionForDisallowedComma(expression: Expression): Expression {
370        const emittedExpression = skipPartiallyEmittedExpressions(expression);
371        const expressionPrecedence = getExpressionPrecedence(emittedExpression);
372        const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken);
373        // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
374        return expressionPrecedence > commaPrecedence ? expression : setTextRange(factory.createParenthesizedExpression(expression), expression);
375    }
376
377    function parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression {
378        const emittedExpression = skipPartiallyEmittedExpressions(expression);
379        if (isCallExpression(emittedExpression)) {
380            const callee = emittedExpression.expression;
381            const kind = skipPartiallyEmittedExpressions(callee).kind;
382            if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) {
383                // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
384                const updated = factory.updateCallExpression(
385                    emittedExpression,
386                    setTextRange(factory.createParenthesizedExpression(callee), callee),
387                    emittedExpression.typeArguments,
388                    emittedExpression.arguments
389                );
390                return factory.restoreOuterExpressions(expression, updated, OuterExpressionKinds.PartiallyEmittedExpressions);
391            }
392        }
393
394        const leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind;
395        if (leftmostExpressionKind === SyntaxKind.ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind.FunctionExpression) {
396            // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
397            return setTextRange(factory.createParenthesizedExpression(expression), expression);
398        }
399
400        return expression;
401    }
402
403    function parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression;
404    function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody;
405    function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody {
406        if (!isBlock(body) && (isCommaSequence(body) || getLeftmostExpression(body, /*stopAtCallExpressions*/ false).kind === SyntaxKind.ObjectLiteralExpression)) {
407            // TODO(rbuckton): Verifiy whether `setTextRange` is needed.
408            return setTextRange(factory.createParenthesizedExpression(body), body);
409        }
410
411        return body;
412    }
413
414    // Type[Extends] :
415    //     FunctionOrConstructorType
416    //     ConditionalType[?Extends]
417
418    // ConditionalType[Extends] :
419    //     UnionType[?Extends]
420    //     [~Extends] UnionType[~Extends] `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends]
421    //
422    // - The check type (the `UnionType`, above) does not allow function, constructor, or conditional types (they must be parenthesized)
423    // - The extends type (the first `Type`, above) does not allow conditional types (they must be parenthesized). Function and constructor types are fine.
424    // - The true and false branch types (the second and third `Type` non-terminals, above) allow any type
425    function parenthesizeCheckTypeOfConditionalType(checkType: TypeNode): TypeNode {
426        switch (checkType.kind) {
427            case SyntaxKind.FunctionType:
428            case SyntaxKind.ConstructorType:
429            case SyntaxKind.ConditionalType:
430                return factory.createParenthesizedType(checkType);
431        }
432        return checkType;
433    }
434
435    function parenthesizeExtendsTypeOfConditionalType(extendsType: TypeNode): TypeNode {
436        switch (extendsType.kind) {
437            case SyntaxKind.ConditionalType:
438                return factory.createParenthesizedType(extendsType);
439        }
440        return extendsType;
441    }
442
443    // UnionType[Extends] :
444    //     `|`? IntersectionType[?Extends]
445    //     UnionType[?Extends] `|` IntersectionType[?Extends]
446    //
447    // - A union type constituent has the same precedence as the check type of a conditional type
448    function parenthesizeConstituentTypeOfUnionType(type: TypeNode) {
449        switch (type.kind) {
450            case SyntaxKind.UnionType: // Not strictly necessary, but a union containing a union should have been flattened
451            case SyntaxKind.IntersectionType: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests
452                return factory.createParenthesizedType(type);
453        }
454        return parenthesizeCheckTypeOfConditionalType(type);
455    }
456
457    function parenthesizeConstituentTypesOfUnionType(members: readonly TypeNode[]): NodeArray<TypeNode> {
458        return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfUnionType));
459    }
460
461    // IntersectionType[Extends] :
462    //     `&`? TypeOperator[?Extends]
463    //     IntersectionType[?Extends] `&` TypeOperator[?Extends]
464    //
465    // - An intersection type constituent does not allow function, constructor, conditional, or union types (they must be parenthesized)
466    function parenthesizeConstituentTypeOfIntersectionType(type: TypeNode) {
467        switch (type.kind) {
468            case SyntaxKind.UnionType:
469            case SyntaxKind.IntersectionType: // Not strictly necessary, but an intersection containing an intersection should have been flattened
470                return factory.createParenthesizedType(type);
471        }
472        return parenthesizeConstituentTypeOfUnionType(type);
473    }
474
475    function parenthesizeConstituentTypesOfIntersectionType(members: readonly TypeNode[]): NodeArray<TypeNode> {
476        return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfIntersectionType));
477    }
478
479    // TypeOperator[Extends] :
480    //     PostfixType
481    //     InferType[?Extends]
482    //     `keyof` TypeOperator[?Extends]
483    //     `unique` TypeOperator[?Extends]
484    //     `readonly` TypeOperator[?Extends]
485    //
486    function parenthesizeOperandOfTypeOperator(type: TypeNode) {
487        switch (type.kind) {
488            case SyntaxKind.IntersectionType:
489                return factory.createParenthesizedType(type);
490        }
491        return parenthesizeConstituentTypeOfIntersectionType(type);
492    }
493
494    function parenthesizeOperandOfReadonlyTypeOperator(type: TypeNode) {
495        switch (type.kind) {
496            case SyntaxKind.TypeOperator:
497                return factory.createParenthesizedType(type);
498        }
499        return parenthesizeOperandOfTypeOperator(type);
500    }
501
502    // PostfixType :
503    //     NonArrayType
504    //     NonArrayType [no LineTerminator here] `!` // JSDoc
505    //     NonArrayType [no LineTerminator here] `?` // JSDoc
506    //     IndexedAccessType
507    //     ArrayType
508    //
509    // IndexedAccessType :
510    //     NonArrayType `[` Type[~Extends] `]`
511    //
512    // ArrayType :
513    //     NonArrayType `[` `]`
514    //
515    function parenthesizeNonArrayTypeOfPostfixType(type: TypeNode) {
516        switch (type.kind) {
517            case SyntaxKind.InferType:
518            case SyntaxKind.TypeOperator:
519            case SyntaxKind.TypeQuery: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests
520                return factory.createParenthesizedType(type);
521        }
522        return parenthesizeOperandOfTypeOperator(type);
523    }
524
525    // TupleType :
526    //     `[` Elision? `]`
527    //     `[` NamedTupleElementTypes `]`
528    //     `[` NamedTupleElementTypes `,` Elision? `]`
529    //     `[` TupleElementTypes `]`
530    //     `[` TupleElementTypes `,` Elision? `]`
531    //
532    // NamedTupleElementTypes :
533    //     Elision? NamedTupleMember
534    //     NamedTupleElementTypes `,` Elision? NamedTupleMember
535    //
536    // NamedTupleMember :
537    //     Identifier `?`? `:` Type[~Extends]
538    //     `...` Identifier `:` Type[~Extends]
539    //
540    // TupleElementTypes :
541    //     Elision? TupleElementType
542    //     TupleElementTypes `,` Elision? TupleElementType
543    //
544    // TupleElementType :
545    //     Type[~Extends] // NOTE: Needs cover grammar to disallow JSDoc postfix-optional
546    //     OptionalType
547    //     RestType
548    //
549    // OptionalType :
550    //     Type[~Extends] `?` // NOTE: Needs cover grammar to disallow JSDoc postfix-optional
551    //
552    // RestType :
553    //     `...` Type[~Extends]
554    //
555    function parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray<TypeNode> {
556        return factory.createNodeArray(sameMap(types, parenthesizeElementTypeOfTupleType));
557    }
558
559    function parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode {
560        if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type);
561        return type;
562    }
563
564    function hasJSDocPostfixQuestion(type: TypeNode | NamedTupleMember): boolean {
565        if (isJSDocNullableType(type)) return type.postfix;
566        if (isNamedTupleMember(type)) return hasJSDocPostfixQuestion(type.type);
567        if (isFunctionTypeNode(type) || isConstructorTypeNode(type) || isTypeOperatorNode(type)) return hasJSDocPostfixQuestion(type.type);
568        if (isConditionalTypeNode(type)) return hasJSDocPostfixQuestion(type.falseType);
569        if (isUnionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types));
570        if (isIntersectionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types));
571        if (isInferTypeNode(type)) return !!type.typeParameter.constraint && hasJSDocPostfixQuestion(type.typeParameter.constraint);
572        return false;
573    }
574
575    function parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode {
576        if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type);
577        return parenthesizeNonArrayTypeOfPostfixType(type);
578    }
579
580    // function parenthesizeMemberOfElementType(member: TypeNode): TypeNode {
581    //     switch (member.kind) {
582    //         case SyntaxKind.UnionType:
583    //         case SyntaxKind.IntersectionType:
584    //         case SyntaxKind.FunctionType:
585    //         case SyntaxKind.ConstructorType:
586    //             return factory.createParenthesizedType(member);
587    //     }
588    //     return parenthesizeMemberOfConditionalType(member);
589    // }
590
591    // function parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode {
592    //     switch (member.kind) {
593    //         case SyntaxKind.TypeQuery:
594    //         case SyntaxKind.TypeOperator:
595    //         case SyntaxKind.InferType:
596    //             return factory.createParenthesizedType(member);
597    //     }
598    //     return parenthesizeMemberOfElementType(member);
599    // }
600
601    function parenthesizeLeadingTypeArgument(node: TypeNode) {
602        return isFunctionOrConstructorTypeNode(node) && node.typeParameters ? factory.createParenthesizedType(node) : node;
603    }
604
605    function parenthesizeOrdinalTypeArgument(node: TypeNode, i: number) {
606        return i === 0 ? parenthesizeLeadingTypeArgument(node) : node;
607    }
608
609    function parenthesizeTypeArguments(typeArguments: NodeArray<TypeNode> | undefined): NodeArray<TypeNode> | undefined {
610        if (some(typeArguments)) {
611            return factory.createNodeArray(sameMap(typeArguments, parenthesizeOrdinalTypeArgument));
612        }
613    }
614}
615
616/** @internal */
617export const nullParenthesizerRules: ParenthesizerRules = {
618    getParenthesizeLeftSideOfBinaryForOperator: _ => identity,
619    getParenthesizeRightSideOfBinaryForOperator: _ => identity,
620    parenthesizeLeftSideOfBinary: (_binaryOperator, leftSide) => leftSide,
621    parenthesizeRightSideOfBinary: (_binaryOperator, _leftSide, rightSide) => rightSide,
622    parenthesizeExpressionOfComputedPropertyName: identity,
623    parenthesizeConditionOfConditionalExpression: identity,
624    parenthesizeBranchOfConditionalExpression: identity,
625    parenthesizeExpressionOfExportDefault: identity,
626    parenthesizeExpressionOfNew: expression => cast(expression, isLeftHandSideExpression),
627    parenthesizeLeftSideOfAccess: expression => cast(expression, isLeftHandSideExpression),
628    parenthesizeOperandOfPostfixUnary: operand => cast(operand, isLeftHandSideExpression),
629    parenthesizeOperandOfPrefixUnary: operand => cast(operand, isUnaryExpression),
630    parenthesizeExpressionsOfCommaDelimitedList: nodes => cast(nodes, isNodeArray),
631    parenthesizeExpressionForDisallowedComma: identity,
632    parenthesizeExpressionOfExpressionStatement: identity,
633    parenthesizeConciseBodyOfArrowFunction: identity,
634    parenthesizeCheckTypeOfConditionalType: identity,
635    parenthesizeExtendsTypeOfConditionalType: identity,
636    parenthesizeConstituentTypesOfUnionType: nodes => cast(nodes, isNodeArray),
637    parenthesizeConstituentTypeOfUnionType: identity,
638    parenthesizeConstituentTypesOfIntersectionType: nodes => cast(nodes, isNodeArray),
639    parenthesizeConstituentTypeOfIntersectionType: identity,
640    parenthesizeOperandOfTypeOperator: identity,
641    parenthesizeOperandOfReadonlyTypeOperator: identity,
642    parenthesizeNonArrayTypeOfPostfixType: identity,
643    parenthesizeElementTypesOfTupleType: nodes => cast(nodes, isNodeArray),
644    parenthesizeElementTypeOfTupleType: identity,
645    parenthesizeTypeOfOptionalType: identity,
646    parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray),
647    parenthesizeLeadingTypeArgument: identity,
648};
649