Skip to content

Commit 49272c1

Browse files
committed
Merge remote-tracking branch 'me/pr-parens-flow'
I.e., this PR branch: benjamn#1127
2 parents 7635920 + 08b0ed8 commit 49272c1

File tree

2 files changed

+195
-20
lines changed

2 files changed

+195
-20
lines changed

lib/fast-path.ts

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,13 @@ FPp.needsParens = function (assumeExpressionContext) {
329329
const name = this.getName();
330330

331331
// If the value of this path is some child of a Node and not a Node
332-
// itself, then it doesn't need parentheses. Only Node objects (in fact,
333-
// only Expression nodes) need parentheses.
332+
// itself, then it doesn't need parentheses. Only Node objects
333+
// need parentheses.
334334
if (this.getValue() !== node) {
335335
return false;
336336
}
337337

338-
// Only statements don't need parentheses.
338+
// Statements don't need parentheses.
339339
if (n.Statement.check(node)) {
340340
return false;
341341
}
@@ -424,13 +424,6 @@ FPp.needsParens = function (assumeExpressionContext) {
424424
return true;
425425
}
426426

427-
case "OptionalIndexedAccessType":
428-
return node.optional && parent.type === "IndexedAccessType";
429-
430-
case "IntersectionTypeAnnotation":
431-
case "UnionTypeAnnotation":
432-
return parent.type === "NullableTypeAnnotation";
433-
434427
case "Literal":
435428
return (
436429
parent.type === "MemberExpression" &&
@@ -531,6 +524,89 @@ FPp.needsParens = function (assumeExpressionContext) {
531524
) {
532525
return true;
533526
}
527+
break;
528+
529+
// Flow type nodes.
530+
//
531+
// (TS type nodes don't need any logic here, because they represent
532+
// parentheses explicitly in the AST, with TSParenthesizedType.)
533+
534+
case "OptionalIndexedAccessType":
535+
switch (parent.type) {
536+
case "IndexedAccessType":
537+
// `(O?.['x'])['y']` is distinct from `O?.['x']['y']`.
538+
return name === "objectType" && parent.objectType === node;
539+
default:
540+
return false;
541+
}
542+
543+
case "IndexedAccessType":
544+
case "ArrayTypeAnnotation":
545+
return false;
546+
547+
case "NullableTypeAnnotation":
548+
switch (parent.type) {
549+
case "OptionalIndexedAccessType":
550+
case "IndexedAccessType":
551+
return name === "objectType" && parent.objectType === node;
552+
case "ArrayTypeAnnotation":
553+
return true;
554+
default:
555+
return false;
556+
}
557+
558+
case "IntersectionTypeAnnotation":
559+
switch (parent.type) {
560+
case "OptionalIndexedAccessType":
561+
case "IndexedAccessType":
562+
return name === "objectType" && parent.objectType === node;
563+
case "ArrayTypeAnnotation":
564+
case "NullableTypeAnnotation":
565+
return true;
566+
default:
567+
return false;
568+
}
569+
570+
case "UnionTypeAnnotation":
571+
switch (parent.type) {
572+
case "OptionalIndexedAccessType":
573+
case "IndexedAccessType":
574+
return name === "objectType" && parent.objectType === node;
575+
case "ArrayTypeAnnotation":
576+
case "NullableTypeAnnotation":
577+
case "IntersectionTypeAnnotation":
578+
return true;
579+
default:
580+
return false;
581+
}
582+
583+
case "FunctionTypeAnnotation":
584+
switch (parent.type) {
585+
case "OptionalIndexedAccessType":
586+
case "IndexedAccessType":
587+
return name === "objectType" && parent.objectType === node;
588+
589+
case "ArrayTypeAnnotation":
590+
// We need parens.
591+
592+
// fallthrough
593+
case "NullableTypeAnnotation":
594+
// We don't *need* any parens here… unless some ancestor
595+
// means we do, by putting a `&` or `|` on the right.
596+
// Just use parens; probably more readable that way anyway.
597+
// (FWIW, this agrees with Prettier's behavior.)
598+
599+
// fallthrough
600+
case "IntersectionTypeAnnotation":
601+
case "UnionTypeAnnotation":
602+
// We need parens if there's another `&` or `|` after this node.
603+
// For consistency, just always use parens.
604+
// (FWIW, this agrees with Prettier's behavior.)
605+
return true;
606+
607+
default:
608+
return false;
609+
}
534610
}
535611

536612
if (

test/flow.ts

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ describe("Flow type syntax", function () {
1414
parser: require("flow-parser"),
1515
};
1616

17+
function checkEquiv(a: string, b: string) {
18+
it(`handles equivalently \`${a}\` vs. \`${b}\``, () => {
19+
const aAst = parse(a, flowParserParseOptions);
20+
const bAst = parse(b, flowParserParseOptions);
21+
types.astNodesAreEquivalent.assert(aAst, bAst);
22+
});
23+
}
24+
1725
function check(source: string, parseOptions?: any) {
1826
it(`handles: ${source}`, () => {
1927
parseOptions = parseOptions || flowParserParseOptions;
@@ -25,14 +33,6 @@ describe("Flow type syntax", function () {
2533
});
2634
}
2735

28-
function checkEquiv(a: string, b: string) {
29-
it(`handles equivalently \`${a}\` vs. \`${b}\``, () => {
30-
const aAst = parse(a, flowParserParseOptions);
31-
const bAst = parse(b, flowParserParseOptions);
32-
types.astNodesAreEquivalent.assert(aAst, bAst);
33-
});
34-
}
35-
3636
describe("should parse and print type annotations correctly", function () {
3737
// Import type annotations
3838
check("import type foo from 'foo';");
@@ -276,6 +276,8 @@ describe("Flow type syntax", function () {
276276
check("type B = Array<string>?.[number];");
277277
check("type C = Obj?.['bar']['baz'];");
278278
check("type D = (Obj?.['bar'])['baz'];");
279+
check("type C3 = Obj?.['foo']['bar']['baz'];");
280+
check("type D3 = (Obj?.['foo']['bar'])['baz'];");
279281
check("type E = Obj?.['bar'][];");
280282
check("type F = Obj?.['bar'][boolean][];");
281283
check("type G = Obj['bar']?.[boolean][];");
@@ -284,15 +286,112 @@ describe("Flow type syntax", function () {
284286

285287
// Since FastPath#needsParens does not currently add any parentheses to
286288
// these expressions, make sure they do not matter for parsing the AST.
287-
288289
checkEquiv(
289290
"type F = (Obj?.['bar'])?.[string][];",
290291
"type F = Obj?.['bar']?.[string][];",
291292
);
292-
293293
checkEquiv(
294294
"type F = (Obj['bar'])?.[string][];",
295295
"type F = Obj['bar']?.[string][];",
296296
);
297297
});
298+
299+
describe("parenthesizes correctly", () => {
300+
// The basic binary operators `&` and `|`.
301+
// `&` binds tighter than `|`
302+
check("type Num = number & (empty | mixed);"); // parens needed
303+
check("type Num = number | empty & mixed;"); // equivalent to `…|(…&…)`
304+
305+
// Unary suffix `[]`, with the above.
306+
// `[]` binds tighter than `&` or `|`
307+
check("type T = (number | string)[];");
308+
check("type T = number | string[];"); // a union
309+
check("type T = (number & mixed)[];");
310+
check("type T = number & mixed[];"); // an intersection
311+
312+
// Unary prefix `?`, with the above.
313+
// `?` binds tighter than `&` or `|`
314+
check("type T = ?(A & B);");
315+
check("type T = ?(A | B);");
316+
// `?` binds less tightly than `[]`
317+
check("type T = (?number)[];"); // array of nullable
318+
check("type T = ?number[];"); // nullable of array
319+
320+
// (Optional) indexed-access types, with the above.
321+
// `[…]` and `?.[…]` bind (their left) tighter than either `&` or `|`
322+
check("type T = (O & P)['x'];");
323+
check("type T = (O | P)['x'];");
324+
check("type T = (O & P)?.['x'];");
325+
check("type T = (O | P)?.['x'];");
326+
// `[…]` and `?.[…]` bind (their left) tighter than `?`
327+
check("type T = (?O)['x'];"); // indexed-access of nullable
328+
check("type T = ?O['x'];"); // nullable of indexed-access
329+
check("type T = (?O)?.['x'];"); // optional-indexed-access of nullable
330+
check("type T = ?O?.['x'];"); // nullable of optional-indexed-access
331+
// `[…]` and `?.[…]` provide brackets on their right, so skip parens:
332+
check("type T = A[B & C];");
333+
check("type T = A[B | C];");
334+
check("type T = A[?B];");
335+
check("type T = A[B[]];");
336+
check("type T = A[B[C]];");
337+
check("type T = A[B?.[C]];");
338+
check("type T = A?.[B & C];");
339+
check("type T = A?.[B | C];");
340+
check("type T = A?.[?B];");
341+
check("type T = A?.[B[]];");
342+
check("type T = A?.[B[C]];");
343+
check("type T = A?.[B?.[C]];");
344+
// `[…]` and `?.[…]` interact in a nonobvious way:
345+
// OptionalIndexedAccessType inside IndexedAccessType.
346+
check("type T = (O?.['x']['y'])['z'];"); // indexed of optional-indexed
347+
check("type T = O?.['x']['y']['z'];"); // optional-indexed throughout
348+
349+
// Function types.
350+
// Function binds less tightly than binary operators at right:
351+
check("type T = (() => number) & O;"); // an intersection
352+
check("type T = (() => number) | void;"); // a union
353+
check("type T = () => number | void;"); // a function
354+
check("type T = (() => void)['x'];");
355+
check("type T = () => void['x'];"); // a function
356+
check("type T = (() => void)?.['x'];");
357+
check("type T = () => void?.['x'];"); // a function
358+
// … and less tightly than suffix operator:
359+
check("type T = (() => void)[];"); // an array
360+
check("type T = () => void[];"); // a function
361+
362+
// Function does bind tighter than prefix operator (how could it not?)
363+
checkEquiv("type T = ?() => void;", "type T = ?(() => void);");
364+
// … and tighter than `&` or `|` at left (ditto):
365+
checkEquiv("type T = A | () => void;", "type T = A | (() => void);");
366+
checkEquiv("type T = A & () => void;", "type T = A & (() => void);");
367+
// … but we choose to insert parens anyway:
368+
check("type T = ?(() => void);");
369+
check("type T = A | (() => void);");
370+
check("type T = A & (() => void);");
371+
// We don't insert parens for the *right* operand of indexed access,
372+
// though, that'd be silly (sillier than writing such a type at all?):
373+
check("type T = A[() => void];");
374+
check("type T = A?.[() => void];");
375+
376+
// Here's one reason we insert those parens we don't strictly have to:
377+
// Even when the parent is something at left so that function binds
378+
// tighter than it, *its* parent (or further ancestor) might be
379+
// something at right that binds tighter than function.
380+
// E.g., union of nullable of function:
381+
check("type T = ?(() => void) | A;");
382+
checkEquiv("type T = ?() => void | A;", "type T = ?() => (void | A);");
383+
// … or intersection of nullable of function:
384+
check("type T = ?(() => void) & A;");
385+
checkEquiv("type T = ?() => void & A;", "type T = ?() => (void & A);");
386+
// … or array or (optional-)indexed-access of nullable of function:
387+
check("type T = ?(() => void)[];");
388+
check("type T = ?(() => void)['x'];");
389+
check("type T = ?(() => void)?.['x'];");
390+
// … or union of intersection:
391+
check("type T = A & (() => void) | B;");
392+
// Or for an example beyond the grandparent: union of cubic nullable:
393+
check("type T = ???(() => void) | B;");
394+
// … or union of intersection of nullable:
395+
check("type T = A & ?(() => void) | B;");
396+
});
298397
});

0 commit comments

Comments
 (0)