• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ocaml / odoc / 3053

19 Feb 2026 10:47AM UTC coverage: 71.712% (-1.2%) from 72.946%
3053

Pull #1399

github

web-flow
Merge bf200aeef into c3f0f46ee
Pull Request #1399: Upstream OxCaml

20 of 280 new or added lines in 21 files covered. (7.14%)

162 existing lines in 11 files now uncovered.

10399 of 14501 relevant lines covered (71.71%)

7008.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.24
/src/model/reference.ml
1
type path = [ `Root of string | `Dot of path * string ]
2

3
let expected_err :
4
    (Format.formatter -> 'a -> unit) -> 'a -> Location_.span -> Error.t =
5
 fun pp_a a -> Error.make "Expected %a." pp_a a
151✔
6

7
let expected_err_str : string -> Location_.span -> Error.t =
8
  expected_err Format.pp_print_string
1,315✔
9

10
let unknown_reference_qualifier : string -> Location_.span -> Error.t =
11
  Error.make "Unknown reference qualifier '%s'."
1,315✔
12

13
let deprecated_reference_kind : string -> string -> Location_.span -> Error.t =
14
  Error.make "'%s' is deprecated, use '%s' instead."
1,315✔
15

16
let reference_kinds_do_not_match : string -> string -> Location_.span -> Error.t
17
    =
18
  Error.make "Old-style reference kind ('%s:') does not match new ('%s-')."
1,315✔
19

20
let should_not_be_empty : what:string -> Location_.span -> Error.t =
21
 fun ~what ->
22
  Error.make "%s should not be empty." (Astring.String.Ascii.capitalize what)
34✔
23

24
let not_allowed :
25
    ?suggestion:string ->
26
    what:string ->
27
    in_what:string ->
28
    Location_.span ->
29
    Error.t =
30
 fun ?suggestion ~what ~in_what ->
31
  Error.make ?suggestion "%s is not allowed in %s."
2✔
32
    (Astring.String.Ascii.capitalize what)
2✔
33
    in_what
34

35
(** Format a list in a human readable way: [A, B, or C]. *)
36
let pp_hum_comma_separated pp_a ppf lst =
37
  let rec loop hd = function
149✔
38
    | [] -> Format.fprintf ppf "or %a" pp_a hd
149✔
39
    | hd' :: tl' ->
483✔
40
        Format.fprintf ppf "%a, " pp_a hd;
41
        loop hd' tl'
483✔
42
  in
43
  match lst with [] -> () | [ a ] -> pp_a ppf a | hd :: tl -> loop hd tl
×
44

45
let deprecated_reference_kind location kind replacement =
46
  deprecated_reference_kind kind replacement location |> Error.raise_warning
8✔
47

48
(* http://caml.inria.fr/pub/docs/manual-ocaml/ocamldoc.html#sec359. *)
49
let match_ocamldoc_reference_kind (_location as loc) s :
50
    [> Paths.Reference.tag_any ] option =
51
  let d = deprecated_reference_kind in
563✔
52
  match s with
53
  | "module" -> Some `TModule
80✔
54
  | "modtype" ->
1✔
55
      d loc "modtype" "module-type";
56
      Some `TModuleType
1✔
57
  | "class" -> Some `TClass
47✔
58
  | "classtype" ->
1✔
59
      d loc "classtype" "class-type";
60
      Some `TClassType
1✔
61
  | "val" -> Some `TValue
27✔
62
  | "type" -> Some `TType
40✔
63
  | "exception" -> Some `TException
17✔
64
  | "attribute" -> None
×
65
  | "method" -> Some `TMethod
47✔
66
  | "section" -> Some `TLabel
34✔
67
  | "const" ->
1✔
68
      d loc "const" "constructor";
69
      Some `TConstructor
1✔
70
  | "recfield" ->
1✔
71
      d loc "recfield" "field";
72
      Some `TField
1✔
73
  | "childpage" -> Some `TChildPage
3✔
74
  | "childmodule" -> Some `TChildModule
5✔
75
  | _ -> None
259✔
76

77
let match_extra_odoc_reference_kind (_location as loc) s :
78
    [> Paths.Reference.tag_any ] option =
79
  let d = deprecated_reference_kind in
259✔
80
  match s with
81
  | "class-type" -> Some `TClassType
19✔
82
  | "constructor" -> Some `TConstructor
56✔
83
  | "exn" ->
1✔
84
      d loc "exn" "exception";
85
      Some `TException
1✔
86
  | "extension" -> Some `TExtension
19✔
87
  | "extension-decl" -> Some `TExtensionDecl
4✔
88
  | "field" -> Some `TField
55✔
89
  | "instance-variable" -> Some `TInstanceVariable
15✔
90
  | "label" ->
1✔
91
      d loc "label" "section";
92
      Some `TLabel
1✔
93
  | "module-type" -> Some `TModuleType
33✔
94
  | "page" -> Some `TPage
43✔
95
  | "asset" -> Some `TAsset
3✔
96
  | "value" ->
2✔
97
      d loc "value" "val";
98
      Some `TValue
2✔
99
  | _ -> None
8✔
100

101
type reference_kind = Paths.Reference.tag_any
102

103
(* Ideally, [tokenize] would call this on every reference kind annotation during
104
   tokenization, when generating the token list. However, that constrains the
105
   phantom tag type to be the same for all tokens in the list (because lists are
106
   homogeneous). So, the parser stores kinds as strings in the token list
107
   instead, and this function is called on each string at the latest possible
108
   time to prevent typing issues.
109

110
   A secondary reason to delay parsing, and store strings in the token list, is
111
   that we need the strings for user-friendly error reporting. *)
112
let match_reference_kind location s : reference_kind =
113
  match s with
1,155✔
114
  | `None -> `TUnknown
592✔
115
  | `Prefixed s | `Old_prefix s -> (
39✔
116
      let result =
117
        match match_ocamldoc_reference_kind location s with
118
        | Some _ as kind -> kind
304✔
119
        | None -> match_extra_odoc_reference_kind location s
259✔
120
      in
121
      match result with
122
      | Some kind -> kind
555✔
123
      | None -> unknown_reference_qualifier s location |> Error.raise_exception)
8✔
124

125
type token = {
126
  kind : [ `None | `Prefixed of string ];
127
  identifier : string;
128
  location : Location_.span;
129
}
130

131
type path_prefix = Path_prefix of string * Location_.span
132

133
(* The string is scanned right-to-left, because we are interested in right-most
134
   hyphens. The tokens are also returned in right-to-left order, because the
135
   traversals that consume them prefer to look at the deepest identifier
136
   first. *)
137
let tokenize location s : token list * path_prefix option =
138
  let rec scan_identifier started_at open_parenthesis_count index tokens =
709✔
139
    match s.[index] with
5,348✔
140
    | exception Invalid_argument _ ->
408✔
141
        let identifier, location = identifier_ended started_at index in
142
        ({ kind = `None; identifier; location } :: tokens, None)
