Skip to content

Commit 8d3c011

Browse files
committed
add dedicated JSX element type mismatch errors
1 parent ee6ae24 commit 8d3c011

12 files changed

+226
-17
lines changed

compiler/bsc/rescript_compiler_main.ml

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,33 @@
1212

1313
let absname = ref false
1414

15-
(* TODO: Maybe there's a better place to do this init. *)
1615
module Error_message_utils_support = struct
1716
external to_comment : Res_comment.t -> Error_message_utils.Parser.comment
1817
= "%identity"
1918
external from_comment : Error_message_utils.Parser.comment -> Res_comment.t
2019
= "%identity"
21-
end
2220

23-
let () =
24-
Error_message_utils.Parser.parse_source :=
25-
fun source ->
26-
let res =
27-
Res_driver.parse_implementation_from_source ~for_printer:false
28-
~display_filename:"<none>" ~source
29-
in
30-
( res.parsetree,
31-
res.comments |> List.map Error_message_utils_support.to_comment )
21+
let setup () =
22+
(Error_message_utils.Parser.parse_source :=
23+
fun source ->
24+
let res =
25+
Res_driver.parse_implementation_from_source ~for_printer:false
26+
~display_filename:"<none>" ~source
27+
in
28+
(res.parsetree, res.comments |> List.map to_comment));
3229

33-
let () =
34-
Error_message_utils.Parser.reprint_source :=
35-
fun parsetree comments ->
36-
Res_printer.print_implementation parsetree
37-
~comments:(comments |> List.map Error_message_utils_support.from_comment)
38-
~width:80
30+
(Error_message_utils.Parser.reprint_source :=
31+
fun parsetree comments ->
32+
Res_printer.print_implementation parsetree
33+
~comments:(comments |> List.map from_comment)
34+
~width:80);
35+
36+
Error_message_utils.configured_jsx_module :=
37+
Some
38+
(match !Js_config.jsx_module with
39+
| React -> "React"
40+
| Generic {module_name} -> module_name)
41+
end
3942

4043
let set_abs_input_name sourcefile =
4144
let sourcefile =
@@ -66,6 +69,7 @@ let process_file sourcefile ?kind ppf =
6669
properly
6770
*)
6871
setup_outcome_printer ();
72+
Error_message_utils_support.setup ();
6973
let kind =
7074
match kind with
7175
| None ->

compiler/ml/error_message_utils.ml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
type extract_concrete_typedecl =
22
Env.t -> Types.type_expr -> Path.t * Path.t * Types.type_declaration
33

4+
let configured_jsx_module : string option ref = ref None
5+
6+
let with_configured_jsx_module s =
7+
match !configured_jsx_module with
8+
| None -> s
9+
| Some module_name -> module_name ^ "." ^ s
10+
411
module Parser : sig
512
type comment
613

