Skip to content

Commit 7a15e62

Browse files
committed
[src][state][file_infos] update to more precise fields
Only keep useful information from the cm*_infos rather than the whole structure. This makes clearer what is actually used from those and reduces the error surface.
1 parent b87daec commit 7a15e62

File tree

5 files changed

+126
-114
lines changed

5 files changed

+126
-114
lines changed

src/deadCode.ml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -448,33 +448,33 @@ let rec load_file state fn =
448448
last_loc := Lexing.dummy_pos;
449449
if !DeadFlag.verbose then Printf.eprintf "Scanning interface from %s\n%!" fn;
450450
init_and_continue state fn (fun state ->
451-
match state.file_infos.cmi_infos with
451+
match state.file_infos.cmi_sign with
452452
| None -> bad_files := fn :: !bad_files
453-
| Some {cmi_sign; _} ->
453+
| Some cmi_sign ->
454454
read_interface fn cmi_sign state
455455
)
456456
in
457457
let process_implementation fn =
458458
last_loc := Lexing.dummy_pos;
459459
if !DeadFlag.verbose then Printf.eprintf "Scanning implementation from %s\n%!" fn;
460460
init_and_continue state fn (fun state ->
461-
match state.file_infos.cmt_infos with
461+
match state.file_infos.cmt_struct with
462462
| None -> bad_files := fn :: !bad_files
463-
| Some {cmt_annots = Implementation x; _} ->
463+
| Some structure ->
464464
regabs state;
465465
let prepare (loc1, loc2) =
466466
DeadObj.add_equal loc1 loc2;
467467
VdNode.merge_locs ~force:true loc2 loc1
468468
in
469469
List.iter prepare state.file_infos.location_dependencies;
470-
ignore (collect_references.Tast_mapper.structure collect_references x);
470+
collect_references.Tast_mapper.structure collect_references structure
471+
|> ignore;
471472
let loc_dep =
472473
if !DeadFlag.exported.DeadFlag.print then
473474
state.file_infos.location_dependencies
474475
else []
475476
in
476477
eof loc_dep
477-
| _ -> () (* todo: support partial_implementation? *)
478478
)
479479
in
480480
match kind fn with

