Skip to content

Commit 5163b5f

Browse files
committed
improve array <-> tuple error message by looking at what the user actually wrote
1 parent 3cbc730 commit 5163b5f

File tree

8 files changed

+114
-9
lines changed

8 files changed

+114
-9
lines changed

compiler/bsc/rescript_compiler_main.ml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@
1212

1313
let absname = ref false
1414

15+
external to_comment : Res_comment.t -> Error_message_utils.comment = "%identity"
16+
external from_comment : Error_message_utils.comment -> Res_comment.t
17+
= "%identity"
18+
19+
let () =
20+
Error_message_utils.parse_source :=
21+
fun source ->
22+
let res =
23+
Res_driver.parse_implementation_from_source ~for_printer:false
24+
~display_filename:"<none>" ~source
25+
in
26+
(res.parsetree, res.comments |> List.map to_comment)
27+
28+
let () =
29+
Error_message_utils.reprint_source :=
30+
fun parsetree comments ->
31+
Res_printer.print_implementation parsetree
32+
~comments:(comments |> List.map from_comment)
33+
~width:80
34+
1535
let set_abs_input_name sourcefile =
1636
let sourcefile =
1737
if !absname && Filename.is_relative sourcefile then

compiler/ml/error_message_utils.ml

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

4+
type comment
5+
let parse_source : (string -> Parsetree.structure * comment list) ref =
6+
ref (fun _ -> ([], []))
7+
let reprint_source : (Parsetree.structure -> comment list -> string) ref =
8+
ref (fun _ _ -> "")
9+
410
type type_clash_statement = FunctionCall
511
type type_clash_context =
612
| SetRecordField
@@ -62,7 +68,14 @@ let is_record_type ~extract_concrete_typedecl ~env ty =
6268
| _ -> false
6369
with _ -> false
6470

65-
let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
71+
let extract_location_string ~src (loc : Location.t) =
72+
let start_pos = loc.loc_start in
73+
let end_pos = loc.loc_end in
74+
let start_offset = start_pos.pos_cnum in
75+
let end_offset = end_pos.pos_cnum in
76+
String.sub src start_offset (end_offset - start_offset)
77+
78+
let print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf
6679
(bottom_aliases : (Types.type_expr * Types.type_expr) option)
6780
type_clash_context =
6881
match (type_clash_context, bottom_aliases) with
@@ -185,6 +198,39 @@ let print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
185198
| _, Some ({Types.desc = Tconstr (p1, _, _)}, _)
186199
when Path.same p1 Predef.path_promise ->
187200
fprintf ppf "\n\n - Did you mean to await this promise before using it?\n"
201+
| _, Some ({Types.desc = Tconstr (p1, _, _)}, {Types.desc = Ttuple _})
202+
when Path.same p1 Predef.path_array ->
203+
let src = Ext_io.load_file loc.Location.loc_start.pos_fname in
204+
let sub_src = extract_location_string ~src loc in
205+
let parsed, comments = !parse_source sub_src in
206+
let suggested_rewrite =
207+
match parsed with
208+
| [
209+
({
210+
Parsetree.pstr_desc =
211+
Pstr_eval (({pexp_desc = Pexp_array items} as exp), l);
212+
} as str_item);
213+
] ->
214+
Some
215+
(!reprint_source
216+
[
217+
{
218+
str_item with
219+
pstr_desc =
220+
Pstr_eval ({exp with pexp_desc = Pexp_tuple items}, l);
221+
};
222+
]
223+
comments)
224+
| _ -> None
225+
in
226+
fprintf ppf
227+
"\n\n - Fix this by passing a tuple instead of an array%s@{<info>%s@}\n"
228+
(match suggested_rewrite with
229+
| Some _ -> ", like: "
230+
| None -> "")
231+
(match suggested_rewrite with
232+
| Some rewrite -> rewrite
233+
| None -> "")
188234
| _ -> ()
189235

190236
let type_clash_context_from_function sexp sfunct =

compiler/ml/typecore.ml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ let rec collect_missing_arguments env type1 type2 =
728728
| None -> None)
729729
| _ -> None
730730

