22
33open DeadCommon
44
5- module TypeLabels = struct
6- (* map from type path (for record/variant label) to its location *)
7-
8- let table = (Hashtbl. create 256 : (DcePath.t, Location.t ) Hashtbl. t)
9- let add path loc = Hashtbl. replace table path loc
10- let find path = Hashtbl. find_opt table path
11- end
12-
135let addTypeReference ~config ~refs ~posFrom ~posTo =
146 if config.DceConfig. cli.debug then
157 Log_. item " addTypeReference %s --> %s@." (posFrom |> Pos. toString)
@@ -26,49 +18,7 @@ let extendTypeDependencies ~config ~refs (loc1 : Location.t) (loc2 : Location.t)
2618 (posFrom |> Pos. toString);
2719 addTypeReference ~config ~refs ~pos From ~pos To)
2820
29- (* Type dependencies between Foo.re and Foo.rei *)
30- let addTypeDependenciesAcrossFiles ~config ~refs ~file ~pathToType ~loc
31- ~typeLabelName =
32- let isInterface = file.FileContext. is_interface in
33- if not isInterface then (
34- let path_1 = pathToType |> DcePath. moduleToInterface in
35- let path_2 = path_1 |> DcePath. typeToInterface in
36- let path1 = typeLabelName :: path_1 in
37- let path2 = typeLabelName :: path_2 in
38- match TypeLabels. find path1 with
39- | None -> (
40- match TypeLabels. find path2 with
41- | None -> ()
42- | Some loc2 ->
43- extendTypeDependencies ~config ~refs loc loc2;
44- if not Config. reportTypesDeadOnlyInInterface then
45- extendTypeDependencies ~config ~refs loc2 loc)
46- | Some loc1 ->
47- extendTypeDependencies ~config ~refs loc loc1;
48- if not Config. reportTypesDeadOnlyInInterface then
49- extendTypeDependencies ~config ~refs loc1 loc)
50- else
51- let path_1 = pathToType |> DcePath. moduleToImplementation in
52- let path1 = typeLabelName :: path_1 in
53- match TypeLabels. find path1 with
54- | None -> ()
55- | Some loc1 ->
56- extendTypeDependencies ~config ~refs loc1 loc;
57- if not Config. reportTypesDeadOnlyInInterface then
58- extendTypeDependencies ~config ~refs loc loc1
59-
60- (* Add type dependencies between implementation and interface in inner module *)
61- let addTypeDependenciesInnerModule ~config ~refs ~pathToType ~loc ~typeLabelName
62- =
63- let path = typeLabelName :: pathToType in
64- match TypeLabels. find path with
65- | Some loc2 ->
66- extendTypeDependencies ~config ~refs loc loc2;
67- if not Config. reportTypesDeadOnlyInInterface then
68- extendTypeDependencies ~config ~refs loc2 loc
69- | None -> TypeLabels. add path loc
70-
71- let addDeclaration ~config ~decls ~refs ~file ~(modulePath : ModulePath.t )
21+ let addDeclaration ~config ~decls ~file ~(modulePath : ModulePath.t )
7222 ~(typeId : Ident.t ) ~(typeKind : Types.type_kind ) =
7323 let pathToType =
7424 (typeId |> Ident. name |> Name. create)
@@ -77,11 +27,7 @@ let addDeclaration ~config ~decls ~refs ~file ~(modulePath : ModulePath.t)
7727 let processTypeLabel ?(posAdjustment = Decl. Nothing ) typeLabelName ~declKind
7828 ~(loc : Location.t ) =
7929 addDeclaration_ ~config ~decls ~file ~decl Kind ~path: pathToType ~loc
80- ~module Loc:modulePath.loc ~pos Adjustment typeLabelName;
81- addTypeDependenciesAcrossFiles ~config ~refs ~file ~path ToType ~loc
82- ~type LabelName;
83- addTypeDependenciesInnerModule ~config ~refs ~path ToType ~loc ~type LabelName;
84- TypeLabels. add (typeLabelName :: pathToType) loc
30+ ~module Loc:modulePath.loc ~pos Adjustment typeLabelName
8531 in
8632 match typeKind with
8733 | Type_record (l , _ ) ->
@@ -118,3 +64,102 @@ let addDeclaration ~config ~decls ~refs ~file ~(modulePath : ModulePath.t)
11864 |> processTypeLabel ~decl Kind:VariantCase ~loc: cd_loc ~pos Adjustment)
11965 decls
12066 | _ -> ()
67+
68+ module PathMap = Map. Make (struct
69+ type t = DcePath .t
70+
71+ let compare = Stdlib. compare
72+ end )
73+
74+ let process_type_label_dependencies ~config ~decls ~refs =
75+ (* Use raw declaration positions, not [declGetLoc], because references are keyed
76+ by raw positions (decl.pos). [declGetLoc] applies [posAdjustment] (e.g. +2
77+ for OtherVariant), which is intended for reporting locations, not for
78+ reference graph keys. *)
79+ let decl_raw_loc (decl : Decl.t ) : Location.t =
80+ {Location. loc_start = decl.pos; loc_end = decl.posEnd; loc_ghost = false }
81+ in
82+ (* Build an index from full label path -> list of locations *)
83+ let index =
84+ Declarations. fold
85+ (fun _pos decl acc ->
86+ match decl.Decl. declKind with
87+ | RecordLabel | VariantCase ->
88+ let loc = decl |> decl_raw_loc in
89+ let path = decl.path in
90+ let existing =
91+ PathMap. find_opt path acc |> Option. value ~default: []
92+ in
93+ PathMap. add path (loc :: existing) acc
94+ | _ -> acc)
95+ decls PathMap. empty
96+ in
97+ (* Inner-module duplicates: if the same full path appears multiple times (e.g. from signature+structure),
98+ connect them together. *)
99+ index
100+ |> PathMap. iter (fun _key locs ->
101+ match locs with
102+ | [] | [_] -> ()
103+ | loc0 :: rest ->
104+ rest
105+ |> List. iter (fun loc ->
106+ extendTypeDependencies ~config ~refs loc loc0;
107+ if not Config. reportTypesDeadOnlyInInterface then
108+ extendTypeDependencies ~config ~refs loc0 loc));
109+
110+ (* Cross-file impl<->intf linking, modeled after the previous lookup logic. *)
111+ let hd_opt = function
112+ | [] -> None
113+ | x :: _ -> Some x
114+ in
115+ let find_one path =
116+ match PathMap. find_opt path index with
117+ | None -> None
118+ | Some locs -> hd_opt locs
119+ in
120+
121+ let is_interface_of_pathToType (pathToType : DcePath.t ) =
122+ match List. rev pathToType with
123+ | moduleNameTag :: _ -> (
124+ try (moduleNameTag |> Name. toString).[0 ] <> '+'
125+ with Invalid_argument _ -> true )
126+ | [] -> true
127+ in
128+
129+ Declarations. iter
130+ (fun _pos decl ->
131+ match decl.Decl. declKind with
132+ | RecordLabel | VariantCase -> (
133+ match decl.path with
134+ | [] -> ()
135+ | typeLabelName :: pathToType -> (
136+ let loc = decl |> decl_raw_loc in
137+ let isInterface = is_interface_of_pathToType pathToType in
138+ if not isInterface then
139+ let path_1 = pathToType |> DcePath. moduleToInterface in
140+ let path_2 = path_1 |> DcePath. typeToInterface in
141+ let path1 = typeLabelName :: path_1 in
142+ let path2 = typeLabelName :: path_2 in
143+ match find_one path1 with
144+ | Some loc1 ->
145+ extendTypeDependencies ~config ~refs loc loc1;
146+ if not Config. reportTypesDeadOnlyInInterface then
147+ extendTypeDependencies ~config ~refs loc1 loc
148+ | None -> (
149+ match find_one path2 with
150+ | Some loc2 ->
151+ extendTypeDependencies ~config ~refs loc loc2;
152+ if not Config. reportTypesDeadOnlyInInterface then
153+ extendTypeDependencies ~config ~refs loc2 loc
154+ | None -> () )
155+ else
156+ let path_1 = pathToType |> DcePath. moduleToImplementation in
157+ let path1 = typeLabelName :: path_1 in
158+ match find_one path1 with
159+ | None -> ()
160+ | Some loc1 ->
161+ extendTypeDependencies ~config ~refs loc1 loc;
162+ if not Config. reportTypesDeadOnlyInInterface then
163+ extendTypeDependencies ~config ~refs loc loc1))
164+ | _ -> () )
165+ decls
0 commit comments