Skip to content

Commit 430988d

Browse files
committed
Simplify binding state tracking in DeadValue
- Remove BindingContext module wrapper (was just forwarding to Current) - Remove Current module entirely (unnecessary abstraction) - Simplify to pass Location.t directly instead of record type - Remove unused max_value_pos_end field - Refactor traverseStructure to use pure functional mapper creation - Update DEADCODE_REFACTOR_PLAN.md to mark task 4.3 as complete This eliminates ~40 lines of wrapper code and makes the binding state tracking pure and simpler to understand.
1 parent ae08f4b commit 430988d

File tree

3 files changed

+98
-124
lines changed

3 files changed

+98
-124
lines changed

analysis/reanalyze/DEADCODE_REFACTOR_PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ Goal: remove `DeadCommon.Current` globals for binding/reporting by threading exp
202202
- [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.
203203
- [x] Replace `Current.maxValuePosEnd` with a per‑reporting state in `Decl.report`/`reportDead` (now encapsulated in `ReportingContext`).
204204
- [x] Replace `addValueReference_state` with `addValueReference ~binding` so reference bookkeeping no longer threads `Current.state` or returns a fake “updated state”.
205-
- [ ] 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.
205+
- [x] 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.
206206

207207
### 4.4 Make `ProcessDeadAnnotations` state explicit
208208

analysis/reanalyze/src/DeadCommon.ml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,6 @@ module Config = struct
1818
let warnOnCircularDependencies = false
1919
end
2020

21-
module Current = struct
22-
type state = {last_binding: Location.t; max_value_pos_end: Lexing.position}
23-
24-
let empty_state =
25-
{last_binding = Location.none; max_value_pos_end = Lexing.dummy_pos}
26-
27-
let get_last_binding (s : state) = s.last_binding
28-
29-
let with_last_binding (loc : Location.t) (s : state) : state =
30-
{s with last_binding = loc}
31-
32-
let get_max_end (s : state) = s.max_value_pos_end
33-
34-
let with_max_end (pos : Lexing.position) (s : state) : state =
35-
{s with max_value_pos_end = pos}
36-
end
3721

3822
let rec checkSub s1 s2 n =
3923
n <= 0

analysis/reanalyze/src/DeadValue.ml

Lines changed: 97 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,6 @@
22

33
open DeadCommon
44

5-
module BindingContext = struct
6-
(* Local, encapsulated mutable state for tracking the current binding location
7-
during traversal. This ref does not escape the module. *)
8-
type t = Current.state ref
9-
10-
let create () : t = ref Current.empty_state
11-
12-
let get_binding (ctx : t) : Location.t = !ctx |> Current.get_last_binding
13-
14-
let with_binding (ctx : t) (loc : Location.t) (f : unit -> 'a) : 'a =
15-
let old_state = !ctx in
16-
ctx := Current.with_last_binding loc old_state;
17-
let result = f () in
18-
ctx := old_state;
19-
result
20-
end
21-
225
let checkAnyValueBindingWithNoSideEffects
236
({vb_pat = {pat_desc}; vb_expr = expr; vb_loc = loc} :
247
Typedtree.value_binding) =
@@ -123,10 +106,10 @@ let processOptionalArgs ~expType ~(locFrom : Location.t) ~locTo ~path args =
123106
(!supplied, !suppliedMaybe)
124107
|> DeadOptionalArgs.addReferences ~locFrom ~locTo ~path)
125108

126-
let rec collectExpr ~(binding_ctx : BindingContext.t) super self
109+
let rec collectExpr ~(last_binding : Location.t) super self
127110
(e : Typedtree.expression) =
128111
let locFrom = e.exp_loc in
129-
let binding = BindingContext.get_binding binding_ctx in
112+
let binding = last_binding in
130113
(match e.exp_desc with
131114
| Texp_ident (_path, _, {Types.val_loc = {loc_ghost = false; _} as locTo}) ->
132115
(* if Path.name _path = "rc" then assert false; *)
@@ -219,7 +202,7 @@ let rec collectExpr ~(binding_ctx : BindingContext.t) super self
219202
->
220203
(* Punned field in OCaml projects has ghost location in expression *)
221204
let e = {e with exp_loc = {exp_loc with loc_ghost = false}} in
222-
collectExpr ~binding_ctx super self e |> ignore
205+
collectExpr ~last_binding super self e |> ignore
223206
| _ -> ())
224207
| _ -> ());
225208
super.Tast_mapper.expr self e
@@ -301,93 +284,101 @@ let rec processSignatureItem ~doTypes ~doValues ~moduleLoc ~path
301284
ModulePath.setCurrent oldModulePath
302285

