Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions analysis/reanalyze/src/cross_file_items.ml
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,36 @@ type optional_arg_call = {

type function_ref = {pos_from: Lexing.position; pos_to: Lexing.position}

type optional_arg_value_escape = {
pos_from: Lexing.position;
pos_to: Lexing.position;
}

(** {2 Types} *)

type t = {
exception_refs: exception_ref list;
optional_arg_calls: optional_arg_call list;
function_refs: function_ref list;
optional_arg_value_escapes: optional_arg_value_escape list;
}

type builder = {
mutable exception_refs: exception_ref list;
mutable optional_arg_calls: optional_arg_call list;
mutable function_refs: function_ref list;
mutable optional_arg_value_escapes: optional_arg_value_escape list;
}

(** {2 Builder API} *)

let create_builder () : builder =
{exception_refs = []; optional_arg_calls = []; function_refs = []}
{
exception_refs = [];
optional_arg_calls = [];
function_refs = [];
optional_arg_value_escapes = [];
}

let add_exception_ref (b : builder) ~exception_path ~loc_from =
b.exception_refs <- {exception_path; loc_from} :: b.exception_refs
Expand All @@ -46,6 +58,10 @@ let add_optional_arg_call (b : builder) ~pos_from ~pos_to ~arg_names
let add_function_reference (b : builder) ~pos_from ~pos_to =
b.function_refs <- {pos_from; pos_to} :: b.function_refs

let add_optional_arg_value_escape (b : builder) ~pos_from ~pos_to =
b.optional_arg_value_escapes <-
{pos_from; pos_to} :: b.optional_arg_value_escapes

(** {2 Merge API} *)

let merge_all (builders : builder list) : t =
Expand All @@ -56,7 +72,15 @@ let merge_all (builders : builder list) : t =
builders |> List.concat_map (fun b -> b.optional_arg_calls)
in
let function_refs = builders |> List.concat_map (fun b -> b.function_refs) in
{exception_refs; optional_arg_calls; function_refs}
let optional_arg_value_escapes =
builders |> List.concat_map (fun b -> b.optional_arg_value_escapes)
in
{
exception_refs;
optional_arg_calls;
function_refs;
optional_arg_value_escapes;
}

(** {2 Builder extraction for reactive merge} *)

Expand All @@ -65,6 +89,7 @@ let builder_to_t (builder : builder) : t =
exception_refs = builder.exception_refs;
optional_arg_calls = builder.optional_arg_calls;
function_refs = builder.function_refs;
optional_arg_value_escapes = builder.optional_arg_value_escapes;
}

(** {2 Processing API} *)
Expand Down
11 changes: 10 additions & 1 deletion analysis/reanalyze/src/cross_file_items.mli
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ type optional_arg_call = {

type function_ref = {pos_from: Lexing.position; pos_to: Lexing.position}

type optional_arg_value_escape = {
pos_from: Lexing.position;
pos_to: Lexing.position;
}

(** {2 Types} *)

type t = {
exception_refs: exception_ref list;
optional_arg_calls: optional_arg_call list;
function_refs: function_ref list;
optional_arg_value_escapes: optional_arg_value_escape list;
}
(** Immutable cross-file items - for processing after merge *)

Expand All @@ -49,7 +55,10 @@ val add_optional_arg_call :

val add_function_reference :
builder -> pos_from:Lexing.position -> pos_to:Lexing.position -> unit
(** Add a cross-file function reference (for optional args combining). *)

val add_optional_arg_value_escape :
builder -> pos_from:Lexing.position -> pos_to:Lexing.position -> unit
(** Record an optional-arg function used as a first-class value. *)

(** {2 Merge API} *)

Expand Down
16 changes: 16 additions & 0 deletions analysis/reanalyze/src/cross_file_items_store.ml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ let iter_function_refs t f =
(fun _path items -> List.iter f items.Cross_file_items.function_refs)
r

let iter_optional_arg_value_escapes t f =
match t with
| Frozen cfi -> List.iter f cfi.Cross_file_items.optional_arg_value_escapes
| Reactive r ->
Reactive.iter
(fun _path items ->
List.iter f items.Cross_file_items.optional_arg_value_escapes)
r

(** Compute optional args state from calls and function references.
Returns a map from position to final OptionalArgs.t state.
Pure function - does not mutate declarations. *)
Expand Down Expand Up @@ -65,3 +74,10 @@ let compute_optional_args_state (store : t) ~find_decl ~is_live :
set_state pos_from updated_from;
set_state pos_to updated_to));
state