389✔
143
    | '-' when open_parenthesis_count = 0 ->
537✔
144
        let identifier, location = identifier_ended started_at index in
536✔
145
        scan_kind identifier location index (index - 1) tokens
532✔
146
    | '.' when open_parenthesis_count = 0 ->
190✔
147
        let identifier, location = identifier_ended started_at index in
189✔
148
        scan_identifier index 0 (index - 1)
188✔
149
          ({ kind = `None; identifier; location } :: tokens)
150
    | '/' when open_parenthesis_count = 0 ->
84✔
151
        let identifier, location = identifier_ended started_at index in
84✔
152
        scan_path index ({ kind = `None; identifier; location } :: tokens)
76✔
153
    | ')' ->
9✔
154
        scan_identifier started_at
155
          (open_parenthesis_count + 1)
156
          (index - 1) tokens
157
    | '(' when open_parenthesis_count > 0 ->
9✔
158
        scan_identifier started_at
9✔
159
          (open_parenthesis_count - 1)
160
          (index - 1) tokens
161
    | '"' -> (
18✔
162
        try
163
          scan_identifier started_at 0
16✔
164
            (String.rindex_from s (index - 1) '"' - 1)
17✔
165
            tokens
166
        with _ ->
2✔
167
          Error.raise_exception (Error.make "Unmatched quotation!" location))
