11type 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+
11+ module Parser : sig
12+ type comment
13+
14+ val parse_source : (string -> Parsetree .structure * comment list ) ref
15+
16+ val reprint_source : (Parsetree .structure -> comment list -> string ) ref
17+
18+ val parse_expr_at_loc :
19+ Warnings .loc -> (Parsetree .expression * comment list ) option
20+
21+ val reprint_expr_at_loc :
22+ ?mapper : (Parsetree .expression -> Parsetree .expression option ) ->
23+ Warnings .loc ->
24+ string option
25+ end = struct
26+ type comment
27+
28+ let parse_source : (string -> Parsetree.structure * comment list) ref =
29+ ref (fun _ -> ([] , [] ))
30+
31+ let reprint_source : (Parsetree.structure -> comment list -> string) ref =
32+ ref (fun _ _ -> " " )
33+
34+ let extract_location_string ~src (loc : Location.t ) =
35+ let start_pos = loc.loc_start in
36+ let end_pos = loc.loc_end in
37+ let start_offset = start_pos.pos_cnum in
38+ let end_offset = end_pos.pos_cnum in
39+ String. sub src start_offset (end_offset - start_offset)
40+
41+ let parse_expr_at_loc loc =
42+ (* TODO: Maybe cache later on *)
43+ let src = Ext_io. load_file loc.Location. loc_start.pos_fname in
44+ let sub_src = extract_location_string ~src loc in
45+ let parsed, comments = ! parse_source sub_src in
46+ match parsed with
47+ | [{Parsetree. pstr_desc = Pstr_eval (exp, _)}] -> Some (exp, comments)
48+ | _ -> None
49+
50+ let wrap_in_structure exp =
51+ [{Parsetree. pstr_desc = Pstr_eval (exp, [] ); pstr_loc = Location. none}]
52+
53+ let reprint_expr_at_loc ?(mapper = fun _ -> None ) loc =
54+ match parse_expr_at_loc loc with
55+ | Some (exp , comments ) -> (
56+ match mapper exp with
57+ | Some exp -> Some (! reprint_source (wrap_in_structure exp) comments)
58+ | None -> None )
59+ | None -> None
60+ end
61+
462type type_clash_statement = FunctionCall
563type type_clash_context =
664 | SetRecordField
@@ -62,7 +120,7 @@ let is_record_type ~extract_concrete_typedecl ~env ty =
62120 | _ -> false
63121 with _ -> false
64122
65- let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
123+ let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf
66124 (bottom_aliases : (Types.type_expr * Types.type_expr) option )
67125 type_clash_context =
68126 match (type_clash_context, bottom_aliases) with
@@ -103,9 +161,9 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
103161 \ Floats and ints have their own mathematical operators. This means \
104162 you cannot %s a float and an int without converting between the two.\n\n \
105163 \ Possible solutions:\n \
106- \ - Ensure all values in this calculation has the type @{<info>%s@}. \
107- You can convert between floats and ints via \
108- @{<info>Belt.Float.toInt@} and @{<info>Belt. Int.fromFloat@}."
164+ \ - Ensure all values in this calculation have the type @{<info>%s@}. \
165+ You can convert between floats and ints via @{<info>Float.toInt@} and \
166+ @{<info>Int.fromFloat@}."
109167 operator_text
110168 (if for_float then " float" else " int" ));
111169 match (is_constant, bottom_aliases) with
@@ -153,7 +211,7 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
153211 " \n\n \
154212 \ Possible solutions:\n \
155213 \ - Unwrap the option to its underlying value using \
156- `yourValue->Belt. Option.getWithDefault (someDefaultValue)`"
214+ `yourValue->Option.getOr (someDefaultValue)`"
157215 | Some ComparisonOperator , _ ->
158216 fprintf ppf " \n\n You can only compare things of the same type."
159217 | Some ArrayValue , _ ->
@@ -165,6 +223,10 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
165223 \ - Use a tuple, if your array is of fixed length. Tuples can mix types \
166224 freely, and compiles to a JavaScript array. Example of a tuple: `let \
167225 myTuple = (10, \" hello\" , 15.5, true)"
226+ | _, Some (_, {desc = Tconstr (p2, _, _)}) when Path. same Predef. path_dict p2
227+ ->
228+ fprintf ppf
229+ " @,@,Dicts are written like: @{<info>dict{\" a\" : 1, \" b\" : 2}@}@,"
168230 | _, Some ({Types. desc = Tconstr (_p1, _, _)}, {desc = Tconstr (p2, _, _)})
169231 when Path. same Predef. path_unit p2 ->
170232 fprintf ppf
@@ -176,15 +238,113 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
176238 | _, Some ({desc = Tobject _}, ({Types. desc = Tconstr _} as t1))
177239 when is_record_type ~extract_concrete_typedecl ~env t1 ->
178240 fprintf ppf
179- " \n\n \
180- \ You're passing a @{<error>ReScript object@} where a @{<info>record@} \
181- is expected. \n\n \
182- \ - Did you mean to pass a record instead of an object? Objects are \
183- written with quoted keys, and records with unquoted keys. Remove the \
184- quotes from the object keys to pass it as a record instead of object. \n\n "
241+ " @,\
242+ @,\
243+ You're passing a @{<error>ReScript object@} where a @{<info>record@} is \
244+ expected. Objects are written with quoted keys, and records with \
245+ unquoted keys." ;
246+
247+ let suggested_rewrite =
248+ Parser. reprint_expr_at_loc loc ~mapper: (fun exp ->
249+ match exp.Parsetree. pexp_desc with
250+ | Pexp_extension
251+ ( {txt = " obj" },
252+ PStr
253+ [
254+ {
255+ pstr_desc =
256+ Pstr_eval (({pexp_desc = Pexp_record _} as record), _);
257+ };
258+ ] ) ->
259+ Some record
260+ | _ -> None )
261+ in
262+ fprintf ppf
263+ " @,\
264+ @,\
265+ Possible solutions: @,\
266+ - Rewrite the object to a record%s@{<info>%s@}@,"
267+ (match suggested_rewrite with
268+ | Some _ -> " , like: "
269+ | None -> " " )
270+ (match suggested_rewrite with
271+ | Some rewrite -> rewrite
272+ | None -> " " )
185273 | _, Some ({Types. desc = Tconstr (p1, _, _)}, _)
186274 when Path. same p1 Predef. path_promise ->
187275 fprintf ppf " \n\n - Did you mean to await this promise before using it?\n "
276+ | _, Some ({Types. desc = Tconstr (p1, _, _)}, {Types. desc = Ttuple _})
277+ when Path. same p1 Predef. path_array ->
278+ let suggested_rewrite =
279+ Parser. reprint_expr_at_loc loc ~mapper: (fun exp ->
280+ match exp.Parsetree. pexp_desc with
281+ | Pexp_array items ->
282+ Some {exp with Parsetree. pexp_desc = Pexp_tuple items}
283+ | _ -> None )
284+ in
285+ fprintf ppf
286+ " \n\n - Fix this by passing a tuple instead of an array%s@{<info>%s@}\n "
287+ (match suggested_rewrite with
288+ | Some _ -> " , like: "
289+ | None -> " " )
290+ (match suggested_rewrite with
291+ | Some rewrite -> rewrite
292+ | None -> " " )
293+ | ( _,
294+ Some
295+ ( {desc = Tconstr (p, type_params, _)},
296+ {desc = Tconstr (Pdot (Pident {name = " Jsx" }, " element" , _), _, _)} )
297+ ) -> (
298+ (* Looking for a JSX element but got something else *)
299+ let is_jsx_element ty =
300+ match Ctype. expand_head env ty with
301+ | {desc = Tconstr (Pdot (Pident {name = "Jsx" } , "element" , _ ), _ , _ )} ->
302+ true
303+ | _ -> false
304+ in
305+
306+ let print_jsx_msg ?(extra = " " ) name target_fn =
307+ fprintf ppf
308+ " @,\
309+ @,\
310+ In JSX, all content must be JSX elements. You can convert %s to a JSX \
311+ element with @{<info>%s@}%s.@,"
312+ name target_fn extra
313+ in
314+
315+ match type_params with
316+ | _ when Path. same p Predef. path_int ->
317+ print_jsx_msg " int" (with_configured_jsx_module " int" )
318+ | _ when Path. same p Predef. path_string ->
319+ print_jsx_msg " string" (with_configured_jsx_module " string" )
320+ | _ when Path. same p Predef. path_float ->
321+ print_jsx_msg " float" (with_configured_jsx_module " float" )
322+ | [_] when Path. same p Predef. path_option ->
323+ fprintf ppf
324+ " @,\
325+ @,\
326+ You need to unwrap this option to its underlying value first, then \
327+ turn that value into a JSX element.@,\
328+ For @{<info>None@}, you can use @{<info>%s@} to output nothing into \
329+ JSX.@,"
330+ (with_configured_jsx_module " null" )
331+ | [tp] when Path. same p Predef. path_array && is_jsx_element tp ->
332+ print_jsx_msg
333+ ~extra:
334+ (" (for example by using a pipe: ->"
335+ ^ with_configured_jsx_module " array"
336+ ^ " ." )
337+ " array"
338+ (with_configured_jsx_module " array" )
339+ | [_] when Path. same p Predef. path_array ->
340+ fprintf ppf
341+ " @,\
342+ @,\
343+ You need to convert each item in this array to a JSX element first, \
344+ then use @{<info>%s@} to convert the array of JSX elements into a \
345+ single JSX element.@,"
346+ (with_configured_jsx_module " array" )
347+ | _ -> () )
188348 | _ -> ()
189349
190350let type_clash_context_from_function sexp sfunct =
0 commit comments