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