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

ocaml / odoc / 2168

10 Jul 2024 02:38PM UTC coverage: 71.437% (-0.4%) from 71.864%
2168

Pull #1142

github

web-flow
Merge 9bc2c3b35 into de54ed266
Pull Request #1142: Parsing of path-references to pages and modules

68 of 127 new or added lines in 6 files covered. (53.54%)

700 existing lines in 17 files now uncovered.

9794 of 13710 relevant lines covered (71.44%)

3534.91 hits per line

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

89.1
/src/model/reference.ml
1
let expected_err :
2
    (Format.formatter -> 'a -> unit) -> 'a -> Location_.span -> Error.t =
3
 fun pp_a a -> Error.make "Expected %a." pp_a a
151✔
4

5
let expected_err_str : string -> Location_.span -> Error.t =
6
  expected_err Format.pp_print_string
982✔
7

8
let unknown_reference_qualifier : string -> Location_.span -> Error.t =
9
  Error.make "Unknown reference qualifier '%s'."
982✔
10

11
let deprecated_reference_kind : string -> string -> Location_.span -> Error.t =
12
  Error.make "'%s' is deprecated, use '%s' instead."
982✔
13

14
let reference_kinds_do_not_match : string -> string -> Location_.span -> Error.t
15
    =
16
  Error.make "Old-style reference kind ('%s:') does not match new ('%s-')."
982✔
17

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

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

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

43
let deprecated_reference_kind location kind replacement =
44
  deprecated_reference_kind kind replacement location |> Error.raise_warning
8✔
45

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

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

98
type reference_kind = [ Paths.Reference.tag_any | `TPathComponent ]
99

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

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

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

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

180
    if identifier = "" then
2,142✔
181
      should_not_be_empty ~what:"Identifier in reference" location
182
      |> Error.raise_exception;
×
183

184
    (identifier, location)
2,110✔
185
  and scan_kind identifier identifier_location started_at index tokens =
186
    match s.[index] with
4,210✔
187
    | exception Invalid_argument _ ->
203✔
188
        let kind, location = kind_ended identifier_location started_at index in
189
        { kind; identifier; location } :: tokens
203✔
190
    | '.' ->
321✔
191
        let kind, location = kind_ended identifier_location started_at index in
192
        scan_identifier index 0 (index - 1)
321✔
193
          ({ kind; identifier; location } :: tokens)
194
    | '/' ->
4✔
195
        let kind, location = kind_ended identifier_location started_at index in
196
        scan_path index (index - 1) ({ kind; identifier; location } :: tokens)
4✔
197
    | _ ->
3,682✔
198
        scan_kind identifier identifier_location started_at (index - 1) tokens
199
  and kind_ended identifier_location started_at index =
200
    let offset = index + 1 in
528✔
201
    let length = started_at - offset in
202
    let kind = `Prefixed (String.sub s offset length) in
528✔
203
    let location = Location_.in_string s ~offset ~length location in
204
    let location = Location_.span [ location; identifier_location ] in
528✔
205
    (kind, location)
528✔
206
  and scan_path started_at index tokens =
207
    (* The parsing rules are different for [/]-separated components. [-"".()] are
208
       no longer meaningful. *)
209
    match s.[index] with
145✔
210
    | exception Invalid_argument _ -> path_ended started_at index :: tokens
32✔
211
    | '/' -> scan_path index (index - 1) (path_ended started_at index :: tokens)
25✔
212
    | _ -> scan_path started_at (index - 1) tokens
88✔
213
  and path_ended started_at index =
214
    let offset = index + 1 in
57✔
215
    let length = started_at - offset in
216
    let identifier = String.sub s offset length in
217
    let location = Location_.in_string s ~offset ~length location in
57✔
218
    { kind = `End_in_slash; identifier; location }
57✔
219
  in
220

221
  scan_identifier (String.length s) 0 (String.length s - 1) [] |> List.rev
1,600✔
222

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

231
(* Parse references that do not contain a [/]. Raises errors and warnings. *)
232
let parse whole_reference_location s :
233
    Paths.Reference.t Error.with_errors_and_warnings =
234
  let open Paths.Reference in
1,633✔
235
  let open Names in
236
  let rec path components next_token tokens : Hierarchy.t =
237
    match (next_token.kind, tokens) with
45✔
238
    | `End_in_slash, [] -> (
21✔
239
        match next_token.identifier with
240
        | "" ->
7✔
241
            (* {!/identifier} *)
242
            (`TAbsolutePath, components)
243
        | "." ->
4✔
244
            (* {!./identifier} *)
245
            (`TRelativePath, components)
246
        | c ->
10✔
247
            (* {!identifier'/identifier} *)
248
            (`TRelativePath, c :: components))
249
    | `End_in_slash, [ { kind = `End_in_slash; identifier = ""; _ } ]
13✔
250
      when next_token.identifier = "" ->
251
        (* {!//identifier} *)
252
        (`TCurrentPackage, components)
8✔
253
    | `End_in_slash, next_token' :: tokens' ->
16✔
254
        if next_token.identifier = "" then
255
          should_not_be_empty ~what:"Identifier in path reference"
256
            next_token.location
NEW
257
          |> Error.raise_exception;
×
258
        (* {!path/identifier} *)
259
        path (next_token.identifier :: components) next_token' tokens'
14✔
260
    | (`None | `Prefixed _), _ ->
261
        (* Cannot be outputed by the lexer. *)
262
        assert false
263
  in
264

265
  let ends_in_slash next_token =
266
    match next_token.kind with `End_in_slash -> true | _ -> false
32✔
267
  in
268

269
  let rec signature { kind; identifier; location } tokens : Signature.t =
270
    let kind = match_reference_kind location kind in
119✔
271
    match tokens with
119✔
272
    | [] -> (
96✔
273
        match kind with
274
        | (`TUnknown | `TModule | `TModuleType) as kind ->
5✔
275
            `Root (identifier, kind)
276
        | `TPathComponent -> assert false
277
        | _ ->
39✔
278
            expected ~expect_paths:true [ "module"; "module-type" ] location
279
            |> Error.raise_exception)
39✔
NEW
280
    | next_token :: tokens when ends_in_slash next_token -> (
×
281
        match kind with
NEW
282
        | `TUnknown | `TModule ->
×
NEW
283
            `Module_path (path [ identifier ] next_token tokens)
×
NEW
284
        | _ ->
×
285
            expected ~expect_paths:true [ "module" ] location
UNCOV
286
            |> Error.raise_exception)
×
287
    | next_token :: tokens -> (
23✔
288
        match kind with
289
        | `TUnknown ->
3✔
290
            `Dot ((parent next_token tokens :> LabelParent.t), identifier)
2✔
291
        | `TModule ->
4✔
292
            `Module (signature next_token tokens, ModuleName.make_std identifier)
3✔
293
        | `TModuleType ->
4✔
294
            `ModuleType
295
              (signature next_token tokens, ModuleTypeName.make_std identifier)
3✔
NEW
UNCOV
296
        | `TPathComponent ->
×
297
            `Module_path (path [ identifier ] next_token tokens)
×
298
        | _ ->
12✔
299
            expected ~expect_paths:true [ "module"; "module-type" ] location
300
            |> Error.raise_exception)
12✔
301
  and parent { kind; identifier; location } tokens : FragmentTypeParent.t =
302
    let kind = match_reference_kind location kind in
101✔
303
    match tokens with
101✔
304
    | [] -> (
52✔
305
        match kind with
306
        | (`TUnknown | `TModule | `TModuleType | `TType) as kind ->
2✔
307
            `Root (identifier, kind)
308
        | _ ->
27✔
309
            expected [ "module"; "module-type"; "type" ] location
310
            |> Error.raise_exception)
27✔
NEW
311
    | next_token :: tokens when ends_in_slash next_token -> (
×
312
        match kind with
NEW
313
        | `TUnknown | `TModule ->
×
NEW
314
            `Module_path (path [ identifier ] next_token tokens)
×
NEW
315
        | _ ->
×
316
            expected ~expect_paths:true [ "module" ] location
UNCOV
317
            |> Error.raise_exception)
×
318
    | next_token :: tokens -> (
49✔
319
        match kind with
320
        | `TUnknown ->
9✔
321
            `Dot ((parent next_token tokens :> LabelParent.t), identifier)
6✔
322
        | `TModule ->
4✔
323
            `Module (signature next_token tokens, ModuleName.make_std identifier)
3✔
324
        | `TModuleType ->
4✔
325
            `ModuleType
326
              (signature next_token tokens, ModuleTypeName.make_std identifier)
3✔
327
        | `TType ->
6✔
328
            `Type (signature next_token tokens, TypeName.make_std identifier)
4✔
329
        | _ ->
26✔
330
            expected [ "module"; "module-type"; "type" ] location
331
            |> Error.raise_exception)
26✔
332
  in
333

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

357
  let any_path { identifier; location; _ } kind next_token tokens =
358
    let path () = path [ identifier ] next_token tokens in
31✔
359
    match kind with
360
    | `TUnknown -> `Any_path (path ())
26✔
361
    | `TModule -> `Module_path (path ())
1✔
362
    | `TPage -> `Page_path (path ())
2✔
363
    | _ ->
1✔
364
        expected ~expect_paths:true [ "module"; "page" ] location
365
        |> Error.raise_exception
1✔
366
  in
367

368
  let rec label_parent ({ kind; identifier; location } as token) tokens :
369
      LabelParent.t =
370
    let kind = match_reference_kind location kind in
181✔
371
    match tokens with
180✔
372
    | [] -> (
109✔
373
        match kind with
374
        | ( `TUnknown | `TModule | `TModuleType | `TType | `TClass | `TClassType
1✔
375
          | `TPage ) as kind ->
7✔
376
            `Root (identifier, kind)
377
        | `TPathComponent -> assert false
378
        | _ ->
8✔
379
            expected ~expect_paths:true
380
              [ "module"; "module-type"; "type"; "class"; "class-type"; "page" ]
381
              location
382
            |> Error.raise_exception)
8✔
383
    | next_token :: tokens when ends_in_slash next_token ->
71✔
384
        any_path token kind next_token tokens
19✔
385
    | next_token :: tokens -> (
52✔
386
        match kind with
387
        | `TUnknown -> `Dot (label_parent next_token tokens, identifier)
27✔
388
        | `TModule ->
3✔
389
            `Module (signature next_token tokens, ModuleName.make_std identifier)
2✔
390
        | `TModuleType ->
3✔
391
            `ModuleType
392
              (signature next_token tokens, ModuleTypeName.make_std identifier)
2✔
393
        | `TType ->
3✔
394
            `Type (signature next_token tokens, TypeName.make_std identifier)
2✔
395
        | `TClass ->
5✔
396
            `Class (signature next_token tokens, ClassName.make_std identifier)
3✔
397
        | `TClassType ->
1✔
398
            `ClassType
399
              (signature next_token tokens, ClassTypeName.make_std identifier)
1✔
400
        | `TPathComponent -> `Page_path (path [ identifier ] next_token tokens)
×
401
        | _ ->
10✔
402
            expected ~expect_paths:true
403
              [ "module"; "module-type"; "type"; "class"; "class-type" ]
404
              location
405
            |> Error.raise_exception)
10✔
406
  in
407

408
  let start_from_last_component ({ kind; identifier; location } as token)
409
      old_kind tokens =
410
    let new_kind = match_reference_kind location kind in
1,600✔
411
    let kind =
1,597✔
412
      match old_kind with
413
      | None -> new_kind
1,558✔
414
      | Some (old_kind_string, old_kind_location) -> (
39✔
415
          let old_kind =
416
            match_reference_kind old_kind_location (`Old_prefix old_kind_string)
417
          in
418
          match new_kind with
35✔
419
          | `TUnknown -> old_kind
32✔
420
          | _ ->
3✔
421
              (if old_kind <> new_kind then
422
                 let new_kind_string =
1✔
423
                   match kind with
NEW
UNCOV
424
                   | `None | `End_in_slash -> ""
×
425
                   | `Prefixed s -> s
1✔
426
                 in
427
                 reference_kinds_do_not_match old_kind_string new_kind_string
428
                   whole_reference_location
429
                 |> Error.raise_warning);
1✔
430
              new_kind)
3✔
431
    in
432

433
    match tokens with
434
    | [] -> (
1,222✔
435
        match kind with
436
        | #Paths.Reference.tag_any as kind -> `Root (identifier, kind)
1,222✔
437
        | `TPathComponent -> assert false)
438
    | next_token :: tokens when ends_in_slash next_token ->
371✔
439
        any_path token kind next_token tokens
13✔
440
    | next_token :: tokens -> (
358✔
441
        match kind with
442
        | `TUnknown -> `Dot (label_parent next_token tokens, identifier)
111✔
443
        | `TModule ->
30✔
444
            `Module (signature next_token tokens, ModuleName.make_std identifier)
6✔
445
        | `TModuleType ->
5✔
446
            `ModuleType
447
              (signature next_token tokens, ModuleTypeName.make_std identifier)
3✔
448
        | `TType ->
16✔
449
            `Type (signature next_token tokens, TypeName.make_std identifier)
11✔
450
        | `TConstructor ->
42✔
451
            `Constructor
452
              (parent next_token tokens, ConstructorName.make_std identifier)
18✔
453
        | `TField ->
44✔
454
            `Field (parent next_token tokens, FieldName.make_std identifier)
13✔
455
        | `TExtension ->
6✔
456
            `Extension
457
              (signature next_token tokens, ExtensionName.make_std identifier)
4✔
458
        | `TExtensionDecl ->
2✔
459
            `ExtensionDecl
460
              (signature next_token tokens, ExtensionName.make_std identifier)
2✔
461
        | `TException ->
4✔
462
            `Exception
463
              (signature next_token tokens, ExceptionName.make_std identifier)
2✔
464
        | `TValue ->
4✔
465
            `Value (signature next_token tokens, ValueName.make_std identifier)
2✔
466
        | `TClass ->
5✔
467
            `Class (signature next_token tokens, ClassName.make_std identifier)
3✔
468
        | `TClassType ->
4✔
469
            `ClassType
470
              (signature next_token tokens, ClassTypeName.make_std identifier)
2✔
471
        | `TMethod ->
36✔
472
            `Method
473
              (class_signature next_token tokens, MethodName.make_std identifier)
9✔
474
        | `TInstanceVariable ->
4✔
475
            `InstanceVariable
476
              ( class_signature next_token tokens,
2✔
477
                InstanceVariableName.make_std identifier )
4✔
478
        | `TLabel ->
19✔
479
            `Label
480
              (label_parent next_token tokens, LabelName.make_std identifier)
19✔
UNCOV
481
        | `TChildPage | `TChildModule ->
×
482
            let suggestion =
483
              Printf.sprintf "'child-%s' should be first." identifier
484
            in
UNCOV
485
            not_allowed ~what:"Child label"
×
486
              ~in_what:"the last component of a reference path" ~suggestion
487
              location
488
            |> Error.raise_exception
×
489
        | `TPage ->
2✔
490
            let () =
491
              match next_token.kind with
NEW
492
              | `End_in_slash -> ()
×
NEW
493
              | `None | `Prefixed _ ->
×
494
                  let suggestion =
495
                    Printf.sprintf "Reference pages as '<parent_path>/%s'."
496
                      identifier
497
                  in
498
                  not_allowed ~what:"Page label"
2✔
499
                    ~in_what:"on the right side of a dot" ~suggestion location
500
                  |> Error.raise_exception
×
501
            in
502
            (* Prefixed pages are not differentiated. *)
NEW
UNCOV
503
            `Page_path (path [ identifier ] next_token tokens)
×
UNCOV
504
        | `TPathComponent -> `Page_path (path [ identifier ] next_token tokens))
×
505
  in
506

507
  let old_kind, s, location =
508
    let rec find_old_reference_kind_separator index =
509
      if index < 0 then raise Not_found
1,592✔
510
      else
511
        match s.[index] with
17,812✔
512
        | ':' -> index
41✔
513
        | ')' -> (
15✔
514
            match String.rindex_from s index '(' with
515
            | index -> find_old_reference_kind_separator (index - 1)
15✔
UNCOV
516
            | exception (Not_found as exn) -> raise exn)
×
517
        | _ -> find_old_reference_kind_separator (index - 1)
17,756✔
518
    in
519
    match find_old_reference_kind_separator (String.length s - 1) with
1,633✔
520
    | index ->
41✔
521
        let old_kind = String.trim (String.sub s 0 index) in
41✔
522
        let old_kind_location =
41✔
523
          Location_.set_end_as_offset_from_start index whole_reference_location
524
        in
525
        let s = String.sub s (index + 1) (String.length s - (index + 1)) in
41✔
526
        let location =
41✔
527
          Location_.nudge_start (index + 1) whole_reference_location
528
        in
529
        (Some (old_kind, old_kind_location), s, location)
41✔
530
    | exception Not_found -> (None, s, whole_reference_location)
1,592✔
531
  in
532

533
  Error.catch_errors_and_warnings (fun () ->
534
      match tokenize location s with
1,633✔
535
      | last_token :: tokens ->
1,600✔
536
          start_from_last_component last_token old_kind tokens
537
      | [] ->
×
538
          should_not_be_empty ~what:"Reference target" whole_reference_location
UNCOV
539
          |> Error.raise_exception)
×
540

541
type path = [ `Root of string | `Dot of Paths.Path.Module.t * string ]
542

543
let read_path_longident location s =
544
  let open Paths.Path in
160✔
545
  let rec loop : string -> int -> path option =
546
   fun s pos ->
547
    try
335✔
548
      let idx = String.rindex_from s pos '.' in
549
      let name = String.sub s (idx + 1) (pos - idx) in
176✔
550
      if String.length name = 0 then None
1✔
551
      else
552
        match loop s (idx - 1) with
175✔
553
        | None -> None
1✔
554
        | Some parent -> Some (`Dot ((parent :> Module.t), name))
174✔
555
    with Not_found ->
159✔
556
      let name = String.sub s 0 (pos + 1) in
557
      if String.length name = 0 then None else Some (`Root name)
1✔
558
  in
559
  Error.catch_warnings (fun () ->
560
      match loop s (String.length s - 1) with
160✔
561
      | Some r -> Result.Ok (r :> path)
158✔
562
      | None -> Result.Error (expected_err_str "a valid path" location))
2✔
563

564
let read_mod_longident location lid =
565
  Error.catch_warnings (fun () ->
30✔
566
      match Error.raise_warnings (parse location lid) with
30✔
UNCOV
567
      | Error _ as e -> e
×
568
      | Ok p -> (
30✔
569
          match p with
UNCOV
570
          | (`Root (_, (`TUnknown | `TModule)) | `Dot (_, _) | `Module (_, _))
×
571
            as r ->
572
              Result.Ok r
NEW
573
          | _ ->
×
NEW
UNCOV
574
              Result.Error (expected_err_str "a reference to a module" location)
×
575
          ))
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