@@ -276,6 +283,52 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf
276283
(match suggested_rewrite with
277284
| Some rewrite -> rewrite
278285
| None -> "")
286+
| ( _,
287+
Some
288+
( {desc = Tconstr (p, type_params, _)},
289+
{desc = Tconstr (Pdot (Pident {name = "Jsx"}, "element", _), _, _)} )
290+
) -> (
291+
(* Looking for a JSX element but got something else *)
292+
let is_jsx_element ty =
293+
match Ctype.expand_head env ty with
294+
| {desc = Tconstr (Pdot (Pident {name = "Jsx"}, "element", _), _, _)} ->
295+
true
296+
| _ -> false
297+
in
298+
299+
let print_jsx_msg ?(extra = "") name target_fn =
300+
fprintf ppf
301+
"@,\
302+
@,\
303+
In JSX, all content must be JSX elements. You can convert %s to a JSX \
304+
element with @{<info>%s@}%s.@,"
305+
name target_fn extra
306+
in
307+
308+
match type_params with
309+
| _ when Path.same p Predef.path_int ->
310+
print_jsx_msg "int" (with_configured_jsx_module "int")
311+
| _ when Path.same p Predef.path_string ->
312+
print_jsx_msg "string" (with_configured_jsx_module "string")
313+
| _ when Path.same p Predef.path_float ->
314+
print_jsx_msg "float" (with_configured_jsx_module "float")
315+
| [tp] when Path.same p Predef.path_array && is_jsx_element tp ->
316+
print_jsx_msg
317+
~extra:
318+
(" (for example by using a pipe: ->"
319+
^ with_configured_jsx_module "array"
320+
^ ".")
321+
"array"
322+
(with_configured_jsx_module "array")
323+
| [_] when Path.same p Predef.path_array ->
324+
fprintf ppf
325+
"@,\
326+
@,\
327+
You need to convert each item in this array to a JSX element first, \
328+
then use @{<info>%s@} to convert the array of JSX elements into a \
329+
single JSX element.@,"
330+
(with_configured_jsx_module "array")
331+
| _ -> ())
279332
| _ -> ()
280333
281334
let type_clash_context_from_function sexp sfunct =
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/jsx_type_mismatch_array_element.res:19:13-30
4+
5+
17 │ }
6+
18 │
7+
19 │ let x = <> {[React.string("")]} </>
8+
20 │
9+
10+
This has type: array<'a>
11+
But it's expected to have type: React.element (defined as Jsx.element)
12+
13+
You need to convert each item in this array to a JSX element first, then use React.array to convert the array of JSX elements into a single JSX element.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/jsx_type_mismatch_array_raw.res:17:13-16
4+
5+
15 │ }
6+
16 │
7+
17 │ let x = <> {[""]} </>
8+
18 │
9+
10+
This has type: array<'a>
11+
But it's expected to have type: React.element (defined as Jsx.element)
12+
13+
You need to convert each item in this array to a JSX element first, then use React.array to convert the array of JSX elements into a single JSX element.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/jsx_type_mismatch_float.res:17:13-14
4+
5+
15 │ }
6+
16 │
7+
17 │ let x = <> {1.} </>
8+
18 │
9+
10+
This has type: float
11+
But it's expected to have type: React.element (defined as Jsx.element)
12+
13+
In JSX, all content must be JSX elements. You can convert float to a JSX element with React.float.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/jsx_type_mismatch_int.res:17:13
4+
5+
15 │ }
6+
16 │
7+
17 │ let x = <> {1} </>
8+
18 │
9+
10+
This has type: int
11+
But it's expected to have type: React.element (defined as Jsx.element)
12+
13+
In JSX, all content must be JSX elements. You can convert int to a JSX element with React.int.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/jsx_type_mismatch_string.res:17:13-14
4+
5+
15 │ }
6+
16 │
7+
17 │ let x = <> {""} </>
8+
18 │
9+
10+
This has type: string
11+
But it's expected to have type: React.element (defined as Jsx.element)
12+
13+
In JSX, all content must be JSX elements. You can convert string to a JSX element with React.string.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@@config({
2+
flags: ["-bs-jsx", "4"],
3+
})
4+
5+
module React = {
6+
type element = Jsx.element
7+
type componentLike<'props, 'return> = 'props => 'return
8+
type component<'props> = Jsx.component<'props>
9+
10+
@module("react/jsx-runtime")
11+
external jsx: (component<'props>, 'props) => element = "jsx"
12+
13+
type fragmentProps = {children?: element}
14+
@module("react/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"
15+
16+
external string: string => element = "%identity"
17+
}
18+
19+
let x = <> {[React.string("")]} </>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@@config({
2+
flags: ["-bs-jsx", "4"],
3+
})
4+
5+
module React = {
6+
type element = Jsx.element
7+
type componentLike<'props, 'return> = 'props => 'return
8+
type component<'props> = Jsx.component<'props>
9+
10+
@module("react/jsx-runtime")
11+
external jsx: (component<'props>, 'props) => element = "jsx"
12+
13+
type fragmentProps = {children?: element}
14+
@module("react/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"
15+
}
16+
17+
let x = <> {[""]} </>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@@config({
2+
flags: ["-bs-jsx", "4"],
3+
})
4+
5+
module React = {
6+
type element = Jsx.element
7+
type componentLike<'props, 'return> = 'props => 'return
8+
type component<'props> = Jsx.component<'props>
9+
10+
@module("react/jsx-runtime")
11+
external jsx: (component<'props>, 'props) => element = "jsx"
12+
13+
type fragmentProps = {children?: element}
14+
@module("react/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"
15+
}
16+
17+
let x = <> {1.} </>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@@config({
2+
flags: ["-bs-jsx", "4"],
3+
})
4+
5+
module React = {
6+
type element = Jsx.element
7+
type componentLike<'props, 'return> = 'props => 'return
8+
type component<'props> = Jsx.component<'props>
9+
10+
@module("react/jsx-runtime")
11+
external jsx: (component<'props>, 'props) => element = "jsx"
12+
13+
type fragmentProps = {children?: element}
14+
@module("react/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"
15+
}
16+
17+
let x = <> {1} </>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@@config({
2+
flags: ["-bs-jsx", "4"],
3+
})
4+
5+
module React = {
6+
type element = Jsx.element
7+
type componentLike<'props, 'return> = 'props => 'return
8+
type component<'props> = Jsx.component<'props>
9+
10+
@module("react/jsx-runtime")
11+
external jsx: (component<'props>, 'props) => element = "jsx"
12+
13+
type fragmentProps = {children?: element}
14+
@module("react/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"
15+
}
16+
17+
let x = <> {""} </>

0 commit comments

Comments
 (0)