1
1
type extract_concrete_typedecl =
2
2
Env .t -> Types .type_expr -> Path .t * Path .t * Types .type_declaration
3
3
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
+
4
62
type type_clash_statement = FunctionCall
5
63
type type_clash_context =
6
64
| SetRecordField
@@ -62,7 +120,7 @@ let is_record_type ~extract_concrete_typedecl ~env ty =
62
120
| _ -> false
63
121
with _ -> false
64
122
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
66
124
(bottom_aliases : (Types.type_expr * Types.type_expr) option )
67
125
type_clash_context =
68
126
match (type_clash_context, bottom_aliases) with
@@ -103,9 +161,9 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
103
161
\ Floats and ints have their own mathematical operators. This means \
104
162
you cannot %s a float and an int without converting between the two.\n\n \
105
163
\ 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@}."
109
167
operator_text
110
168
(if for_float then " float" else " int" ));
111
169
match (is_constant, bottom_aliases) with
@@ -153,7 +211,7 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
153
211
" \n\n \
154
212
\ Possible solutions:\n \
155
213
\ - Unwrap the option to its underlying value using \
156
- `yourValue->Belt. Option.getWithDefault (someDefaultValue)`"
214
+ `yourValue->Option.getOr (someDefaultValue)`"
157
215
| Some ComparisonOperator , _ ->
158
216
fprintf ppf " \n\n You can only compare things of the same type."
159
217
| Some ArrayValue , _ ->
@@ -165,6 +223,10 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
165
223
\ - Use a tuple, if your array is of fixed length. Tuples can mix types \
166
224
freely, and compiles to a JavaScript array. Example of a tuple: `let \
167
225
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}@}@,"
168
230
| _, Some ({Types. desc = Tconstr (_p1, _, _)}, {desc = Tconstr (p2, _, _)})
169
231
when Path. same Predef. path_unit p2 ->
170
232
fprintf ppf
@@ -176,15 +238,113 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
176
238
| _, Some ({desc = Tobject _}, ({Types. desc = Tconstr _} as t1))
177
239
when is_record_type ~extract_concrete_typedecl ~env t1 ->
178
240
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 -> " " )
185
273
| _, Some ({Types. desc = Tconstr (p1, _, _)}, _)
186
274
when Path. same p1 Predef. path_promise ->
187
275
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
+ | _ -> () )
188
348
| _ -> ()
189
349
190
350
let type_clash_context_from_function sexp sfunct =
0 commit comments