diff --git a/.yarn/patches/@rescript-react-npm-0.14.0-e462ba0c5d.patch b/.yarn/patches/@rescript-react-npm-0.14.0-e462ba0c5d.patch new file mode 100644 index 00000000000..4a313a4570b --- /dev/null +++ b/.yarn/patches/@rescript-react-npm-0.14.0-e462ba0c5d.patch @@ -0,0 +1,13 @@ +diff --git a/src/React.res b/src/React.res +index 0676012234c8a9cdd93030d732274b5ed3914b11..1ff7a036a06a69e76a680b0396a3f8a67a59c328 100644 +--- a/src/React.res ++++ b/src/React.res +@@ -13,7 +13,7 @@ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> + + type component<'props> = Jsx.component<'props> + +-external component: componentLike<'props, element> => component<'props> = "%identity" ++external component: componentLike<'props, element> => component<'props> = "%component_identity" + + @module("react") + external createElement: (component<'props>, 'props) => element = "createElement" diff --git a/CHANGELOG.md b/CHANGELOG.md index 579b2508fda..016da97df83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ #### :boom: Breaking Change +- Make Jsx.component abstract. https://github.com/rescript-lang/rescript/pull/8390 + #### :eyeglasses: Spec Compliance #### :rocket: New Feature diff --git a/compiler/frontend/bs_ast_invariant.ml b/compiler/frontend/bs_ast_invariant.ml index cbe5a4432ee..60e1ad285d0 100644 --- a/compiler/frontend/bs_ast_invariant.ml +++ b/compiler/frontend/bs_ast_invariant.ml @@ -124,10 +124,11 @@ let emit_external_warnings : iterator = | ({pval_loc; pval_prim = [byte_name]; pval_type} : Parsetree.value_description) -> ( match byte_name with - | "%identity" when not (Ast_core_type.is_arity_one pval_type) -> + | ("%identity" | "%component_identity") + when not (Ast_core_type.is_arity_one pval_type) -> Location.raise_errorf ~loc:pval_loc - "%%identity expects a function type of the form 'a => 'b (arity \ - 1)" + "%s expects a function type of the form 'a => 'b (arity 1)" + byte_name | _ -> if byte_name <> "" then let c = String.unsafe_get byte_name 0 in diff --git a/compiler/ml/error_message_utils.ml b/compiler/ml/error_message_utils.ml index 1805844fd96..19995a6145c 100644 --- a/compiler/ml/error_message_utils.ml +++ b/compiler/ml/error_message_utils.ml @@ -107,6 +107,7 @@ type type_clash_context = is_constant: string option; } | FunctionArgument of {optional: bool; name: string option} + | JsxComponent | BracedIdent | Statement of type_clash_statement | ForLoopCondition @@ -127,6 +128,7 @@ let context_to_string = function | Some TryReturn -> "TryReturn" | Some StringConcat -> "StringConcat" | Some (FunctionArgument _) -> "FunctionArgument" + | Some JsxComponent -> "JsxComponent" | Some ComparisonOperator -> "ComparisonOperator" | Some IfReturn -> "IfReturn" | Some TernaryReturn -> "TernaryReturn" @@ -145,6 +147,7 @@ let error_type_text ppf type_clash_context = | Some ArrayValue -> "This array item has type:" | Some (SetRecordField _) -> "You're assigning something to this field that has type:" + | Some JsxComponent -> "This JSX tag has type:" | _ -> "This has type:" in fprintf ppf "%s" text @@ -162,6 +165,7 @@ let error_expected_type_text ppf type_clash_context = | None -> ()); fprintf ppf " is expecting:" + | Some JsxComponent -> fprintf ppf "But JSX component positions require:" | Some ComparisonOperator -> fprintf ppf "But it's being compared to something of type:" | Some SwitchReturn -> fprintf ppf "But this switch is expected to return:" @@ -224,6 +228,12 @@ let is_variant_type ~(extract_concrete_typedecl : extract_concrete_typedecl) | _ -> false with _ -> false +let is_jsx_component_type ~env ty = + match Ctype.expand_head env ty with + | {desc = Tconstr (Pdot (Pident {name = "Jsx"}, "component", _), _, _)} -> + true + | _ -> false + let get_variant_constructors ~(extract_concrete_typedecl : extract_concrete_typedecl) ~env ty = match extract_concrete_typedecl env ty with @@ -396,6 +406,17 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf \ - Remove the @{await@} if this is not expected to be a promise\n\ \ - Wrap the expression in @{Promise.resolve@} to convert the \ expression to a promise" + | Some JsxComponent, _ -> + fprintf ppf + "\n\n\ + \ JSX tags must be React components, not plain functions.\n\n\ + \ Possible solutions:\n\ + \ - If this function takes labeled props, annotate it with \ + @{@react.component@}\n\ + \ - If this function takes a single props record, annotate it with \ + @{@react.componentWithProps@}\n\ + \ - If this is already a valid component-like value, wrap it with \ + @{React.component(...)@}" | Some IfReturn, _ -> fprintf ppf "\n\n\ @@ -423,6 +444,17 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf \ - Use a tuple, if your array is of fixed length. Tuples can mix types \ freely, and compiles to a JavaScript array. Example of a tuple: `let \ myTuple = (10, \"hello\", 15.5, true)" + | _, Some ({desc = Tarrow _}, expected) + when is_jsx_component_type ~env expected -> + fprintf ppf + "\n\n\ + \ A React component is expected here, but this expression is a plain \ + function.\n\n\ + \ Possible solutions:\n\ + \ - Extract it to a component annotated with @{@react.component@} \ + or @{@react.componentWithProps@}\n\ + \ - If this is already a valid component-like value, wrap it with \ + @{React.component(...)@}" | _, Some (_, {desc = Tconstr (p2, _, _)}) when Path.same Predef.path_dict p2 -> fprintf ppf diff --git a/compiler/ml/translcore.ml b/compiler/ml/translcore.ml index 44006f4dad3..ecf3a73528d 100644 --- a/compiler/ml/translcore.ml +++ b/compiler/ml/translcore.ml @@ -236,6 +236,7 @@ let primitives_table = create_hashtable [| ("%identity", Pidentity); + ("%component_identity", Pidentity); ("%ignore", Pignore); ("%revapply", Prevapply); ("%apply", Pdirapply); diff --git a/compiler/ml/typecore.ml b/compiler/ml/typecore.ml index dc5c0d1d513..4f0b3be38e4 100644 --- a/compiler/ml/typecore.ml +++ b/compiler/ml/typecore.ml @@ -1808,6 +1808,24 @@ let rec final_subexpression sexp = (* Generalization criterion for expressions *) +let is_component_identity = function + | Texp_ident + (_, _, {val_kind = Val_prim {Primitive.prim_name = "%component_identity"}}) + -> + true + | _ -> false + +let is_identity_coercion = function + | Texp_ident + ( _, + _, + { + val_kind = + Val_prim {Primitive.prim_name = "%identity" | "%component_identity"}; + } ) -> + true + | _ -> false + let rec is_nonexpansive exp = List.exists (function @@ -1822,6 +1840,26 @@ let rec is_nonexpansive exp = List.for_all (fun vb -> is_nonexpansive vb.vb_expr) pat_exp_list && is_nonexpansive body | Texp_function _ -> true + (* `%component_identity` is a typed no-op coercion that lets generated + component wrappers keep the same generalization behavior as their + underlying function values. This preserves polymorphic props for + components such as: + + @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 `%component_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}; args; _} when is_component_identity exp_desc + -> + 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 @@ -2238,12 +2276,6 @@ let is_ignore ~env ~arity funct = with Unify _ -> false) | _ -> false -let not_identity = function - | Texp_ident (_, _, {val_kind = Val_prim {Primitive.prim_name = "%identity"}}) - -> - false - | _ -> true - let rec lower_args env seen ty_fun = let ty = expand_head env ty_fun in if List.memq ty seen then () @@ -2494,7 +2526,10 @@ and type_expect_ ?deprecated_context ~context ?in_function ?(recarg = Rejected) wrap_trace_gadt_instances env (lower_args env []) ty; begin_def (); let total_app = not partial in - let context = type_clash_context_from_function sexp sfunct in + let context = + if transformed_jsx then Some JsxComponent + else type_clash_context_from_function sexp sfunct + in let args, ty_res, fully_applied = match translate_unified_ops env funct sargs with | Some (targs, result_type) -> (targs, result_type, true) @@ -3828,8 +3863,10 @@ and type_application ~context total_app env funct (sargs : sargs) : (* This is a total application when the toplevel type is a polymorphic variable, so the function type including arity can be inferred. *) let t1 = newvar () and t2 = newvar () in - if ty_fun.level >= t1.level && not_identity funct.exp_desc then - Location.prerr_warning sarg1.pexp_loc Warnings.Unused_argument; + if + ty_fun.level >= t1.level + && not (is_identity_coercion funct.exp_desc) + then Location.prerr_warning sarg1.pexp_loc Warnings.Unused_argument; unify env ty_fun (newty (Tarrow @@ -3892,15 +3929,19 @@ and type_application ~context total_app env funct (sargs : sargs) : if (not optional) && is_optional l' then Location.prerr_warning sarg0.pexp_loc (Warnings.Nonoptional_label (Printtyp.string_of_label l)); + let argument_context = + match (context, args) with + | Some JsxComponent, [] -> Some JsxComponent + | Some JsxComponent, _ -> + type_clash_context_for_function_argument ~label:l' None sarg0 + | _ -> + type_clash_context_for_function_argument ~label:l' context sarg0 + in ( sargs, omitted, Some (if (not optional) || is_optional l' then fun () -> - type_argument - ~context: - (type_clash_context_for_function_argument ~label:l' context - sarg0) - env sarg0 ty ty0 + type_argument ~context:argument_context env sarg0 ty ty0 else fun () -> option_some (type_argument diff --git a/compiler/syntax/src/jsx_v4.ml b/compiler/syntax/src/jsx_v4.ml index 83bbb0dcdc4..5b8e83bedb8 100644 --- a/compiler/syntax/src/jsx_v4.ml +++ b/compiler/syntax/src/jsx_v4.ml @@ -49,6 +49,11 @@ 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)] + (* Helper method to filter out any attribute that isn't [@react.component] *) let other_attrs_pure (loc, _) = match loc.txt with @@ -66,20 +71,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 = @@ -540,7 +531,7 @@ let vb_match_expr named_arg_list expr = in aux (List.rev named_arg_list) -let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = +let map_binding ~config ~empty_loc ~pstr_loc ~file_name binding = (* Traverse the component body and force every reachable return expression to be annotated as `Jsx.element`. This walks through the wrapper constructs the PPX introduces (fun/newtype/let/sequence) so that the constraint ends up on @@ -590,7 +581,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 +601,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 +648,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 `%component_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:empty_loc full_expression + in let rec returned_expression patterns_with_label patterns_with_nolabel ({pexp_desc} as expr) = match pexp_desc with @@ -781,28 +783,14 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = |> List.fold_left (fun e newtype -> Exp.newtype newtype e) expression in (* let make = ({id, name, ...}: props<'id, 'name, ...>) => { ... } *) - let binding, new_binding = - match rec_flag with - | 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) ) + 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 @@ -813,7 +801,6 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = } in let fn_name = get_fn_name modified_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 @@ -860,18 +847,9 @@ 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 - { - txt = - Lident - (match rec_flag with - | Recursive -> internal_fn_name - | Nonrecursive -> fn_name); - loc; - }) + (Exp.ident {txt = Lident fn_name; loc}) [(Nolabel, Exp.ident {txt = Lident "props"; loc})] in let applied_expression = @@ -887,6 +865,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}) @@ -894,11 +875,7 @@ let map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding = in let new_binding = - match rec_flag with - | Recursive -> None - | Nonrecursive -> - Some - (make_new_binding ~loc:empty_loc ~full_module_name modified_binding) + Some (make_new_binding ~loc:empty_loc ~full_module_name modified_binding) in let binding_expr = { @@ -1004,7 +981,7 @@ let transform_structure_item ~config item = let empty_loc = Location.in_file file_name in let process_binding binding (new_items, bindings, new_bindings) = let new_item, binding, new_binding = - map_binding ~config ~empty_loc ~pstr_loc ~file_name ~rec_flag binding + map_binding ~config ~empty_loc ~pstr_loc ~file_name binding in let new_items = match new_item with @@ -1027,7 +1004,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..90b4a9fa158 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" +external component: componentLike<'props, element> => component<'props> = "%component_identity" diff --git a/packages/playground/package.json b/packages/playground/package.json index f5946f261b5..75e45a7627b 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -10,7 +10,7 @@ "serve-bundle": "node serve-bundle.mjs" }, "dependencies": { - "@rescript/react": "^0.14.0", + "@rescript/react": "patch:@rescript/react@npm%3A0.14.0#~/.yarn/patches/@rescript-react-npm-0.14.0-e462ba0c5d.patch", "rescript": "workspace:^" }, "devDependencies": { diff --git a/packages/playground/playground_test.cjs b/packages/playground/playground_test.cjs index b38acc7f112..051cb9efd6f 100644 --- a/packages/playground/playground_test.cjs +++ b/packages/playground/playground_test.cjs @@ -29,6 +29,7 @@ const result = compiler.rescript.compile(` module B = { type props = { a: string } + @react.componentWithProps let make = ({a}) => { } 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..e984928dea1 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> = "%component_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/build_tests/jsx_settings_inheritance/node_modules/@rescript/react/src/React.res b/tests/build_tests/jsx_settings_inheritance/node_modules/@rescript/react/src/React.res index 01f4ebc91ab..33d33e2f313 100644 --- a/tests/build_tests/jsx_settings_inheritance/node_modules/@rescript/react/src/React.res +++ b/tests/build_tests/jsx_settings_inheritance/node_modules/@rescript/react/src/React.res @@ -12,7 +12,7 @@ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> type component<'props> = Jsx.component<'props> -external component: componentLike<'props, element> => component<'props> = "%identity" +external component: componentLike<'props, element> => component<'props> = "%component_identity" @module("react") external createElement: (component<'props>, 'props) => element = "createElement" diff --git a/tests/build_tests/react_ppx/src/React.res b/tests/build_tests/react_ppx/src/React.res index 4350f6b2e54..38a5649b159 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> = "%component_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/identity_generalization_test.res b/tests/build_tests/react_ppx/src/identity_generalization_test.res new file mode 100644 index 00000000000..edf5dce2b7d --- /dev/null +++ b/tests/build_tests/react_ppx/src/identity_generalization_test.res @@ -0,0 +1,8 @@ +external componentId: 'a => 'a = "%component_identity" + +// `%component_identity` applications should generalize like the wrapped expression. +// Otherwise, `f` becomes monomorphic and one of these calls fails to typecheck. +let f = componentId(x => x) + +let intValue = f(1) +let stringValue = f("one") diff --git a/tests/build_tests/react_ppx/src/identity_generalization_test.res.js b/tests/build_tests/react_ppx/src/identity_generalization_test.res.js new file mode 100644 index 00000000000..68936de442b --- /dev/null +++ b/tests/build_tests/react_ppx/src/identity_generalization_test.res.js @@ -0,0 +1,17 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + + +function f(x) { + return x; +} + +let intValue = 1; + +let stringValue = "one"; + +export { + f, + intValue, + stringValue, +} +/* No side effect */ 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/react_ppx/src/recursive_explicit_component_test.res b/tests/build_tests/react_ppx/src/recursive_explicit_component_test.res new file mode 100644 index 00000000000..a0d4baf6b54 --- /dev/null +++ b/tests/build_tests/react_ppx/src/recursive_explicit_component_test.res @@ -0,0 +1,24 @@ +module SelfCreateElement = { + @react.component + let rec make = (~foo) => React.createElement(React.component(make), {foo: foo - 1}) +} + +module RawSiblingCreateElement = { + @react.component + let rec make = (~foo) => React.createElement(React.component(other), {foo: foo}) + and other = ({foo}) => React.string(foo->Int.toString) +} + +module ComponentWithProps = { + type props = {foo: int} + + @react.componentWithProps + let rec make = (props: props) => + if props.foo <= 0 { + React.null + } else { + React.createElement(React.component(make), {foo: props.foo - 1}) + } +} + +let componentWithPropsElement = React.createElement(ComponentWithProps.make, {foo: 1}) diff --git a/tests/build_tests/react_ppx/src/recursive_explicit_component_test.res.js b/tests/build_tests/react_ppx/src/recursive_explicit_component_test.res.js new file mode 100644 index 00000000000..9577ffd28ab --- /dev/null +++ b/tests/build_tests/react_ppx/src/recursive_explicit_component_test.res.js @@ -0,0 +1,58 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE + +import * as React from "react"; + +function make(param) { + return React.createElement(make, { + foo: param.foo - 1 | 0 + }); +} + +let Recursive_explicit_component_test$SelfCreateElement = make; + +let SelfCreateElement = { + make: Recursive_explicit_component_test$SelfCreateElement +}; + +function other(param) { + return param.foo.toString(); +} + +function Recursive_explicit_component_test$RawSiblingCreateElement(props) { + return React.createElement(other, { + foo: props.foo + }); +} + +let RawSiblingCreateElement = { + other: other, + make: Recursive_explicit_component_test$RawSiblingCreateElement +}; + +function make$1(props) { + if (props.foo <= 0) { + return null; + } else { + return React.createElement(make$1, { + foo: props.foo - 1 | 0 + }); + } +} + +let Recursive_explicit_component_test$ComponentWithProps = make$1; + +let ComponentWithProps = { + make: Recursive_explicit_component_test$ComponentWithProps +}; + +let componentWithPropsElement = React.createElement(Recursive_explicit_component_test$ComponentWithProps, { + foo: 1 +}); + +export { + SelfCreateElement, + RawSiblingCreateElement, + ComponentWithProps, + componentWithPropsElement, +} +/* componentWithPropsElement Not a pure module */ diff --git a/tests/build_tests/super_errors/expected/identity_does_not_generalize.res.expected b/tests/build_tests/super_errors/expected/identity_does_not_generalize.res.expected new file mode 100644 index 00000000000..eccd98daabd --- /dev/null +++ b/tests/build_tests/super_errors/expected/identity_does_not_generalize.res.expected @@ -0,0 +1,13 @@ + + We've found a bug for you! + /.../fixtures/identity_does_not_generalize.res:6:21-25 + + 4 │ + 5 │ let intValue = f(1) + 6 │ let stringValue = f("one") + 7 │ + + This has type: string + But this function argument is expecting: int + + You can convert string to int with Int.fromString. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/jsx_component_prop_plain_function.res.expected b/tests/build_tests/super_errors/expected/jsx_component_prop_plain_function.res.expected new file mode 100644 index 00000000000..9258619424b --- /dev/null +++ b/tests/build_tests/super_errors/expected/jsx_component_prop_plain_function.res.expected @@ -0,0 +1,19 @@ + + We've found a bug for you! + /.../fixtures/jsx_component_prop_plain_function.res:19:46-58 + + 17 │ } + 18 │ + 19 │ let _ = React.null} /> + 20 │ + + This has type: 'a => 'b + But it's expected to have type: + React.component (defined as + Jsx.component) + + A React component is expected here, but this expression is a plain function. + + Possible solutions: + - Extract it to a component annotated with @react.component or @react.componentWithProps + - If this is already a valid component-like value, wrap it with React.component(...) \ No newline at end of file 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_plain_function_component.res.expected b/tests/build_tests/super_errors/expected/jsx_plain_function_component.res.expected new file mode 100644 index 00000000000..c4c1c500e29 --- /dev/null +++ b/tests/build_tests/super_errors/expected/jsx_plain_function_component.res.expected @@ -0,0 +1,19 @@ + + We've found a bug for you! + /.../fixtures/jsx_plain_function_component.res:26:10-16 + + 24 │ } + 25 │ + 26 │ let _ = <Wrapper value="hello" /> + 27 │ + + This JSX tag has type: Wrapper.props => Jsx.element + But JSX component positions require: + React.component<'a> (defined as Jsx.component<'a>) + + JSX tags must be React components, not plain functions. + + Possible solutions: + - If this function takes labeled props, annotate it with @react.component + - If this function takes a single props record, annotate it with @react.componentWithProps + - If this is already a valid component-like value, wrap it with React.component(...) \ No newline at end of file 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/expected/recursive_component_create_element_requires_component.res.expected b/tests/build_tests/super_errors/expected/recursive_component_create_element_requires_component.res.expected new file mode 100644 index 00000000000..f531ae3fbb0 --- /dev/null +++ b/tests/build_tests/super_errors/expected/recursive_component_create_element_requires_component.res.expected @@ -0,0 +1,18 @@ + + We've found a bug for you! + /.../fixtures/recursive_component_create_element_requires_component.res:13:46-49 + + 11 │ + 12 │ @react.component + 13 │ let rec make = (~foo) => React.createElement(make, {foo: foo}) + 14 │ + + This has type: props<'a> => React.element + But this function argument is expecting: + React.component<'b> (defined as Jsx.component<'b>) + + A React component is expected here, but this expression is a plain function. + + Possible solutions: + - Extract it to a component annotated with @react.component or @react.componentWithProps + - If this is already a valid component-like value, wrap it with React.component(...) \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/identity_does_not_generalize.res b/tests/build_tests/super_errors/fixtures/identity_does_not_generalize.res new file mode 100644 index 00000000000..b84499be542 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/identity_does_not_generalize.res @@ -0,0 +1,6 @@ +external id: 'a => 'a = "%identity" + +let f = id(x => x) + +let intValue = f(1) +let stringValue = f("one") diff --git a/tests/build_tests/super_errors/fixtures/jsx_component_prop_plain_function.res b/tests/build_tests/super_errors/fixtures/jsx_component_prop_plain_function.res new file mode 100644 index 00000000000..39b4e47d5fb --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/jsx_component_prop_plain_function.res @@ -0,0 +1,19 @@ +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> = "%component_identity" + @module("react/jsx-runtime") + external jsx: (component<'props>, 'props) => element = "jsx" +} + +module List = { + type separatorProps = {index: int} + + @react.component + let make = (~itemSeparatorComponent: React.component) => React.null +} + +let _ = React.null} /> 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..77f1a0c8df3 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> = "%component_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..0a2113562cb 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> = "%component_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..975318989c2 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> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" diff --git a/tests/build_tests/super_errors/fixtures/jsx_invalid_prop_ast0_conversion.res b/tests/build_tests/super_errors/fixtures/jsx_invalid_prop_ast0_conversion.res index 8ded1f8566e..3876d99228d 100644 --- a/tests/build_tests/super_errors/fixtures/jsx_invalid_prop_ast0_conversion.res +++ b/tests/build_tests/super_errors/fixtures/jsx_invalid_prop_ast0_conversion.res @@ -5,7 +5,7 @@ module React = { @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" + external component: componentLike<'props, element> => component<'props> = "%component_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..c9efce42f1c 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> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" diff --git a/tests/build_tests/super_errors/fixtures/jsx_plain_function_component.res b/tests/build_tests/super_errors/fixtures/jsx_plain_function_component.res new file mode 100644 index 00000000000..b7cfc238fb6 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/jsx_plain_function_component.res @@ -0,0 +1,26 @@ +module React = { + type element = Jsx.element + type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> + type component<'props> = Jsx.component<'props> + + external component: componentLike<'props, element> => component<'props> = "%component_identity" + external string: string => element = "%identity" + @module("react/jsx-runtime") + external jsx: (component<'props>, 'props) => element = "jsx" + + @module("react/jsx-runtime") + external jsxs: (component<'props>, 'props) => element = "jsxs" +} + +module ReactDOM = { + external someElement: React.element => option = "%identity" + @module("react/jsx-runtime") + external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx" +} + +module Wrapper = { + type props = {value: string} + let make = (props: props) =>
{React.string(props.value)}
+} + +let _ = 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..d3b70571e32 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> = "%component_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..7bbd491da15 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> = "%component_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..cc76e13f962 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> = "%component_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..7ef46cf1ca4 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> = "%component_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..9d73eb095a8 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> = "%component_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..8fc85fb38f7 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> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" diff --git a/tests/build_tests/super_errors/fixtures/missing_required_prop.res b/tests/build_tests/super_errors/fixtures/missing_required_prop.res index 3faa09aaa61..78f2489adc5 100644 --- a/tests/build_tests/super_errors/fixtures/missing_required_prop.res +++ b/tests/build_tests/super_errors/fixtures/missing_required_prop.res @@ -3,7 +3,7 @@ module React = { @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" + external component: componentLike<'props, element> => component<'props> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" external string: string => element = "%identity" 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 index ab468eda49a..41e61b50db3 100644 --- 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 @@ -3,7 +3,7 @@ module React = { @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" + external component: componentLike<'props, element> => component<'props> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" @module("react/jsx-runtime") 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 index 97b0438f3f4..eaa02221a48 100644 --- 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 @@ -3,7 +3,7 @@ module React = { @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" + external component: componentLike<'props, element> => component<'props> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" external string: string => element = "%identity" diff --git a/tests/build_tests/super_errors/fixtures/recursive_component_create_element_requires_component.res b/tests/build_tests/super_errors/fixtures/recursive_component_create_element_requires_component.res new file mode 100644 index 00000000000..542db6fdb99 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/recursive_component_create_element_requires_component.res @@ -0,0 +1,13 @@ +module React = { + type element = Jsx.element + @val external null: element = "null" + external string: string => element = "%identity" + type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> + type component<'props> = Jsx.component<'props> + external component: componentLike<'props, element> => component<'props> = "%component_identity" + @module("react") + external createElement: (component<'props>, 'props) => element = "createElement" +} + +@react.component +let rec make = (~foo) => React.createElement(make, {foo: foo}) diff --git a/tests/build_tests/super_errors/fixtures/wrong_type_prop_punning.res b/tests/build_tests/super_errors/fixtures/wrong_type_prop_punning.res index 797539e1720..d2f81ba7a34 100644 --- a/tests/build_tests/super_errors/fixtures/wrong_type_prop_punning.res +++ b/tests/build_tests/super_errors/fixtures/wrong_type_prop_punning.res @@ -3,7 +3,7 @@ module React = { @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" + external component: componentLike<'props, element> => component<'props> = "%component_identity" @module("react/jsx-runtime") external jsx: (component<'props>, 'props) => element = "jsx" } diff --git a/tests/dependencies/rescript-react/src/React.res b/tests/dependencies/rescript-react/src/React.res index 0beaa590807..a62d8501460 100644 --- a/tests/dependencies/rescript-react/src/React.res +++ b/tests/dependencies/rescript-react/src/React.res @@ -12,7 +12,7 @@ type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> type component<'props> = Jsx.component<'props> -external component: componentLike<'props, element> => component<'props> = "%identity" +external component: componentLike<'props, element> => component<'props> = "%component_identity" @module("react") external createElement: (component<'props>, 'props) => element = "createElement" diff --git a/tests/gentype_tests/typescript-react-example/package.json b/tests/gentype_tests/typescript-react-example/package.json index 72a93675b29..00e5b605552 100644 --- a/tests/gentype_tests/typescript-react-example/package.json +++ b/tests/gentype_tests/typescript-react-example/package.json @@ -8,7 +8,7 @@ "typecheck": "tsc" }, "dependencies": { - "@rescript/react": "^0.14.0", + "@rescript/react": "patch:@rescript/react@npm%3A0.14.0#~/.yarn/patches/@rescript-react-npm-0.14.0-e462ba0c5d.patch", "react": "^18.3.1", "react-dom": "^18.3.1", "rescript": "workspace:^" 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 =