Skip to content

Commit d1b2481

Browse files
committed
Collect occurrences info
Signed-off-by: Paul-Elliot <[email protected]>
1 parent 78391df commit d1b2481

File tree

30 files changed

+688
-28
lines changed

30 files changed

+688
-28
lines changed

doc/driver.mld

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ Compiling a file with [odoc] requires a few arguments: the file to compile, an
131131
optional parent, a list of include paths, a list of children for [.mld] files,
132132
optional parent and name for source implementation, and an output path. Include
133133
paths can be just ['.'], and we can calculate the output file from the input
134-
because all of the files are going into the same directory.
134+
because all of the files are going into the same directory. If we wish to count
135+
occurrences of each identifier, we need to pass the [--count-occurrences] flag.
135136

136137
Linking a file with [odoc] requires the input file and a list of include paths. As
137138
for compile, we will hard-code the include path.
@@ -144,6 +145,9 @@ Using the [--source] argument with an [.odocl] file that was not compiled with
144145
[--source-parent-file] and [--source-name] will result in an error, as will omitting [--source] when generating HTML of an [odocl] that was
145146
compiled with [--source-parent-file] and [--source-name].
146147

148+
To get the number of uses of each identifier, we can use the [count-occurrences]
149+
command.
150+
147151
In all of these, we'll capture [stdout] and [stderr] so we can check it later.
148152

149153
{[
@@ -168,7 +172,7 @@ let add_prefixed_output cmd list prefix lines =
168172
!list
169173
@ Bos.Cmd.to_string cmd :: List.map (fun l -> prefix ^ ": " ^ l) lines
170174

171-
let compile file ?parent ?(output_dir = Fpath.v "./")
175+
let compile file ?(count_occurrences = true) ?parent ?(output_dir = Fpath.v "./")
172176
?(ignore_output = false) ?source_args children =
173177
let output_file =
174178
let ext = Fpath.get_ext file in
@@ -195,8 +199,9 @@ let compile file ?parent ?(output_dir = Fpath.v "./")
195199
| _ -> Cmd.empty
196200
else Cmd.empty
197201
in
202+
let occ = if count_occurrences then Cmd.v "--count-occurrences" else Cmd.empty in
198203
let cmd =
199-
odoc % "compile" % Fpath.to_string file %% source_args %% cmt_arg
204+
odoc % "compile" % Fpath.to_string file %% source_args %% occ %% cmt_arg
200205
% "-I" % "." % "-o"
201206
% p (Fpath.( / ) output_dir output_file)
202207
|> List.fold_right (fun child cmd -> cmd % "--child" % child) children
@@ -233,6 +238,11 @@ let support_files () =
233238
let open Cmd in
234239
let cmd = odoc % "support-files" % "-o" % "html/odoc" in
235240
run cmd
241+
242+
let count_occurrences output =
243+
let open Cmd in
244+
let cmd = odoc % "count-occurrences" % "-I" % "." % "-o" % p output in
245+
run cmd
236246
]}
237247

238248
We'll now make some library lists. We have not only external dependency
@@ -595,6 +605,7 @@ The following code executes all of the above, and we're done!
595605
{[
596606
let compiled = compile_all () in
597607
let linked = link_all compiled in
608+
let _ = count_occurrences (Fpath.v "occurrences.txt") in
598609
generate_all linked
599610
]}
600611