303286
(* Traverse the AST *)
304-
let traverseStructure ~doTypes ~doExternals =
305-
let binding_ctx = BindingContext.create () in
306-
let customize super =
307-
let expr self e = e |> collectExpr ~binding_ctx super self in
308-
let value_binding self vb =
309-
let current_binding = BindingContext.get_binding binding_ctx in
310-
let loc = vb |> collectValueBinding ~current_binding in
311-
BindingContext.with_binding binding_ctx loc (fun () ->
312-
super.Tast_mapper.value_binding self vb)
313-
in
314-
let pat self p = p |> collectPattern super self in
315-
let structure_item self (structureItem : Typedtree.structure_item) =
316-
let oldModulePath = ModulePath.getCurrent () in
317-
(match structureItem.str_desc with
318-
| Tstr_module {mb_expr; mb_id; mb_loc} -> (
319-
let hasInterface =
320-
match mb_expr.mod_desc with
321-
| Tmod_constraint _ -> true
322-
| _ -> false
323-
in
324-
ModulePath.setCurrent
325-
{
326-
oldModulePath with
327-
loc = mb_loc;
328-
path = (mb_id |> Ident.name |> Name.create) :: oldModulePath.path;
329-
};
330-
if hasInterface then
331-
match mb_expr.mod_type with
332-
| Mty_signature signature ->
333-
signature
334-
|> List.iter
335-
(processSignatureItem ~doTypes ~doValues:false
336-
~moduleLoc:mb_expr.mod_loc
337-
~path:
338-
((ModulePath.getCurrent ()).path
339-
@ [!Common.currentModuleName]))
340-
| _ -> ())
341-
| Tstr_primitive vd when doExternals && !Config.analyzeExternals ->
342-
let currentModulePath = ModulePath.getCurrent () in
343-
let path = currentModulePath.path @ [!Common.currentModuleName] in
344-
let exists =
345-
match PosHash.find_opt decls vd.val_loc.loc_start with
346-
| Some {declKind = Value _} -> true
347-
| _ -> false
348-
in
349-
let id = vd.val_id |> Ident.name in
350-
Printf.printf "Primitive %s\n" id;
351-
if
352-
(not exists) && id <> "unsafe_expr"
353-
(* see https://github.com/BuckleScript/bucklescript/issues/4532 *)
354-
then
355-
id
356-
|> Name.create ~isInterface:false
357-
|> addValueDeclaration ~path ~loc:vd.val_loc
358-
~moduleLoc:currentModulePath.loc ~sideEffects:false
359-
| Tstr_type (_recFlag, typeDeclarations) when doTypes ->
360-
if !Config.analyzeTypes then
361-
typeDeclarations
362-
|> List.iter (fun (typeDeclaration : Typedtree.type_declaration) ->
363-
DeadType.addDeclaration ~typeId:typeDeclaration.typ_id
364-
~typeKind:typeDeclaration.typ_type.type_kind)
365-
| Tstr_include {incl_mod; incl_type} -> (
366-
match incl_mod.mod_desc with
367-
| Tmod_ident (_path, _lid) ->
368-
let currentPath =
369-
(ModulePath.getCurrent ()).path @ [!Common.currentModuleName]
370-
in
371-
incl_type
372-
|> List.iter
373-
(processSignatureItem ~doTypes
374-
~doValues:false (* TODO: also values? *)
375-
~moduleLoc:incl_mod.mod_loc ~path:currentPath)
376-
| _ -> ())
377-
| Tstr_exception {ext_id = id; ext_loc = loc} ->
378-
let path =
379-
(ModulePath.getCurrent ()).path @ [!Common.currentModuleName]
380-
in
381-
let name = id |> Ident.name |> Name.create in
382-
name |> DeadException.add ~path ~loc ~strLoc:structureItem.str_loc
383-
| _ -> ());
384-
let result = super.structure_item self structureItem in
385-
ModulePath.setCurrent oldModulePath;
386-
result
287+
let traverseStructure ~doTypes ~doExternals (structure : Typedtree.structure) :
288+
unit =
289+
let rec create_mapper (last_binding : Location.t) =
290+
let super = Tast_mapper.default in
291+
let rec mapper =
292+
{
293+
super with
294+
expr = (fun _self e -> e |> collectExpr ~last_binding super mapper);
295+
pat = (fun _self p -> p |> collectPattern super mapper);
296+
structure_item =
297+
(fun _self (structureItem : Typedtree.structure_item) ->
298+
let oldModulePath = ModulePath.getCurrent () in
299+
(match structureItem.str_desc with
300+
| Tstr_module {mb_expr; mb_id; mb_loc} -> (
301+
let hasInterface =
302+
match mb_expr.mod_desc with
303+
| Tmod_constraint _ -> true
304+
| _ -> false
305+
in
306+
ModulePath.setCurrent
307+
{
308+
oldModulePath with
309+
loc = mb_loc;
310+
path =
311+
(mb_id |> Ident.name |> Name.create) :: oldModulePath.path;
312+
};
313+
if hasInterface then
314+
match mb_expr.mod_type with
315+
| Mty_signature signature ->
316+
signature
317+
|> List.iter
318+
(processSignatureItem ~doTypes ~doValues:false
319+
~moduleLoc:mb_expr.mod_loc
320+
~path:
321+
((ModulePath.getCurrent ()).path
322+
@ [!Common.currentModuleName]))
323+
| _ -> ())
324+
| Tstr_primitive vd when doExternals && !Config.analyzeExternals ->
325+
let currentModulePath = ModulePath.getCurrent () in
326+
let path = currentModulePath.path @ [!Common.currentModuleName] in
327+
let exists =
328+
match PosHash.find_opt decls vd.val_loc.loc_start with
329+
| Some {declKind = Value _} -> true
330+
| _ -> false
331+
in
332+
let id = vd.val_id |> Ident.name in
333+
Printf.printf "Primitive %s\n" id;
334+
if
335+
(not exists) && id <> "unsafe_expr"
336+
(* see https://github.com/BuckleScript/bucklescript/issues/4532 *)
337+
then
338+
id
339+
|> Name.create ~isInterface:false
340+
|> addValueDeclaration ~path ~loc:vd.val_loc
341+
~moduleLoc:currentModulePath.loc ~sideEffects:false
342+
| Tstr_type (_recFlag, typeDeclarations) when doTypes ->
343+
if !Config.analyzeTypes then
344+
typeDeclarations
345+
|> List.iter
346+
(fun (typeDeclaration : Typedtree.type_declaration) ->
347+
DeadType.addDeclaration ~typeId:typeDeclaration.typ_id
348+
~typeKind:typeDeclaration.typ_type.type_kind)
349+
| Tstr_include {incl_mod; incl_type} -> (
350+
match incl_mod.mod_desc with
351+
| Tmod_ident (_path, _lid) ->
352+
let currentPath =
353+
(ModulePath.getCurrent ()).path @ [!Common.currentModuleName]
354+
in
355+
incl_type
356+
|> List.iter
357+
(processSignatureItem ~doTypes
358+
~doValues:false (* TODO: also values? *)
359+
~moduleLoc:incl_mod.mod_loc ~path:currentPath)
360+
| _ -> ())
361+
| Tstr_exception {ext_id = id; ext_loc = loc} ->
362+
let path =
363+
(ModulePath.getCurrent ()).path @ [!Common.currentModuleName]
364+
in
365+
let name = id |> Ident.name |> Name.create in
366+
name |> DeadException.add ~path ~loc ~strLoc:structureItem.str_loc
367+
| _ -> ());
368+
let result = super.structure_item mapper structureItem in
369+
ModulePath.setCurrent oldModulePath;
370+
result);
371+
value_binding =
372+
(fun _self vb ->
373+
let loc = vb |> collectValueBinding ~current_binding:last_binding in
374+
let nested_mapper = create_mapper loc in
375+
super.Tast_mapper.value_binding nested_mapper vb);
376+
}
387377
in
388-
{super with expr; pat; structure_item; value_binding}
378+
mapper
389379
in
390-
customize Tast_mapper.default
380+
let mapper = create_mapper Location.none in
381+
mapper.structure mapper structure |> ignore
391382

392383
(* Merge a location's references to another one's *)
393384
let processValueDependency
@@ -411,7 +402,6 @@ let processValueDependency
411402

412403
let processStructure ~cmt_value_dependencies ~doTypes ~doExternals
413404
(structure : Typedtree.structure) =
414-
let traverseStructure = traverseStructure ~doTypes ~doExternals in
415-
structure |> traverseStructure.structure traverseStructure |> ignore;
405+
traverseStructure ~doTypes ~doExternals structure;
416406
let valueDependencies = cmt_value_dependencies |> List.rev in
417407
valueDependencies |> List.iter processValueDependency

0 commit comments

Comments
 (0)