731-
let print_expr_type_clash ?type_clash_context env trace ppf =
731+
let print_expr_type_clash ?type_clash_context env loc trace ppf =
732732
(* this is the most frequent error. We should do whatever we can to provide
733733
specific guidance to this generic error before giving up *)
734734
let bottom_aliases_result = bottom_aliases trace in
@@ -785,7 +785,7 @@ let print_expr_type_clash ?type_clash_context env trace ppf =
785785
(function
786786
| ppf -> error_type_text ppf type_clash_context)
787787
(function ppf -> error_expected_type_text ppf type_clash_context);
788-
print_extra_type_clash_help ~extract_concrete_typedecl ~env ppf
788+
print_extra_type_clash_help ~extract_concrete_typedecl ~env loc ppf
789789
bottom_aliases_result type_clash_context;
790790
show_extra_help ppf env trace
791791

@@ -4173,7 +4173,7 @@ let type_expr ppf typ =
41734173
Printtyp.reset_and_mark_loops typ;
41744174
Printtyp.type_expr ppf typ
41754175
4176-
let report_error env ppf error =
4176+
let report_error env loc ppf error =
41774177
match error with
41784178
| Polymorphic_label lid ->
41794179
fprintf ppf "@[The record field %a is polymorphic.@ %s@]" longident lid
@@ -4237,7 +4237,7 @@ let report_error env ppf error =
42374237
| Expr_type_clash (trace, type_clash_context) ->
42384238
(* modified *)
42394239
fprintf ppf "@[<v>";
4240-
print_expr_type_clash ?type_clash_context env trace ppf;
4240+
print_expr_type_clash ?type_clash_context env loc trace ppf;
42414241
fprintf ppf "@]"
42424242
| Apply_non_function typ -> (
42434243
(* modified *)
@@ -4542,13 +4542,13 @@ let report_error env ppf error =
45424542
fprintf ppf
45434543
"Direct field access on a dict is not supported. Use Dict.get instead."
45444544
4545-
let report_error env ppf err =
4546-
Printtyp.wrap_printing_env env (fun () -> report_error env ppf err)
4545+
let report_error env loc ppf err =
4546+
Printtyp.wrap_printing_env env (fun () -> report_error env loc ppf err)
45474547
45484548
let () =
45494549
Location.register_error_of_exn (function
45504550
| Error (loc, env, err) ->
4551-
Some (Location.error_of_printer loc (report_error env) err)
4551+
Some (Location.error_of_printer loc (report_error env loc) err)
45524552
| Error_forward err -> Some err
45534553
| _ -> None)
45544554

compiler/ml/typecore.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ type error =
108108
exception Error of Location.t * Env.t * error
109109
exception Error_forward of Location.error
110110

111-
val report_error : Env.t -> formatter -> error -> unit
111+
val report_error : Env.t -> Location.t -> formatter -> error -> unit
112112
(* Deprecated. Use Location.{error_of_exn, report_error}. *)
113113

114114
(* Forward declaration, to be filled in by Typemod.type_module *)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/array_literal_passed_to_tuple.res:5:17-34
4+
5+
3 │ }
6+
4 │
7+
5 │ let x = doStuff(["hello", "world"])
8+
6 │
9+
10+
This has type: array<'a>
11+
But it's expected to have type: (string, string)
12+
13+
- Fix this by passing a tuple instead of an array, like: ("hello", "world")
14+

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/array_var_passed_to_tuple.res:7:17-18
4+
5+
5 │ let xx = ["hello", "world"]
6+
6 │
7+
7 │ let x = doStuff(xx)
8+
8 │
9+
10+
This has type: array<string>
11+
But this function argument is expecting: (string, string)
12+
13+
- Fix this by passing a tuple instead of an array
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let doStuff = ((one, two)) => {
2+
one ++ two
3+
}
4+
5+
let x = doStuff(["hello", "world"])
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let doStuff = ((one, two)) => {
2+
one ++ two
3+
}
4+
5+
let xx = ["hello", "world"]
6+
7+
let x = doStuff(xx)

0 commit comments

Comments
 (0)