src/document/generator.ml

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,40 @@ module Make (Syntax : SYNTAX) = struct
265265
| `SourceLocationInternal (_, local) ->
266266
Some (Anchor (LocalName.to_string local))
267267
| _ -> None)
268+
| ModulePath (`Resolved p)
269+
when not (Paths.Path.Resolved.is_hidden (p :> Paths.Path.Resolved.t))
270+
-> (
271+
let id = Paths.Path.Resolved.(identifier (p :> t)) in
272+
match Url.from_identifier ~stop_before:false id with
273+
| Ok link -> Some (Link link)
274+
| _ -> None)
275+
| TypePath (`Resolved p)
276+
when not (Paths.Path.Resolved.is_hidden (p :> Paths.Path.Resolved.t))
277+
-> (
278+
let id = Paths.Path.Resolved.(identifier (p :> t)) in
279+
match Url.from_identifier ~stop_before:false id with
280+
| Ok link -> Some (Link link)
281+
| _ -> None)
282+
| ValuePath (`Resolved p)
283+
when not (Paths.Path.Resolved.is_hidden (p :> Paths.Path.Resolved.t))
284+
-> (
285+
let id = Paths.Path.Resolved.(identifier (p :> t)) in
286+
match Url.from_identifier ~stop_before:false id with
287+
| Ok link -> Some (Link link)
288+
| _ -> None)
289+
| ConstructorPath (`Resolved p)
290+
when not (Paths.Path.Resolved.is_hidden (p :> Paths.Path.Resolved.t))
291+
-> (
292+
let id = Paths.Path.Resolved.(identifier (p :> t)) in
293+
match Url.from_identifier ~stop_before:false id with
294+
| Ok link -> Some (Link link)
295+
| _ -> None)
296+
| ModulePath _ -> None
297+
| ClassPath _ -> None
298+
| TypePath _ -> None
299+
| MtyPath _ -> None
300+
| ValuePath _ -> None
301+
| ConstructorPath _ -> None
268302

269303
let source id syntax_info infos source_code =
270304
let url = path id in
@@ -1776,7 +1810,7 @@ module Make (Syntax : SYNTAX) = struct
17761810
in
17771811
let source_anchor =
17781812
match t.source_info with
1779-
| Some src -> Some (Source_page.url src.id)
1813+
| Some { id; _ } -> Some (Source_page.url id)
17801814
| None -> None
17811815
in
17821816
let page = make_expansion_page ~source_anchor url [ unit_doc ] items in

src/loader/compat.cppo.ml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#if OCAML_VERSION >= (4,14,0)
2+
let get_type_desc = Types.get_desc
3+
#else
4+
let get_type_desc t = t.Types.desc
5+
#endif
6+
7+
type 'a pattern =
8+
#if OCAML_VERSION >= (4,11,0)
9+
'a Typedtree.general_pattern
10+
#else
11+
Typedtree.pattern
12+
#endif
13+
14+
type 'a pattern_desc =
15+
#if OCAML_VERSION >= (4,11,0)
16+
'a Typedtree.pattern_desc
17+
#else
18+
Typedtree.pattern_desc
19+
#endif
20+
21+
(** Extract longident and constructor description from a pattern construct,
22+
when it is one. *)
23+
let get_pattern_construct_info (type a) : a pattern_desc -> _ = function
24+
#if OCAML_VERSION >= (4,13,0)
25+
| Typedtree.Tpat_construct (l, { cstr_res; _ }, _, _)
26+
#else
27+
| Tpat_construct (l, { cstr_res; _ }, _)
28+
#endif
29+
-> Some (l, cstr_res)
30+
| _ -> None
31+
32+
33+
module Tast_iterator = struct
34+
#if OCAML_VERSION >= (4,09,0)
35+
include Tast_iterator
36+
#else
37+
open Asttypes
38+
open Typedtree
39+
40+
type iterator =
41+
{
42+
expr: iterator -> expression -> unit;
43+
module_expr: iterator -> module_expr -> unit;
44+
class_type: iterator -> class_type -> unit;
45+
module_type: iterator -> module_type -> unit;
46+
pat: iterator -> pattern -> unit;
47+
typ: iterator -> core_type -> unit;
48+
}
49+
50+
let default_iterator = {
51+
expr = fun _ _ -> () ;
52+
module_expr = fun _ _ -> () ;
53+
class_type = fun _ _ -> () ;
54+
module_type = fun _ _ -> () ;
55+
pat = fun _ _ -> () ;
56+
typ = fun _ _ -> () ;
57+
}
58+
#endif
59+
end

src/loader/dune

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@
1616
%{workspace_root}
1717
(run %{bin:cppo} -V OCAML:%{ocaml_version} %{x} -o %{targets}))))
1818

19+
(rule
20+
(targets compat.ml)
21+
(deps
22+
(:x compat.cppo.ml))
23+
(action
24+
(chdir
25+
%{workspace_root}
26+
(run %{bin:cppo} -V OCAML:%{ocaml_version} %{x} -o %{targets}))))
27+
1928
(library
2029
(name odoc_loader)
2130
(public_name odoc.loader)

src/loader/implementation.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,14 +504,14 @@ let of_cmt (source_id : Odoc_model.Paths.Identifier.SourcePage.t)
504504
(uid_to_id, postprocess_poses source_id vs uid_to_id uid_to_loc)
505505

506506
let read_cmt_infos source_id_opt id cmt_info =
507+
let occ_infos = Occurrences.of_cmt cmt_info in
507508
match Odoc_model.Compat.shape_of_cmt_infos cmt_info with
508509
| Some shape -> (
509510
let uid_to_loc = cmt_info.cmt_uid_to_loc in
510511
match (source_id_opt, cmt_info.cmt_annots) with
511512
| Some source_id, Implementation impl ->
512-
let map, source_infos =
513-
of_cmt source_id id impl uid_to_loc
514-
in
513+
let map, source_infos = of_cmt source_id id impl uid_to_loc in
514+
let source_infos = List.rev_append source_infos occ_infos in
515515
( Some (shape, map),
516516
Some
517517
{

src/loader/occurrences.ml

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
open Odoc_model.Lang.Source_info
2+
3+
let pos_of_loc loc = (loc.Location.loc_start.pos_cnum, loc.loc_end.pos_cnum)
4+
5+
module Global_analysis = struct
6+
let rec docparent_of_path (path : Path.t) : _ option =
7+
match path with
8+
| Pident id ->
9+
let id_s = Ident.name id in
10+
if Ident.persistent id then Some (`Root id_s) else None
11+
| Pdot (i, l) -> (
12+
match docparent_of_path i with
13+
| None -> None
14+
| Some i -> Some (`Dot (i, l)))
15+
| Papply (_, _) ->
16+
(* When resolving Path, [odoc] currently assert it contains no functor. So we cannot use:
17+
[docparent_of_path i] *)
18+
None
19+
20+
(* Types path (for instance) cannot be just `Root _, it needs to be `Dot. An
21+
ocaml path to a type whose ident is persistent will always start with a
22+
`Dot, but the typer does not know that. So, we need this function. *)
23+
let childpath_of_path (path : Path.t) =
24+
match path with
25+
| Pident _ -> None (* is never persistent *)
26+
| Pdot (i, l) -> (
27+
match docparent_of_path i with
28+
| None -> None
29+
| Some i -> Some (`Dot (i, l)))
30+
| Papply (_i, _) ->
31+
(* When resolving Path, [odoc] currently assert it contains no functor. So we cannot use:
32+
[childpath_of_path i] *)
33+
None
34+
35+
let expr poses expr =
36+
match expr with
37+
| { Typedtree.exp_desc = Texp_ident (p, _, _); exp_loc; _ } -> (
38+
match childpath_of_path p with
39+
| None -> ()
40+
| Some ref_ -> poses := (ValuePath ref_, pos_of_loc exp_loc) :: !poses)
41+
| {
42+
Typedtree.exp_desc = Texp_construct (l, { cstr_res; _ }, _);
43+
exp_loc;
44+
_;
45+
} -> (
46+
let desc = Compat.get_type_desc cstr_res in
47+
match desc with
48+
| Types.Tconstr (p, _, _) -> (
49+
match childpath_of_path p with
50+
| None -> ()
51+
| Some ref_ ->
52+
poses :=
53+
( ConstructorPath (`Dot (ref_, Longident.last l.txt)),
54+
pos_of_loc exp_loc )
55+
:: !poses)
56+
| _ -> ())
57+
| _ -> ()
58+
59+
let pat poses : _ Compat.pattern -> unit = function
60+
| { Typedtree.pat_desc; pat_loc; _ } -> (
61+
match Compat.get_pattern_construct_info pat_desc with
62+
| Some (l, cstr_res) -> (
63+
let desc = Compat.get_type_desc cstr_res in
64+
match desc with
65+
| Types.Tconstr (p, _, _) -> (
66+
match childpath_of_path p with
67+
| None -> ()
68+
| Some ref_ ->
69+
poses :=
70+
( ConstructorPath (`Dot (ref_, Longident.last l.txt)),
71+
pos_of_loc pat_loc )
72+
:: !poses)
73+
| _ -> ())
74+
| None -> ())
75+
76+
let module_expr poses mod_expr =
77+
match mod_expr with
78+
| { Typedtree.mod_desc = Tmod_ident (p, _); mod_loc; _ } -> (
79+
match docparent_of_path p with
80+
| None -> ()
81+
| Some ref_ -> poses := (ModulePath ref_, pos_of_loc mod_loc) :: !poses)
82+
| _ -> ()
83+
84+
let class_type poses cltyp =
85+
match cltyp with
86+
| { Typedtree.cltyp_desc = Tcty_constr (p, _, _); cltyp_loc; _ } -> (
87+
match childpath_of_path p with
88+
| None -> ()
89+
| Some p -> poses := (ClassPath p, pos_of_loc cltyp_loc) :: !poses)
90+
| _ -> ()
91+
92+
let module_type poses mty_expr =
93+
match mty_expr with
94+
| { Typedtree.mty_desc = Tmty_ident (p, _); mty_loc; _ } -> (
95+
match childpath_of_path p with
96+
| None -> ()
97+
| Some p -> poses := (MtyPath p, pos_of_loc mty_loc) :: !poses)
98+
| _ -> ()
99+
100+
let core_type poses ctyp_expr =
101+
match ctyp_expr with
102+
| { Typedtree.ctyp_desc = Ttyp_constr (p, _, _); ctyp_loc; _ } -> (
103+
match childpath_of_path p with
104+
| None -> ()
105+
| Some p -> poses := (TypePath p, pos_of_loc ctyp_loc) :: !poses)
106+
| _ -> ()
107+
end
108+
109+
let of_cmt (cmt : Cmt_format.cmt_infos) =
110+
let ttree = cmt.cmt_annots in
111+
match ttree with
112+
| Cmt_format.Implementation structure ->
113+
let poses = ref [] in
114+
let module_expr iterator mod_expr =
115+
Global_analysis.module_expr poses mod_expr;
116+
Compat.Tast_iterator.default_iterator.module_expr iterator mod_expr
117+
in
118+
let expr iterator e =
119+
Global_analysis.expr poses e;
120+
Compat.Tast_iterator.default_iterator.expr iterator e
121+
in
122+
let pat iterator e =
123+
Global_analysis.pat poses e;
124+
Compat.Tast_iterator.default_iterator.pat iterator e
125+
in
126+
let typ iterator ctyp_expr =
127+
Global_analysis.core_type poses ctyp_expr;
128+
Compat.Tast_iterator.default_iterator.typ iterator ctyp_expr
129+
in
130+
let module_type iterator mty =
131+
Global_analysis.module_type poses mty;
132+
Compat.Tast_iterator.default_iterator.module_type iterator mty
133+
in
134+
let class_type iterator cl_type =
135+
Global_analysis.class_type poses cl_type;
136+
Compat.Tast_iterator.default_iterator.class_type iterator cl_type
137+
in
138+
let iterator =
139+
{
140+
Compat.Tast_iterator.default_iterator with
141+
expr;
142+
pat;
143+
module_expr;
144+
typ;
145+
module_type;
146+
class_type;
147+
}
148+
in
149+
iterator.structure iterator structure;
150+
!poses
151+
| _ -> []

src/model/lang.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ module Source_info = struct
2121
type annotation =
2222
| Definition of Paths.Identifier.SourceLocation.t
2323
| Value of Paths.Identifier.SourceLocation.t
24+
| ValuePath of Path.Value.t
25+
| ModulePath of Path.Module.t
26+
| ClassPath of Path.ClassType.t
27+
| MtyPath of Path.ModuleType.t
28+
| TypePath of Path.Type.t
29+
| ConstructorPath of Path.Constructor.t
2430

2531
type 'a with_pos = 'a * (int * int)
2632

0 commit comments

Comments
 (0)