this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix PPX spurious source links and same-module value linking in odoc

Two source rendering fixes:

1. Skip ghost structure/signature items in typedtree traversal to prevent
PPX-generated code (e.g. [@@deriving]) from creating wrong source links.
ppxlib wraps derived code in ghost-located `include struct ... end`.

2. Add GlobalDefinition annotation so top-level values within the same
module get source links. Previously these were neither "local" nor
"persistent" and produced unresolved paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+33 -20
+5 -1
src/loader/implementation.cppo.ml
··· 180 180 in 181 181 LocHashtbl.add loc_to_id loc identifier; 182 182 IdentHashtbl.add local_ident_to_loc id loc 183 + | Typedtree_traverse.Analysis.GlobalDefinition id, loc -> 184 + (* Track ident→loc for same-module reference resolution. 185 + The loc→id mapping is handled by populate_global_defs. *) 186 + IdentHashtbl.add local_ident_to_loc id loc 183 187 | _ -> ()) 184 188 poses 185 189 ··· 380 384 process p Ident_env.Path.read_type fallback_read_type 381 385 |> Option.iter @@ fun l -> 382 386 AnnotHashtbl.replace occ_tbl (Type l, pos_of_loc loc) () 383 - | LocalDefinition _, _ -> ()) 387 + | LocalDefinition _, _ | GlobalDefinition _, _ -> ()) 384 388 poses; 385 389 AnnotHashtbl.fold (fun k () acc -> k :: acc) occ_tbl [] 386 390
+15 -6
src/loader/typedtree_traverse.cppo.ml
··· 3 3 module Analysis = struct 4 4 type annotation = 5 5 | LocalDefinition of Ident.t 6 + | GlobalDefinition of Ident.t 6 7 | Value of Path.t 7 8 | Module of Path.t 8 9 | ModuleType of Path.t ··· 54 55 let maybe_localvalue id loc = 55 56 match Ident_env.identifier_of_loc env loc with 56 57 | None -> Some (LocalDefinition id, loc) 57 - | Some _ -> None 58 + | Some _ -> Some (GlobalDefinition id, loc) 58 59 in 59 60 let () = 60 61 match pat_desc with ··· 101 102 | { Typedtree.mb_id = Some id; mb_loc; _ } when not mb_loc.loc_ghost -> ( 102 103 match Ident_env.identifier_of_loc env mb_loc with 103 104 | None -> poses := (LocalDefinition id, mb_loc) :: !poses 104 - | Some _ -> ()) 105 + | Some _ -> poses := (GlobalDefinition id, mb_loc) :: !poses) 105 106 | _ -> () 106 107 107 108 let module_expr poses mod_expr = ··· 198 199 iter.open_description iterator od 199 200 in 200 201 let structure_item iterator item = 201 - Analysis.structure_item poses item; 202 - iter.structure_item iterator item 202 + (* Skip ghost structure items entirely — these are typically PPX-generated 203 + code (e.g. [@@deriving]) wrapped by ppxlib in an include with 204 + loc_ghost=true. Sub-expressions within may have non-ghost locations 205 + copied from the original source, which would create spurious links. *) 206 + if not item.Typedtree.str_loc.loc_ghost then begin 207 + Analysis.structure_item poses item; 208 + iter.structure_item iterator item 209 + end 203 210 in 204 211 let signature_item iterator item = 205 - Analysis.signature_item poses item; 206 - iter.signature_item iterator item 212 + if not item.Typedtree.sig_loc.loc_ghost then begin 213 + Analysis.signature_item poses item; 214 + iter.signature_item iterator item 215 + end 207 216 in 208 217 let iterator = 209 218 {
+13 -13
test/occurrences/double_wrapped.t/run.t
··· 77 77 78 78 "Aliased" values are not counted since they become persistent 79 79 $ occurrences_print main__B.odoc-occurrences | sort 80 - Main was used directly 0 times and indirectly 7 times 81 - Main.A was used directly 2 times and indirectly 5 times 80 + Main was used directly 0 times and indirectly 8 times 81 + Main.A was used directly 3 times and indirectly 5 times 82 82 Main.A.(||>) was used directly 1 times and indirectly 0 times 83 83 Main.A.M was used directly 2 times and indirectly 0 times 84 84 Main.A.t was used directly 1 times and indirectly 0 times ··· 101 101 102 102 $ occurrences_print aggregated.odoc-occurrences | sort > all_merged 103 103 $ cat all_merged 104 - Main was used directly 0 times and indirectly 11 times 105 - Main.A was used directly 4 times and indirectly 6 times 104 + Main was used directly 0 times and indirectly 12 times 105 + Main.A was used directly 5 times and indirectly 6 times 106 106 Main.A.(||>) was used directly 1 times and indirectly 0 times 107 107 Main.A.M was used directly 2 times and indirectly 0 times 108 108 Main.A.t was used directly 1 times and indirectly 0 times ··· 119 119 120 120 $ odoc count-occurrences main__B -o b.odoc-occurrences --include-hidden 121 121 $ occurrences_print b.odoc-occurrences | sort 122 - Main was used directly 0 times and indirectly 7 times 123 - Main.A was used directly 2 times and indirectly 5 times 122 + Main was used directly 0 times and indirectly 8 times 123 + Main.A was used directly 3 times and indirectly 5 times 124 124 Main.A.(||>) was used directly 1 times and indirectly 0 times 125 125 Main.A.M was used directly 2 times and indirectly 0 times 126 126 Main.A.t was used directly 1 times and indirectly 0 times ··· 128 128 129 129 $ odoc count-occurrences . -o all.odoc-occurrences --include-hidden 130 130 $ occurrences_print all.odoc-occurrences | sort 131 - Main was used directly 0 times and indirectly 11 times 132 - Main.A was used directly 4 times and indirectly 6 times 131 + Main was used directly 0 times and indirectly 12 times 132 + Main.A was used directly 5 times and indirectly 6 times 133 133 Main.A.(||>) was used directly 1 times and indirectly 0 times 134 134 Main.A.M was used directly 2 times and indirectly 0 times 135 135 Main.A.t was used directly 1 times and indirectly 0 times ··· 143 143 $ odoc compile-index --json -o index.json --occurrences all.odoc-occurrences main.odocl 144 144 145 145 $ cat index.json | jq sort | jq '.[]' -c 146 - {"id":[{"kind":"Root","name":"Main"}],"doc":"Handwritten top-level module","kind":{"kind":"Module"},"display":{"url":"Main/index.html","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"entry-name\">Main</span></code><div class=\"entry-comment\"><div><p>Handwritten top-level module</p></div></div>"},"occurrences":{"direct":0,"indirect":11}} 147 - {"id":[{"kind":"Root","name":"Main"},{"kind":"Module","name":"A"}],"doc":"","kind":{"kind":"Module"},"display":{"url":"Main/index.html#module-A","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"prefix-name\">Main.</span><span class=\"entry-name\">A</span></code><div class=\"entry-comment\"><div></div></div>"},"occurrences":{"direct":4,"indirect":6}} 146 + {"id":[{"kind":"Root","name":"Main"}],"doc":"Handwritten top-level module","kind":{"kind":"Module"},"display":{"url":"Main/index.html","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"entry-name\">Main</span></code><div class=\"entry-comment\"><div><p>Handwritten top-level module</p></div></div>"},"occurrences":{"direct":0,"indirect":12}} 147 + {"id":[{"kind":"Root","name":"Main"},{"kind":"Module","name":"A"}],"doc":"","kind":{"kind":"Module"},"display":{"url":"Main/index.html#module-A","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"prefix-name\">Main.</span><span class=\"entry-name\">A</span></code><div class=\"entry-comment\"><div></div></div>"},"occurrences":{"direct":5,"indirect":6}} 148 148 {"id":[{"kind":"Root","name":"Main"},{"kind":"Module","name":"B"}],"doc":"","kind":{"kind":"Module"},"display":{"url":"Main/index.html#module-B","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"prefix-name\">Main.</span><span class=\"entry-name\">B</span></code><div class=\"entry-comment\"><div></div></div>"},"occurrences":{"direct":1,"indirect":0}} 149 149 {"id":[{"kind":"Root","name":"Main"},{"kind":"Module","name":"B"},{"kind":"Module","name":"M"}],"doc":"","kind":{"kind":"Module"},"display":{"url":"Main/B/index.html#module-M","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"prefix-name\">Main.B.</span><span class=\"entry-name\">M</span></code><div class=\"entry-comment\"><div></div></div>"},"occurrences":{"direct":0,"indirect":0}} 150 150 {"id":[{"kind":"Root","name":"Main"},{"kind":"Module","name":"B"},{"kind":"Module","name":"Y"}],"doc":"","kind":{"kind":"Module"},"display":{"url":"Main/B/index.html#module-Y","html":"<code class=\"entry-kind\">mod</code><code class=\"entry-title\"><span class=\"prefix-name\">Main.B.</span><span class=\"entry-name\">Y</span></code><div class=\"entry-comment\"><div></div></div>"},"occurrences":{"direct":0,"indirect":0}} ··· 177 177 }, 178 178 "occurrences": { 179 179 "direct": 0, 180 - "indirect": 11 180 + "indirect": 12 181 181 } 182 182 }, 183 183 { ··· 194 194 "doc": "", 195 195 196 196 $ cat index.json | jq -r '.[] | "\(.id | map("\(.kind)-\(.name)") | join(".")), direct: \(.occurrences.direct), indirect: \(.occurrences.indirect)"' | sort 197 - Root-Main, direct: 0, indirect: 11 198 - Root-Main.Module-A, direct: 4, indirect: 6 197 + Root-Main, direct: 0, indirect: 12 198 + Root-Main.Module-A, direct: 5, indirect: 6 199 199 Root-Main.Module-A.ModuleType-M, direct: 2, indirect: 0 200 200 Root-Main.Module-A.Type-t, direct: 1, indirect: 0 201 201 Root-Main.Module-A.Value-(||>), direct: 1, indirect: 0