@@ -84,8 +84,13 @@ type error =
8484 | Unknown_literal of string * char
8585 | Illegal_letrec_pat
8686 | Empty_record_literal
87- | Uncurried_arity_mismatch of
88- type_expr * int * int * Asttypes.Noloc .arg_label list
87+ | Uncurried_arity_mismatch of {
88+ function_type : type_expr ;
89+ expected_arity : int ;
90+ provided_arity : int ;
91+ provided_args : Asttypes.Noloc .arg_label list ;
92+ function_name : Longident .t option ;
93+ }
8994 | Field_not_optional of string * type_expr
9095 | Type_params_not_supported of Longident .t
9196 | Field_access_on_dict_type
@@ -2230,6 +2235,11 @@ let not_function env ty =
22302235 let ls, tvar = list_labels env ty in
22312236 ls = [] && not tvar
22322237
2238+ let extract_function_name funct =
2239+ match funct.exp_desc with
2240+ | Texp_ident (path , _ , _ ) -> Some (Longident. parse (Path. name path))
2241+ | _ -> None
2242+
22332243type lazy_args =
22342244 (Asttypes.Noloc .arg_label * (unit -> Typedtree .expression ) option ) list
22352245
@@ -3522,10 +3532,13 @@ and type_application ~context total_app env funct (sargs : sargs) :
35223532 ( funct.exp_loc,
35233533 env,
35243534 Uncurried_arity_mismatch
3525- ( funct.exp_type,
3526- arity,
3527- List. length sargs,
3528- sargs |> List. map (fun (a , _ ) -> to_noloc a) ) ));
3535+ {
3536+ function_type = funct.exp_type;
3537+ expected_arity = arity;
3538+ provided_arity = List. length sargs;
3539+ provided_args = sargs |> List. map (fun (a , _ ) -> to_noloc a);
3540+ function_name = extract_function_name funct;
3541+ } ));
35293542 arity
35303543 | None -> max_int
35313544 in
@@ -3541,10 +3554,13 @@ and type_application ~context total_app env funct (sargs : sargs) :
35413554 ( funct.exp_loc,
35423555 env,
35433556 Uncurried_arity_mismatch
3544- ( funct.exp_type,
3545- required_args + newarity,
3546- required_args,
3547- sargs |> List. map (fun (a , _ ) -> to_noloc a) ) )));
3557+ {
3558+ function_type = funct.exp_type;
3559+ expected_arity = required_args + newarity;
3560+ provided_arity = required_args;
3561+ provided_args = sargs |> List. map (fun (a , _ ) -> to_noloc a);
3562+ function_name = extract_function_name funct;
3563+ } )));
35483564 let new_t =
35493565 if fully_applied then new_t
35503566 else
@@ -4247,6 +4263,40 @@ let spellcheck ppf unbound_name valid_names =
42474263let spellcheck_idents ppf unbound valid_idents =
42484264 spellcheck ppf (Ident. name unbound) (List. map Ident. name valid_idents)
42494265
4266+ let strip_arity_suffix name =
4267+ let len = String. length name in
4268+ let rec scan_back i =
4269+ if i < 0 || name.[i] < '0' || name.[i] > '9' then i + 1
4270+ else scan_back (i - 1 )
4271+ in
4272+ let start_of_digits = scan_back (len - 1 ) in
4273+ if start_of_digits > 0 && start_of_digits < len then
4274+ String. sub name 0 start_of_digits
4275+ else name
4276+
4277+ let find_arity_suggestion env function_name target_arity =
4278+ let base_name = strip_arity_suffix function_name in
4279+ let candidate =
4280+ if target_arity = 1 then base_name
4281+ else base_name ^ string_of_int target_arity
4282+ in
4283+ try
4284+ let path, desc = Env. lookup_value (Longident. parse candidate) env in
4285+ if Builtin_attributes. deprecated_of_attrs desc.val_attributes <> None then
4286+ None
4287+ else
4288+ let expanded_type = Ctype. expand_head env desc.val_type in
4289+ let actual_arity =
4290+ match Ctype. get_arity env expanded_type with
4291+ | Some arity -> arity
4292+ | None -> 0
4293+ in
4294+ if actual_arity = target_arity then Some (Printtyp. string_of_path path)
4295+ else None
4296+ with
4297+ | Not_found -> None
4298+ | _ -> None
4299+
42504300open Format
42514301let longident = Printtyp. longident
42524302let super_report_unification_error = Printtyp. super_report_unification_error
@@ -4516,7 +4566,14 @@ let report_error env loc ppf error =
45164566 fprintf ppf
45174567 " Empty record literal {} should be type annotated or used in a record \
45184568 context."
4519- | Uncurried_arity_mismatch (typ , arity , args , sargs ) ->
4569+ | Uncurried_arity_mismatch
4570+ {
4571+ function_type = typ;
4572+ expected_arity = arity;
4573+ provided_arity = args;
4574+ provided_args = sargs;
4575+ function_name = function_name_opt;
4576+ } ->
45204577 (* We need:
45214578 - Any arg that's required but isn't passed
45224579 - Any arg that is passed but isn't in the fn definition (optional or labelled)
@@ -4625,6 +4682,26 @@ let report_error env loc ppf error =
46254682 (if args = 1 then " " else " s" )
46264683 arity;
46274684
4685+ (* Add suggestions for related functions with correct arity *)
4686+ (match function_name_opt with
4687+ | Some function_name -> (
4688+ let function_name_str =
4689+ let buffer = Buffer. create 16 in
4690+ let formatter = Format. formatter_of_buffer buffer in
4691+ Printtyp. longident formatter function_name;
4692+ Format. pp_print_flush formatter () ;
4693+ Buffer. contents buffer
4694+ in
4695+ let suggestion = find_arity_suggestion env function_name_str args in
4696+ match suggestion with
4697+ | None -> ()
4698+ | Some suggestion_str ->
4699+ fprintf ppf
4700+ " @,@,Hint: Try @{<info>%s@} instead (takes @{<info>%d@} argument%s)."
4701+ suggestion_str args
4702+ (if args = 1 then " " else " s" ))
4703+ | None -> () );
4704+
46284705 fprintf ppf " @]"
46294706 | Field_not_optional (name , typ ) ->
46304707 fprintf ppf " Field @{<info>%s@} is not optional in type %a. Use without ?"
0 commit comments