Skip to content

Commit df3784c

Browse files
committed
Preserve required coercion grouping and update formatted sources
Signed-off-by: Christoph Knittel <ck@cca.io>
1 parent 27fee8b commit df3784c

11 files changed

Lines changed: 49 additions & 34 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262

6363
#### :nail_care: Polish
6464

65-
- Omit unnecessary parentheses around coercions in call arguments, bindings, and collection elements while preserving expression grouping. https://github.com/rescript-lang/rescript/pull/8614
65+
- Omit unnecessary parentheses around coercions in call arguments, collection elements, and array indices while preserving expression grouping. https://github.com/rescript-lang/rescript/pull/8614
6666
- Print external declarations in signatures and type errors with their processed attributes instead of the `"#rescript-external"` placeholder, and print inline constants using `@inline` syntax. https://github.com/rescript-lang/rescript/pull/8581
6767
- Improve diagnostics for dynamic imports of local values and attempts to use `import` as a first-class value. https://github.com/rescript-lang/rescript/pull/8582
6868
- Allow inferred labeled functions to be called with labels in any order by removing legacy curried-arrow commutation locks. https://github.com/rescript-lang/rescript/pull/8547

compiler/syntax/src/res_parens.ml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@ let expr ?(allow_coercion = false) expr =
1616
| {pexp_desc = Pexp_constraint _ | Pexp_coerce _} -> Parenthesized
1717
| _ -> Nothing)
1818

19-
(* A source annotation may precede :> directly, but nested coercions need
20-
grouping. Preserve explicit braces through the normal expression rule. *)
21-
let coerce_expr_operand expression =
22-
match (expr expression, expression.Parsetree.pexp_desc) with
23-
| Parenthesized, Pexp_constraint _ -> Nothing
24-
| kind, _ -> kind
25-
2619
let expr_record_row_rhs ~optional e =
2720
let kind = expr e in
2821
match kind with

compiler/syntax/src/res_parens.mli

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
type kind = Parenthesized | Braced of Location.t | Nothing
22

33
(* Set [allow_coercion] to [true] only in grammar positions accepting a trailing
4-
coercion without parentheses, such as call arguments and binding right-hand
5-
sides. Arrow bodies require parentheses. *)
4+
coercion without parentheses, such as call arguments and collection elements.
5+
Bindings and arrow bodies require parentheses. *)
66
val expr : ?allow_coercion:bool -> Parsetree.expression -> kind
77
val structure_expr : Parsetree.expression -> kind
88

@@ -40,5 +40,3 @@ val arrow_return_typ_expr : Parsetree.core_type -> bool
4040
val pattern_record_row_rhs : Parsetree.pattern -> bool
4141

4242
val expr_record_row_rhs : optional:bool -> Parsetree.expression -> kind
43-
44-
val coerce_expr_operand : Parsetree.expression -> kind

compiler/syntax/src/res_printer.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2473,7 +2473,7 @@ and print_value_binding ~state ~rec_flag (vb : Parsetree.value_binding) cmt_tbl
24732473
let opt_braces, expr = Parsetree_viewer.process_braces_attr vb.pvb_expr in
24742474
let printed_expr =
24752475
let doc = print_expression_with_comments ~state vb.pvb_expr cmt_tbl in
2476-
match Parens.expr ~allow_coercion:true vb.pvb_expr with
2476+
match Parens.expr vb.pvb_expr with
24772477
| Parens.Parenthesized -> add_parens doc
24782478
| Braced braces -> print_braces doc expr braces
24792479
| Nothing -> doc
@@ -3844,7 +3844,7 @@ and print_expression ~state (e : Parsetree.expression) cmt_tbl =
38443844
let doc_expr = print_expression_with_comments ~state expr cmt_tbl in
38453845
let doc_typ = print_typ_expr ~state typ cmt_tbl in
38463846
let doc_expr =
3847-
match Parens.coerce_expr_operand expr with
3847+
match Parens.expr expr with
38483848
| Parens.Parenthesized -> add_parens doc_expr
38493849
| Braced braces -> print_braces doc_expr expr braces
38503850
| Nothing -> doc_expr