src/state/file_infos.ml

Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
type t = {
22
builddir : string;
33
cm_file : string;
4-
cmi_infos : Cmi_format.cmi_infos option;
5-
cmt_infos : Cmt_format.cmt_infos option;
6-
cmti_infos : Cmt_format.cmt_infos option;
4+
cmi_sign : Types.signature option;
5+
cmt_struct : Typedtree.structure option;
6+
cmti_uid_to_decl : Location_dependencies.uid_to_decl option;
77
location_dependencies : Location_dependencies.t;
88
modname : string;
99
sourcepath : string option;
@@ -12,119 +12,125 @@ type t = {
1212
let empty = {
1313
builddir = "!!UNKNOWN_BUILDDIR!!";
1414
cm_file = "";
15-
cmi_infos = None;
16-
cmt_infos = None;
17-
cmti_infos = None;
15+
cmi_sign = None;
16+
cmt_struct = None;
17+
cmti_uid_to_decl = None;
1818
location_dependencies = Location_dependencies.empty;
1919
modname = "!!UNKNOWN_MODNAME!!";
2020
sourcepath = None;
2121
}
2222

23-
(** [init_from_all_cm_infos ~orig ~cm_file cmi_infos cmt_infos] creates a [t] with:
24-
- information from [cmt_infos];
23+
(** [init_from_all_cm_infos ~cm_file ~cmi_infos cmt_infos] creates a [t] with:
24+
- information from [cmt_infos] : [builddir], [modname], [sourcepath];
2525
- [cm_file];
26-
- [cmi_infos];
27-
- [cmt_infos = Some cmt_infos] if [orig = `Cmt];
28-
- [cmti_infos = Some cmt_infos] if [orig = `Cmti]. *)
29-
let init_from_all_cm_infos ~orig ~cm_file cmi_infos cmt_infos =
26+
- [cmi_sign = Some cm_infos.cmi_sign] if [cmi_infos = Some _]; *)
27+
let init_from_all_cm_infos ~cm_file ~cmi_infos cmt_infos =
3028
let builddir = cmt_infos.Cmt_format.cmt_builddir in
3129
let sourcepath =
3230
Option.map (Filename.concat builddir) cmt_infos.cmt_sourcefile
3331
in
3432
let modname = cmt_infos.cmt_modname in
35-
let cmt_infos, cmti_infos =
36-
match orig with
37-
| `Cmt -> Some cmt_infos, None
38-
| `Cmti -> None, Some cmt_infos
39-
in
33+
let cmi_sign = Option.map (fun Cmi_format.{cmi_sign; _} -> cmi_sign) cmi_infos in
4034
{empty with builddir;
4135
cm_file;
42-
cmi_infos;
43-
cmt_infos;
44-
cmti_infos;
36+
cmi_sign;
4537
modname;
4638
sourcepath}
4739

48-
(** [init_from_cm_file ~orig cm_file] returns an [Ok t] with [t] filled using
49-
the [cm_file] (see [init_from_cmt_infos]).
40+
(** [init_from_cm_file cm_file] returns an [Ok t] with [t] filled with general
41+
info expected for both cmt and cmti files, using the [cm_file] (see
42+
[init_from_all_cm_infos]).
5043
In case the file does not exist or it cannot be read (see
5144
[Cmt_format.read_cmt]) then it returns an [Err msg] with msg a string
5245
describing the issue. *)
53-
let init_from_cm_file ~orig cm_file =
46+
let init_from_cm_file cm_file =
5447
if not (Sys.file_exists cm_file) then Result.error (cm_file ^ ": file not found")
5548
else
5649
match Cmt_format.read cm_file with
5750
| exception _ -> Result.error (cm_file ^ ": error reading file")
5851
| _, None -> Result.error (cm_file ^ ": cmt_infos not found")
5952
| cmi_infos, Some cmt_infos ->
60-
init_from_all_cm_infos ~orig ~cm_file cmi_infos cmt_infos
61-
|> Result.ok
53+
let file_infos =
54+
init_from_all_cm_infos ~cm_file ~cmi_infos cmt_infos
55+
in
56+
Result.ok (file_infos, cmt_infos)
6257

6358
let ( let* ) x f = Result.bind x f
59+
let ( let+ ) x f = Result.map f x
6460

65-
let init cm_file =
66-
let* orig =
67-
match Filename.extension cm_file with
68-
| ".cmt" -> Result.ok `Cmt
69-
| ".cmti" -> Result.ok `Cmti
70-
| _ -> Result.error (cm_file ^ ": not a .cmti or .cmt file")
61+
let init_from_cmti_file cmti_file =
62+
let+ file_infos, cmt_infos = init_from_cm_file cmti_file in
63+
let cmti_uid_to_decl = Some cmt_infos.cmt_uid_to_decl in
64+
{file_infos with cmti_uid_to_decl}
65+
66+
let init_from_cmt_file cmt_file =
67+
let* file_infos, cmt_infos = init_from_cm_file cmt_file in
68+
let* cmt_struct =
69+
match cmt_infos.cmt_annots with
70+
| Implementation structure -> Result.ok structure
71+
| _ -> Result.error (cmt_file ^ ": does not contain an implementation")
7172
in
72-
let* file_infos = init_from_cm_file ~orig cm_file in
73-
let cmi_infos, cmt_infos, cmti_infos =
74-
let no_ext = Filename.remove_extension cm_file in
75-
match orig with
76-
| `Cmt ->
77-
let cmi_infos, cmti_infos =
78-
init_from_cm_file ~orig:`Cmti (no_ext ^ ".cmti")
79-
|> Result.map (fun {cmi_infos; cmti_infos; _} -> cmi_infos, cmti_infos)
80-
|> Result.value ~default:(file_infos.cmi_infos, file_infos.cmti_infos)
81-
in
82-
cmi_infos, file_infos.cmt_infos, cmti_infos
83-
| `Cmti ->
84-
let cmt_infos =
85-
init_from_cm_file ~orig:`Cmt (no_ext ^ ".cmt")
86-
|> Result.map (fun {cmt_infos; _} -> cmt_infos)
87-
|> Result.value ~default:file_infos.cmt_infos
88-
in
89-
file_infos.cmi_infos, cmt_infos, file_infos.cmti_infos
73+
let cmt_struct = Some cmt_struct in
74+
(* Read the cmti if it exists. We always want to do it in case a user
75+
specified the cmt before the cmti to ensure the location_dependencies
76+
are idempotent. *)
77+
let cmti_uid_to_decl =
78+
let cmti_file = Filename.remove_extension cmt_file ^ ".cmti" in
79+
match init_from_cmti_file cmti_file with
80+
| Error _ -> None
81+
| Ok file_infos -> file_infos.cmti_uid_to_decl
82+
in
83+
let+ location_dependencies =
84+
Location_dependencies.init cmt_infos cmti_uid_to_decl
9085
in
91-
let* location_dependencies =
92-
match orig with
93-
| `Cmt -> Location_dependencies.init cmt_infos cmti_infos
94-
| `Cmti -> Result.ok Location_dependencies.empty
86+
let file_infos =
87+
{file_infos with cmt_struct; cmti_uid_to_decl; location_dependencies}
9588
in
96-
Result.ok {file_infos with cmi_infos;
97-
cmt_infos;
98-
cmti_infos;
99-
location_dependencies}
89+
file_infos, cmt_infos
90+
91+
let init cm_file =
92+
match Filename.extension cm_file with
93+
| ".cmt" ->
94+
let+ file_infos, _ = init_from_cmt_file cm_file in
95+
file_infos
96+
| ".cmti" -> (
97+
(* Using cmt_infos is not critical. The intent is to mirror the behavior
98+
on cmt files, where both cmt and cmti are read. *)
99+
let filled_with_cmt_infos =
100+
let cmt_file = Filename.remove_extension cm_file ^ ".cmt" in
101+
let* file_infos, cmt_infos = init_from_cmt_file cmt_file in
102+
let+ location_dependencies =
103+
Location_dependencies.init cmt_infos file_infos.cmti_uid_to_decl
104+
in
105+
{file_infos with location_dependencies}
106+
in
107+
match filled_with_cmt_infos with
108+
| Ok {cmt_struct; cmti_uid_to_decl; location_dependencies; _} ->
109+
let+ res, _ = init_from_cm_file cm_file in
110+
{res with cmt_struct; cmti_uid_to_decl; location_dependencies}
111+
| Error _ -> init_from_cmti_file cm_file
112+
)
113+
| _ -> Result.error (cm_file ^ ": not a .cmti or .cmt file")
100114

101115
let change_file file_infos cm_file =
102116
let no_ext = Filename.remove_extension cm_file in
103117
assert(no_ext = Filename.remove_extension file_infos.cm_file);
104118
match Filename.extension cm_file, file_infos with
105-
| ".cmt", {cmt_infos=Some cmt_infos; cmi_infos; cmti_infos; _} ->
106-
let res =
107-
init_from_all_cm_infos ~orig:`Cmt ~cm_file cmi_infos cmt_infos
108-
in
109-
let res = {res with cmi_infos; cmti_infos} in
110-
let* location_dependencies =
111-
Location_dependencies.init res.cmt_infos res.cmti_infos
112-
in
113-
Result.ok {res with location_dependencies}
114-
| ".cmti", {cmti_infos=Some cmti_infos; cmi_infos; cmt_infos; _} ->
115-
let res =
116-
init_from_all_cm_infos ~orig:`Cmti ~cm_file cmi_infos cmti_infos
117-
in
118-
Result.ok {res with cmt_infos}
119-
| _, {cmi_infos; cmt_infos; cmti_infos; _} -> (* corresponding info is None or invalid extension *)
120-
let* res = init cm_file in
121-
let choose opt1 opt2 =
122-
if Option.is_some opt1 then opt1 else opt2
119+
| ".cmt", {cmt_struct = (Some _ as cs); cmi_sign; cmti_uid_to_decl; _} ->
120+
let* res, cmt_infos = init_from_cm_file cm_file in
121+
let+ location_dependencies =
122+
match file_infos.location_dependencies with
123+
| [] -> Location_dependencies.init cmt_infos cmti_uid_to_decl
124+
| loc_dep -> (* They have already been computed *)
125+
Result.ok loc_dep
123126
in
124-
let cmi_infos = choose res.cmi_infos cmi_infos in
125-
let cmt_infos = choose res.cmt_infos cmt_infos in
126-
let cmti_infos = choose res.cmti_infos cmti_infos in
127-
Result.ok {res with cmi_infos; cmt_infos; cmti_infos}
127+
{res with cmt_struct = cs; cmi_sign; cmti_uid_to_decl; location_dependencies}
128+
| ".cmti", {cmti_uid_to_decl = (Some _ as cutd); cmt_struct; location_dependencies; _} ->
129+
let+ res, _ = init_from_cm_file cm_file in
130+
{res with cmti_uid_to_decl = cutd; cmt_struct; location_dependencies}
131+
| _ ->
132+
(* invalid extension or the corresponding info is None *)
133+
init cm_file
128134

129135
let has_sourcepath file_infos = Option.is_some file_infos.sourcepath
130136

src/state/file_infos.mli

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
type t = {
44
builddir : string; (** The [cmt_builddir] *)
55
cm_file : string; (** The filepath currently analyzed *)
6-
cmi_infos : Cmi_format.cmi_infos option;
7-
cmt_infos : Cmt_format.cmt_infos option;
8-
cmti_infos : Cmt_format.cmt_infos option;
6+
cmi_sign : Types.signature option; (** Extracted from [cmi_infos] *)
7+
cmt_struct : Typedtree.structure option;
8+
(** Extracted from a cmt's [cmt_infos.cmt_annots] *)
9+
cmti_uid_to_decl : Location_dependencies.uid_to_decl option;
10+
(** Extracted from a cmti's [cmt_infos] *)
911
location_dependencies : Location_dependencies.t;
1012
(** Dependencies similar to [cmt_infos.cmt_value_dependencies] in OCaml 5.2 *)
1113
modname : string; (** Either [cmti_name] or [cmt_modname] *)

src/state/location_dependencies.ml

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ let empty = []
44

55
module UidTbl = Shape.Uid.Tbl
66

7+
type uid_to_decl = Typedtree.item_declaration UidTbl.t
8+
79
let fill_from_structure (structure : Typedtree.structure) res_uid_to_loc =
810
let open Types in
911
let rec fill_from_signature_item = function
@@ -32,7 +34,7 @@ let fill_from_structure (structure : Typedtree.structure) res_uid_to_loc =
3234
iterator.structure iterator structure;
3335
res_uid_to_loc
3436

35-
let fill_from_cmt_infos cmt_infos res_uid_to_loc =
37+
let fill_from_cmt_tbl uid_to_decl res_uid_to_loc =
3638
let open Typedtree in
3739
let loc_of_item_decl = function
3840
| Value {val_loc = loc; _}
@@ -44,7 +46,7 @@ let fill_from_cmt_infos cmt_infos res_uid_to_loc =
4446
let loc = loc_of_item_decl item_decl in
4547
Option.iter (UidTbl.replace res_uid_to_loc uid) loc
4648
in
47-
UidTbl.iter add_uid_loc cmt_infos.Cmt_format.cmt_uid_to_decl;
49+
UidTbl.iter add_uid_loc uid_to_decl;
4850
res_uid_to_loc
4951

5052
let cmt_decl_dep_to_loc_dep cmt_decl_dep uid_to_loc =
@@ -59,21 +61,20 @@ let cmt_decl_dep_to_loc_dep cmt_decl_dep uid_to_loc =
5961
in
6062
List.filter_map convert_pair cmt_decl_dep
6163

62-
let init cmt_infos cmti_infos =
63-
match cmt_infos with
64-
| None -> Result.error "No cmt_infos available"
65-
| Some (Cmt_format.{cmt_annots = Implementation structure; _} as cmt_infos) -> (
66-
let fill_from_cmti_infos tbl =
67-
match cmti_infos with
68-
| None -> tbl
69-
| Some cmti_infos -> fill_from_cmt_infos cmti_infos tbl
70-
in
71-
(* TODO: Evaluate a generally good size for the tbl ? *)
72-
UidTbl.create 512
73-
|> fill_from_structure structure
74-
|> fill_from_cmt_infos cmt_infos
75-
|> fill_from_cmti_infos
76-
|> cmt_decl_dep_to_loc_dep cmt_infos.cmt_declaration_dependencies
77-
|> Result.ok
78-
)
79-
| Some _ -> Result.error "No implementation found in cmt_infos"
64+
let init cmt_infos cmti_uid_to_decl =
65+
match cmt_infos.Cmt_format.cmt_annots with
66+
| Implementation structure ->
67+
let fill_from_cmti_tbl tbl =
68+
match cmti_uid_to_decl with
69+
| None -> tbl
70+
| Some cmti_uid_to_decl ->
71+
fill_from_cmt_tbl cmti_uid_to_decl tbl
72+
in
73+
(* TODO: Evaluate a generally good size for the tbl ? *)
74+
UidTbl.create 512
75+
|> fill_from_structure structure
76+
|> fill_from_cmt_tbl cmt_infos.cmt_uid_to_decl
77+
|> fill_from_cmti_tbl
78+
|> cmt_decl_dep_to_loc_dep cmt_infos.cmt_declaration_dependencies
79+
|> Result.ok
80+
| _ -> Result.error "No implementation found in cmt_infos"

src/state/location_dependencies.mli

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ type t = (Lexing.position * Lexing.position) list
33

44
val empty : t (** No signature read *)
55

6-
val init : Cmt_format.cmt_infos option -> Cmt_format.cmt_infos option -> (t, string) result
7-
(** [init cmt_infos cmti_infos] expects [cmt_infos = Some _].
8-
It reads the [cmt_infos] and the |cmti_infos] to retrieve their
9-
[cmt_declaration_dependencies] and convert them into a single [t].
6+
type uid_to_decl = Typedtree.item_declaration Shape.Uid.Tbl.t
7+
8+
val init : Cmt_format.cmt_infos -> uid_to_decl option -> (t, string) result
9+
(** [init cmt_infos cmti_infos cmti_uid_to_decl] expects
10+
[cmt_infos.cmt_annots = Implementation _].
11+
It reads the [cmt_infos] and the [cmti_uid_to_decl] to retrieve their
12+
and converts [cmt_infos.cmt_declaration_dependencies] into a single [t].
1013
It returns an [Ok t] with [t] on success.
11-
In case the [cmt_infos] is missing or does not contain an implementation,
12-
it returns an [Err msg] with msg a string describing the issue. *)
14+
In case the [cmt_infos] does not contain an implementation, it returns an
15+
[Err msg] with msg a string describing the issue. *)

0 commit comments

Comments
 (0)