Skip to content

Commit b98b29a

Browse files
committed
Make dead module issues reactive
Add modules_with_reported = flatMap of issues_by_file to collect modules with reported values. Add dead_module_issues = reactive join of dead_modules + modules_with_reported. Now collect_issues only iterates pre-computed reactive collections: - incorrect_dead_decls (few) - issues_by_file (pre-computed per-file issues) - dead_module_issues (pre-computed module issues) Timing improvement (benchmark, cache hit): - iter_modules: 0.06-0.07ms (was ~5-6ms when iterating all dead_modules) - dead_code total: 0.66-1.3ms (was ~7-10ms) All 380/19000 issues still match between reactive and non-reactive modes.
1 parent fbc5bbc commit b98b29a

File tree

1 file changed

+39
-33
lines changed

1 file changed

+39
-33
lines changed

analysis/reanalyze/src/ReactiveSolver.ml

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(** Reactive dead code solver.
22
33
Reactive pipeline: decls + live + annotations → dead_decls, live_decls, dead_modules,
4-
dead_decls_by_file, issues_by_file, incorrect_dead_decls
4+
dead_decls_by_file, issues_by_file, incorrect_dead_decls, dead_module_issues
55
66
Current status:
77
- All collections are reactive (zero recomputation on cache hit for unchanged files)
@@ -11,11 +11,11 @@
1111
- value_refs_from_by_file = refs grouped by source file (reactive flatMap with merge)
1212
- issues_by_file = per-file issue generation (reactive flatMap)
1313
- incorrect_dead_decls = live decls with @dead annotation (reactive join)
14+
- dead_module_issues = dead_modules joined with modules_with_reported (reactive join)
1415
- is_pos_live uses reactive live collection (no resolvedDead mutation)
1516
- shouldReport callback replaces report field mutation (no mutation needed)
1617
- isInsideReportedValue is per-file only, so files are independent
1718
- hasRefBelow uses per-file refs: O(file_refs) per dead decl (was O(total_refs))
18-
- Module issues generated in collect_issues from dead_modules + modules_with_reported_values
1919
2020
All issues now match between reactive and non-reactive modes (380 on deadcode test):
2121
- Dead code issues: 362 (Exception:2, Module:31, Type:87, Value:233, ValueWithSideEffects:8)
@@ -39,6 +39,8 @@ type t = {
3939
Second component: modules with at least one reported value (for module issue generation). *)
4040
incorrect_dead_decls: (Lexing.position, Decl.t) Reactive.t;
4141
(** Live declarations with @dead annotation. Reactive join of live_decls + annotations. *)
42+
dead_module_issues: (Name.t, Issue.t) Reactive.t;
43+
(** Dead module issues. Reactive join of dead_modules + modules_with_reported. *)
4244
config: DceConfig.t;
4345
}
4446

@@ -193,6 +195,35 @@ let create ~(decls : (Lexing.position, Decl.t) Reactive.t)
193195
()
194196
in
195197

198+
(* Reactive modules_with_reported: modules that have at least one reported dead value *)
199+
let modules_with_reported =
200+
Reactive.flatMap issues_by_file
201+
~f:(fun _file (_issues, modules_list) ->
202+
List.map (fun m -> (m, ())) modules_list)
203+
()
204+
in
205+
206+
(* Reactive dead module issues: dead_modules joined with modules_with_reported *)
207+
let dead_module_issues =
208+
Reactive.join dead_modules modules_with_reported
209+
~key_of:(fun moduleName _loc -> moduleName)
210+
~f:(fun moduleName loc has_reported_opt ->
211+
match has_reported_opt with
212+
| Some () ->
213+
let loc =
214+
if loc.Location.loc_ghost then
215+
let pos_fname = loc.loc_start.pos_fname in
216+
let pos =
217+
{Lexing.pos_fname; pos_lnum = 0; pos_bol = 0; pos_cnum = 0}
218+
in
219+
{Location.loc_start = pos; loc_end = pos; loc_ghost = false}
220+
else loc
221+
in
222+
[(moduleName, AnalysisResult.make_dead_module_issue ~loc ~moduleName)]
223+
| None -> [])
224+
()
225+
in
226+
196227
{
197228
decls;
198229
live;
@@ -204,6 +235,7 @@ let create ~(decls : (Lexing.position, Decl.t) Reactive.t)
204235
dead_decls_by_file;
205236
issues_by_file;
206237
incorrect_dead_decls;
238+
dead_module_issues;
207239
config;
208240
}
209241

@@ -260,44 +292,18 @@ let collect_issues ~(t : t) ~(config : DceConfig.t)
260292
(* Collect issues from reactive issues_by_file *)
261293
let num_files = ref 0 in
262294
let dead_issues = ref [] in
263-
(* Track modules that have at least one reported value (for module issue generation) *)
264-
let modules_with_reported_values : (Name.t, unit) Hashtbl.t =
265-
Hashtbl.create 64
266-
in
267295
Reactive.iter
268-
(fun _file (file_issues, modules_list) ->
296+
(fun _file (file_issues, _modules_list) ->
269297
incr num_files;
270-
dead_issues := file_issues @ !dead_issues;
271-
(* Collect modules that have reported values *)
272-
List.iter
273-
(fun moduleName ->
274-
Hashtbl.replace modules_with_reported_values moduleName ())
275-
modules_list)
298+
dead_issues := file_issues @ !dead_issues)
276299
t.issues_by_file;
277300
let t2 = Unix.gettimeofday () in
278301

279-
(* Generate module issues: only for modules that are dead AND have a reported value *)
302+
(* Collect module issues from reactive dead_module_issues *)
280303
let module_issues = ref [] in
281-
let reported_modules : (Name.t, unit) Hashtbl.t = Hashtbl.create 64 in
282304
Reactive.iter
283-
(fun moduleName loc ->
284-
(* Only report if module has at least one reported dead value *)
285-
if Hashtbl.mem modules_with_reported_values moduleName then
286-
if not (Hashtbl.mem reported_modules moduleName) then (
287-
Hashtbl.replace reported_modules moduleName ();
288-
let loc =
289-
if loc.Location.loc_ghost then
290-
let pos_fname = loc.loc_start.pos_fname in
291-
let pos =
292-
{Lexing.pos_fname; pos_lnum = 0; pos_bol = 0; pos_cnum = 0}
293-
in
294-
{Location.loc_start = pos; loc_end = pos; loc_ghost = false}
295-
else loc
296-
in
297-
module_issues :=
298-
AnalysisResult.make_dead_module_issue ~loc ~moduleName
299-
:: !module_issues))
300-
t.dead_modules;
305+
(fun _moduleName issue -> module_issues := issue :: !module_issues)
306+
t.dead_module_issues;
301307
let t3 = Unix.gettimeofday () in
302308

303309
if !Cli.timing then

0 commit comments

Comments
 (0)