Skip to content

Commit bf8f1a3

Browse files
committed
Fix issue with JSX V4 and newtype
Make the following changes: 1) Don't replace type variable `a` with `"type-a`", but leave it alone. 2) If the original `make` uses `(type a b, ...` wrap the first generated `make` with the same new type definitions 3) In the constraint `:props<'a, 'b>` where `'a` and `'b` were not constrained, use `:props<_, _>` instead. This avoids issues when e.g. `'a` is inferred to be a newtype bound in the make function, which would need to be mentioned explicitly and would give a type error. Fixes #5978 Replaces #6000
1 parent c19687d commit bf8f1a3

20 files changed

+191
-78
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ These are only breaking changes for unformatted code.
7373
- Fix formatting of `switch` expressions that contain brace `cases` inside https://github.com/rescript-lang/rescript-compiler/pull/6015
7474
- Support `@gentype.import` as an alias to `@genType.import` in the compiler https://github.com/rescript-lang/rescript-compiler/pull/6020
7575
- Fix issue with integer overflow check https://github.com/rescript-lang/rescript-compiler/pull/6028
76+
- Fix issue with JSX V4 and newtype https://github.com/rescript-lang/rescript-compiler/pull/6029
7677

7778
#### :nail_care: Polish
7879

jscomp/test/build.ninja

Lines changed: 2 additions & 1 deletion
Large diffs are not rendered by default.

jscomp/test/jsxv4_newtype.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
4+
function Jsxv4_newtype$V4A(props) {
5+
return null;
6+
}
7+
8+
var V4A = {
9+
make: Jsxv4_newtype$V4A
10+
};
11+
12+
function Jsxv4_newtype$V4A1(props) {
13+
return null;
14+
}
15+
16+
var V4A1 = {
17+
make: Jsxv4_newtype$V4A1
18+
};
19+
20+
function Jsxv4_newtype$V4A2(props) {
21+
return null;
22+
}
23+
24+
var V4A2 = {
25+
make: Jsxv4_newtype$V4A2
26+
};
27+
28+
function Jsxv4_newtype$V4A3(props) {
29+
return props.foo;
30+
}
31+
32+
var V4A3 = {
33+
make: Jsxv4_newtype$V4A3
34+
};
35+
36+
exports.V4A = V4A;
37+
exports.V4A1 = V4A1;
38+
exports.V4A2 = V4A2;
39+
exports.V4A3 = V4A3;
40+
/* No side effect */

jscomp/test/jsxv4_newtype.res

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@@bs.config({
2+
flags: ["-bs-jsx", "4"],
3+
})
4+
5+
module V4A = {
6+
@react.component
7+
let make = (type a, ~a: a, ~b: array<option<[#Foo(a)]>>, ~c: 'a, _) => React.null
8+
}
9+
10+
module V4A1 = {
11+
@react.component
12+
let make = (type x y, ~a:x, ~b:array<y>, ~c:'a) => React.null
13+
}
14+
15+
module type T = {
16+
type t
17+
}
18+
19+
module V4A2 = {
20+
@react.component
21+
let make = (type a, ~foo: module(T with type t = a)) => {
22+
module T = unpack(foo)
23+
React.null
24+
}
25+
}
26+
27+
module V4A3 = {
28+
@react.component
29+
let make = (type a, ~foo) => {
30+
module T = unpack(foo: T with type t = a)
31+
foo
32+
}
33+
}

res_syntax/src/reactjs_jsx_v4.ml

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ let makePropsTypeParams ?(stripExplicitOption = false)
277277
For example, if JSX ppx is used for React Native, type would be different.
278278
*)
279279
match interiorType with
280-
| {ptyp_desc = Ptyp_var "ref"} -> Some (refType loc)
280+
| {ptyp_desc = Ptyp_any} -> Some (refType loc)
281281
| _ ->
282282
(* Strip explicit Js.Nullable.t in case of forwardRef *)
283283
if stripExplicitJsNullableOfRef then stripJsNullable interiorType
@@ -687,46 +687,18 @@ let rec recursivelyTransformNamedArgsForMake mapper expr args newtypes coreType
687687
(Some coreType)
688688
| _ -> (args, newtypes, coreType)
689689

690-
let newtypeToVar newtype type_ =
691-
let var_desc = Ptyp_var ("type-" ^ newtype) in
692-
let typ (mapper : Ast_mapper.mapper) typ =
693-
match typ.ptyp_desc with
694-
| Ptyp_constr ({txt = Lident name}, _) when name = newtype ->
695-
{typ with ptyp_desc = var_desc}
696-
| _ -> Ast_mapper.default_mapper.typ mapper typ
697-
in
698-
let mapper = {Ast_mapper.default_mapper with typ} in
699-
mapper.typ mapper type_
700-
701-
let argToType ~newtypes ~(typeConstraints : core_type option) types
690+
let argToType types
702691
((name, default, {ppat_attributes = attrs}, _alias, loc, type_) :
703692
arg_label * expression option * pattern * label * 'loc * core_type option)
704693
=
705-
let rec getType name coreType =
706-
match coreType with
707-
| {ptyp_desc = Ptyp_arrow (arg, c1, c2)} ->
708-
if name = arg then Some c1 else getType name c2
709-
| _ -> None
710-
in
711-
let typeConst = Option.bind typeConstraints (getType name) in
712-
let type_ =
713-
List.fold_left
714-
(fun type_ newtype ->
715-
match (type_, typeConst) with
716-
| _, Some typ | Some typ, None -> Some (newtypeToVar newtype.txt typ)
717-
| _ -> None)
718-
type_ newtypes
719-
in
720694
match (type_, name, default) with
721695
| Some type_, name, _ when isOptional name ->
722696
(true, getLabel name, attrs, loc, type_) :: types
723697
| Some type_, name, _ -> (false, getLabel name, attrs, loc, type_) :: types
724698
| None, name, _ when isOptional name ->
725-
(true, getLabel name, attrs, loc, Typ.var ~loc (safeTypeFromValue name))
726-
:: types
699+
(true, getLabel name, attrs, loc, Typ.any ~loc ()) :: types
727700
| None, name, _ when isLabelled name ->
728-
(false, getLabel name, attrs, loc, Typ.var ~loc (safeTypeFromValue name))
729-
:: types
701+
(false, getLabel name, attrs, loc, Typ.any ~loc ()) :: types
730702
| _ -> types
731703

732704
let hasDefaultValue nameArgList =
@@ -1027,16 +999,12 @@ let transformStructureItem ~config mapper item =
1027999
modifiedBinding binding
10281000
in
10291001
(* do stuff here! *)
1030-
let namedArgList, newtypes, typeConstraints =
1002+
let namedArgList, newtypes, _typeConstraints =
10311003
recursivelyTransformNamedArgsForMake mapper
10321004
(modifiedBindingOld binding)
10331005
[] [] None
10341006
in
1035-
let namedTypeList =
1036-
List.fold_left
1037-
(argToType ~newtypes ~typeConstraints)
1038-
[] namedArgList
1039-
in
1007+
let namedTypeList = List.fold_left argToType [] namedArgList in
10401008
let vbMatch (name, default, _, alias, loc, _) =
10411009
let label = getLabel name in
10421010
match default with
@@ -1229,6 +1197,13 @@ let transformStructureItem ~config mapper item =
12291197
| _ -> [Typ.any ()]))))
12301198
expression
12311199
in
1200+
let expression =
1201+
(* Add new tupes (type a,b,c) to make's definition *)
1202+
newtypes
1203+
|> List.fold_left
1204+
(fun e newtype -> Exp.newtype newtype e)
1205+
expression
1206+
in
12321207
(* let make = ({id, name, ...}: props<'id, 'name, ...>) => { ... } *)
12331208
let bindings, newBinding =
12341209
match recFlag with

res_syntax/tests/ppx/react/expected/aliasProps.res.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module C0 = {
44
type props<'priority, 'text> = {priority: 'priority, text?: 'text}
55

66
@react.component
7-
let make = (props: props<'priority, 'text>) => {
7+
let make = (props: props<_, _>) => {
88
let _ = props.priority
99
let text = switch props.text {
1010
| Some(text) => text
@@ -24,7 +24,7 @@ module C1 = {
2424
type props<'priority, 'text> = {priority: 'priority, text?: 'text}
2525

2626
@react.component
27-
let make = (props: props<'priority, 'text>) => {
27+
let make = (props: props<_, _>) => {
2828
let p = props.priority
2929
let text = switch props.text {
3030
| Some(text) => text
@@ -44,7 +44,7 @@ module C2 = {
4444
type props<'foo> = {foo?: 'foo}
4545

4646
@react.component
47-
let make = (props: props<'foo>) => {
47+
let make = (props: props<_>) => {
4848
let bar = switch props.foo {
4949
| Some(foo) => foo
5050
| None => ""

res_syntax/tests/ppx/react/expected/commentAtTop.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
type props<'msg> = {msg: 'msg} // test React JSX file
22

33
@react.component
4-
let make = ({msg, _}: props<'msg>) => {
4+
let make = ({msg, _}: props<_>) => {
55
ReactDOM.jsx("div", {children: ?ReactDOM.someElement({msg->React.string})})
66
}
77
let make = {

res_syntax/tests/ppx/react/expected/defaultValueProp.res.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module C0 = {
22
type props<'a, 'b> = {a?: 'a, b?: 'b}
33
@react.component
4-
let make = (props: props<'a, 'b>) => {
4+
let make = (props: props<_, _>) => {
55
let a = switch props.a {
66
| Some(a) => a
77
| None => 2
@@ -23,7 +23,7 @@ module C1 = {
2323
type props<'a, 'b> = {a?: 'a, b: 'b}
2424

2525
@react.component
26-
let make = (props: props<'a, 'b>) => {
26+
let make = (props: props<_, _>) => {
2727
let a = switch props.a {
2828
| Some(a) => a
2929
| None => 2

res_syntax/tests/ppx/react/expected/fileLevelConfig.res.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module V4C = {
2121
type props<'msg> = {msg: 'msg}
2222

2323
@react.component
24-
let make = ({msg, _}: props<'msg>) => {
24+
let make = ({msg, _}: props<_>) => {
2525
ReactDOM.createDOMElementVariadic("div", [{msg->React.string}])
2626
}
2727
let make = {
@@ -37,7 +37,7 @@ module V4A = {
3737
type props<'msg> = {msg: 'msg}
3838

3939
@react.component
40-
let make = ({msg, _}: props<'msg>) => {
40+
let make = ({msg, _}: props<_>) => {
4141
ReactDOM.jsx("div", {children: ?ReactDOM.someElement({msg->React.string})})
4242
}
4343
let make = {

res_syntax/tests/ppx/react/expected/firstClassModules.res.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,12 @@ module Select = {
6666

6767
@react.component
6868
let make = (
69+
type a key,
6970
{model, selected, onChange, items, _}: props<
70-
module(T with type t = '\"type-a" and type key = '\"type-key"),
71-
option<'\"type-key">,
72-
option<'\"type-key"> => unit,
73-
array<'\"type-a">,
71+
module(T with type t = a and type key = key),
72+
option<key>,
73+
option<key> => unit,
74+
array<a>,
7475
>,
7576
) => {
7677
let _ = (model, selected, onChange, items)

res_syntax/tests/ppx/react/expected/forwardRef.res.txt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ module V4C = {
7777

7878
@react.component
7979
let make = (
80-
{?className, children, _}: props<'className, 'children, ReactRef.currentDomRef>,
80+
{?className, children, _}: props<_, _, ReactRef.currentDomRef>,
8181
ref: Js.Nullable.t<ReactRef.currentDomRef>,
8282
) =>
8383
ReactDOM.createDOMElementVariadic(
@@ -134,7 +134,7 @@ module V4CUncurried = {
134134

135135
@react.component
136136
let make = (
137-
{?className, children, _}: props<'className, 'children, ReactRef.currentDomRef>,
137+
{?className, children, _}: props<_, _, ReactRef.currentDomRef>,
138138
ref: Js.Nullable.t<ReactRef.currentDomRef>,
139139
) =>
140140
ReactDOM.createDOMElementVariadic(
@@ -192,10 +192,7 @@ module V4A = {
192192
}
193193

194194
@react.component
195-
let make = (
196-
{?className, children, _}: props<'className, 'children, ReactDOM.Ref.currentDomRef>,
197-
ref,
198-
) =>
195+
let make = ({?className, children, _}: props<_, _, ReactDOM.Ref.currentDomRef>, ref) =>
199196
ReactDOM.jsxs(
200197
"div",
201198
{
@@ -249,10 +246,7 @@ module V4AUncurried = {
249246
}
250247

251248
@react.component
252-
let make = (
253-
{?className, children, _}: props<'className, 'children, ReactDOM.Ref.currentDomRef>,
254-
ref,
255-
) =>
249+
let make = ({?className, children, _}: props<_, _, ReactDOM.Ref.currentDomRef>, ref) =>
256250
ReactDOM.jsxs(
257251
"div",
258252
{

res_syntax/tests/ppx/react/expected/interface.res.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module A = {
22
type props<'x> = {x: 'x}
3-
@react.component let make = ({x, _}: props<'x>) => React.string(x)
3+
@react.component let make = ({x, _}: props<_>) => React.string(x)
44
let make = {
55
let \"Interface$A" = (props: props<_>) => make(props)
66
\"Interface$A"

res_syntax/tests/ppx/react/expected/mangleKeyword.res.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ module C4C0 = {
2323
type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type}
2424

2525
@react.component
26-
let make = ({@as("open") _open, @as("type") _type, _}: props<'T_open, string>) =>
27-
React.string(_open)
26+
let make = ({@as("open") _open, @as("type") _type, _}: props<_, string>) => React.string(_open)
2827
let make = {
2928
let \"MangleKeyword$C4C0" = (props: props<_>) => make(props)
3029

@@ -46,8 +45,7 @@ module C4A0 = {
4645
type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type}
4746

4847
@react.component
49-
let make = ({@as("open") _open, @as("type") _type, _}: props<'T_open, string>) =>
50-
React.string(_open)
48+
let make = ({@as("open") _open, @as("type") _type, _}: props<_, string>) => React.string(_open)
5149
let make = {
5250
let \"MangleKeyword$C4A0" = (props: props<_>) => make(props)
5351

0 commit comments

Comments
 (0)