Skip to content

Commit 9765092

Browse files
committed
suggest different arity fns when it makes sense
1 parent 327b986 commit 9765092

8 files changed

+137
-13
lines changed

compiler/ml/typecore.ml

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,13 @@ type error =
7979
| Unknown_literal of string * char
8080
| Illegal_letrec_pat
8181
| Empty_record_literal
82-
| Uncurried_arity_mismatch of
83-
type_expr * int * int * Asttypes.Noloc.arg_label list
82+
| Uncurried_arity_mismatch of {
83+
function_type: type_expr;
84+
expected_arity: int;
85+
provided_arity: int;
86+
provided_args: Asttypes.Noloc.arg_label list;
87+
function_name: Longident.t option;
88+
}
8489
| Field_not_optional of string * type_expr
8590
| Type_params_not_supported of Longident.t
8691
| Field_access_on_dict_type
@@ -2218,6 +2223,11 @@ let not_function env ty =
22182223
let ls, tvar = list_labels env ty in
22192224
ls = [] && not tvar
22202225
2226+
let extract_function_name funct =
2227+
match funct.exp_desc with
2228+
| Texp_ident (path, _, _) -> Some (Longident.parse (Path.name path))
2229+
| _ -> None
2230+
22212231
type lazy_args =
22222232
(Asttypes.Noloc.arg_label * (unit -> Typedtree.expression) option) list
22232233
@@ -3510,10 +3520,13 @@ and type_application ~context total_app env funct (sargs : sargs) :
35103520
( funct.exp_loc,
35113521
env,
35123522
Uncurried_arity_mismatch
3513-
( funct.exp_type,
3514-
arity,
3515-
List.length sargs,
3516-
sargs |> List.map (fun (a, _) -> to_noloc a) ) ));
3523+
{
3524+
function_type = funct.exp_type;
3525+
expected_arity = arity;
3526+
provided_arity = List.length sargs;
3527+
provided_args = sargs |> List.map (fun (a, _) -> to_noloc a);
3528+
function_name = extract_function_name funct;
3529+
} ));
35173530
arity
35183531
| None -> max_int
35193532
in
@@ -3529,10 +3542,13 @@ and type_application ~context total_app env funct (sargs : sargs) :
35293542
( funct.exp_loc,
35303543
env,
35313544
Uncurried_arity_mismatch
3532-
( funct.exp_type,
3533-
required_args + newarity,
3534-
required_args,
3535-
sargs |> List.map (fun (a, _) -> to_noloc a) ) )));
3545+
{
3546+
function_type = funct.exp_type;
3547+
expected_arity = required_args + newarity;
3548+
provided_arity = required_args;
3549+
provided_args = sargs |> List.map (fun (a, _) -> to_noloc a);
3550+
function_name = extract_function_name funct;
3551+
} )));
35363552
let new_t =
35373553
if fully_applied then new_t
35383554
else
@@ -4230,6 +4246,40 @@ let spellcheck ppf unbound_name valid_names =
42304246
let spellcheck_idents ppf unbound valid_idents =
42314247
spellcheck ppf (Ident.name unbound) (List.map Ident.name valid_idents)
42324248
4249+
let strip_arity_suffix name =
4250+
let len = String.length name in
4251+
let rec scan_back i =
4252+
if i < 0 || name.[i] < '0' || name.[i] > '9' then i + 1
4253+
else scan_back (i - 1)
4254+
in
4255+
let start_of_digits = scan_back (len - 1) in
4256+
if start_of_digits > 0 && start_of_digits < len then
4257+
String.sub name 0 start_of_digits
4258+
else name
4259+
4260+
let find_arity_suggestion env function_name target_arity =
4261+
let base_name = strip_arity_suffix function_name in
4262+
let candidate =
4263+
if target_arity = 1 then base_name
4264+
else base_name ^ string_of_int target_arity
4265+
in
4266+
try
4267+
let path, desc = Env.lookup_value (Longident.parse candidate) env in
4268+
if Builtin_attributes.deprecated_of_attrs desc.val_attributes <> None then
4269+
None
4270+
else
4271+
let expanded_type = Ctype.expand_head env desc.val_type in
4272+
let actual_arity =
4273+
match Ctype.get_arity env expanded_type with
4274+
| Some arity -> arity
4275+
| None -> 0
4276+
in
4277+
if actual_arity = target_arity then Some (Printtyp.string_of_path path)
4278+
else None
4279+
with
4280+
| Not_found -> None
4281+
| _ -> None
4282+
42334283
open Format
42344284
let longident = Printtyp.longident
42354285
let super_report_unification_error = Printtyp.super_report_unification_error
@@ -4489,7 +4539,14 @@ let report_error env loc ppf error =
44894539
fprintf ppf
44904540
"Empty record literal {} should be type annotated or used in a record \
44914541
context."
4492-
| Uncurried_arity_mismatch (typ, arity, args, sargs) ->
4542+
| Uncurried_arity_mismatch
4543+
{
4544+
function_type = typ;
4545+
expected_arity = arity;
4546+
provided_arity = args;
4547+
provided_args = sargs;
4548+
function_name = function_name_opt;
4549+
} ->
44934550
(* We need:
44944551
- Any arg that's required but isn't passed
44954552
- Any arg that is passed but isn't in the fn definition (optional or labelled)
@@ -4598,6 +4655,26 @@ let report_error env loc ppf error =
45984655
(if args = 1 then "" else "s")
45994656
arity;
46004657
4658+
(* Add suggestions for functions with correct arity *)
4659+
(match function_name_opt with
4660+
| Some function_name -> (
4661+
let function_name_str =
4662+
let buffer = Buffer.create 16 in
4663+
let formatter = Format.formatter_of_buffer buffer in
4664+
Printtyp.longident formatter function_name;
4665+
Format.pp_print_flush formatter ();
4666+
Buffer.contents buffer
4667+
in
4668+
let suggestion = find_arity_suggestion env function_name_str args in
4669+
match suggestion with
4670+
| None -> () (* No suggestion found *)
4671+
| Some suggestion_str ->
4672+
fprintf ppf
4673+
"@,@,Hint: Try @{<info>%s@} instead (takes @{<info>%d@} argument%s)."
4674+
suggestion_str args
4675+
(if args = 1 then "" else "s"))
4676+
| None -> () (* Function name not available *));
4677+
46014678
fprintf ppf "@]"
46024679
| Field_not_optional (name, typ) ->
46034680
fprintf ppf "Field @{<info>%s@} is not optional in type %a. Use without ?"

compiler/ml/typecore.mli

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,13 @@ type error =
111111
| Unknown_literal of string * char
112112
| Illegal_letrec_pat
113113
| Empty_record_literal
114-
| Uncurried_arity_mismatch of
115-
type_expr * int * int * Asttypes.Noloc.arg_label list
114+
| Uncurried_arity_mismatch of {
115+
function_type: type_expr;
116+
expected_arity: int;
117+
provided_arity: int;
118+
provided_args: Asttypes.Noloc.arg_label list;
119+
function_name: Longident.t option;
120+
}
116121
| Field_not_optional of string * type_expr
117122
| Type_params_not_supported of Longident.t
118123
| Field_access_on_dict_type
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/suggest_existing_arity_fn_1.res:1:1-11
4+
5+
1 │ Console.log(1, 2)
6+
7+
This function call is incorrect.
8+
The function has type:
9+
'a => unit
10+
11+
- The function takes just 1 unlabelled argument, but is called with 2
12+
13+
Hint: Try Console.log2 instead (takes 2 arguments).
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/suggest_existing_arity_fn_2.res:1:1-12
4+
5+
1 │ Console.log2(1)
6+
7+
This function call is incorrect.
8+
The function has type:
9+
(int, 'a) => unit
10+
11+
- The function takes 2 unlabelled arguments, but is called with just 1
12+
13+
Hint: Try Console.log instead (takes 1 argument).
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/suggest_existing_arity_fn_3.res:1:1-12
4+
5+
1 │ Console.log4(1, 2)
6+
7+
This function call is incorrect.
8+
The function has type:
9+
(int, int, 'a, 'b) => unit
10+
11+
- The function takes 4 unlabelled arguments, but is called with just 2
12+
13+
Hint: Try Console.log2 instead (takes 2 arguments).
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Console.log(1, 2)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Console.log2(1)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Console.log4(1, 2)

0 commit comments

Comments
 (0)