2✔
168
    | _ -> scan_identifier started_at open_parenthesis_count (index - 1) tokens
4,095✔
169
  and identifier_ended started_at index =
170
    let offset = index + 1 in
1,217✔
171
    let length = started_at - offset in
172
    let identifier = String.sub s offset length in
173
    let identifier =
1,217✔
174
      Astring.String.cuts ~sep:"\"" identifier
175
      |> List.mapi (fun i s ->
1,217✔
176
             if i mod 2 = 0 then
1,249✔
177
               Astring.String.cuts s ~sep:" " |> String.concat ""
1,233✔
178
             else s)
16✔
179
      |> String.concat ""
1,217✔
180
    in
181
    let location = Location_.in_string s ~offset ~length location in
1,217✔
182

183
    if identifier = "" then
1,217✔
184
      should_not_be_empty ~what:"Identifier in reference" location
185
      |> Error.raise_exception;
×
186

187
    (identifier, location)
1,185✔
188
  and scan_kind identifier identifier_location started_at index tokens =
189
    match s.[index] with
4,233✔
190
    | exception Invalid_argument _ ->
202✔
191
        let kind, location = kind_ended identifier_location started_at index in
192
        ({ kind; identifier; location } :: tokens, None)
202✔
193
    | '.' ->
321✔
194
        let kind, location = kind_ended identifier_location started_at index in
195
        scan_identifier index 0 (index - 1)
321✔
196
          ({ kind; identifier; location } :: tokens)
197
    | '/' ->
9✔
198
        let kind, location = kind_ended identifier_location started_at index in
199
        scan_path index ({ kind; identifier; location } :: tokens)
9✔
200
    | _ ->
3,701✔
201
        scan_kind identifier identifier_location started_at (index - 1) tokens
202
  and kind_ended identifier_location started_at index =
203
    let offset = index + 1 in
532✔
204
    let length = started_at - offset in
205
    let kind = `Prefixed (String.sub s offset length) in
532✔
206
    let location = Location_.in_string s ~offset ~length location in
207
    let location = Location_.span [ location; identifier_location ] in
532✔
208
    (kind, location)
532✔
209
  and scan_path started_at tokens =
210
    let location =
85✔
211
      Location_.in_string s ~offset:0 ~length:(started_at + 1) location
212
    in
213
    (tokens, Some (Path_prefix (String.sub s 0 (started_at + 1), location)))
85✔
214
  in
215

216
  scan_identifier (String.length s) 0 (String.length s - 1) []
709✔
217
  |> fun (toks, p) -> (List.rev toks, p)
676✔
218

219
let expected ?(expect_paths = false) allowed location =
79✔
220
  let unqualified = [ "an unqualified reference" ] in
149✔
221
  let unqualified =
222
    if expect_paths then "a path" :: unqualified else unqualified
70✔
223
  in
224
  let allowed = List.map (Printf.sprintf "'%s-'") allowed @ unqualified in
149✔
225
  expected_err (pp_hum_comma_separated Format.pp_print_string) allowed location
149✔
226

227
let parse_path whole_path_location p =
228
  let segs = Astring.String.cuts ~sep:"/" p in
91✔
229
  let check segs start =
91✔
230
    let _finish =
91✔
231
      List.fold_left
232
        (fun offset seg ->
233
          match seg with
162✔
234
          | "" ->
2✔
235
              let location =
236
                Location_.in_string p ~offset ~length:0 whole_path_location
237
              in
238
              should_not_be_empty ~what:"Identifier in path reference" location
2✔
239
              |> Error.raise_exception
2✔
240
          | seg -> offset + String.length seg + 1)
160✔
241
        start segs
242
    in
243
    ()
89✔
244
  in
245
  match segs with
246
  | "." :: segs ->
14✔
247
      check segs 2;
248
      (`TRelativePath, segs)
14✔
249
  | "" :: "" :: segs ->