let compute_live_optional_arg_value_escapes (store : t) ~is_live : Pos_set.t =
let escapes = ref Pos_set.empty in
iter_optional_arg_value_escapes store
(fun {Cross_file_items.pos_from; pos_to} ->
if is_live pos_from then escapes := Pos_set.add pos_to !escapes);
!escapes
8 changes: 8 additions & 0 deletions analysis/reanalyze/src/cross_file_items_store.mli
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@ val iter_optional_arg_calls :
val iter_function_refs : t -> (Cross_file_items.function_ref -> unit) -> unit
(** Iterate over all function refs *)

val iter_optional_arg_value_escapes :
t -> (Cross_file_items.optional_arg_value_escape -> unit) -> unit
(** Iterate over optional-arg functions used as first-class values *)

val compute_optional_args_state :
t ->
find_decl:(Lexing.position -> Decl.t option) ->
is_live:(Lexing.position -> bool) ->
Optional_args_state.t
(** Compute optional args state from calls and function references *)

val compute_live_optional_arg_value_escapes :
t -> is_live:(Lexing.position -> bool) -> Pos_set.t
(** Compute optional-arg declarations with live first-class value escapes *)
8 changes: 5 additions & 3 deletions analysis/reanalyze/src/dead_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,13 @@ let addDeclaration_ ~config ~decls ~(file : File_context.t) ?pos_end ?pos_start
Declarations.add decls pos decl)

let add_value_declaration ~config ~decls ~file ?(is_toplevel = true)
~(loc : Location.t) ~module_loc ?(optional_args = Optional_args.empty) ~path
~side_effects name =
?(reports_optional_args = false) ~(loc : Location.t) ~module_loc
?(optional_args = Optional_args.empty) ~path ~side_effects name =
name
|> addDeclaration_ ~config ~decls ~file
~decl_kind:(Value {is_toplevel; optional_args; side_effects})
~decl_kind:
(Value
{is_toplevel; reports_optional_args; optional_args; side_effects})
~loc ~module_loc ~path

(** Create a dead code issue. Pure - no side effects. *)
Expand Down
31 changes: 26 additions & 5 deletions analysis/reanalyze/src/dead_optional_args.ml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ let add_function_reference ~config ~decls ~cross_file ~(loc_from : Location.t)
if active () then
let pos_to = loc_to.loc_start in
let pos_from = loc_from.loc_start in
(* Check if target has optional args - for filtering and debug logging *)
let should_add =
match Declarations.find_opt_builder decls pos_to with
let has_optional_arg_state pos =
match Declarations.find_opt_builder decls pos with
| Some {decl_kind = Value {optional_args}} ->
not (Optional_args.is_empty optional_args)
| _ -> false
in
let should_add =
if
pos_to.pos_fname <> pos_from.pos_fname
&& (file_is_implementation_of pos_to.pos_fname pos_from.pos_fname
|| file_is_implementation_of pos_from.pos_fname pos_to.pos_fname)
then has_optional_arg_state pos_to
else has_optional_arg_state pos_from && has_optional_arg_state pos_to
in
if should_add then (
if config.Dce_config.cli.debug then
Log_.item "OptionalArgs.addFunctionReference %s %s@."
Expand All @@ -39,6 +46,18 @@ let rec from_type_expr (texpr : Types.type_expr) =
| Tsubst t -> from_type_expr t
| _ -> []

let rec from_type_expr_with_arity (texpr : Types.type_expr) arity =
if arity <= 0 then []
else
match texpr.desc with
| _ when not (active ()) -> []
| Tarrow ({lbl = Optional {txt = s}}, t_to, _, _) ->
s :: from_type_expr_with_arity t_to (arity - 1)
| Tarrow (_, t_to, _, _) -> from_type_expr_with_arity t_to (arity - 1)
| Tlink t -> from_type_expr_with_arity t arity
| Tsubst t -> from_type_expr_with_arity t arity
| _ -> []

let add_references ~config ~cross_file ~(loc_from : Location.t)
~(loc_to : Location.t) ~(binding : Location.t) ~path
(arg_names, arg_names_maybe) =
Expand All @@ -59,10 +78,12 @@ let add_references ~config ~cross_file ~(loc_from : Location.t)

(** Check for optional args issues. Returns issues instead of logging.
Uses optional_args_state map for final computed state. *)
let check ~optional_args_state ~ann_store ~config:_ decl : Issue.t list =
let check ~optional_args_state ~optional_arg_value_escapes ~ann_store ~config:_
decl : Issue.t list =
match decl with
| {Decl.decl_kind = Value {optional_args}}
| {Decl.decl_kind = Value {reports_optional_args = true; optional_args}}
when active ()
&& (not (Pos_set.mem decl.pos optional_arg_value_escapes))
&& not
(Annotation_store.is_annotated_gentype_or_live ann_store decl.pos)
->
Expand Down
Loading
Loading