Skip to content

Commit 55a50a9

Browse files
authored
Fix unpacking first-class module in default argument of react component (#8296)
1 parent a8029d4 commit 55a50a9

File tree

10 files changed

+147
-52
lines changed

10 files changed

+147
-52
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- Reanalyze server: invalidate cache and recompute results when config changes in `rescript.json`. https://github.com/rescript-lang/rescript/pull/8262
2929
- Fix `null` and array values incorrectly matching the `Object` branch when pattern matching on `JSON.t` (or other untagged variants with an `Object` case) in statement position. https://github.com/rescript-lang/rescript/pull/8279
3030
- Fix rewatch panic when `package.json` has no `name` field. https://github.com/rescript-lang/rescript/pull/8291
31+
- Fix unpacking first-class module in default argument of react component. https://github.com/rescript-lang/rescript/pull/8296
3132

3233
#### :memo: Documentation
3334

compiler/syntax/src/jsx_v4.ml

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -498,15 +498,23 @@ let modified_binding ~binding_loc ~binding_pat_loc ~fn_name binding =
498498
in
499499
(wrap_expression_with_binding wrap_expression, has_forward_ref, expression)
500500

501-
let vb_match ~expr (name, default, _, alias, loc, _) =
501+
let rec strip_constraint_unpack pattern =
502+
match pattern with
503+
| {ppat_desc = Ppat_constraint (_, {ptyp_desc = Ptyp_package _})} -> pattern
504+
| {ppat_desc = Ppat_constraint (pattern, _)} ->
505+
strip_constraint_unpack pattern
506+
| _ -> pattern
507+
508+
let vb_match ~expr (name, default, pattern, _alias, loc, _) =
502509
let label = get_label name in
503510
match default with
504511
| Some default ->
512+
let resolved_name = "__" ^ label ^ "_value" in
505513
let value_binding =
506514
Vb.mk
507-
(Pat.var (Location.mkloc alias loc))
515+
(Pat.var (Location.mkloc resolved_name loc))
508516
(Exp.match_
509-
(Exp.ident {txt = Lident ("__" ^ alias); loc = Location.none})
517+
(Exp.ident {txt = Lident ("__" ^ label); loc = Location.none})
510518
[
511519
Exp.case
512520
(Pat.construct
@@ -518,7 +526,10 @@ let vb_match ~expr (name, default, _, alias, loc, _) =
518526
default;
519527
])
520528
in
521-
Exp.let_ Nonrecursive [value_binding] expr
529+
Exp.let_ Nonrecursive [value_binding]
530+
(Exp.let_ Nonrecursive
531+
[Vb.mk pattern (Exp.ident (Location.mknoloc @@ Lident resolved_name))]
532+
expr)
522533
| None -> expr
523534

524535
let vb_match_expr named_arg_list expr =
@@ -652,22 +663,6 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding =
652663
]
653664
(Exp.ident ~loc:pstr_loc {loc = empty_loc; txt = Lident txt})
654665
in
655-
let rec strip_constraint_unpack ~label pattern =
656-
match pattern with
657-
| {ppat_desc = Ppat_constraint (_, {ptyp_desc = Ptyp_package _})} ->
658-
pattern
659-
| {ppat_desc = Ppat_constraint (pattern, _)} ->
660-
strip_constraint_unpack ~label pattern
661-
| _ -> pattern
662-
in
663-
let safe_pattern_label pattern =
664-
match pattern with
665-
| {ppat_desc = Ppat_var {txt; loc}} ->
666-
{pattern with ppat_desc = Ppat_var {txt = "__" ^ txt; loc}}
667-
| {ppat_desc = Ppat_alias (p, {txt; loc})} ->
668-
{pattern with ppat_desc = Ppat_alias (p, {txt = "__" ^ txt; loc})}
669-
| _ -> pattern
670-
in
671666
let rec returned_expression patterns_with_label patterns_with_nolabel
672667
({pexp_desc} as expr) =
673668
match pexp_desc with
@@ -688,17 +683,20 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding =
688683
lhs = {ppat_loc; ppat_desc} as pattern;
689684
rhs = expr;
690685
} -> (
691-
let pattern_without_constraint =
692-
strip_constraint_unpack ~label:(get_label arg_label) pattern
693-
in
686+
let pattern_without_constraint = strip_constraint_unpack pattern in
694687
(*
695688
If prop has the default value as Ident, it will get a build error
696689
when the referenced Ident value and the prop have the same name.
697-
So we add a "__" to label to resolve the build error.
690+
So we bind a temp "__<label>" in the props pattern and destructure
691+
the original pattern later after resolving the default.
698692
*)
699693
let pattern_with_safe_label =
700694
match default with
701-
| Some _ -> safe_pattern_label pattern_without_constraint
695+
| Some _ ->
696+
Pat.var
697+
(Location.mkloc
698+
("__" ^ get_label arg_label)
699+
pattern_without_constraint.ppat_loc)
702700
| _ -> pattern_without_constraint
703701
in
704702
if is_labelled arg_label || is_optional arg_label then
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module M = {
2+
@react.component
3+
let make = () => React.null
4+
}
5+
6+
module type S = module type of M
7+
8+
@react.component
9+
let make = (~component as module(C: S)=module(M: S)) => <C />

tests/build_tests/react_ppx/src/issue_7917_test.res.js

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@@jsxConfig({version: 4})
2+
3+
module C0 = {
4+
module M = {
5+
@react.component
6+
let make = () => React.null
7+
}
8+
9+
module type S = module type of M
10+
11+
@react.component
12+
let make = (~component as module(C: S)=module(M: S)) => <C />
13+
}

tests/syntax_tests/data/ppx/react/expected/aliasProps.res.txt

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ module C0 = {
88
}
99

1010
let make = ({priority: _, text: ?__text, _}: props<_, _>) => {
11-
let text = switch __text {
11+
let __text_value = switch __text {
1212
| Some(text) => text
1313
| None => "Test"
1414
}
15+
let text = __text_value
1516
(React.string(text): React.element)
1617
}
1718
let make = {
@@ -29,10 +30,11 @@ module C1 = {
2930
}
3031

3132
let make = ({priority: p, text: ?__text, _}: props<_, _>) => {
32-
let text = switch __text {
33+
let __text_value = switch __text {
3334
| Some(text) => text
3435
| None => "Test"
3536
}
37+
let text = __text_value
3638
(React.string(p ++ text): React.element)
3739
}
3840
let make = {
@@ -48,11 +50,12 @@ module C2 = {
4850
foo?: 'foo,
4951
}
5052

51-
let make = ({foo: ?__bar, _}: props<_>) => {
52-
let bar = switch __bar {
53+
let make = ({foo: ?__foo, _}: props<_>) => {
54+
let __foo_value = switch __foo {
5355
| Some(foo) => foo
5456
| None => ""
5557
}
58+
let bar = __foo_value
5659
(React.string(bar): React.element)
5760
}
5861
let make = {
@@ -70,15 +73,17 @@ module C3 = {
7073
b: 'b,
7174
}
7275

73-
let make = ({foo: ?__bar, a: ?__a, b, _}: props<_, _, _>) => {
74-
let bar = switch __bar {
76+
let make = ({foo: ?__foo, a: ?__a, b, _}: props<_, _, _>) => {
77+
let __foo_value = switch __foo {
7578
| Some(foo) => foo
7679
| None => ""
7780
}
78-
let a = switch __a {
81+
let bar = __foo_value
82+
let __a_value = switch __a {
7983
| Some(a) => a
8084
| None => bar
8185
}
86+
let a = __a_value
8287
(
8388
{
8489
React.string(bar ++ a ++ b)
@@ -100,10 +105,11 @@ module C4 = {
100105
}
101106

102107
let make = ({a: b, x: ?__x, _}: props<_, _>) => {
103-
let x = switch __x {
108+
let __x_value = switch __x {
104109
| Some(x) => x
105110
| None => true
106111
}
112+
let x = __x_value
107113
(ReactDOM.jsx("div", {children: ?ReactDOM.someElement(b)}): React.element)
108114
}
109115
let make = {
@@ -121,10 +127,11 @@ module C5 = {
121127
}
122128

123129
let make = ({a: (x, y), z: ?__z, _}: props<_, _>) => {
124-
let z = switch __z {
130+
let __z_value = switch __z {
125131
| Some(z) => z
126132
| None => 3
127133
}
134+
let z = __z_value
128135
(x + y + z: React.element)
129136
}
130137
let make = {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@@jsxConfig({version: 4})
2+
3+
module C0 = {
4+
module M = {
5+
@res.jsxComponentProps
6+
type props = {}
7+
8+
let make = (_: props): React.element => React.null
9+
let make = {
10+
let \"DefaultPatternProp$C0$M" = props => make(props)
11+
12+
\"DefaultPatternProp$C0$M"
13+
}
14+
}
15+
16+
module type S = module type of M
17+
@res.jsxComponentProps
18+
type props<'component> = {
19+
component?: 'component,
20+
}
21+
22+
let make = ({component: ?__component, _}: props<_>) => {
23+
let __component_value = switch __component {
24+
| Some(component) => component
25+
| None => module(M: S)
26+
}
27+
let module(C: S) = __component_value
28+
(React.jsx(C.make, {}): React.element)
29+
}
30+
let make = {
31+
let \"DefaultPatternProp$C0" = (props: props<_>) => make(props)
32+
33+
\"DefaultPatternProp$C0"
34+
}
35+
}

tests/syntax_tests/data/ppx/react/expected/defaultValueProp.res.txt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ module C0 = {
55
b?: 'b,
66
}
77
let make = ({a: ?__a, b: ?__b, _}: props<_, _>) => {
8-
let a = switch __a {
8+
let __a_value = switch __a {
99
| Some(a) => a
1010
| None => 2
1111
}
12-
let b = switch __b {
12+
let a = __a_value
13+
let __b_value = switch __b {
1314
| Some(b) => b
1415
| None => a * 2
1516
}
17+
let b = __b_value
1618
(React.int(a + b): React.element)
1719
}
1820
let make = {
@@ -29,10 +31,11 @@ module C1 = {
2931
}
3032

3133
let make = ({a: ?__a, b, _}: props<_, _>) => {
32-
let a = switch __a {
34+
let __a_value = switch __a {
3335
| Some(a) => a
3436
| None => 2
3537
}
38+
let a = __a_value
3639
(React.int(a + b): React.element)
3740
}
3841
let make = {
@@ -50,10 +53,11 @@ module C2 = {
5053
}
5154

5255
let make = ({a: ?__a, _}: props<_>) => {
53-
let a = switch __a {
56+
let __a_value = switch __a {
5457
| Some(a) => a
5558
| None => a
5659
}
60+
let a = __a_value
5761
(React.string(a): React.element)
5862
}
5963
let make = {
@@ -69,11 +73,12 @@ module C3 = {
6973
disabled?: 'disabled,
7074
}
7175

72-
let make = ({disabled: ?__everythingDisabled, _}: props<bool>) => {
73-
let everythingDisabled = switch __everythingDisabled {
76+
let make = ({disabled: ?__disabled, _}: props<bool>) => {
77+
let __disabled_value = switch __disabled {
7478
| Some(disabled) => disabled
7579
| None => false
7680
}
81+
let everythingDisabled: bool = __disabled_value
7782
(
7883
{
7984
React.string(everythingDisabled ? "true" : "false")

tests/syntax_tests/data/ppx/react/expected/uncurriedProps.res.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ type props<'a> = {
55
}
66

77
let make = ({a: ?__a, _}: props<unit => unit>) => {
8-
let a = switch __a {
8+
let __a_value = switch __a {
99
| Some(a) => a
1010
| None => () => ()
1111
}
12+
let a: unit => unit = __a_value
1213
(React.null: React.element)
1314
}
1415
let make = {
@@ -34,10 +35,11 @@ module Foo = {
3435
}
3536

3637
let make = ({callback: ?__callback, _}: props<(string, bool, bool) => unit>) => {
37-
let callback = switch __callback {
38+
let __callback_value = switch __callback {
3839
| Some(callback) => callback
3940
| None => (_, _, _) => ()
4041
}
42+
let callback: (string, bool, bool) => unit = __callback_value
4143
(
4244
{
4345
React.null

tests/tests/src/alias_default_value_test.mjs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@
44
function Alias_default_value_test$C0(props) {
55
let __b = props.b;
66
let __a = props.a;
7-
let a = __a !== undefined ? __a : 2;
8-
let b = __b !== undefined ? __b : (a << 1);
9-
return a + b | 0;
7+
let __a_value = __a !== undefined ? __a : 2;
8+
let __b_value = __b !== undefined ? __b : (__a_value << 1);
9+
return __a_value + __b_value | 0;
1010
}
1111

1212
let C0 = {
1313
make: Alias_default_value_test$C0
1414
};
1515

1616
function Alias_default_value_test$C1(props) {
17-
let __bar = props.foo;
18-
if (__bar !== undefined) {
19-
return __bar;
17+
let __foo = props.foo;
18+
if (__foo !== undefined) {
19+
return __foo;
2020
} else {
2121
return "";
2222
}
@@ -28,10 +28,10 @@ let C1 = {
2828

2929
function Alias_default_value_test$C2(props) {
3030
let __a = props.a;
31-
let __bar = props.foo;
32-
let bar = __bar !== undefined ? __bar : "";
33-
let a = __a !== undefined ? __a : bar;
34-
return bar + a + props.b;
31+
let __foo = props.foo;
32+
let __foo_value = __foo !== undefined ? __foo : "";
33+
let __a_value = __a !== undefined ? __a : __foo_value;
34+
return __foo_value + __a_value + props.b;
3535
}
3636

3737
let C2 = {

0 commit comments

Comments
 (0)