diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86fab3a77e..aa80314989 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,7 @@
- Fix `index out of bounds` exception thrown in rare cases by `rescript-editor-analysis.exe codeAction` command. https://github.com/rescript-lang/rescript/pull/7523
- Don't produce duplicate type definitions for recursive types on hover. https://github.com/rescript-lang/rescript/pull/7524
- Prop punning when types don't match results in I/O error: _none_: No such file or directory. https://github.com/rescript-lang/rescript/pull/7533
+- Pass location to children prop in jsx ppx. https://github.com/rescript-lang/rescript/pull/7540
#### :nail_care: Polish
diff --git a/compiler/syntax/src/jsx_v4.ml b/compiler/syntax/src/jsx_v4.ml
index 32fd92155b..29b227faa7 100644
--- a/compiler/syntax/src/jsx_v4.ml
+++ b/compiler/syntax/src/jsx_v4.ml
@@ -1208,7 +1208,7 @@ let append_children_prop (config : Jsx_common.jsx_config) mapper
| "react" -> Lident "ReactDOM"
| _generic -> module_access_name config "Elements"
in
- Exp.apply
+ Exp.apply ~loc:child.pexp_loc
(Exp.ident
{txt = Ldot (element_binding, "someElement"); loc = Location.none})
[(Nolabel, mapper.expr mapper child)]
@@ -1220,18 +1220,24 @@ let append_children_prop (config : Jsx_common.jsx_config) mapper
in
props
@ [
- JSXPropValue ({txt = "children"; loc = Location.none}, is_optional, expr);
+ JSXPropValue
+ ({txt = "children"; loc = child.pexp_loc}, is_optional, expr);
]
- | JSXChildrenItems xs ->
+ | JSXChildrenItems (head :: _ as xs) ->
+ let loc =
+ match List.rev xs with
+ | [] -> head.pexp_loc
+ | lastChild :: _ ->
+ {head.pexp_loc with loc_end = lastChild.pexp_loc.loc_end}
+ in
(* this is a hack to support react components that introspect into their children *)
props
@ [
JSXPropValue
- ( {txt = "children"; loc = Location.none},
+ ( {txt = "children"; loc},
false,
- Exp.apply
- (Exp.ident
- {txt = module_access_name config "array"; loc = Location.none})
+ Exp.apply ~loc
+ (Exp.ident {txt = module_access_name config "array"; loc})
[(Nolabel, Exp.array (List.map (mapper.expr mapper) xs))] );
]
diff --git a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt
index a0b06162e0..7f0911c667 100644
--- a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt
+++ b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt
@@ -23,21 +23,6 @@
addValueReference ComponentAsProp.res:12:24 --> ComponentAsProp.res:12:13
addValueReference ComponentAsProp.res:13:16 --> React.res:3:0
addValueReference ComponentAsProp.res:11:14 --> ComponentAsProp.res:6:34
- addValueReference ComponentAsProp.res:9:6 --> ComponentAsProp.res:6:12
- addValueReference ComponentAsProp.res:10:6 --> ComponentAsProp.res:6:20
- addValueReference ComponentAsProp.res:12:24 --> ComponentAsProp.res:12:13
- addValueReference ComponentAsProp.res:13:16 --> React.res:3:0
- addValueReference ComponentAsProp.res:11:14 --> ComponentAsProp.res:6:34
- addValueReference ComponentAsProp.res:9:6 --> ComponentAsProp.res:6:12
- addValueReference ComponentAsProp.res:10:6 --> ComponentAsProp.res:6:20
- addValueReference ComponentAsProp.res:12:24 --> ComponentAsProp.res:12:13
- addValueReference ComponentAsProp.res:13:16 --> React.res:3:0
- addValueReference ComponentAsProp.res:11:14 --> ComponentAsProp.res:6:34
- addValueReference ComponentAsProp.res:9:6 --> ComponentAsProp.res:6:12
- addValueReference ComponentAsProp.res:10:6 --> ComponentAsProp.res:6:20
- addValueReference ComponentAsProp.res:12:24 --> ComponentAsProp.res:12:13
- addValueReference ComponentAsProp.res:13:16 --> React.res:3:0
- addValueReference ComponentAsProp.res:11:14 --> ComponentAsProp.res:6:34
addTypeReference _none_:1:-1 --> ComponentAsProp.res:6:12
addTypeReference _none_:1:-1 --> ComponentAsProp.res:6:20
addTypeReference _none_:1:-1 --> ComponentAsProp.res:6:34
@@ -422,52 +407,17 @@
addValueReference Hooks.res:10:29 --> Hooks.res:4:12
addValueReference Hooks.res:10:75 --> Hooks.res:5:7
addValueReference Hooks.res:9:7 --> React.res:7:0
- addTypeReference Hooks.res:10:29 --> Hooks.res:1:16
- addValueReference Hooks.res:10:29 --> Hooks.res:4:12
- addValueReference Hooks.res:10:75 --> Hooks.res:5:7
- addValueReference Hooks.res:9:7 --> React.res:7:0
- addValueReference Hooks.res:13:54 --> React.res:7:0
addValueReference Hooks.res:13:54 --> React.res:7:0
addValueReference Hooks.res:13:40 --> Hooks.res:5:7
addValueReference Hooks.res:13:26 --> Hooks.res:5:14
addValueReference Hooks.res:14:5 --> ImportHooks.res:13:0
addValueReference Hooks.res:15:7 --> React.res:7:0
addValueReference Hooks.res:15:32 --> React.res:7:0
- addValueReference Hooks.res:15:7 --> React.res:7:0
- addValueReference Hooks.res:15:32 --> React.res:7:0
addValueReference Hooks.res:14:76 --> Hooks.res:14:57
addValueReference Hooks.res:14:63 --> React.res:7:0
addValueReference Hooks.res:17:5 --> ImportHookDefault.res:6:0
addValueReference Hooks.res:19:7 --> React.res:7:0
addValueReference Hooks.res:19:32 --> React.res:7:0
- addValueReference Hooks.res:19:7 --> React.res:7:0
- addValueReference Hooks.res:19:32 --> React.res:7:0
- addValueReference Hooks.res:18:74 --> Hooks.res:18:55
- addValueReference Hooks.res:18:61 --> React.res:7:0
- addTypeReference Hooks.res:10:29 --> Hooks.res:1:16
- addValueReference Hooks.res:10:29 --> Hooks.res:4:12
- addValueReference Hooks.res:10:75 --> Hooks.res:5:7
- addValueReference Hooks.res:9:7 --> React.res:7:0
- addTypeReference Hooks.res:10:29 --> Hooks.res:1:16
- addValueReference Hooks.res:10:29 --> Hooks.res:4:12
- addValueReference Hooks.res:10:75 --> Hooks.res:5:7
- addValueReference Hooks.res:9:7 --> React.res:7:0
- addValueReference Hooks.res:13:54 --> React.res:7:0
- addValueReference Hooks.res:13:54 --> React.res:7:0
- addValueReference Hooks.res:13:40 --> Hooks.res:5:7
- addValueReference Hooks.res:13:26 --> Hooks.res:5:14
- addValueReference Hooks.res:14:5 --> ImportHooks.res:13:0
- addValueReference Hooks.res:15:7 --> React.res:7:0
- addValueReference Hooks.res:15:32 --> React.res:7:0
- addValueReference Hooks.res:15:7 --> React.res:7:0
- addValueReference Hooks.res:15:32 --> React.res:7:0
- addValueReference Hooks.res:14:76 --> Hooks.res:14:57
- addValueReference Hooks.res:14:63 --> React.res:7:0
- addValueReference Hooks.res:17:5 --> ImportHookDefault.res:6:0
- addValueReference Hooks.res:19:7 --> React.res:7:0
- addValueReference Hooks.res:19:32 --> React.res:7:0
- addValueReference Hooks.res:19:7 --> React.res:7:0
- addValueReference Hooks.res:19:32 --> React.res:7:0
addValueReference Hooks.res:18:74 --> Hooks.res:18:55
addValueReference Hooks.res:18:61 --> React.res:7:0
addTypeReference _none_:1:-1 --> Hooks.res:4:12
@@ -478,21 +428,14 @@
addTypeReference Hooks.res:29:66 --> Hooks.res:1:16
addValueReference Hooks.res:29:66 --> Hooks.res:29:14
addValueReference Hooks.res:29:34 --> React.res:7:0
- addTypeReference Hooks.res:29:66 --> Hooks.res:1:16
- addValueReference Hooks.res:29:66 --> Hooks.res:29:14
- addValueReference Hooks.res:29:34 --> React.res:7:0
addTypeReference _none_:1:-1 --> Hooks.res:29:14
addRecordLabelDeclaration vehicle Hooks.res:33:16 path:+Hooks.Inner.Inner2.props
addRecordLabelDeclaration vehicle Hooks.res:33:16 path:+Hooks.Inner.Inner2.props
addTypeReference Hooks.res:33:68 --> Hooks.res:1:16
addValueReference Hooks.res:33:68 --> Hooks.res:33:16
addValueReference Hooks.res:33:36 --> React.res:7:0
- addTypeReference Hooks.res:33:68 --> Hooks.res:1:16
- addValueReference Hooks.res:33:68 --> Hooks.res:33:16
- addValueReference Hooks.res:33:36 --> React.res:7:0
addTypeReference _none_:1:-1 --> Hooks.res:33:16
addValueReference Hooks.res:39:25 --> React.res:3:0
- addValueReference Hooks.res:39:25 --> React.res:3:0
addTypeReference Hooks.res:47:2 --> Hooks.res:1:16
addValueReference Hooks.res:45:4 --> Hooks.res:45:31
addTypeReference Hooks.res:47:14 --> Hooks.res:1:16
diff --git a/tests/build_tests/super_errors/expected/missing_required_prop.res.expected b/tests/build_tests/super_errors/expected/missing_required_prop.res.expected
new file mode 100644
index 0000000000..98e2403974
--- /dev/null
+++ b/tests/build_tests/super_errors/expected/missing_required_prop.res.expected
@@ -0,0 +1,11 @@
+
+ [1;31mWe've found a bug for you![0m
+ [36m/.../fixtures/missing_required_prop.res[0m:[2m28:7-37[0m
+
+ 26 [2m┆[0m let make = () => {
+ 27 [2m┆[0m
+ [1;31m28[0m [2m┆[0m [1;31m {""->React.string}
[0m
+ 29 [2m┆[0m
+ 30 [2m┆[0m }
+
+ This JSX component does not accept child elements. It has no [1;31mchildren[0m prop [1;33m [0m
\ No newline at end of file
diff --git a/tests/build_tests/super_errors/expected/missing_required_prop_when_children.res.expected b/tests/build_tests/super_errors/expected/missing_required_prop_when_children.res.expected
new file mode 100644
index 0000000000..154d54ffb4
--- /dev/null
+++ b/tests/build_tests/super_errors/expected/missing_required_prop_when_children.res.expected
@@ -0,0 +1,13 @@
+
+ [1;31mWe've found a bug for you![0m
+ [36m/.../fixtures/missing_required_prop_when_children.res[0m:[2m31:7-32:37[0m
+
+ 29 [2m┆[0m let make = () => {
+ 30 [2m┆[0m
+ [1;31m31[0m [2m┆[0m [1;31m {"yo"->React.string} [0m
+ [1;31m32[0m [2m┆[0m [1;31m {""->React.string}
[0m
+ 33 [2m┆[0m
+ 34 [2m┆[0m }
+
+ The component [1;33m [0m is missing these required props:
+ value
\ No newline at end of file
diff --git a/tests/build_tests/super_errors/expected/missing_required_prop_when_single_child.res.expected b/tests/build_tests/super_errors/expected/missing_required_prop_when_single_child.res.expected
new file mode 100644
index 0000000000..e8e32f6ce5
--- /dev/null
+++ b/tests/build_tests/super_errors/expected/missing_required_prop_when_single_child.res.expected
@@ -0,0 +1,12 @@
+
+ [1;31mWe've found a bug for you![0m
+ [36m/.../fixtures/missing_required_prop_when_single_child.res[0m:[2m28:7-37[0m
+
+ 26 [2m┆[0m let make = () => {
+ 27 [2m┆[0m
+ [1;31m28[0m [2m┆[0m [1;31m {""->React.string}
[0m
+ 29 [2m┆[0m
+ 30 [2m┆[0m }
+
+ The component [1;33m [0m is missing these required props:
+ value
\ No newline at end of file
diff --git a/tests/build_tests/super_errors/fixtures/missing_required_prop.res b/tests/build_tests/super_errors/fixtures/missing_required_prop.res
new file mode 100644
index 0000000000..3faa09aaa6
--- /dev/null
+++ b/tests/build_tests/super_errors/fixtures/missing_required_prop.res
@@ -0,0 +1,31 @@
+module React = {
+ type element = Jsx.element
+ @val external null: element = "null"
+ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>
+ type component<'props> = Jsx.component<'props>
+ external component: componentLike<'props, element> => component<'props> = "%identity"
+ @module("react/jsx-runtime")
+ external jsx: (component<'props>, 'props) => element = "jsx"
+ external string: string => element = "%identity"
+}
+module ReactDOM = {
+ external someElement: React.element => option = "%identity"
+ @module("react/jsx-runtime")
+ external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx"
+}
+
+module Wrapper = {
+ @react.component
+ let make = (~value: 'value) => {
+ {React.null}
+ }
+}
+
+module SomeComponent = {
+ @react.component
+ let make = () => {
+
+ {""->React.string}
+
+ }
+}
diff --git a/tests/build_tests/super_errors/fixtures/missing_required_prop_when_children.res b/tests/build_tests/super_errors/fixtures/missing_required_prop_when_children.res
new file mode 100644
index 0000000000..ab468eda49
--- /dev/null
+++ b/tests/build_tests/super_errors/fixtures/missing_required_prop_when_children.res
@@ -0,0 +1,35 @@
+module React = {
+ type element = Jsx.element
+ @val external null: element = "null"
+ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>
+ type component<'props> = Jsx.component<'props>
+ external component: componentLike<'props, element> => component<'props> = "%identity"
+ @module("react/jsx-runtime")
+ external jsx: (component<'props>, 'props) => element = "jsx"
+ @module("react/jsx-runtime")
+ external jsxs: (component<'props>, 'props) => element = "jsxs"
+ external string: string => element = "%identity"
+ external array: array => element = "%identity"
+}
+module ReactDOM = {
+ external someElement: React.element => option = "%identity"
+ @module("react/jsx-runtime")
+ external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx"
+}
+
+module Wrapper = {
+ @react.component
+ let make = (~value: 'value, ~children: React.element) => {
+ {children}
+ }
+}
+
+module SomeComponent = {
+ @react.component
+ let make = () => {
+
+ {"yo"->React.string}
+ {""->React.string}
+
+ }
+}
diff --git a/tests/build_tests/super_errors/fixtures/missing_required_prop_when_single_child.res b/tests/build_tests/super_errors/fixtures/missing_required_prop_when_single_child.res
new file mode 100644
index 0000000000..97b0438f3f
--- /dev/null
+++ b/tests/build_tests/super_errors/fixtures/missing_required_prop_when_single_child.res
@@ -0,0 +1,31 @@
+module React = {
+ type element = Jsx.element
+ @val external null: element = "null"
+ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>
+ type component<'props> = Jsx.component<'props>
+ external component: componentLike<'props, element> => component<'props> = "%identity"
+ @module("react/jsx-runtime")
+ external jsx: (component<'props>, 'props) => element = "jsx"
+ external string: string => element = "%identity"
+}
+module ReactDOM = {
+ external someElement: React.element => option = "%identity"
+ @module("react/jsx-runtime")
+ external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx"
+}
+
+module Wrapper = {
+ @react.component
+ let make = (~value: 'value, ~children: React.element) => {
+ {children}
+ }
+}
+
+module SomeComponent = {
+ @react.component
+ let make = () => {
+
+ {""->React.string}
+
+ }
+}
diff --git a/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt b/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt
index 1762c37f26..6ed9d65c47 100644
--- a/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt
+++ b/tests/syntax_tests/data/ast-mapping/expected/JSXFragments.res.txt
@@ -1,11 +1,6 @@
let empty = React.jsx(React.jsxFragment, {})
-let fragmentWithBracedExpresssion = React.jsx(
- React.jsxFragment,
- {
- children: {React.int(1 + 2)},
- },
-)
+let fragmentWithBracedExpresssion = React.jsx(React.jsxFragment, {children: {React.int(1 + 2)}})
let fragmentWithJSXElements = React.jsxs(
React.jsxFragment,
@@ -23,12 +18,7 @@ let nestedFragments = React.jsxs(
children: React.array([
ReactDOM.jsx("h1", {children: ?ReactDOM.someElement({React.string("Hi")})}),
ReactDOM.jsx("p", {children: ?ReactDOM.someElement({React.string("Hello")})}),
- React.jsx(
- React.jsxFragment,
- {
- children: {React.string("Bye")},
- },
- ),
+ React.jsx(React.jsxFragment, {children: {React.string("Bye")}}),
]),
},
)
diff --git a/tests/syntax_tests/data/ppx/react/expected/fragment.res.txt b/tests/syntax_tests/data/ppx/react/expected/fragment.res.txt
index 225a99a4c0..80c3bb1d86 100644
--- a/tests/syntax_tests/data/ppx/react/expected/fragment.res.txt
+++ b/tests/syntax_tests/data/ppx/react/expected/fragment.res.txt
@@ -1,29 +1,14 @@
@@jsxConfig({version: 4})
let _ = React.jsx(React.jsxFragment, {})
-let _ = React.jsx(
- React.jsxFragment,
- {
- children: ReactDOM.jsx("div", {}),
- },
-)
+let _ = React.jsx(React.jsxFragment, {children: ReactDOM.jsx("div", {})})
let _ = React.jsxs(
React.jsxFragment,
{children: React.array([ReactDOM.jsx("div", {}), ReactDOM.jsx("div", {})])},
)
-let _ = React.jsx(
- React.jsxFragment,
- {
- children: React.jsx(React.jsxFragment, {}),
- },
-)
+let _ = React.jsx(React.jsxFragment, {children: React.jsx(React.jsxFragment, {})})
let _ = React.jsx(Z.make, {})
-let _ = React.jsx(
- Z.make,
- {
- children: ReactDOM.jsx("div", {}),
- },
-)
+let _ = React.jsx(Z.make, {children: ReactDOM.jsx("div", {})})
let _ = React.jsx(Z.make, {a: "a", children: ReactDOM.jsx("div", {})})
let _ = React.jsxs(
Z.make,