@@ -27,12 +27,28 @@ let collectValueBinding ~config ~decls ~file ~(current_binding : Location.t)
2727 when (not loc_ghost) && not vb.vb_loc.loc_ghost ->
2828 let name = Ident. name id |> Name. create ~is Interface:false in
2929 let optionalArgs =
30- vb.vb_expr.exp_type |> DeadOptionalArgs. fromTypeExpr
31- |> OptionalArgs. fromList
30+ match vb.vb_expr.exp_desc with
31+ | Texp_function {arity = Some arity ; _} ->
32+ vb.vb_expr.exp_type
33+ |> (fun texpr -> DeadOptionalArgs. fromTypeExprWithArity texpr arity)
34+ |> OptionalArgs. fromList
35+ | Texp_function _ ->
36+ vb.vb_expr.exp_type |> DeadOptionalArgs. fromTypeExpr
37+ |> OptionalArgs. fromList
38+ | _ -> OptionalArgs. empty
39+ in
40+ (* Only actual function declarations own optional-arg diagnostics.
41+ Aliases to function values can expose the same optional-arg type, but
42+ warnings should stay attached to the declaration site. *)
43+ let ownsOptionalArgs =
44+ match vb.vb_expr.exp_desc with
45+ | Texp_function _ -> true
46+ | _ -> false
3247 in
3348 let exists =
3449 match Declarations. find_opt_builder decls loc_start with
3550 | Some {declKind = Value r } ->
51+ r.ownsOptionalArgs < - ownsOptionalArgs;
3652 r.optionalArgs < - optionalArgs;
3753 true
3854 | _ -> false
@@ -48,8 +64,9 @@ let collectValueBinding ~config ~decls ~file ~(current_binding : Location.t)
4864 let isToplevel = oldLastBinding = Location. none in
4965 let sideEffects = SideEffects. checkExpr vb.vb_expr in
5066 name
51- |> addValueDeclaration ~config ~decls ~file ~is Toplevel ~loc
52- ~module Loc:modulePath.loc ~optional Args ~path ~side Effects);
67+ |> addValueDeclaration ~config ~decls ~file ~is Toplevel
68+ ~owns OptionalArgs ~loc ~module Loc:modulePath.loc ~optional Args
69+ ~path ~side Effects);
5370 (match Declarations. find_opt_builder decls loc_start with
5471 | None -> ()
5572 | Some decl ->
@@ -74,8 +91,8 @@ let collectValueBinding ~config ~decls ~file ~(current_binding : Location.t)
7491 in
7592 loc
7693
77- let processOptionalArgs ~config ~cross_file ~ expType ~( locFrom : Location.t )
78- ~(binding : Location.t ) ~locTo ~path args =
94+ let processOptionalArgs ~config ~decls ~ cross_file ~ expType
95+ ~(locFrom : Location.t ) ~( binding : Location.t ) ~locTo ~path args =
7996 if expType |> DeadOptionalArgs. hasOptionalArgs then (
8097 let supplied = ref [] in
8198 let suppliedMaybe = ref [] in
@@ -104,13 +121,36 @@ let processOptionalArgs ~config ~cross_file ~expType ~(locFrom : Location.t)
104121 if argIsSupplied = None then suppliedMaybe := s :: ! suppliedMaybe
105122 | _ -> () );
106123 (! supplied, ! suppliedMaybe)
107- |> DeadOptionalArgs. addReferences ~config ~cross_file ~loc From ~loc To
124+ |> DeadOptionalArgs. addReferences ~config ~decls ~ cross_file ~loc From ~loc To
108125 ~binding ~path )
109126
110- let rec collectExpr ~config ~refs ~file_deps ~cross_file
127+ let rec collectExpr ~config ~decls ~ refs ~file_deps ~cross_file ~ callee_locs
111128 ~(last_binding : Location.t ) super self (e : Typedtree.expression ) =
112129 let locFrom = e.exp_loc in
113130 let binding = last_binding in
131+ let suppressOptionalArgs pos =
132+ match Declarations. find_opt_builder decls pos with
133+ | Some ({declKind = Value ({optionalArgs; _} as value_kind)} as decl)
134+ when not (OptionalArgs. isEmpty optionalArgs) ->
135+ Declarations. replace_builder decls pos
136+ {
137+ decl with
138+ declKind = Value {value_kind with optionalArgs = OptionalArgs. empty};
139+ }
140+ | _ -> ()
141+ in
142+ let rec remove_first target = function
143+ | [] -> []
144+ | x :: xs when x = target -> xs
145+ | x :: xs -> x :: remove_first target xs
146+ in
147+ let callee_loc_opt =
148+ match e.exp_desc with
149+ | Texp_apply {funct = {exp_desc = Texp_ident (_ , _ , _ ); exp_loc} ; _} ->
150+ Some exp_loc
151+ | _ -> None
152+ in
153+ Option. iter (fun loc -> callee_locs := loc :: ! callee_locs) callee_loc_opt;
114154 (match e.exp_desc with
115155 | Texp_ident (_path , _ , {Types. val_loc = {loc_ghost = false ; _} as locTo } ) ->
116156 (* if Path.name _path = "rc" then assert false; *)
@@ -123,9 +163,11 @@ let rec collectExpr ~config ~refs ~file_deps ~cross_file
123163 (locTo.loc_start |> Pos. toString);
124164 References. add_value_ref refs ~pos To:locTo.loc_start
125165 ~pos From:Location. none.loc_start)
126- else
166+ else (
127167 addValueReference ~config ~refs ~file_deps ~binding ~add FileReference:true
128- ~loc From ~loc To
168+ ~loc From ~loc To;
169+ if not (List. mem locFrom ! callee_locs) then
170+ suppressOptionalArgs locTo.loc_start)
129171 | Texp_apply
130172 {
131173 funct =
@@ -138,7 +180,7 @@ let rec collectExpr ~config ~refs ~file_deps ~cross_file
138180 args;
139181 } ->
140182 args
141- |> processOptionalArgs ~config ~cross_file ~exp Type:exp_type
183+ |> processOptionalArgs ~config ~decls ~ cross_file ~exp Type:exp_type
142184 ~loc From:(locFrom : Location.t )
143185 ~binding: last_binding ~loc To ~path
144186 | Texp_let
@@ -179,7 +221,7 @@ let rec collectExpr ~config ~refs ~file_deps ~cross_file
179221 && Ident. name etaArg = " eta"
180222 && Path. name idArg2 = " arg" ->
181223 args
182- |> processOptionalArgs ~config ~cross_file ~exp Type:exp_type
224+ |> processOptionalArgs ~config ~decls ~ cross_file ~exp Type:exp_type
183225 ~loc From:(locFrom : Location.t )
184226 ~binding: last_binding ~loc To ~path
185227 | Texp_field
@@ -206,12 +248,16 @@ let rec collectExpr ~config ~refs ~file_deps ~cross_file
206248 ->
207249 (* Punned field in OCaml projects has ghost location in expression *)
208250 let e = {e with exp_loc = {exp_loc with loc_ghost = false }} in
209- collectExpr ~config ~refs ~file_deps ~cross_file ~last_binding
210- super self e
251+ collectExpr ~config ~decls ~ refs ~file_deps ~cross_file
252+ ~callee_locs ~last_binding super self e
211253 |> ignore
212254 | _ -> () )
213255 | _ -> () );
214- super.Tast_mapper. expr self e
256+ let result = super.Tast_mapper. expr self e in
257+ Option. iter
258+ (fun loc -> callee_locs := remove_first loc ! callee_locs)
259+ callee_loc_opt;
260+ result
215261
216262(*
217263 type k. is a locally abstract type
@@ -279,13 +325,17 @@ let rec processSignatureItem ~config ~decls ~file ~doTypes ~doValues ~moduleLoc
279325 let optionalArgs =
280326 val_type |> DeadOptionalArgs. fromTypeExpr |> OptionalArgs. fromList
281327 in
328+ (* Signature items only expose the function type, so we conservatively
329+ seed ownership from the presence of optional args. The implementation
330+ pass above refines this for aliases that should not own warnings. *)
331+ let ownsOptionalArgs = not (OptionalArgs. isEmpty optionalArgs) in
282332
283333 (* if Ident.name id = "someValue" then
284334 Printf.printf "XXX %s\n" (Ident.name id); *)
285335 Ident. name id
286336 |> Name. create ~is Interface:false
287337 |> addValueDeclaration ~config ~decls ~file ~loc ~module Loc
288- ~optional Args ~path ~side Effects:false
338+ ~owns OptionalArgs ~ optional Args ~path ~side Effects:false
289339 | Sig_module (id, {Types. md_type = moduleType; md_loc = moduleLoc}, _)
290340 | Sig_modtype (id , {Types. mtd_type = Some moduleType ; mtd_loc = moduleLoc } ) ->
291341 let modulePath' =
@@ -309,6 +359,7 @@ let rec processSignatureItem ~config ~decls ~file ~doTypes ~doValues ~moduleLoc
309359(* Traverse the AST *)
310360let traverseStructure ~config ~decls ~refs ~file_deps ~cross_file ~file ~doTypes
311361 ~doExternals (structure : Typedtree.structure ) : unit =
362+ let callee_locs = ref [] in
312363 let rec create_mapper (last_binding : Location.t ) (modulePath : ModulePath.t )
313364 =
314365 let super = Tast_mapper. default in
@@ -317,9 +368,8 @@ let traverseStructure ~config ~decls ~refs ~file_deps ~cross_file ~file ~doTypes
317368 super with
318369 expr =
319370 (fun _self e ->
320- e
321- |> collectExpr ~config ~refs ~file_deps ~cross_file ~last_binding
322- super mapper);
371+ collectExpr ~config ~decls ~refs ~file_deps ~cross_file ~callee_locs
372+ ~last_binding super mapper e);
323373 pat = (fun _self p -> p |> collectPattern ~config ~refs super mapper);
324374 structure_item =
325375 (fun _self (structureItem : Typedtree.structure_item ) ->
0 commit comments