From a4b751310a0b1d17da78cca3c8010c3a32637a7b Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 25 Apr 2026 11:26:48 +0200 Subject: [PATCH 01/16] Make Jsx.component abstract --- compiler/ml/typecore.ml | 28 ++++ compiler/syntax/src/jsx_v4.ml | 141 +++++++++++------ packages/@rescript/runtime/Jsx.res | 6 +- .../src/GenericJsx.res | 4 +- .../deadcode/expected/deadcode.txt | 142 ++++++++++-------- .../tests-reanalyze/deadcode/src/Hooks.res | 7 +- .../tests/src/expected/CodeLens.res.txt | 3 + .../tests/src/expected/Hover.res.txt | 4 +- tests/build_tests/react_ppx/src/React.res | 4 +- .../react_ppx/src/abstract_component_test.res | 14 ++ .../src/abstract_component_test.res.js | 31 ++++ .../react_ppx/src/gpr_3987_test.res | 1 + .../react_ppx/src/gpr_3987_test.res.js | 6 +- .../src/recursive_component_test.res.js | 16 +- ...jsx_custom_component_children.res.expected | 10 +- ...ustom_component_optional_prop.res.expected | 10 +- ...ustom_component_type_mismatch.res.expected | 10 +- .../jsx_maybe_missing_fragment.res.expected | 12 +- ...x_type_mismatch_array_element.res.expected | 10 +- .../jsx_type_mismatch_array_raw.res.expected | 10 +- .../jsx_type_mismatch_float.res.expected | 10 +- .../jsx_type_mismatch_int.res.expected | 10 +- .../jsx_type_mismatch_option.res.expected | 10 +- .../jsx_type_mismatch_string.res.expected | 10 +- .../jsx_custom_component_children.res | 1 + .../jsx_custom_component_optional_prop.res | 1 + .../jsx_custom_component_type_mismatch.res | 1 + .../fixtures/jsx_maybe_missing_fragment.res | 1 + .../jsx_type_mismatch_array_element.res | 1 + .../fixtures/jsx_type_mismatch_array_raw.res | 1 + .../fixtures/jsx_type_mismatch_float.res | 1 + .../fixtures/jsx_type_mismatch_int.res | 1 + .../fixtures/jsx_type_mismatch_option.res | 1 + .../fixtures/jsx_type_mismatch_string.res | 1 + .../typescript-react-example/src/Hooks.res | 8 +- .../ppx/react/expected/aliasProps.res.txt | 28 ++-- .../ppx/react/expected/asyncAwait.res.txt | 8 +- .../ppx/react/expected/commentAtTop.res.txt | 4 +- .../react/expected/defaultPatternProp.res.txt | 8 +- .../react/expected/defaultValueProp.res.txt | 16 +- .../react/expected/fileLevelConfig.res.txt | 4 +- .../ppx/react/expected/forwardRef.res.txt | 8 +- .../data/ppx/react/expected/interface.res.txt | 8 +- .../ppx/react/expected/mangleKeyword.res.txt | 4 +- .../data/ppx/react/expected/nested.res.txt | 8 +- .../data/ppx/react/expected/newtype.res.txt | 24 +-- .../ppx/react/expected/noPropsWithKey.res.txt | 8 +- .../expected/optimizeAutomaticMode.res.txt | 4 +- .../react/expected/returnConstraint.res.txt | 12 +- .../ppx/react/expected/sharedProps.res.txt | 16 +- .../expected/sharedPropsWithProps.res.txt | 28 ++-- .../data/ppx/react/expected/topLevel.res.txt | 4 +- .../ppx/react/expected/typeConstraint.res.txt | 4 +- .../ppx/react/expected/uncurriedProps.res.txt | 12 +- .../data/ppx/react/expected/v4.res.txt | 64 ++++---- tests/tests/src/UncurriedAlways.res | 2 +- tests/tests/src/jsx_preserve_test.mjs | 16 +- tests/tests/src/jsx_preserve_test.res | 1 + tests/tests/src/recursive_react_component.mjs | 10 +- 59 files changed, 498 insertions(+), 330 deletions(-) create mode 100644 tests/build_tests/react_ppx/src/abstract_component_test.res create mode 100644 tests/build_tests/react_ppx/src/abstract_component_test.res.js diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index dc5c0d1d513..9f15b85a12f 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -1822,6 +1822,34 @@ let rec is_nonexpansive exp = List.for_all (fun vb -> is_nonexpansive vb.vb_expr) pat_exp_list && is_nonexpansive body | Texp_function _ -> true + (* `%identity` is a typed no-op coercion. Treating it like an ordinary + function call makes values such as `React.component(fn)` expansive, which + prevents generalization of polymorphic props: + + @react.component + let make = (~x) => + switch x { + | #a => React.string("A") + | #b => React.string("B") + | _ => React.string("other") + } + + The JSX transform emits a function value and then coerces it through + `React.component`, whose implementation is `%identity`. Since no runtime + computation happens beyond evaluating the argument, the application is + non-expansive exactly when all supplied arguments are non-expansive. *) + | Texp_apply + { + funct = + { + exp_desc = + Texp_ident + (_, _, {val_kind = Val_prim {Primitive.prim_name = "%identity"}}); + }; + args; + _; + } -> + List.for_all is_nonexpansive_opt (List.map snd args) | Texp_apply {partial = true; _} -> (* ReScript partial applications (`foo(args, ...)`) lower to wrapper functions in codegen, so creating the partial itself is nonexpansive diff --git a/compiler/syntax/src/jsx_v4.ml b/compiler/syntax/src/jsx_v4.ml index 83bbb0dcdc4..272cebdf86a 100644 --- a/compiler/syntax/src/jsx_v4.ml +++ b/compiler/syntax/src/jsx_v4.ml @@ -49,6 +49,56 @@ let ref_type loc = let jsx_element_type config ~loc = Typ.constr ~loc {loc; txt = module_access_name config "element"} [] +let jsx_component_expr config ~loc expr = + Exp.apply ~loc + (Exp.ident ~loc {loc; txt = module_access_name config "component"}) + [(Nolabel, expr)] + +let wrap_recursive_component_self_references ~config ~fn_name expr = + let jsx_module = String.capitalize_ascii config.Jsx_common.module_ in + let accepts_component = function + | { + pexp_desc = + Pexp_ident + { + txt = + Ldot + ( Lident module_name, + ( "createElement" | "createElementVariadic" | "jsx" + | "jsxKeyed" | "jsxs" | "jsxsKeyed" ) ); + }; + } -> + module_name = jsx_module + | _ -> false + in + let mapper = + { + Ast_mapper.default_mapper with + expr = + (fun mapper expr -> + match expr.pexp_desc with + | Pexp_apply ({funct; args} as apply) when accepts_component funct -> + let funct = mapper.expr mapper funct in + let args = + args + |> List.map (fun (label, arg) -> (label, mapper.expr mapper arg)) + in + let args = + match args with + | ( Nolabel, + ({pexp_desc = Pexp_ident {txt = Lident name}; pexp_loc = loc} + as self_ref) ) + :: rest + when name = fn_name -> + (Nolabel, jsx_component_expr config ~loc self_ref) :: rest + | _ -> args + in + {expr with pexp_desc = Pexp_apply {apply with funct; args}} + | _ -> Ast_mapper.default_mapper.expr mapper expr); + } + in + mapper.expr mapper expr + (* Helper method to filter out any attribute that isn't [@react.component] *) let other_attrs_pure (loc, _) = match loc.txt with @@ -66,20 +116,6 @@ let rec get_fn_name binding = Jsx_common.raise_error ~loc:ppat_loc "JSX component calls cannot be destructured." -let make_new_binding binding expression new_name = - match binding with - | {pvb_pat = {ppat_desc = Ppat_var ppat_var} as pvb_pat} -> - { - binding with - pvb_pat = - {pvb_pat with ppat_desc = Ppat_var {ppat_var with txt = new_name}}; - pvb_expr = expression; - pvb_attributes = []; - } - | {pvb_loc} -> - Jsx_common.raise_error ~loc:pvb_loc - "JSX component calls cannot be destructured." - (* Lookup the filename from the location information on the AST node and turn it into a valid module identifier *) let filename_from_loc (pstr_loc : Location.t) = let file_name = @@ -590,7 +626,6 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = } in let fn_name = get_fn_name binding.pvb_pat in - let internal_fn_name = fn_name ^ "$Internal" in let full_module_name = make_module_name file_name config.nested_modules fn_name in @@ -611,12 +646,7 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = in let inner_expression = Exp.apply - (Exp.ident - (Location.mknoloc - @@ Lident - (match rec_flag with - | Recursive -> internal_fn_name - | Nonrecursive -> fn_name))) + (Exp.ident (Location.mknoloc @@ Lident fn_name)) ([(Nolabel, Exp.ident (Location.mknoloc @@ Lident "props"))] @ match has_forward_ref with @@ -663,6 +693,23 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = ] (Exp.ident ~loc:pstr_loc {loc = empty_loc; txt = Lident txt}) in + (* Build a plain function first, then coerce that function to the abstract + component type: + + let make = React.component({ + let "File$Component" = props => make(props) + "File$Component" + }) + + Putting the coercion directly around the function argument, as in + `React.component(props => make(props))`, hides the function under an + application during typing and breaks inference for polymorphic props. + The typechecker treats this `%identity` coercion as non-expansive, so + the inferred prop type can still be generalized. *) + let full_expression = + if has_forward_ref then full_expression + else jsx_component_expr config ~loc:pstr_loc full_expression + in let rec returned_expression patterns_with_label patterns_with_nolabel ({pexp_desc} as expr) = match pexp_desc with @@ -780,29 +827,30 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = newtypes |> List.fold_left (fun e newtype -> Exp.newtype newtype e) expression in - (* let make = ({id, name, ...}: props<'id, 'name, ...>) => { ... } *) - let binding, new_binding = + let expression = match rec_flag with + | Nonrecursive -> expression | Recursive -> - ( binding_wrapper - (Exp.let_ ~loc:empty_loc Nonrecursive - [make_new_binding binding expression internal_fn_name] - (Exp.let_ ~loc:empty_loc Nonrecursive - [ - Vb.mk - (Pat.var {loc = empty_loc; txt = fn_name}) - full_expression; - ] - (Exp.ident {loc = empty_loc; txt = Lident fn_name}))), - None ) - | Nonrecursive -> - ( { - binding with - pvb_expr = expression; - pvb_pat = Pat.var {txt = fn_name; loc = Location.none}; - }, - Some (binding_wrapper full_expression) ) + (* Inside `let rec make = ...`, `make` still has to be the callable + implementation so recursive calls like `make(props)` keep working. + But component-position APIs now expect the abstract component type: + + React.createElement(make, props) + + Wrap only those self-references in the `%identity` component + coercion. The generated JavaScript still receives the same function, + while the typed AST sees a `React.component<_>`. *) + wrap_recursive_component_self_references ~config ~fn_name expression in + (* let make = ({id, name, ...}: props<'id, 'name, ...>) => { ... } *) + let binding = + { + binding with + pvb_expr = expression; + pvb_pat = Pat.var {txt = fn_name; loc = Location.none}; + } + in + let new_binding = Some (binding_wrapper full_expression) in (Some props_record_type, binding, new_binding)) else if Jsx_common.has_attr_on_binding Jsx_common.has_attr_with_props binding then @@ -860,7 +908,6 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = | _ -> Pat.var {txt = "props"; loc}) | _ -> Pat.var {txt = "props"; loc} in - let applied_expression = Exp.apply (Exp.ident @@ -887,6 +934,9 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = [Vb.mk (Pat.var {txt = full_module_name; loc}) wrapper_expr] (Exp.ident {txt = Lident full_module_name; loc}) in + let internal_expression = + jsx_component_expr config ~loc internal_expression + in Vb.mk ~attrs:modified_binding.pvb_attributes (Pat.var {txt = fn_name; loc}) @@ -1027,7 +1077,12 @@ let transform_structure_item ~config item = match new_bindings with | [] -> [] | new_bindings -> - [{pstr_loc = empty_loc; pstr_desc = Pstr_value (rec_flag, new_bindings)}]) + [ + { + pstr_loc = empty_loc; + pstr_desc = Pstr_value (Nonrecursive, new_bindings); + }; + ]) | _ -> [item] let transform_signature_item ~config item = diff --git a/packages/@rescript/runtime/Jsx.res b/packages/@rescript/runtime/Jsx.res index ec62d61644a..c2593003fd1 100644 --- a/packages/@rescript/runtime/Jsx.res +++ b/packages/@rescript/runtime/Jsx.res @@ -16,7 +16,11 @@ external array: array => element = "%identity" external promise: promise => element = "%identity" type componentLike<'props, 'return> = 'props => 'return -type component<'props> = componentLike<'props, element> + +/* Components consume props. If one component can accept broader props, it can + safely stand in for a component that only needs narrower props, just like a + function argument type. That makes the props parameter contravariant. */ +type component<-'props> /* this function exists to prepare for making `component` abstract */ external component: componentLike<'props, element> => component<'props> = "%identity" diff --git a/tests/analysis_tests/tests-generic-jsx-transform/src/GenericJsx.res b/tests/analysis_tests/tests-generic-jsx-transform/src/GenericJsx.res index d5f15273de1..b9e6341e62b 100644 --- a/tests/analysis_tests/tests-generic-jsx-transform/src/GenericJsx.res +++ b/tests/analysis_tests/tests-generic-jsx-transform/src/GenericJsx.res @@ -5,6 +5,8 @@ type component<'props> = Jsx.component<'props> type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> +external component: componentLike<'props, element> => component<'props> = "%identity" + @module("preact") external jsx: (component<'props>, 'props) => element = "jsx" @@ -60,4 +62,4 @@ module Elements = { external jsxsKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsxs" external someElement: element => option = "%identity" -} \ No newline at end of file +} diff --git a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt index 7ea5d1387e7..ba27040076b 100644 --- a/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt +++ b/tests/analysis_tests/tests-reanalyze/deadcode/expected/deadcode.txt @@ -31,6 +31,7 @@ addTypeReference _none_:1:-1 --> ComponentAsProp.res:6:12 addTypeReference _none_:1:-1 --> ComponentAsProp.res:6:20 addTypeReference _none_:1:-1 --> ComponentAsProp.res:6:34 + addValueReference ComponentAsProp.res:6:4 --> React.res:15:0 Scanning CreateErrorHandler1.cmt Source:CreateErrorHandler1.res addValueDeclaration +notification CreateErrorHandler1.res:3:6 path:+CreateErrorHandler1.Error1 addValueReference CreateErrorHandler1.res:3:6 --> CreateErrorHandler1.res:3:21 @@ -156,6 +157,7 @@ addValueReference DeadTest.res:117:32 --> DeadTest.res:117:12 addValueReference DeadTest.res:117:19 --> React.res:7:0 addTypeReference _none_:1:-1 --> DeadTest.res:117:12 + addValueReference DeadTest.res:117:4 --> React.res:15:0 addValueReference DeadTest.res:119:16 --> DeadTest.res:117:4 addVariantCaseDeclaration A DeadTest.res:140:11 path:+DeadTest.WithInclude.t addVariantCaseDeclaration A DeadTest.res:143:13 path:+DeadTest.WithInclude.T.t @@ -297,8 +299,10 @@ addValueReference DynamicallyLoadedComponent.res:2:32 --> DynamicallyLoadedComponent.res:2:12 addValueReference DynamicallyLoadedComponent.res:2:19 --> React.res:7:0 addTypeReference _none_:1:-1 --> DynamicallyLoadedComponent.res:2:12 + addValueReference DynamicallyLoadedComponent.res:2:4 --> React.res:15:0 Scanning EmptyArray.cmt Source:EmptyArray.res addValueDeclaration +make EmptyArray.res:5:6 path:+EmptyArray.Z + addValueReference EmptyArray.res:5:6 --> React.res:15:0 addValueReference EmptyArray.res:10:9 --> EmptyArray.res:5:6 Scanning ErrorHandler.cmt Source:ErrorHandler.res addValueDeclaration +notify ErrorHandler.res:7:6 path:+ErrorHandler.Make @@ -364,12 +368,12 @@ addValueReference ForOf.res:10:4 --> ForOf.res:8:0 Scanning Hooks.cmt Source:Hooks.res addValueDeclaration +make Hooks.res:4:4 path:+Hooks - addValueDeclaration +default Hooks.res:25:4 path:+Hooks - addValueDeclaration +make Hooks.res:29:6 path:+Hooks.Inner - addValueDeclaration +make Hooks.res:33:8 path:+Hooks.Inner.Inner2 - addValueDeclaration +make Hooks.res:39:6 path:+Hooks.NoProps - addValueDeclaration +functionWithRenamedArgs Hooks.res:45:4 path:+Hooks - addValueDeclaration +make Hooks.res:63:6 path:+Hooks.RenderPropRequiresConversion + addValueDeclaration +default Hooks.res:28:4 path:+Hooks + addValueDeclaration +make Hooks.res:32:6 path:+Hooks.Inner + addValueDeclaration +make Hooks.res:36:8 path:+Hooks.Inner.Inner2 + addValueDeclaration +make Hooks.res:42:6 path:+Hooks.NoProps + addValueDeclaration +functionWithRenamedArgs Hooks.res:48:4 path:+Hooks + addValueDeclaration +make Hooks.res:66:6 path:+Hooks.RenderPropRequiresConversion addRecordLabelDeclaration name Hooks.res:1:16 path:+Hooks.vehicle addRecordLabelDeclaration vehicle Hooks.res:4:12 path:+Hooks.props addValueReference Hooks.res:5:26 --> React.res:134:0 @@ -386,52 +390,59 @@ 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: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:18:74 --> Hooks.res:18:55 - addValueReference Hooks.res:18:61 --> React.res:7:0 + addValueReference Hooks.res:17:7 --> React.res:7:0 + addValueReference Hooks.res:17:32 --> React.res:7:0 + addValueReference Hooks.res:16:50 --> Hooks.res:16:32 + addValueReference Hooks.res:16:37 --> React.res:7:0 + addValueReference Hooks.res:16:16 --> React.res:15:0 + addValueReference Hooks.res:19:5 --> ImportHookDefault.res:6:0 + addValueReference Hooks.res:22:7 --> React.res:7:0 + addValueReference Hooks.res:22:32 --> React.res:7:0 + addValueReference Hooks.res:21:50 --> Hooks.res:21:32 + addValueReference Hooks.res:21:37 --> React.res:7:0 + addValueReference Hooks.res:21:16 --> React.res:15:0 addTypeReference _none_:1:-1 --> Hooks.res:4:12 - addValueReference Hooks.res:25:4 --> Hooks.res:4:4 - addRecordLabelDeclaration vehicle Hooks.res:29:14 path:+Hooks.Inner.props - addRecordLabelDeclaration vehicle Hooks.res:33:16 path:+Hooks.Inner.Inner2.props - addRecordLabelDeclaration vehicle Hooks.res:29:14 path:+Hooks.Inner.props - 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 - addValueReference Hooks.res:45:4 --> Hooks.res:45:37 - addValueReference Hooks.res:45:4 --> Hooks.res:45:31 - addValueReference Hooks.res:45:4 --> Hooks.res:45:45 - addRecordLabelDeclaration x Hooks.res:50:10 path:+Hooks.r - addRecordLabelDeclaration renderVehicle Hooks.res:63:14 path:+Hooks.RenderPropRequiresConversion.props - addRecordLabelDeclaration renderVehicle Hooks.res:63:14 path:+Hooks.RenderPropRequiresConversion.props - addValueDeclaration +car Hooks.res:64:8 path:+Hooks.RenderPropRequiresConversion - addValueReference Hooks.res:65:30 --> Hooks.res:64:8 - addValueReference Hooks.res:65:18 --> Hooks.res:65:18 - addValueReference Hooks.res:65:4 --> Hooks.res:63:14 - addTypeReference _none_:1:-1 --> Hooks.res:63:14 + addValueReference Hooks.res:4:4 --> React.res:15:0 + addValueReference Hooks.res:28:4 --> Hooks.res:4:4 + addRecordLabelDeclaration vehicle Hooks.res:32:14 path:+Hooks.Inner.props + addRecordLabelDeclaration vehicle Hooks.res:36:16 path:+Hooks.Inner.Inner2.props + addRecordLabelDeclaration vehicle Hooks.res:32:14 path:+Hooks.Inner.props + addTypeReference Hooks.res:32:66 --> Hooks.res:1:16 + addValueReference Hooks.res:32:66 --> Hooks.res:32:14 + addValueReference Hooks.res:32:34 --> React.res:7:0 + addTypeReference Hooks.res:32:66 --> Hooks.res:1:16 + addValueReference Hooks.res:32:66 --> Hooks.res:32:14 + addValueReference Hooks.res:32:34 --> React.res:7:0 + addTypeReference _none_:1:-1 --> Hooks.res:32:14 + addValueReference Hooks.res:32:6 --> React.res:15:0 + addRecordLabelDeclaration vehicle Hooks.res:36:16 path:+Hooks.Inner.Inner2.props + addRecordLabelDeclaration vehicle Hooks.res:36:16 path:+Hooks.Inner.Inner2.props + addTypeReference Hooks.res:36:68 --> Hooks.res:1:16 + addValueReference Hooks.res:36:68 --> Hooks.res:36:16 + addValueReference Hooks.res:36:36 --> React.res:7:0 + addTypeReference Hooks.res:36:68 --> Hooks.res:1:16 + addValueReference Hooks.res:36:68 --> Hooks.res:36:16 + addValueReference Hooks.res:36:36 --> React.res:7:0 + addTypeReference _none_:1:-1 --> Hooks.res:36:16 + addValueReference Hooks.res:36:8 --> React.res:15:0 + addValueReference Hooks.res:42:25 --> React.res:3:0 + addValueReference Hooks.res:42:25 --> React.res:3:0 + addValueReference Hooks.res:42:6 --> React.res:15:0 + addTypeReference Hooks.res:50:2 --> Hooks.res:1:16 + addValueReference Hooks.res:48:4 --> Hooks.res:48:31 + addTypeReference Hooks.res:50:14 --> Hooks.res:1:16 + addValueReference Hooks.res:48:4 --> Hooks.res:48:37 + addValueReference Hooks.res:48:4 --> Hooks.res:48:31 + addValueReference Hooks.res:48:4 --> Hooks.res:48:45 + addRecordLabelDeclaration x Hooks.res:53:10 path:+Hooks.r + addRecordLabelDeclaration renderVehicle Hooks.res:66:14 path:+Hooks.RenderPropRequiresConversion.props + addRecordLabelDeclaration renderVehicle Hooks.res:66:14 path:+Hooks.RenderPropRequiresConversion.props + addValueDeclaration +car Hooks.res:67:8 path:+Hooks.RenderPropRequiresConversion + addValueReference Hooks.res:68:30 --> Hooks.res:67:8 + addValueReference Hooks.res:68:18 --> Hooks.res:68:18 + addValueReference Hooks.res:68:4 --> Hooks.res:66:14 + addTypeReference _none_:1:-1 --> Hooks.res:66:14 + addValueReference Hooks.res:66:6 --> React.res:15:0 Scanning IgnoreInterface.cmt Source:IgnoreInterface.res Scanning IgnoreInterface.cmti Source:IgnoreInterface.resi Scanning ImmutableArray.cmt Source:ImmutableArray.res @@ -955,6 +966,7 @@ Scanning JsxV4.cmt Source:JsxV4.res addValueDeclaration +make JsxV4.res:4:23 path:+JsxV4.C addValueReference JsxV4.res:4:36 --> React.res:3:0 + addValueReference JsxV4.res:4:23 --> React.res:15:0 addValueReference JsxV4.res:7:9 --> JsxV4.res:4:23 Scanning LetPrivate.cmt Source:LetPrivate.res addValueDeclaration +y LetPrivate.res:7:4 path:+LetPrivate @@ -1938,10 +1950,9 @@ Forward Liveness Analysis decls: 698 - roots(external targets): 134 - decl-deps: decls_with_out=409 edges_to_decls=287 + roots(external targets): 135 + decl-deps: decls_with_out=410 edges_to_decls=287 - Root (annotated): Value +Hooks.+default Root (external ref): Value +FirstClassModules.M.InnerModule2.+k Root (external ref): VariantCase DeadRT.moduleAccessPath.Root Root (external ref): Value +TypeReexport.VariantUseOriginal.+value @@ -1965,7 +1976,6 @@ Forward Liveness Analysis Root (annotated): Value +TypeParams3.+test Root (annotated): Value +Variants.+sunday Root (annotated): Value +NestedModules.Universe.Nested2.Nested3.+nested3Value - Root (annotated): Value +Hooks.+functionWithRenamedArgs Root (external ref): RecordLabel +Unison.t.doc Root (annotated): Value +Tuples.+computeAreaWithIdent Root (annotated): Value +LetPrivate.+y @@ -1978,7 +1988,6 @@ Forward Liveness Analysis Root (external ref): Value +Newton.+f Root (external ref): RecordLabel +Records.record.v Root (external ref): VariantCase +DeadTest.VariantUsedOnlyInImplementation.t.A - Root (external ref): Value +Hooks.RenderPropRequiresConversion.+car Root (external ref): RecordLabel +Records.person.address Root (annotated): Value +Variants.+testConvert2 Root (annotated): Value +Tuples.+coord2d @@ -2004,22 +2013,22 @@ Forward Liveness Analysis Root (annotated): Value +ImportJsValue.+default Root (annotated): Value +VariantsWithPayload.+testVariant1Int Root (external ref): Value +DynamicallyLoadedComponent.+make + Root (annotated): Value +Hooks.RenderPropRequiresConversion.+make Root (annotated): Value +Uncurried.+uncurried2 Root (annotated): Value +UseImportJsValue.+useTypeImportedInOtherModule - Root (annotated): Value +Hooks.NoProps.+make + Root (annotated): Value +Hooks.Inner.+make Root (external ref): Value +OptArg.+foo Root (annotated): Value +Variants.+fortytwoOK Root (external ref): Value OptArg.+bar Root (annotated): Value +Records.+payloadValue Root (external ref): RecordLabel +DeadTest.props.s + Root (annotated): Value +Hooks.+default Root (external ref): VariantCase +Unison.break_.Never Root (annotated): Value +TestEmitInnerModules.Inner.+y Root (external ref): VariantCase InnerModuleTypes.I.t.Foo Root (annotated): Value +Types.+selfRecursiveConverter Root (external ref): Value +DeadTest.+thisIsUsedTwice Root (annotated): Value +Opaque.+testConvertNestedRecordFromOtherFile - Root (annotated): Value +Hooks.Inner.Inner2.+make - Root (external ref): RecordLabel +Hooks.Inner.Inner2.props.vehicle Root (external ref): Value +OptArg.+bar Root (annotated): Value +TestFirstClassModules.+convertRecord Root (external ref): VariantCase DeadTypeTest.deadType.OnlyInInterface @@ -2036,6 +2045,7 @@ Forward Liveness Analysis Root (external ref): Value +DeadTest.+deadIncorrect Root (external ref): RecordLabel +TypeReexport.UseReexported.reexportedType.usedField Root (annotated): Value +Docstrings.+signMessage + Root (external ref): RecordLabel +Hooks.Inner.Inner2.props.vehicle Root (external ref): Value +DeadExn.+eInside Root (external ref): RecordLabel +ComponentAsProp.props.button Root (annotated): Value +ImportJsValue.+returnedFromHigherOrder @@ -2059,6 +2069,7 @@ Forward Liveness Analysis Root (annotated): Value +References.+get Root (annotated): Value +ModuleAliases.+testNested Root (external ref): Value +FirstClassModules.SomeFunctor.+ww + Root (annotated): Value +Hooks.Inner.Inner2.+make Root (annotated): Value +ImportJsValue.+area Root (annotated): Value +Records.+testMyRec Root (annotated): Value +DeadTest.GloobLive.+globallyLive3 @@ -2075,13 +2086,13 @@ Forward Liveness Analysis Root (annotated): Value +ScopedAnnotationsLiveVsDead.LiveScope.+root Root (external ref): Value +Newton.+result Root (annotated): Value +Records.+findAllAddresses + Root (annotated): Value +Hooks.NoProps.+make Root (annotated): Value +ImportJsValue.+useGetProp Root (annotated): Value +Variants.+id2 Root (annotated): Value +Uncurried.+sumU Root (external ref): Value +TestOptArg.+bar Root (external ref): RecordLabel +DeadTest.record.yyy Root (annotated): Value +Docstrings.+one - Root (annotated): Value +Hooks.Inner.+make Root (external ref): Value +OptArg.+twoArgs Root (annotated): Value +OcamlWarningSuppressToplevel.+suppressed1 Root (external ref): RecordLabel +Records.business2.address2 @@ -2102,7 +2113,6 @@ Forward Liveness Analysis Root (annotated): Value +TestImmutableArray.+testImmutableArrayGet Root (external ref): Value +TypeReexport.UseOriginal.+value Root (annotated): Value +Docstrings.+unnamed2 - Root (annotated): Value +Hooks.RenderPropRequiresConversion.+make Root (annotated): Value +LetPrivate.local_1.+x Root (annotated): Value +TestImport.+make Root (annotated): Value +DeadTest.GloobLive.+globallyLive1 @@ -2180,6 +2190,7 @@ Forward Liveness Analysis Root (annotated): Value +Variants.+fortytwoBAD Root (external ref): Value +DeadTest.+thisIsUsedOnce Root (external ref): RecordLabel +Unison.t.break_ + Root (external ref): RecordLabel +Hooks.Inner.props.vehicle Root (external ref): Value ImmutableArray.+fromArray Root (external ref): Value +RepeatedLabel.+userData Root (annotated): Value +Variants.+testConvert2to3 @@ -2201,7 +2212,7 @@ Forward Liveness Analysis Root (annotated): Value +ModuleAliases.+testInner2 Root (annotated): RecordLabel +ImportHooks.props.person Root (external ref): Value DeadValueTest.+valueAlive - Root (external ref): RecordLabel +Hooks.Inner.props.vehicle + Root (external ref): RecordLabel +Hooks.RenderPropRequiresConversion.props.renderVehicle Root (annotated): Value +Shadow.M.+test Root (annotated): Value +ComponentAsProp.+make Root (annotated): Value +Records.+testMyRec2 @@ -2226,6 +2237,7 @@ Forward Liveness Analysis Root (external ref): VariantCase +TypeReexport.VariantUseOriginal.reexportedType.A Root (annotated): Value +Variants.+polyWithOpt Root (annotated): Value +References.+destroysRefIdentity + Root (external ref): Value +Hooks.RenderPropRequiresConversion.+car Root (external ref): Value +FirstClassModules.M.+x Root (external ref): Value +TypeReexportCrossFileB.+recordValue Root (annotated): Value +Records.+computeArea4 @@ -2262,11 +2274,10 @@ Forward Liveness Analysis Root (annotated): Value +Tuples.+computeAreaNoConverters Root (annotated): Value +Types.+mutuallyRecursiveConverter Root (annotated): Value +UseImportJsValue.+useGetProp - Root (external ref): RecordLabel +Hooks.RenderPropRequiresConversion.props.renderVehicle + Root (annotated): Value +Hooks.+functionWithRenamedArgs 322 roots found - Propagate: +Hooks.+default -> +Hooks.+make Propagate: DeadRT.moduleAccessPath.Root -> +DeadRT.moduleAccessPath.Root Propagate: +TypeReexportCrossFileB.reexportedRecord.usedField -> +TypeReexportCrossFileA.originalRecord.usedField Propagate: +DeadTypeTest.deadType.OnlyInImplementation -> DeadTypeTest.deadType.OnlyInImplementation @@ -2277,6 +2288,7 @@ Forward Liveness Analysis Propagate: +DeadTest.VariantUsedOnlyInImplementation.t.A -> +DeadTest.VariantUsedOnlyInImplementation.t.A Propagate: +DeadTest.+thisIsMarkedLive -> +DeadTest.+thisIsKeptAlive Propagate: +TypeReexport.UseOriginal.reexportedType.directlyUsed -> +TypeReexport.UseOriginal.originalType.directlyUsed + Propagate: +Hooks.+default -> +Hooks.+make Propagate: InnerModuleTypes.I.t.Foo -> +InnerModuleTypes.I.t.Foo Propagate: DeadTypeTest.deadType.OnlyInInterface -> +DeadTypeTest.deadType.OnlyInInterface Propagate: +TypeReexport.UseReexported.reexportedType.usedField -> +TypeReexport.UseReexported.originalType.usedField @@ -4239,7 +4251,7 @@ Forward Liveness Analysis sideEffects is never used and could have side effects Warning Dead Type - Hooks.res:50:11-19 + Hooks.res:53:11-19 r.x is a record label never used to read a value Warning Dead Value diff --git a/tests/analysis_tests/tests-reanalyze/deadcode/src/Hooks.res b/tests/analysis_tests/tests-reanalyze/deadcode/src/Hooks.res index b3fd7dc7724..1ebc2d8e365 100644 --- a/tests/analysis_tests/tests-reanalyze/deadcode/src/Hooks.res +++ b/tests/analysis_tests/tests-reanalyze/deadcode/src/Hooks.res @@ -11,11 +11,14 @@ let make = (~vehicle) => { )}

- React.string(x["randomString"])}> + React.string(x["randomString"]))}> {React.string("child1")} {React.string("child2")} React.string(x["randomString"])}> + person={name: "DefaultImport", age: 42} + renderMe={React.component(x => React.string(x["randomString"]))}> {React.string("child1")} {React.string("child2")} diff --git a/tests/analysis_tests/tests/src/expected/CodeLens.res.txt b/tests/analysis_tests/tests/src/expected/CodeLens.res.txt index e75a2865164..bc9288e8c95 100644 --- a/tests/analysis_tests/tests/src/expected/CodeLens.res.txt +++ b/tests/analysis_tests/tests/src/expected/CodeLens.res.txt @@ -1,5 +1,8 @@ Code Lens src/CodeLens.res [{ + "range": {"start": {"line": 9, "character": 4}, "end": {"line": 9, "character": 8}}, + "command": {"title": "React.componentLike, React.element> => React.component>", "command": ""} + }, { "range": {"start": {"line": 4, "character": 4}, "end": {"line": 4, "character": 6}}, "command": {"title": "(~opt1: int=?, ~a: int, ~b: int, unit, ~opt2: int=?, unit, ~c: int) => int", "command": ""} }, { diff --git a/tests/analysis_tests/tests/src/expected/Hover.res.txt b/tests/analysis_tests/tests/src/expected/Hover.res.txt index f9f1746e150..b5c1da0f91e 100644 --- a/tests/analysis_tests/tests/src/expected/Hover.res.txt +++ b/tests/analysis_tests/tests/src/expected/Hover.res.txt @@ -22,10 +22,10 @@ Hover src/Hover.res 33:4 {"contents": {"kind": "markdown", "value": "```rescript\nunit => int\n```\n---\nDoc comment for functionWithTypeAnnotation"}} Hover src/Hover.res 37:13 -{"contents": {"kind": "markdown", "value": "```rescript\nstring\n```"}} +{"contents": {"kind": "markdown", "value": "```rescript\nReact.componentLike<\n props,\n React.element,\n> => React.component>\n```\n\n---\n\n```\n \n```\n```rescript\ntype React.componentLike<\n 'props,\n 'return,\n> = Jsx.componentLike<'props, 'return>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C10%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype props<'name> = {name: 'name}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C36%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.element = Jsx.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.component<'props> = Jsx.component<'props>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n"}} Hover src/Hover.res 42:15 -{"contents": {"kind": "markdown", "value": "```rescript\nstring\n```"}} +{"contents": {"kind": "markdown", "value": "```rescript\nReact.componentLike<\n props,\n React.element,\n> => React.component>\n```\n\n---\n\n```\n \n```\n```rescript\ntype React.componentLike<\n 'props,\n 'return,\n> = Jsx.componentLike<'props, 'return>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C10%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype props<'name> = {name: 'name}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C41%2C2%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.element = Jsx.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n\n---\n\n```\n \n```\n```rescript\ntype React.component<'props> = Jsx.component<'props>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n"}} Hover src/Hover.res 46:10 {"contents": {"kind": "markdown", "value": "```rescript\nint\n```"}} diff --git a/tests/build_tests/react_ppx/src/React.res b/tests/build_tests/react_ppx/src/React.res index 4350f6b2e54..6e16687642c 100644 --- a/tests/build_tests/react_ppx/src/React.res +++ b/tests/build_tests/react_ppx/src/React.res @@ -8,7 +8,9 @@ external array: array => element = "%identity" type componentLike<'props, 'return> = 'props => 'return -type component<'props> = componentLike<'props, element> +type component<-'props> + +external component: componentLike<'props, element> => component<'props> = "%identity" @module("react") external createElement: (component<'props>, 'props) => element = "createElement" diff --git a/tests/build_tests/react_ppx/src/abstract_component_test.res b/tests/build_tests/react_ppx/src/abstract_component_test.res new file mode 100644 index 00000000000..067f75c2a35 --- /dev/null +++ b/tests/build_tests/react_ppx/src/abstract_component_test.res @@ -0,0 +1,14 @@ +module PolyVariantLowerBound = { + @react.component + let make = (~x) => + switch x { + | #a => React.string("A") + | #b => React.string("B") + | _ => React.string("other") + } +} + +module GenericRenderProp = { + @react.component + let make = (~x: 'x, ~render: 'x => React.element) => render(x) +} diff --git a/tests/build_tests/react_ppx/src/abstract_component_test.res.js b/tests/build_tests/react_ppx/src/abstract_component_test.res.js new file mode 100644 index 00000000000..8863c77600f --- /dev/null +++ b/tests/build_tests/react_ppx/src/abstract_component_test.res.js @@ -0,0 +1,31 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + + +function Abstract_component_test$PolyVariantLowerBound(props) { + let x = props.x; + if (x === "a") { + return "A"; + } else if (x === "b") { + return "B"; + } else { + return "other"; + } +} + +let PolyVariantLowerBound = { + make: Abstract_component_test$PolyVariantLowerBound +}; + +function Abstract_component_test$GenericRenderProp(props) { + return props.render(props.x); +} + +let GenericRenderProp = { + make: Abstract_component_test$GenericRenderProp +}; + +export { + PolyVariantLowerBound, + GenericRenderProp, +} +/* No side effect */ diff --git a/tests/build_tests/react_ppx/src/gpr_3987_test.res b/tests/build_tests/react_ppx/src/gpr_3987_test.res index 6b73ce7a6c8..03a65d0e6d1 100644 --- a/tests/build_tests/react_ppx/src/gpr_3987_test.res +++ b/tests/build_tests/react_ppx/src/gpr_3987_test.res @@ -26,6 +26,7 @@ let makeContainer = text => { module Gpr3987ReproOk = { type props = {value: string, onChange: (string, int) => unit} + @react.componentWithProps let make = (_props: props) => React.null } diff --git a/tests/build_tests/react_ppx/src/gpr_3987_test.res.js b/tests/build_tests/react_ppx/src/gpr_3987_test.res.js index 378f9156caf..30bedfb06b7 100644 --- a/tests/build_tests/react_ppx/src/gpr_3987_test.res.js +++ b/tests/build_tests/react_ppx/src/gpr_3987_test.res.js @@ -16,15 +16,15 @@ function makeContainer(text) { return content; } -function make(_props) { +function Gpr_3987_test$Gpr3987ReproOk(props) { return null; } let Gpr3987ReproOk = { - make: make + make: Gpr_3987_test$Gpr3987ReproOk }; -JsxRuntime.jsx(make, { +JsxRuntime.jsx(Gpr_3987_test$Gpr3987ReproOk, { value: "test", onChange: (param, param$1) => {} }); diff --git a/tests/build_tests/react_ppx/src/recursive_component_test.res.js b/tests/build_tests/react_ppx/src/recursive_component_test.res.js index 4e3fd9951f4..8c8e26e1254 100644 --- a/tests/build_tests/react_ppx/src/recursive_component_test.res.js +++ b/tests/build_tests/react_ppx/src/recursive_component_test.res.js @@ -1,21 +1,23 @@ // Generated by ReScript, PLEASE EDIT WITH CARE +function make(param) { + return mm({ + b: param.b + }); +} + function mm(x) { return make({ b: !x.b }); } -function make(props) { - return mm({ - b: props.b - }); -} +let Recursive_component_test$Rec = make; let Rec = { - make: make, - mm: mm + mm: mm, + make: Recursive_component_test$Rec }; export { diff --git a/tests/build_tests/super_errors/expected/jsx_custom_component_children.res.expected b/tests/build_tests/super_errors/expected/jsx_custom_component_children.res.expected index 46b4d181884..a1eb83e9d5a 100644 --- a/tests/build_tests/super_errors/expected/jsx_custom_component_children.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_custom_component_children.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_custom_component_children.res:24:28-29 + /.../fixtures/jsx_custom_component_children.res:25:28-29 - 22 │ } - 23 │ - 24 │ let x = {1.} - 25 │ + 23 │ } + 24 │ + 25 │ let x = {1.} + 26 │ This has type: float But children passed to this component must be of type: diff --git a/tests/build_tests/super_errors/expected/jsx_custom_component_optional_prop.res.expected b/tests/build_tests/super_errors/expected/jsx_custom_component_optional_prop.res.expected index 334f245a49e..d2cdb5aae82 100644 --- a/tests/build_tests/super_errors/expected/jsx_custom_component_optional_prop.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_custom_component_optional_prop.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_custom_component_optional_prop.res:33:34 + /.../fixtures/jsx_custom_component_optional_prop.res:34:34 - 31 │ let o = Some(1.) - 32 │ - 33 │ let x = - 34 │ + 32 │ let o = Some(1.) + 33 │ + 34 │ let x = + 35 │ This has type: option But the component prop someOpt is expected to have type: float diff --git a/tests/build_tests/super_errors/expected/jsx_custom_component_type_mismatch.res.expected b/tests/build_tests/super_errors/expected/jsx_custom_component_type_mismatch.res.expected index 9db22a386a8..04cbd8877f4 100644 --- a/tests/build_tests/super_errors/expected/jsx_custom_component_type_mismatch.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_custom_component_type_mismatch.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_custom_component_type_mismatch.res:31:34-40 + /.../fixtures/jsx_custom_component_type_mismatch.res:32:34-40 - 29 │ } - 30 │ - 31 │ let x = - 32 │ + 30 │ } + 31 │ + 32 │ let x = + 33 │ This has type: string But the component prop someOpt is expected to have type: float diff --git a/tests/build_tests/super_errors/expected/jsx_maybe_missing_fragment.res.expected b/tests/build_tests/super_errors/expected/jsx_maybe_missing_fragment.res.expected index a21dfbd5b99..9324ee38131 100644 --- a/tests/build_tests/super_errors/expected/jsx_maybe_missing_fragment.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_maybe_missing_fragment.res.expected @@ -1,12 +1,12 @@ We've found a bug for you! - /.../fixtures/jsx_maybe_missing_fragment.res:18:3-8 + /.../fixtures/jsx_maybe_missing_fragment.res:19:3-8 - 16 │ - 17 │ let x = { - 18 │ <>  - 19 │ <> - 20 │ } + 17 │ + 18 │ let x = { + 19 │ <>  + 20 │ <> + 21 │ } This has type: React.element (defined as Jsx.element) But it's expected to have type: unit diff --git a/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_element.res.expected b/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_element.res.expected index 1cf8426d78c..b83f56dc6d8 100644 --- a/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_element.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_element.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_type_mismatch_array_element.res:19:13-30 + /.../fixtures/jsx_type_mismatch_array_element.res:20:13-30 - 17 │ } - 18 │ - 19 │ let x = <> {[React.string("")]} - 20 │ + 18 │ } + 19 │ + 20 │ let x = <> {[React.string("")]} + 21 │ This has type: array<'a> But it's expected to have type: React.element (defined as Jsx.element) diff --git a/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_raw.res.expected b/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_raw.res.expected index 1eb1d4870d4..6524468d197 100644 --- a/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_raw.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_type_mismatch_array_raw.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_type_mismatch_array_raw.res:17:13-16 + /.../fixtures/jsx_type_mismatch_array_raw.res:18:13-16 - 15 │ } - 16 │ - 17 │ let x = <> {[""]} - 18 │ + 16 │ } + 17 │ + 18 │ let x = <> {[""]} + 19 │ This has type: array<'a> But it's expected to have type: React.element (defined as Jsx.element) diff --git a/tests/build_tests/super_errors/expected/jsx_type_mismatch_float.res.expected b/tests/build_tests/super_errors/expected/jsx_type_mismatch_float.res.expected index 2244de59be7..6f709a191d9 100644 --- a/tests/build_tests/super_errors/expected/jsx_type_mismatch_float.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_type_mismatch_float.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_type_mismatch_float.res:17:13-14 + /.../fixtures/jsx_type_mismatch_float.res:18:13-14 - 15 │ } - 16 │ - 17 │ let x = <> {1.} - 18 │ + 16 │ } + 17 │ + 18 │ let x = <> {1.} + 19 │ This has type: float But children of JSX fragments must be of type: diff --git a/tests/build_tests/super_errors/expected/jsx_type_mismatch_int.res.expected b/tests/build_tests/super_errors/expected/jsx_type_mismatch_int.res.expected index 8bcf5e984bc..53ec9873d03 100644 --- a/tests/build_tests/super_errors/expected/jsx_type_mismatch_int.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_type_mismatch_int.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_type_mismatch_int.res:17:13 + /.../fixtures/jsx_type_mismatch_int.res:18:13 - 15 │ } - 16 │ - 17 │ let x = <> {1} - 18 │ + 16 │ } + 17 │ + 18 │ let x = <> {1} + 19 │ This has type: int But children of JSX fragments must be of type: diff --git a/tests/build_tests/super_errors/expected/jsx_type_mismatch_option.res.expected b/tests/build_tests/super_errors/expected/jsx_type_mismatch_option.res.expected index fee1ae03c6b..d68678a10af 100644 --- a/tests/build_tests/super_errors/expected/jsx_type_mismatch_option.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_type_mismatch_option.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_type_mismatch_option.res:17:13-16 + /.../fixtures/jsx_type_mismatch_option.res:18:13-16 - 15 │ } - 16 │ - 17 │ let x = <> {None} - 18 │ + 16 │ } + 17 │ + 18 │ let x = <> {None} + 19 │ This has type: option<'a> But it's expected to have type: React.element (defined as Jsx.element) diff --git a/tests/build_tests/super_errors/expected/jsx_type_mismatch_string.res.expected b/tests/build_tests/super_errors/expected/jsx_type_mismatch_string.res.expected index 63d11ba4f8c..42a24931952 100644 --- a/tests/build_tests/super_errors/expected/jsx_type_mismatch_string.res.expected +++ b/tests/build_tests/super_errors/expected/jsx_type_mismatch_string.res.expected @@ -1,11 +1,11 @@ We've found a bug for you! - /.../fixtures/jsx_type_mismatch_string.res:17:13-14 + /.../fixtures/jsx_type_mismatch_string.res:18:13-14 - 15 │ } - 16 │ - 17 │ let x = <> {""} - 18 │ + 16 │ } + 17 │ + 18 │ let x = <> {""} + 19 │ This has type: string But children of JSX fragments must be of type: diff --git a/tests/build_tests/super_errors/fixtures/jsx_custom_component_children.res b/tests/build_tests/super_errors/fixtures/jsx_custom_component_children.res index b4059e242b6..6dd8fc64321 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_custom_component_children.res +++ b/tests/build_tests/super_errors/fixtures/jsx_custom_component_children.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_custom_component_optional_prop.res b/tests/build_tests/super_errors/fixtures/jsx_custom_component_optional_prop.res index 3c3b220fdbc..613bc87c666 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_custom_component_optional_prop.res +++ b/tests/build_tests/super_errors/fixtures/jsx_custom_component_optional_prop.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_custom_component_type_mismatch.res b/tests/build_tests/super_errors/fixtures/jsx_custom_component_type_mismatch.res index 58759ac7e89..a03c235ccd1 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_custom_component_type_mismatch.res +++ b/tests/build_tests/super_errors/fixtures/jsx_custom_component_type_mismatch.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_maybe_missing_fragment.res b/tests/build_tests/super_errors/fixtures/jsx_maybe_missing_fragment.res index b0c101a4f0e..c148532b07d 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_maybe_missing_fragment.res +++ b/tests/build_tests/super_errors/fixtures/jsx_maybe_missing_fragment.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_element.res b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_element.res index cebe870397b..ec78b8038df 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_element.res +++ b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_element.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_raw.res b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_raw.res index 3a3c531b503..5d681e9c904 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_raw.res +++ b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_array_raw.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_float.res b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_float.res index 208eacac397..ea6a65c2eb1 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_float.res +++ b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_float.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_int.res b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_int.res index 6836a4865de..587f55e912f 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_int.res +++ b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_int.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_option.res b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_option.res index 334d11978c5..a0d833a9e39 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_option.res +++ b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_option.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_string.res b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_string.res index 55ec5786e67..448c58c7b8d 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_string.res +++ b/tests/build_tests/super_errors/fixtures/jsx_type_mismatch_string.res @@ -6,6 +6,7 @@ module React = { type element = Jsx.element type componentLike<'props, 'return> = '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" diff --git a/tests/gentype_tests/typescript-react-example/src/Hooks.res b/tests/gentype_tests/typescript-react-example/src/Hooks.res index b57ce241557..a8bcf090a0d 100644 --- a/tests/gentype_tests/typescript-react-example/src/Hooks.res +++ b/tests/gentype_tests/typescript-react-example/src/Hooks.res @@ -15,12 +15,16 @@ let make = (~vehicle) => { )}

- React.string(x["randomString"])}> + React.string(x["randomString"]))} + > {React.string("child1")} {React.string("child2")} React.string(x["randomString"])} + person={name: "DefaultImport", age: 42} + renderMe={React.component(x => React.string(x["randomString"]))} > {React.string("child1")} {React.string("child2")} diff --git a/tests/syntax_tests/data/ppx/react/expected/aliasProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/aliasProps.res.txt index f2ae63a276f..cf1e0bc6ad1 100644 --- a/tests/syntax_tests/data/ppx/react/expected/aliasProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/aliasProps.res.txt @@ -15,11 +15,11 @@ module C0 = { let text = __text_value (React.string(text): React.element) } - let make = { + let make = React.component({ let \"AliasProps$C0" = (props: props<_>) => make(props) \"AliasProps$C0" - } + }) } module C1 = { @@ -37,11 +37,11 @@ module C1 = { let text = __text_value (React.string(p ++ text): React.element) } - let make = { + let make = React.component({ let \"AliasProps$C1" = (props: props<_>) => make(props) \"AliasProps$C1" - } + }) } module C2 = { @@ -58,11 +58,11 @@ module C2 = { let bar = __foo_value (React.string(bar): React.element) } - let make = { + let make = React.component({ let \"AliasProps$C2" = (props: props<_>) => make(props) \"AliasProps$C2" - } + }) } module C3 = { @@ -90,11 +90,11 @@ module C3 = { }: React.element ) } - let make = { + let make = React.component({ let \"AliasProps$C3" = (props: props<_>) => make(props) \"AliasProps$C3" - } + }) } module C4 = { @@ -112,11 +112,11 @@ module C4 = { let x = __x_value (ReactDOM.jsx("div", {children: ?ReactDOM.someElement(b)}): React.element) } - let make = { + let make = React.component({ let \"AliasProps$C4" = (props: props<_>) => make(props) \"AliasProps$C4" - } + }) } module C5 = { @@ -134,11 +134,11 @@ module C5 = { let z = __z_value (x + y + z: React.element) } - let make = { + let make = React.component({ let \"AliasProps$C5" = (props: props<_>) => make(props) \"AliasProps$C5" - } + }) } module C6 = { @@ -156,9 +156,9 @@ module C6 = { let make = ({comp: module(Comp: Comp), x: (a, b), _}: props<_, _>): React.element => React.jsx(Comp.make, {}) - let make = { + let make = React.component({ let \"AliasProps$C6" = (props: props<_>) => make(props) \"AliasProps$C6" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt index d77b11decd3..d78934b3c3d 100644 --- a/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/asyncAwait.res.txt @@ -10,11 +10,11 @@ module C0 = { let a = await f(a) (ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}): React.element) } - let make = { + let make = React.component({ let \"AsyncAwait$C0" = (props: props<_>) => Jsx.promise(make(props)) \"AsyncAwait$C0" - } + }) } module C1 = { @@ -29,9 +29,9 @@ module C1 = { | #off => React.string("off") } } - let make = { + let make = React.component({ let \"AsyncAwait$C1" = (props: props<_>) => Jsx.promise(make(props)) \"AsyncAwait$C1" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/commentAtTop.res.txt b/tests/syntax_tests/data/ppx/react/expected/commentAtTop.res.txt index e8242d70cb3..033f9b418d6 100644 --- a/tests/syntax_tests/data/ppx/react/expected/commentAtTop.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/commentAtTop.res.txt @@ -6,8 +6,8 @@ type props<'msg> = { let make = ({msg, _}: props<_>): React.element => { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({msg->React.string})}) } -let make = { +let make = React.component({ let \"CommentAtTop" = (props: props<_>) => make(props) \"CommentAtTop" -} +}) diff --git a/tests/syntax_tests/data/ppx/react/expected/defaultPatternProp.res.txt b/tests/syntax_tests/data/ppx/react/expected/defaultPatternProp.res.txt index 97ed9f45567..b1ea9117691 100644 --- a/tests/syntax_tests/data/ppx/react/expected/defaultPatternProp.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/defaultPatternProp.res.txt @@ -6,11 +6,11 @@ module C0 = { type props = {} let make = (_: props): React.element => React.null - let make = { + let make = React.component({ let \"DefaultPatternProp$C0$M" = props => make(props) \"DefaultPatternProp$C0$M" - } + }) } module type S = module type of M @@ -27,9 +27,9 @@ module C0 = { let module(C: S) = __component_value (React.jsx(C.make, {}): React.element) } - let make = { + let make = React.component({ let \"DefaultPatternProp$C0" = (props: props<_>) => make(props) \"DefaultPatternProp$C0" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/defaultValueProp.res.txt b/tests/syntax_tests/data/ppx/react/expected/defaultValueProp.res.txt index c8a7759839e..5406c7724d4 100644 --- a/tests/syntax_tests/data/ppx/react/expected/defaultValueProp.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/defaultValueProp.res.txt @@ -17,10 +17,10 @@ module C0 = { let b = __b_value (React.int(a + b): React.element) } - let make = { + let make = React.component({ let \"DefaultValueProp$C0" = (props: props<_>) => make(props) \"DefaultValueProp$C0" - } + }) } module C1 = { @@ -38,11 +38,11 @@ module C1 = { let a = __a_value (React.int(a + b): React.element) } - let make = { + let make = React.component({ let \"DefaultValueProp$C1" = (props: props<_>) => make(props) \"DefaultValueProp$C1" - } + }) } module C2 = { @@ -60,11 +60,11 @@ module C2 = { let a = __a_value (React.string(a): React.element) } - let make = { + let make = React.component({ let \"DefaultValueProp$C2" = (props: props<_>) => make(props) \"DefaultValueProp$C2" - } + }) } module C3 = { @@ -85,9 +85,9 @@ module C3 = { }: React.element ) } - let make = { + let make = React.component({ let \"DefaultValueProp$C3" = (props: props<_>) => make(props) \"DefaultValueProp$C3" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/fileLevelConfig.res.txt b/tests/syntax_tests/data/ppx/react/expected/fileLevelConfig.res.txt index 77cde0caea1..c5566f8e46e 100644 --- a/tests/syntax_tests/data/ppx/react/expected/fileLevelConfig.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/fileLevelConfig.res.txt @@ -9,9 +9,9 @@ module V4A = { let make = ({msg, _}: props<_>): React.element => { ReactDOM.jsx("div", {children: ?ReactDOM.someElement({msg->React.string})}) } - let make = { + let make = React.component({ let \"FileLevelConfig$V4A" = (props: props<_>) => make(props) \"FileLevelConfig$V4A" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt b/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt index 8cd3bcff8ce..4d48136cc0f 100644 --- a/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/forwardRef.res.txt @@ -51,11 +51,11 @@ module V4A = { ): React.element ) } - let make = { + let make = React.component({ let \"ForwardRef$V4A" = props => make(props) \"ForwardRef$V4A" - } + }) } module V4AUncurried = { @@ -109,9 +109,9 @@ module V4AUncurried = { ): React.element ) } - let make = { + let make = React.component({ let \"ForwardRef$V4AUncurried" = props => make(props) \"ForwardRef$V4AUncurried" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/interface.res.txt b/tests/syntax_tests/data/ppx/react/expected/interface.res.txt index df0f7137fb5..77b6e77aec2 100644 --- a/tests/syntax_tests/data/ppx/react/expected/interface.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/interface.res.txt @@ -4,10 +4,10 @@ module A = { x: 'x, } let make = ({x, _}: props<_>): React.element => React.string(x) - let make = { + let make = React.component({ let \"Interface$A" = (props: props<_>) => make(props) \"Interface$A" - } + }) } module NoProps = { @@ -15,9 +15,9 @@ module NoProps = { type props = {} let make = (_: props): React.element => ReactDOM.jsx("div", {}) - let make = { + let make = React.component({ let \"Interface$NoProps" = props => make(props) \"Interface$NoProps" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/mangleKeyword.res.txt b/tests/syntax_tests/data/ppx/react/expected/mangleKeyword.res.txt index f1e40360ec5..fd905c590ba 100644 --- a/tests/syntax_tests/data/ppx/react/expected/mangleKeyword.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/mangleKeyword.res.txt @@ -9,11 +9,11 @@ module C4A0 = { let make = ({@as("open") _open, @as("type") _type, _}: props<_, string>): React.element => React.string(_open) - let make = { + let make = React.component({ let \"MangleKeyword$C4A0" = (props: props<_>) => make(props) \"MangleKeyword$C4A0" - } + }) } module C4A1 = { @res.jsxComponentProps @live diff --git a/tests/syntax_tests/data/ppx/react/expected/nested.res.txt b/tests/syntax_tests/data/ppx/react/expected/nested.res.txt index 70f8efde0e3..4931436a500 100644 --- a/tests/syntax_tests/data/ppx/react/expected/nested.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/nested.res.txt @@ -7,17 +7,17 @@ module Outer = { type props = {} let make = (_: props): React.element => ReactDOM.jsx("div", {}) - let make = { + let make = React.component({ let \"Nested$Outer" = props => make(props) \"Nested$Outer" - } + }) } React.jsx(Inner.make, {}) } - let make = { + let make = React.component({ let \"Nested$Outer" = props => make(props) \"Nested$Outer" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/newtype.res.txt b/tests/syntax_tests/data/ppx/react/expected/newtype.res.txt index 2fdecb86740..5873c71b31e 100644 --- a/tests/syntax_tests/data/ppx/react/expected/newtype.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/newtype.res.txt @@ -10,11 +10,11 @@ module V4A = { let make = (type a, {a, b, c, _}: props>, 'a>): React.element => ReactDOM.jsx("div", {}) - let make = { + let make = React.component({ let \"Newtype$V4A" = (props: props<_>) => make(props) \"Newtype$V4A" - } + }) } module V4A1 = { @@ -27,11 +27,11 @@ module V4A1 = { let make = (type x y, {a, b, c, _}: props, 'a>): React.element => ReactDOM.jsx("div", {}) - let make = { + let make = React.component({ let \"Newtype$V4A1" = (props: props<_>) => make(props) \"Newtype$V4A1" - } + }) } module type T = { @@ -48,11 +48,11 @@ module V4A2 = { module T = unpack(foo) ReactDOM.jsx("div", {}) } - let make = { + let make = React.component({ let \"Newtype$V4A2" = (props: props<_>) => make(props) \"Newtype$V4A2" - } + }) } module V4A3 = { @@ -65,11 +65,11 @@ module V4A3 = { module T = unpack(foo: T with type t = a) foo } - let make = { + let make = React.component({ let \"Newtype$V4A3" = (props: props<_>) => make(props) \"Newtype$V4A3" - } + }) } @res.jsxComponentProps type props<'x, 'q> = { @@ -78,11 +78,11 @@ type props<'x, 'q> = { } let make = ({x, q, _}: props<('a, 'b), 'a>): React.element => [fst(x), q] -let make = { +let make = React.component({ let \"Newtype" = (props: props<_>) => make(props) \"Newtype" -} +}) @@uncurried @@ -93,9 +93,9 @@ module Uncurried = { } let make = (type a, {?foo, _}: props<_>): React.element => React.null - let make = { + let make = React.component({ let \"Newtype$Uncurried" = (props: props<_>) => make(props) \"Newtype$Uncurried" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/noPropsWithKey.res.txt b/tests/syntax_tests/data/ppx/react/expected/noPropsWithKey.res.txt index aa8dc64f353..443bb57c62e 100644 --- a/tests/syntax_tests/data/ppx/react/expected/noPropsWithKey.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/noPropsWithKey.res.txt @@ -5,11 +5,11 @@ module V4CA = { type props = {} let make = (_: props): React.element => ReactDOM.jsx("div", {}) - let make = { + let make = React.component({ let \"NoPropsWithKey$V4CA" = props => make(props) \"NoPropsWithKey$V4CA" - } + }) } module V4CB = { @@ -34,9 +34,9 @@ module V4C = { ]), }, ) - let make = { + let make = React.component({ let \"NoPropsWithKey$V4C" = props => make(props) \"NoPropsWithKey$V4C" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/optimizeAutomaticMode.res.txt b/tests/syntax_tests/data/ppx/react/expected/optimizeAutomaticMode.res.txt index dfb3506590e..7ccf95dfb35 100644 --- a/tests/syntax_tests/data/ppx/react/expected/optimizeAutomaticMode.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/optimizeAutomaticMode.res.txt @@ -12,9 +12,9 @@ module User = { let make = ({doctor, _}: props<_>): React.element => { ReactDOM.jsx("h1", {id: "h1", children: ?ReactDOM.someElement({React.string(format(doctor))})}) } - let make = { + let make = React.component({ let \"OptimizeAutomaticMode$User" = (props: props<_>) => make(props) \"OptimizeAutomaticMode$User" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/returnConstraint.res.txt b/tests/syntax_tests/data/ppx/react/expected/returnConstraint.res.txt index 7d017b6d9b7..43e3ca14aef 100644 --- a/tests/syntax_tests/data/ppx/react/expected/returnConstraint.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/returnConstraint.res.txt @@ -5,11 +5,11 @@ module Standard = { type props = {} let make = (_: props): React.element => React.string("ok") - let make = { + let make = React.component({ let \"ReturnConstraint$Standard" = props => make(props) \"ReturnConstraint$Standard" - } + }) } module ForwardRef = { @@ -29,10 +29,10 @@ module WithProps = { let make = (props: props): React.element => ReactDOM.jsx("span", {children: ?ReactDOM.someElement({React.int(props.value)})}) - let make = { + let make = React.component({ let \"ReturnConstraint$WithProps" = (props: props): React.element => make(props) \"ReturnConstraint$WithProps" - } + }) } module Async = { @@ -41,9 +41,9 @@ module Async = { let make = async (_: props): React.element => ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.string("async")})}) - let make = { + let make = React.component({ let \"ReturnConstraint$Async" = props => Jsx.promise(make(props)) \"ReturnConstraint$Async" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/sharedProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/sharedProps.res.txt index 83c7a5fb041..34d29a757e4 100644 --- a/tests/syntax_tests/data/ppx/react/expected/sharedProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/sharedProps.res.txt @@ -4,44 +4,44 @@ module V4A1 = { type props = sharedProps let make = ({x, y, _}: props): React.element => React.string(x ++ y) - let make = { + let make = React.component({ let \"SharedProps$V4A1" = props => make(props) \"SharedProps$V4A1" - } + }) } module V4A2 = { type props<'a> = sharedProps<'a> let make = ({x, y, _}: props<_>): React.element => React.string(x ++ y) - let make = { + let make = React.component({ let \"SharedProps$V4A2" = (props: props<_>) => make(props) \"SharedProps$V4A2" - } + }) } module V4A3 = { type props<'a> = sharedProps let make = ({x, y, _}: props<_>): React.element => React.string(x ++ y) - let make = { + let make = React.component({ let \"SharedProps$V4A3" = (props: props<_>) => make(props) \"SharedProps$V4A3" - } + }) } module V4A4 = { type props = sharedProps let make = ({x, y, _}: props): React.element => React.string(x ++ y) - let make = { + let make = React.component({ let \"SharedProps$V4A4" = props => make(props) \"SharedProps$V4A4" - } + }) } module V4A5 = { diff --git a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt index 8abf4452b0f..c1a149f1c6e 100644 --- a/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/sharedPropsWithProps.res.txt @@ -5,37 +5,37 @@ let f = a => Js.Promise.resolve(a + a) module V4A1 = { type props = sharedProps let make = (props): React.element => React.string(props.x ++ props.y) - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A1" = (props): React.element => make(props) \"SharedPropsWithProps$V4A1" - } + }) } module V4A2 = { type props = sharedProps let make = (props: props): React.element => React.string(props.x ++ props.y) - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A2" = (props: props): React.element => make(props) \"SharedPropsWithProps$V4A2" - } + }) } module V4A3 = { type props<'a> = sharedProps<'a> let make = ({x, y}: props<_>): React.element => React.string(x ++ y) - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A3" = (props: props<_>): React.element => make(props) \"SharedPropsWithProps$V4A3" - } + }) } module V4A4 = { type props<'a> = sharedProps let make = ({x, y}: props<_>): React.element => React.string(x ++ y) - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A4" = (props: props<_>): React.element => make(props) \"SharedPropsWithProps$V4A4" - } + }) } module V4A5 = { @@ -44,10 +44,10 @@ module V4A5 = { let a = await f(a) (ReactDOM.jsx("div", {children: ?ReactDOM.someElement({React.int(a)})}): React.element) } - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A5" = (props: props<_>): React.element => Jsx.promise(make(props)) \"SharedPropsWithProps$V4A5" - } + }) } module V4A6 = { @@ -58,10 +58,10 @@ module V4A6 = { | #off => React.string("off") } } - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A6" = (props: props<_>): React.element => Jsx.promise(make(props)) \"SharedPropsWithProps$V4A6" - } + }) } module V4A7 = { @@ -70,9 +70,9 @@ module V4A7 = { let make = (props): React.element => { React.int(props.count) } - let make = { + let make = React.component({ let \"SharedPropsWithProps$V4A7" = @directive("'use memo'") (props): React.element => make(props) \"SharedPropsWithProps$V4A7" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt b/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt index 9330346a961..619d35c0355 100644 --- a/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/topLevel.res.txt @@ -11,9 +11,9 @@ module V4A = { Js.log("This function should be named 'TopLevel.react'") (ReactDOM.jsx("div", {}): React.element) } - let make = { + let make = React.component({ let \"TopLevel$V4A" = (props: props<_>) => make(props) \"TopLevel$V4A" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/typeConstraint.res.txt b/tests/syntax_tests/data/ppx/react/expected/typeConstraint.res.txt index 68a3b0278b3..491f5c7d191 100644 --- a/tests/syntax_tests/data/ppx/react/expected/typeConstraint.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/typeConstraint.res.txt @@ -8,9 +8,9 @@ module V4A = { } let make = (type a, {a, b, _}: props<_, _>): React.element => ReactDOM.jsx("div", {}) - let make = { + let make = React.component({ let \"TypeConstraint$V4A" = (props: props<_>) => make(props) \"TypeConstraint$V4A" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/uncurriedProps.res.txt b/tests/syntax_tests/data/ppx/react/expected/uncurriedProps.res.txt index 9d81169bd1e..9e635bafdd6 100644 --- a/tests/syntax_tests/data/ppx/react/expected/uncurriedProps.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/uncurriedProps.res.txt @@ -12,11 +12,11 @@ let make = ({a: ?__a, _}: props unit>) => { let a: unit => unit = __a_value (React.null: React.element) } -let make = { +let make = React.component({ let \"UncurriedProps" = (props: props<_>) => make(props) \"UncurriedProps" -} +}) let func = (~callback: (string, bool, bool) => unit=(_, _, _) => (), ()) => { let _ = callback @@ -46,11 +46,11 @@ module Foo = { }: React.element ) } - let make = { + let make = React.component({ let \"UncurriedProps$Foo" = (props: props<_>) => make(props) \"UncurriedProps$Foo" - } + }) } module Bar = { @@ -58,9 +58,9 @@ module Bar = { type props = {} let make = (_: props): React.element => React.jsx(Foo.make, {callback: {(_, _, _) => ()}}) - let make = { + let make = React.component({ let \"UncurriedProps$Bar" = props => make(props) \"UncurriedProps$Bar" - } + }) } diff --git a/tests/syntax_tests/data/ppx/react/expected/v4.res.txt b/tests/syntax_tests/data/ppx/react/expected/v4.res.txt index a6f05b0e17f..a9c093b3475 100644 --- a/tests/syntax_tests/data/ppx/react/expected/v4.res.txt +++ b/tests/syntax_tests/data/ppx/react/expected/v4.res.txt @@ -4,10 +4,10 @@ type props<'x, 'y> = { y: 'y, } let make = ({x, y, _}: props): React.element => React.string(x ++ y) -let make = { +let make = React.component({ let \"V4" = (props: props<_>) => make(props) \"V4" -} +}) module AnotherName = { @res.jsxComponentProps @@ -17,11 +17,11 @@ module AnotherName = { } let anotherName = ({x, _}: props<_>): React.element => React.string(x) - let anotherName = { + let anotherName = React.component({ let \"V4$AnotherName$anotherName" = (props: props<_>) => anotherName(props) \"V4$AnotherName$anotherName" - } + }) } module Uncurried = { @@ -31,11 +31,11 @@ module Uncurried = { } let make = ({x, _}: props<_>): React.element => React.string(x) - let make = { + let make = React.component({ let \"V4$Uncurried" = (props: props<_>) => make(props) \"V4$Uncurried" - } + }) } module type TUncurried = { @@ -69,50 +69,42 @@ module Rec = { @res.jsxComponentProps type props = {} - let rec make = { - let \"make$Internal" = (_: props): React.element => { - make(({}: props)) - } - let make = { - let \"V4$Rec" = props => \"make$Internal"(props) - - \"V4$Rec" - } - make + let rec make = (_: props): React.element => { + make(({}: props)) } + let make = React.component({ + let \"V4$Rec" = props => make(props) + + \"V4$Rec" + }) } module Rec1 = { @res.jsxComponentProps type props = {} - let rec make = { - let \"make$Internal" = (_: props): React.element => { - React.null - } - let make = { - let \"V4$Rec1" = props => \"make$Internal"(props) - - \"V4$Rec1" - } - make + let rec make = (_: props): React.element => { + React.null } + let make = React.component({ + let \"V4$Rec1" = props => make(props) + + \"V4$Rec1" + }) } module Rec2 = { @res.jsxComponentProps type props = {} - let rec make = { - let \"make$Internal" = (_: props): React.element => { - mm(({}: props)) - } - let make = { - let \"V4$Rec2" = props => \"make$Internal"(props) - - \"V4$Rec2" - } - make + let rec make = (_: props): React.element => { + mm(({}: props)) } + and mm = x => make(x) + let make = React.component({ + let \"V4$Rec2" = props => make(props) + + \"V4$Rec2" + }) } diff --git a/tests/tests/src/UncurriedAlways.res b/tests/tests/src/UncurriedAlways.res index 8bb4a44cf15..68be69c634c 100644 --- a/tests/tests/src/UncurriedAlways.res +++ b/tests/tests/src/UncurriedAlways.res @@ -25,7 +25,7 @@ let foo3 = (x, y, z) => x + y + z let bar3: _ => _ = foo3(_, 3, 4) type cmp = Jsx.component -let q: cmp = _ => Jsx.null // Check that subtyping works past type definitions +let q: cmp = Jsx.component(_ => Jsx.null) // Check that subtyping works past type definitions @inline let inl = () => () diff --git a/tests/tests/src/jsx_preserve_test.mjs b/tests/tests/src/jsx_preserve_test.mjs index 570ca4607cf..bcc16a2d3ce 100644 --- a/tests/tests/src/jsx_preserve_test.mjs +++ b/tests/tests/src/jsx_preserve_test.mjs @@ -208,12 +208,12 @@ let _youtube_iframe =