Skip to content
Open
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
5 changes: 3 additions & 2 deletions analysis/reanalyze/DEADCODE_REFACTOR_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,9 @@ Each bullet above should be done as a separate patch touching only a small set o
Goal: remove `DeadCommon.Current` globals for binding/reporting by threading explicit state.

- [x] Add `Current.state`/helpers in `DeadCommon` and thread it through `DeadValue` (bindings) and `DeadException.markAsUsed` so `last_binding` is no longer a global ref.
- [x] Replace `Current.maxValuePosEnd` with a per‑reporting `Current.state` in `Decl.report`/`reportDead`.
- [ ] Follow‑up: remove `Current.state ref` usage by making traversals return an updated state (pure, no mutation). Adjust `addValueReference_state` (or its successor) to be purely functional and always return the new state.
- [x] Replace `Current.maxValuePosEnd` with a per‑reporting state in `Decl.report`/`reportDead` (now encapsulated in `ReportingContext`).
- [x] Replace `addValueReference_state` with `addValueReference ~binding` so reference bookkeeping no longer threads `Current.state` or returns a fake “updated state”.
- [ ] Follow‑up: remove the remaining local `Current.state ref` in `BindingContext` by making traversals return an updated binding context (pure, no mutation). At that point, binding context becomes an explicit input/output of the traversal, not hidden state.

### 4.4 Make `ProcessDeadAnnotations` state explicit

Expand Down
51 changes: 25 additions & 26 deletions analysis/reanalyze/src/DeadCommon.ml
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,10 @@ module Config = struct
end

module Current = struct
type state = {
last_binding: Location.t;
max_value_pos_end: Lexing.position;
}
type state = {last_binding: Location.t; max_value_pos_end: Lexing.position}

let empty_state =
{
last_binding = Location.none;
max_value_pos_end = Lexing.dummy_pos;
}
{last_binding = Location.none; max_value_pos_end = Lexing.dummy_pos}

let get_last_binding (s : state) = s.last_binding

Expand Down Expand Up @@ -83,6 +77,17 @@ module ValueReferences = struct
let find pos = PosHash.findSet table pos
end

(* Local reporting context used only while emitting dead-code warnings.
It tracks, per file, the end position of the last value we reported on,
so nested values inside that range don't get duplicate warnings. *)
module ReportingContext = struct
type t = Lexing.position ref

let create () : t = ref Lexing.dummy_pos
let get_max_end (ctx : t) = !ctx
let set_max_end (ctx : t) (pos : Lexing.position) = ctx := pos
end

module TypeReferences = struct
(** all type references *)
let table = (PosHash.create 256 : PosSet.t PosHash.t)
Expand All @@ -103,14 +108,9 @@ let declGetLoc decl =
in
{Location.loc_start; loc_end = decl.posEnd; loc_ghost = false}

let addValueReference_state ~(current : Current.state) ~addFileReference
let addValueReference ~(binding : Location.t) ~addFileReference
~(locFrom : Location.t) ~(locTo : Location.t) : unit =
let lastBinding = current.last_binding in
let effectiveFrom =
match lastBinding = Location.none with
| true -> locFrom
| false -> lastBinding
in
let effectiveFrom = if binding = Location.none then locFrom else binding in
if not effectiveFrom.loc_ghost then (
if !Cli.debug then
Log_.item "addValueReference %s --> %s@."
Expand All @@ -121,8 +121,7 @@ let addValueReference_state ~(current : Current.state) ~addFileReference
addFileReference && (not locTo.loc_ghost)
&& (not effectiveFrom.loc_ghost)
&& effectiveFrom.loc_start.pos_fname <> locTo.loc_start.pos_fname
then FileReferences.add effectiveFrom locTo);
()
then FileReferences.add effectiveFrom locTo)

let iterFilesFromRootsToLeaves iterFun =
(* For each file, the number of incoming references *)
Expand Down Expand Up @@ -519,20 +518,21 @@ module Decl = struct
(fname1, lnum1, bol1, cnum1, kind1)
(fname2, lnum2, bol2, cnum2, kind2)

let isInsideReportedValue (current_state : Current.state ref) decl =
let max_end = Current.get_max_end !current_state in
let isInsideReportedValue (ctx : ReportingContext.t) decl =
let max_end = ReportingContext.get_max_end ctx in
let fileHasChanged = max_end.pos_fname <> decl.pos.pos_fname in
let insideReportedValue =
decl |> isValue && (not fileHasChanged) && max_end.pos_cnum > decl.pos.pos_cnum
decl |> isValue && (not fileHasChanged)
&& max_end.pos_cnum > decl.pos.pos_cnum
in
if not insideReportedValue then
if decl |> isValue then
if fileHasChanged || decl.posEnd.pos_cnum > max_end.pos_cnum then
current_state := Current.with_max_end decl.posEnd !current_state;
ReportingContext.set_max_end ctx decl.posEnd;
insideReportedValue

let report current_state decl =
let insideReportedValue = decl |> isInsideReportedValue current_state in
let report (ctx : ReportingContext.t) decl =
let insideReportedValue = decl |> isInsideReportedValue ctx in
if decl.report then
let name, message =
match decl.declKind with
Expand Down Expand Up @@ -729,6 +729,5 @@ let reportDead ~checkOptionalArg =
let sortedDeadDeclarations =
!deadDeclarations |> List.fast_sort Decl.compareForReporting
in
(* XXX *)
let current_state = ref Current.empty_state in
sortedDeadDeclarations |> List.iter (Decl.report current_state)
let reporting_ctx = ReportingContext.create () in
sortedDeadDeclarations |> List.iter (Decl.report reporting_ctx)
10 changes: 4 additions & 6 deletions analysis/reanalyze/src/DeadException.ml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ let forceDelayedItems () =
| None -> ()
| Some locTo ->
(* Delayed exception references don't need a binding context; use an empty state. *)
DeadCommon.addValueReference_state
~current:DeadCommon.Current.empty_state ~addFileReference:true
~locFrom ~locTo)
DeadCommon.addValueReference ~binding:Location.none
~addFileReference:true ~locFrom ~locTo)

let markAsUsed ~(current_state : Current.state ref) ~(locFrom : Location.t)
let markAsUsed ~(binding : Location.t) ~(locFrom : Location.t)
~(locTo : Location.t) path_ =
if locTo.loc_ghost then
(* Probably defined in another file, delay processing and check at the end *)
Expand All @@ -35,5 +34,4 @@ let markAsUsed ~(current_state : Current.state ref) ~(locFrom : Location.t)
in
delayedItems := {exceptionPath; locFrom} :: !delayedItems
else
DeadCommon.addValueReference_state ~current:!current_state
~addFileReference:true ~locFrom ~locTo
DeadCommon.addValueReference ~binding ~addFileReference:true ~locFrom ~locTo
Loading