27✔
250
      check segs 2;
251
      (`TCurrentPackage, segs)
26✔
252
  | "" :: segs ->
30✔
253
      check segs 1;
254
      (`TAbsolutePath, segs)
30✔
255
  | segs ->
20✔
256
      check segs 0;
257
      (`TRelativePath, segs)
19✔
258

259
let parse_path_prefix (Path_prefix (p, path_location)) identifier
260
    prefix_location =
261
  parse_path (Location_.span [ path_location; prefix_location ]) (p ^ identifier)
84✔
262

263
(* Parse references that do not contain a [/]. Raises errors and warnings. *)
264
let parse whole_reference_location s :
265
    Paths.Reference.t Error.with_errors_and_warnings =
266
  let open Paths.Reference in
709✔
267
  let open Names in
268
  let parse_from_last_component { kind; identifier; location } old_kind tokens
269
      path_prefix =
270
    let rec signature { kind; identifier; location } tokens : Signature.t =
676✔
271
      let kind = match_reference_kind location kind in
119✔
272
      match tokens with
119✔
273
      | [] -> (
96✔
274
          match path_prefix with
275
          | None -> (
96✔
276
              match kind with
277
              | (`TUnknown | `TModule | `TModuleType) as kind ->
5✔
278
                  `Root (identifier, kind)
279
              | _ ->
39✔
280
                  expected ~expect_paths:true
281
                    [ "module"; "module-type" ]
282
                    location
283
                  |> Error.raise_exception)
39✔
284
          | Some p -> (
×
285
              match kind with
286
              | `TUnknown | `TModule ->
×
287
                  `Module_path (parse_path_prefix p identifier location)
×
288
              | _ ->
×
289
                  expected ~expect_paths:true [ "module" ] location
290
                  |> Error.raise_exception))
×
291
      | next_token :: tokens -> (
23✔
292
          match kind with
293
          | `TUnknown ->
3✔
294
              `Dot ((parent next_token tokens :> LabelParent.t), identifier)
2✔
295
          | `TModule ->
4✔
296
              `Module
297
                (signature next_token tokens, ModuleName.make_std identifier)
3✔
298
          | `TModuleType ->
4✔
299
              `ModuleType
300
                (signature next_token tokens, ModuleTypeName.make_std identifier)
3✔
301
          | _ ->
12✔
302
              expected ~expect_paths:true [ "module"; "module-type" ] location
303
              |> Error.raise_exception)
12✔
304
    and parent { kind; identifier; location } tokens : FragmentTypeParent.t =
305
      let kind = match_reference_kind location kind in
101✔
306
      match tokens with
101✔
307
      | [] -> (
52✔
308
          match path_prefix with
309
          | None -> (
52✔
310
              match kind with
311
              | (`TUnknown | `TModule | `TModuleType | `TType) as kind ->
2✔
312
                  `Root (identifier, kind)
313
              | _ ->
27✔
314
                  expected [ "module"; "module-type"; "type" ] location
315
                  |> Error.raise_exception)
27✔
316
          | Some p -> (
×
317
              match kind with
318
              | `TUnknown | `TModule ->
×
319
                  `Module_path (parse_path_prefix p identifier location)
×
320
              | _ ->
×
321
                  expected ~expect_paths:true [ "module" ] location
322
                  |> Error.raise_exception))
×
323
      | next_token :: tokens -> (
49✔
324
          match kind with
325
          | `TUnknown ->
9✔
326
              `Dot ((parent next_token tokens :> LabelParent.t), identifier)
6✔
327
          | `TModule ->
4✔
328
              `Module
329
                (signature next_token tokens, ModuleName.make_std identifier)
3✔
330
          | `TModuleType ->
4✔
331
              `ModuleType
332
                (signature next_token tokens, ModuleTypeName.make_std identifier)
3✔
333
          | `TType ->
6✔
334
              `Type (signature next_token tokens, TypeName.make_std identifier)
4✔
335
          | _ ->
26✔
336
              expected [ "module"; "module-type"; "type" ] location
337
              |> Error.raise_exception)
26✔
338
    in
339

