Skip to content

Commit 705b1c9

Browse files
mununkicknitt
andauthored
Add loc to each props in JSX4 (rescript-lang#5937)
Co-authored-by: Christoph Knittel <ck@cca.io>
1 parent 01eb614 commit 705b1c9

16 files changed

+68
-124
lines changed

res_syntax/src/reactjs_jsx_v4.ml

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,9 @@ let recordFromProps ~loc ~removeKey callArguments =
242242
(* let make = ({id, name, children}: props<'id, 'name, 'children>) *)
243243
let makePropsTypeParamsTvar namedTypeList =
244244
namedTypeList
245-
|> List.filter_map (fun (_isOptional, label, _, _interiorType) ->
245+
|> List.filter_map (fun (_isOptional, label, _, loc, _interiorType) ->
246246
if label = "key" then None
247-
else Some (Typ.var @@ safeTypeFromValue (Labelled label)))
247+
else Some (Typ.var ~loc @@ safeTypeFromValue (Labelled label)))
248248

249249
let stripOption coreType =
250250
match coreType with
@@ -268,7 +268,7 @@ let stripJsNullable coreType =
268268
let makePropsTypeParams ?(stripExplicitOption = false)
269269
?(stripExplicitJsNullableOfRef = false) namedTypeList =
270270
namedTypeList
271-
|> List.filter_map (fun (isOptional, label, _, interiorType) ->
271+
|> List.filter_map (fun (isOptional, label, _, loc, interiorType) ->
272272
if label = "key" then None
273273
(* TODO: Worth thinking how about "ref_" or "_ref" usages *)
274274
else if label = "ref" then
@@ -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 Location.none)
280+
| {ptyp_desc = Ptyp_var "ref"} -> Some (refType loc)
281281
| _ ->
282282
(* Strip explicit Js.Nullable.t in case of forwardRef *)
283283
if stripExplicitJsNullableOfRef then stripJsNullable interiorType
@@ -287,9 +287,25 @@ let makePropsTypeParams ?(stripExplicitOption = false)
287287
else if isOptional && stripExplicitOption then stripOption interiorType
288288
else Some interiorType)
289289

290-
let makeLabelDecls ~loc namedTypeList =
290+
let makeLabelDecls namedTypeList =
291+
let rec checkDuplicatedLabel l =
292+
let rec mem_label ((_, (la : string), _, _, _) as x) = function
293+
| [] -> false
294+
| (_, (lb : string), _, _, _) :: l -> lb = la || mem_label x l
295+
in
296+
match l with
297+
| [] -> ()
298+
| hd :: tl ->
299+
if mem_label hd tl then
300+
let _, label, _, loc, _ = hd in
301+
React_jsx_common.raiseError ~loc "JSX: found the duplicated prop `%s`"
302+
label
303+
else checkDuplicatedLabel tl
304+
in
305+
let () = namedTypeList |> List.rev |> checkDuplicatedLabel in
306+
291307
namedTypeList
292-
|> List.map (fun (isOptional, label, attrs, interiorType) ->
308+
|> List.map (fun (isOptional, label, attrs, loc, interiorType) ->
293309
if label = "key" then
294310
Type.field ~loc ~attrs:(optionalAttrs @ attrs) {txt = label; loc}
295311
interiorType
@@ -301,7 +317,7 @@ let makeLabelDecls ~loc namedTypeList =
301317
(Typ.var @@ safeTypeFromValue @@ Labelled label))
302318

303319
let makeTypeDecls propsName loc namedTypeList =
304-
let labelDeclList = makeLabelDecls ~loc namedTypeList in
320+
let labelDeclList = makeLabelDecls namedTypeList in
305321
(* 'id, 'className, ... *)
306322
let params =
307323
makePropsTypeParamsTvar namedTypeList
@@ -702,17 +718,22 @@ let argToType ~newtypes ~(typeConstraints : core_type option) types
702718
in
703719
match (type_, name, default) with
704720
| Some type_, name, _ when isOptional name ->
705-
(true, getLabel name, attrs, {type_ with ptyp_attributes = optionalAttrs})
721+
( true,
722+
getLabel name,
723+
attrs,
724+
loc,
725+
{type_ with ptyp_attributes = optionalAttrs} )
706726
:: types
707-
| Some type_, name, _ -> (false, getLabel name, attrs, type_) :: types
727+
| Some type_, name, _ -> (false, getLabel name, attrs, loc, type_) :: types
708728
| None, name, _ when isOptional name ->
709729
( true,
710730
getLabel name,
711731
attrs,
732+
loc,
712733
Typ.var ~loc ~attrs:optionalAttrs (safeTypeFromValue name) )
713734
:: types
714735
| None, name, _ when isLabelled name ->
715-
(false, getLabel name, attrs, Typ.var ~loc (safeTypeFromValue name))
736+
(false, getLabel name, attrs, loc, Typ.var ~loc (safeTypeFromValue name))
716737
:: types
717738
| _ -> types
718739

@@ -721,10 +742,12 @@ let argWithDefaultValue (name, default, _, _, _, _) =
721742
| Some default when isOptional name -> Some (getLabel name, default)
722743
| _ -> None
723744

724-
let argToConcreteType types (name, attrs, _loc, type_) =
745+
let argToConcreteType types (name, attrs, loc, type_) =
725746
match name with
726-
| name when isLabelled name -> (false, getLabel name, attrs, type_) :: types
727-
| name when isOptional name -> (true, getLabel name, attrs, type_) :: types
747+
| name when isLabelled name ->
748+
(false, getLabel name, attrs, loc, type_) :: types
749+
| name when isOptional name ->
750+
(true, getLabel name, attrs, loc, type_) :: types
728751
| _ -> types
729752

730753
let check_string_int_attribute_iter =
@@ -1306,7 +1329,8 @@ let transformSignatureItem ~config _mapper item =
13061329
makePropsRecordTypeSig ~coreTypeOfAttr ~typVarsOfCoreType "props"
13071330
psig_loc
13081331
((* If there is Nolabel arg, regard the type as ref in forwardRef *)
1309-
(if !hasForwardRef then [(true, "ref", [], refType Location.none)]
1332+
(if !hasForwardRef then
1333+
[(true, "ref", [], Location.none, refType Location.none)]
13101334
else [])
13111335
@ namedTypeList)
13121336
in

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
@@jsxConfig({version: 4, mode: "automatic"})
22

33
module C0 = {
4-
type props<'priority, 'text> = {
5-
priority: 'priority,
6-
text?: 'text,
7-
}
4+
type props<'priority, 'text> = {priority: 'priority, text?: 'text}
85

96
@react.component
107
let make = ({priority: _, ?text, _}: props<'priority, 'text>) => {
@@ -23,10 +20,7 @@ module C0 = {
2320
}
2421

2522
module C1 = {
26-
type props<'priority, 'text> = {
27-
priority: 'priority,
28-
text?: 'text,
29-
}
23+
type props<'priority, 'text> = {priority: 'priority, text?: 'text}
3024

3125
@react.component
3226
let make = ({priority: p, ?text, _}: props<'priority, 'text>) => {

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
type props<'msg> = { // test React JSX file
2-
msg: 'msg,
3-
}
1+
type props<'msg> = {msg: 'msg} // test React JSX file
42

53
@react.component
64
let make = ({msg, _}: props<'msg>) => {

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ let t = React.createElement(Foo.component, Foo.componentProps(~a=1, ~b={"1"}, ()
1313
@@jsxConfig({version: 4, mode: "classic"})
1414

1515
module Foo = {
16-
type props<'a, 'b> = {
17-
a: 'a,
18-
b: 'b,
19-
}
16+
type props<'a, 'b> = {a: 'a, b: 'b}
2017

2118
@module("Foo")
2219
external component: React.componentLike<props<int, string>, React.element> = "component"
@@ -27,10 +24,7 @@ let t = React.createElement(Foo.component, {a: 1, b: "1"})
2724
@@jsxConfig({version: 4, mode: "automatic"})
2825

2926
module Foo = {
30-
type props<'a, 'b> = {
31-
a: 'a,
32-
b: 'b,
33-
}
27+
type props<'a, 'b> = {a: 'a, b: 'b}
3428

3529
@module("Foo")
3630
external component: React.componentLike<props<int, string>, React.element> = "component"

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ module V3 = {
1818
@@jsxConfig({version: 4, mode: "classic"})
1919

2020
module V4C = {
21-
type props<'msg> = {
22-
msg: 'msg,
23-
}
21+
type props<'msg> = {msg: 'msg}
2422

2523
@react.component
2624
let make = ({msg, _}: props<'msg>) => {
@@ -36,9 +34,7 @@ module V4C = {
3634
@@jsxConfig({version: 4, mode: "automatic"})
3735

3836
module V4A = {
39-
type props<'msg> = {
40-
msg: 'msg,
41-
}
37+
type props<'msg> = {msg: 'msg}
4238

4339
@react.component
4440
let make = ({msg, _}: props<'msg>) => {

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
module A = {
2-
type props<'x> = {
3-
x: 'x,
4-
}
2+
type props<'x> = {x: 'x}
53
@react.component let make = ({x, _}: props<'x>) => React.string(x)
64
let make = {
75
let \"Interface$A" = (props: props<_>) => make(props)

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
module A: {
2-
type props<'x> = {
3-
x: 'x,
4-
}
2+
type props<'x> = {x: 'x}
53
let make: React.componentLike<props<string>, React.element>
64
}
75

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
type props<'x, 'ref> = {
2-
x: 'x,
3-
ref?: 'ref,
4-
}
1+
type props<'x, 'ref> = {x: 'x, ref?: 'ref}
52
@react.component
63
let make = (
74
{x, _}: props<string, ReactDOM.Ref.currentDomRef>,
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
type props<'x, 'ref> = {
2-
x: 'x,
3-
ref?: 'ref,
4-
}
1+
type props<'x, 'ref> = {x: 'x, ref?: 'ref}
52
let make: React.componentLike<props<string, ReactDOM.Ref.currentDomRef>, React.element>

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ let c31 = React.createElement(C31.make, C31.makeProps(~_open="x", ()))
2020
@@jsxConfig({version: 4, mode: "classic"})
2121

2222
module C4C0 = {
23-
type props<'T_open, 'T_type> = {
24-
@as("open") _open: 'T_open,
25-
@as("type") _type: 'T_type,
26-
}
23+
type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type}
2724

2825
@react.component
2926
let make = ({@as("open") _open, @as("type") _type, _}: props<'T_open, string>) =>
@@ -35,10 +32,7 @@ module C4C0 = {
3532
}
3633
}
3734
module C4C1 = {
38-
type props<'T_open, 'T_type> = {
39-
@as("open") _open: 'T_open,
40-
@as("type") _type: 'T_type,
41-
}
35+
type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type}
4236

4337
external make: @as("open") React.componentLike<props<string, string>, React.element> = "default"
4438
}
@@ -49,10 +43,7 @@ let c4c1 = React.createElement(C4C1.make, {_open: "x", _type: "t"})
4943
@@jsxConfig({version: 4, mode: "automatic"})
5044

5145
module C4A0 = {
52-
type props<'T_open, 'T_type> = {
53-
@as("open") _open: 'T_open,
54-
@as("type") _type: 'T_type,
55-
}
46+
type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type}
5647

5748
@react.component
5849
let make = ({@as("open") _open, @as("type") _type, _}: props<'T_open, string>) =>
@@ -64,10 +55,7 @@ module C4A0 = {
6455
}
6556
}
6657
module C4A1 = {
67-
type props<'T_open, 'T_type> = {
68-
@as("open") _open: 'T_open,
69-
@as("type") _type: 'T_type,
70-
}
58+
type props<'T_open, 'T_type> = {@as("open") _open: 'T_open, @as("type") _type: 'T_type}
7159

7260
external make: @as("open") React.componentLike<props<string, string>, React.element> = "default"
7361
}

0 commit comments

Comments
 (0)