packages/dev-playground/src/CompilerApi.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ let applyConfig = (
218218
~experimentalFeatures: array<PlaygroundConfig.experimentalFeature>,
219219
) => {
220220
if hasFunction(instance, "setModuleSystem") {
221-
instance->Instance.setModuleSystem((moduleSystem :> string))
221+
instance->Instance.setModuleSystem(moduleSystem :> string)
222222
}
223223
if hasFunction(instance, "setWarnFlags") {
224224
instance->Instance.setWarnFlags(warnFlags === "" ? defaultConfig.warnFlags : warnFlags)

tests/syntax_tests/data/printer/expr/coerce.res

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let foo = (~a=(3:int:>int), b) => 34
2525

2626
let x = (/* c0 */ x /* c1 */ :> /* c2 */ int /* c3 */)
2727

28-
// Delimited arguments and binding right-hand sides need no extra parentheses.
28+
// Delimited arguments need no extra parentheses.
2929
foo(v :> b)
3030
foo((v :> b))
3131
foo(~arg=(v :> b), ~optional=?(v :> b))
@@ -76,3 +76,15 @@ let piped = (value :> b)->foo
7676
let blockTail = {foo(); (value :> b)}
7777
let blockHead = {(value :> b); foo()}
7878
let dictSpread = dict{...(value :> dict<b>), "field": value}
79+
80+
// A constrained operand needs parentheses when the coercion is a binding RHS.
81+
let constrainedOperand = (x: t) :> u
82+
let constrainedChain = ((x: t) :> u) :> v
83+
let constrainedBlock = {(x: t) :> u}
84+
call((x: t) :> u)
85+
86+
// A following JSX element must not be read as coercion type arguments.
87+
let beforeJsx = () => {
88+
let value = (x :> string)
89+
<option value />
90+
}

tests/syntax_tests/data/printer/expr/expected/coerce.res.txt

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
let z = x :> int
1+
let z = (x :> int)
22

3-
let z2 = x :> int
3+
let z2 = (x :> int)
44

55
let foo = (x: int) => (x :> int)
66

77
let foo2 = (x: int) => (x :> int)
88

9-
let bar = x => (x: t :> int)
9+
let bar = x => ((x: t) :> int)
1010

11-
let bar2 = x => (x: t :> int)
11+
let bar2 = x => ((x: t) :> int)
1212

13-
call(~x=y :> int, ~z=w: int :> int, ~a, ~b)
13+
call(~x=y :> int, ~z=(w: int) :> int, ~a, ~b)
1414

1515
let foo = (~a=3: int, b) => 34
1616

1717
let foo = (~a=3 :> int, b) => 34
1818

19-
let foo = (~a=3: int :> int, b) => 34
19+
let foo = (~a=(3: int) :> int, b) => 34
2020

2121
// THESE SHOULD NOT PARSE: no magic in the syntax
2222
// let x: int :> string = y
2323
// let x :> string = y
2424
// let x : int1 :> int2 = 3 :> int3
2525

26-
let x = /* c0 */ x /* c1 */ :> /* c2 */ int /* c3 */
26+
let x = (/* c0 */ x /* c1 */ :> /* c2 */ int /* c3 */)
2727

28-
// Delimited arguments and binding right-hand sides need no extra parentheses.
28+
// Delimited arguments need no extra parentheses.
2929
foo(v :> b)
3030
foo(v :> b)
3131
foo(~arg=v :> b, ~optional=?v :> b)
@@ -41,15 +41,15 @@ let variant = #Value(v :> b)
4141
// Preserve grouping when the surrounding expression needs it.
4242
let result = x => (x :> b)
4343
let callback = foo(x => (x :> b))
44-
let coercedFunction = x => x :> a => b
44+
let coercedFunction = (x => x :> a => b)
4545
let call = (f :> a => b)(v)
4646
let field = (v :> b).name
4747
let objectField = (v :> b)["name"]
4848
let equalLeft = (v :> b) == w
4949
let equalRight = v == (w :> b)
5050
let unary = !(v :> b)
5151
let awaited = await (v :> b)
52-
let nested = (v :> b) :> c
52+
let nested = ((v :> b) :> c)
5353
let record = {field: (v :> b)}
5454
let conditional = condition ? (v :> b) : (w :> b)
5555
let block = {v :> b}
@@ -59,14 +59,14 @@ let jsx = <Component value={v :> b}> {v :> b} </Component>
5959
// Comments must stay attached when parentheses disappear.
6060
foo(/* before */ v /* operand */ :> /* type */ b /* after */)
6161
foo(v :> b) // trailing line comment
62-
let attributed = @foo v :> b
63-
let coercionAttribute = @foo (v :> b)
62+
let attributed = (@foo v :> b)
63+
let coercionAttribute = (@foo (v :> b))
6464
let long = functionWithAVeryLongName(valueWithAVeryLongName :> typeWithAVeryLongName)
6565

6666
let arrayIndex = values[index :> int]
6767
values[index :> int] = (value :> b)
6868
let recordSpread = {...value :> b, field: value}
69-
let bracedOperand = {value} :> b
69+
let bracedOperand = ({value} :> b)
7070
let blockSequence = {
7171
{
7272
foo()
@@ -86,3 +86,15 @@ let blockHead = {
8686
foo()
8787
}
8888
let dictSpread = dict{...value :> dict<b>, "field": value}
89+
90+
// A constrained operand needs parentheses when the coercion is a binding RHS.
91+
let constrainedOperand = ((x: t) :> u)
92+
let constrainedChain = (((x: t) :> u) :> v)
93+
let constrainedBlock = {(x: t) :> u}
94+
call((x: t) :> u)
95+
96+
// A following JSX element must not be read as coercion type arguments.
97+
let beforeJsx = () => {
98+
let value = (x :> string)
99+
<option value />
100+
}

tests/tests/src/Coercion.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ type r2 = {x: int}
88
type t1 = array<r1>
99
type t2 = array<r2>
1010

11-
let foo = (x: t1) => {(x :> t2)}
11+
let foo = (x: t1) => {x :> t2}

tests/tests/src/VariantCoercion.res

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ module CoerceFromPolyvariantToVariant = {
124124
module CoerceVariantBinaryOp = {
125125
type flag = | @as(0) A | @as(2) B
126126

127-
let x = 0->Int.bitwiseOr((B :> int))
127+
let x = 0->Int.bitwiseOr(B :> int)
128128

129129
let v = B
130130
let f1 = () =>

tests/tests/src/poly_variant_test.res

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ describe(__MODULE__, () => {
8282
})
8383

8484
test("emoji poly variant conversion", () => {
85-
eq(__LOC__, "🚀", (#"🚀": t :> string))
86-
eq(__LOC__, "🔥", (#"🔥": t :> string))
85+
eq(__LOC__, "🚀", (#"🚀": t) :> string)
86+
eq(__LOC__, "🔥", (#"🔥": t) :> string)
8787
})
8888
})

0 commit comments

Comments
 (0)