340
    let class_signature { kind; identifier; location } tokens : ClassSignature.t
341
        =
342
      let kind = match_reference_kind location kind in
40✔
343
      match tokens with
40✔
344
      | [] -> (
19✔
345
          match kind with
346
          | (`TUnknown | `TClass | `TClassType) as kind ->
1✔
347
              `Root (identifier, kind)
348
          | _ ->
14✔
349
              expected [ "class"; "class-type" ] location
350
              |> Error.raise_exception)
14✔
351
      | next_token :: tokens -> (
21✔
352
          match kind with
353
          | `TUnknown ->
3✔
354
              `Dot ((parent next_token tokens :> LabelParent.t), identifier)
2✔
355
          | `TClass ->
3✔
356
              `Class (signature next_token tokens, TypeName.make_std identifier)
2✔
357
          | `TClassType ->
3✔
358
              `ClassType
359
                (signature next_token tokens, TypeName.make_std identifier)
2✔
360
          | _ ->
12✔
361
              expected [ "class"; "class-type" ] location
362
              |> Error.raise_exception)
12✔
363
    in
364

365
    let label_parent_path kind path_prefix identifier location =
366
      match kind with
20✔
367
      | `TUnknown ->
19✔
368
          `Any_path (parse_path_prefix path_prefix identifier location)
19✔
369
      | `TModule ->
×
370
          `Module_path (parse_path_prefix path_prefix identifier location)
×
371
      | `TPage -> `Page_path (parse_path_prefix path_prefix identifier location)
1✔
372
      | _ ->
×
373
          expected ~expect_paths:true [ "module"; "page" ] location
374
          |> Error.raise_exception
×
375
    in
376

377
    let any_path kind path_prefix identifier location =
378
      match kind with
65✔
379
      | `TUnknown ->
57✔
380
          `Any_path (parse_path_prefix path_prefix identifier location)
55✔
381
      | `TModule ->
1✔
382
          `Module_path (parse_path_prefix path_prefix identifier location)
1✔
383
      | `TPage -> `Page_path (parse_path_prefix path_prefix identifier location)
3✔
384
      | `TAsset ->
3✔
385
          `Asset_path (parse_path_prefix path_prefix identifier location)
3✔
386
      | _ ->
1✔
387
          expected ~expect_paths:true [ "module"; "page" ] location
388
          |> Error.raise_exception
1✔
389
    in
390

391
    let rec label_parent { kind; identifier; location } tokens : LabelParent.t =
392
      let kind = match_reference_kind location kind in
180✔
393
      match tokens with
179✔
394
      | [] -> (
129✔
395
          match path_prefix with
396
          | None -> (
109✔
397
              match kind with
398
              | ( `TUnknown | `TModule | `TModuleType | `TType | `TClass
2✔
399
                | `TClassType | `TPage ) as kind ->
1✔
400
                  `Root (identifier, kind)
401
              | _ ->
8✔
402
                  expected ~expect_paths:true
403
                    [
404
                      "module";
405
                      "module-type";
406
                      "type";
407
                      "class";
408
                      "class-type";
409
                      "page";
410
                    ]
411
                    location
412
                  |> Error.raise_exception)
8✔
413
          | Some p -> label_parent_path kind p identifier location)
20✔
414
      | next_token :: tokens -> (
50✔
415
          match kind with
416
          | `TUnknown -> `Dot (label_parent next_token tokens, identifier)
25✔
417
          | `TModule ->
3✔
418
              `Module
419
                (signature next_token tokens, ModuleName.make_std identifier)
2✔
420
          | `TModuleType ->
3✔
421
              `ModuleType
422
                (signature next_token tokens, ModuleTypeName.make_std identifier)
2✔
423
          | `TType ->
3✔
424
              `Type (signature next_token tokens, TypeName.make_std identifier)
2✔
425
          | `TClass ->
5✔
426
              `Class (signature next_token tokens, TypeName.make_std identifier)
3✔
427
          | `TClassType ->
1✔
428
              `ClassType
429
                (signature next_token tokens, TypeName.make_std identifier)
1✔
430
          | _ ->
10✔
431
              expected ~expect_paths:true
432
                [ "module"; "module-type"; "type"; "class"; "class-type" ]
433
                location
434
              |> Error.raise_exception)
10✔
435
    in
436

437
    let start_from_last_component { kind; identifier; location } old_kind tokens
438
        =
439
      let new_kind = match_reference_kind location kind in
676✔
440
      let kind =
673✔
441
        match old_kind with
442
        | None -> new_kind
634✔
443
        | Some (old_kind_string, old_kind_location) -> (
39✔
444
            let old_kind =
445
              match_reference_kind old_kind_location
446
                (`Old_prefix old_kind_string)
447
            in
448
            match new_kind with
35✔
449
            | `TUnknown -> old_kind
32✔
450
            | _ ->
3✔
451
                (if old_kind <> new_kind then
452
                   let new_kind_string =
1✔
453
                     match kind with `None -> "" | `Prefixed s -> s
×
454
                   in
455
                   reference_kinds_do_not_match old_kind_string new_kind_string
456
                     whole_reference_location
457
                   |> Error.raise_warning);
1✔
458
                new_kind)
3✔
459
      in
460

461
      match tokens with
462
      | [] -> (
310✔
463
          match path_prefix with
464
          | None -> `Root (identifier, kind)
245✔
465
          | Some p -> any_path kind p identifier location)
65✔
466
      | next_token :: tokens -> (
359✔
467
          match kind with
468
          | `TUnknown -> `Dot (label_parent next_token tokens, identifier)
112✔
469
          | `TModule ->
30✔
470
              `Module
471
                (signature next_token tokens, ModuleName.make_std identifier)
6✔
472
          | `TModuleType ->
5✔
473
              `ModuleType
474
                (signature next_token tokens, ModuleTypeName.make_std identifier)
3✔
475
          | `TType ->
16✔
476
              `Type (signature next_token tokens, TypeName.make_std identifier)
11✔
477
          | `TConstructor ->
42✔
478
              `Constructor
479
                (parent next_token tokens, ConstructorName.make_std identifier)
18✔
480
          | `TField ->
44✔
481
              `Field (parent next_token tokens, FieldName.make_std identifier)
13✔
NEW
482
          | `TUnboxedField ->
×
NEW
483
              `UnboxedField (parent next_token tokens, UnboxedFieldName.make_std identifier)
×
484
          | `TExtension ->
6✔
485
              `Extension
486
                (signature next_token tokens, ExtensionName.make_std identifier)
4✔
487
          | `TExtensionDecl ->
2✔
488
              `ExtensionDecl
489
                (signature next_token tokens, ExtensionName.make_std identifier)
2✔
490
          | `TException ->
4✔
491
              `Exception
492
                (signature next_token tokens, ExceptionName.make_std identifier)
2✔
493
          | `TValue ->
4✔
494
              `Value (signature next_token tokens, ValueName.make_std identifier)
2✔
495
          | `TClass ->
5✔
496
              `Class (signature next_token tokens, TypeName.make_std identifier)
3✔
497
          | `TClassType ->
4✔
498
              `ClassType
499
                (signature next_token tokens, TypeName.make_std identifier)
2✔
500
          | `TMethod ->
36✔
501
              `Method
502
                ( class_signature next_token tokens,
9✔
503
                  MethodName.make_std identifier )
36✔
504
          | `TInstanceVariable ->
4✔
505
              `InstanceVariable
506
                ( class_signature next_token tokens,
2✔
507
                  InstanceVariableName.make_std identifier )
4✔
508
          | `TLabel ->
19✔
509
              `Label
510
                (label_parent next_token tokens, LabelName.make_std identifier)
19✔
511
          | `TChildPage | `TChildModule ->
×
512
              let suggestion =
513
                Printf.sprintf "'child-%s' should be first." identifier
514
              in
515
              not_allowed ~what:"Child label"
×
516
                ~in_what:"the last component of a reference path" ~suggestion
517
                location
518
              |> Error.raise_exception
×
519
          | `TPage ->
2✔
520
              let suggestion =
521
                Printf.sprintf "Reference pages as '<parent_path>/%s'."
522
                  identifier
523
              in
524
              not_allowed ~what:"Page label"
2✔
525
                ~in_what:"on the right side of a dot" ~suggestion location
526
              |> Error.raise_exception
2✔
527
          | `TAsset ->
×
528
              let suggestion =
529
                Printf.sprintf "Reference assets as '<parent_path>/%s'."
530
                  identifier
531
              in
532
              not_allowed ~what:"Asset label"
×
533
                ~in_what:"on the right side of a dot" ~suggestion location
534
              |> Error.raise_exception)
×
535
    in
536
    start_from_last_component { kind; identifier; location } old_kind tokens
537
  in
538
  Error.catch_errors_and_warnings (fun () ->
539
      let old_kind, s, location =
709✔
540
        let rec find_old_reference_kind_separator index =
541
          if index < 0 then raise Not_found
668✔
542
          else
543
            match s.[index] with
9,488✔
544
            | ':' -> index
41✔
545
            | ')' -> (
15✔
546
                match String.rindex_from s index '(' with
547
                | index -> find_old_reference_kind_separator (index - 1)
15✔
548
                | exception (Not_found as exn) -> raise exn)
×
549
            | _ -> find_old_reference_kind_separator (index - 1)
9,432✔
550
        in
551
        match find_old_reference_kind_separator (String.length s - 1) with
709✔
552
        | index ->
41✔
553
            let old_kind = String.trim (String.sub s 0 index) in
41✔
554
            let old_kind_location =
41✔
555
              Location_.set_end_as_offset_from_start index
556
                whole_reference_location
557
            in
558
            let s = String.sub s (index + 1) (String.length s - (index + 1)) in
41✔
559
            let location =
41✔
560
              Location_.nudge_start (index + 1) whole_reference_location
561
            in
562
            (Some (old_kind, old_kind_location), s, location)
41✔
563
        | exception Not_found -> (None, s, whole_reference_location)
668✔
564
      in
565
      match tokenize location s with
566
      | last_token :: tokens, path_prefix ->
676✔
567
          parse_from_last_component last_token old_kind tokens path_prefix
568
      | [], _ ->
×
569
          should_not_be_empty ~what:"Reference target" whole_reference_location
570
          |> Error.raise_exception)
×
571

572
(* Parse references that do not contain a [/]. Raises errors and warnings. *)
573
let parse_asset whole_reference_location s :
574
    Paths.Reference.Asset.t Error.with_errors_and_warnings =
575
  let path = parse_path whole_reference_location s in
7✔
576
  Error.catch_errors_and_warnings (fun () -> `Asset_path path)
7✔
577

578
let read_path_longident location s =
579
  let rec loop : string -> int -> path option =
160✔
580
   fun s pos ->
581
    try
335✔
582
      let idx = String.rindex_from s pos '.' in
583
      let name = String.sub s (idx + 1) (pos - idx) in
176✔
584
      if String.length name = 0 then None
1✔
585
      else
586
        match loop s (idx - 1) with
175✔
587
        | None -> None
1✔
588
        | Some parent -> Some (`Dot (parent, name))
174✔
589
    with Not_found ->
159✔
590
      let name = String.sub s 0 (pos + 1) in
591
      if String.length name = 0 then None else Some (`Root name)
1✔
592
  in
593
  Error.catch_warnings (fun () ->
594
      match loop s (String.length s - 1) with
160✔
595
      | Some r -> Ok (r :> path)
158✔
596
      | None -> Error (expected_err_str "a valid path" location))
2✔
597

598
let read_mod_longident location lid =
599
  Error.catch_warnings (fun () ->
30✔
600
      match Error.raise_warnings (parse location lid) with
30✔
601
      | Error _ as e -> e
×
602
      | Ok p -> (
30✔
603
          match p with
604
          | (`Root (_, (`TUnknown | `TModule)) | `Dot (_, _) | `Module (_, _))
×
605
            as r ->
606
              Ok r
607
          | _ -> Error (expected_err_str "a reference to a module" location)))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc