-
Notifications
You must be signed in to change notification settings - Fork 98
/
Copy pathlocation_check.ml
737 lines (662 loc) · 27.7 KB
/
location_check.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
open Import
module Non_intersecting_ranges : sig
type t
val empty : t
val insert : node_name:string -> Location.t -> t -> t
val union : t -> t -> t
val covered_by : t -> loc:Location.t -> bool
(** [covered_by t ~loc = true] iff [t] is covered by [loc] *)
val find_outside : Location.t -> t -> string * Location.t
end = struct
type t = {
min_pos : Lexing.position option;
max_pos : Lexing.position option;
ranges : (string * Location.t) list;
}
let empty = { min_pos = None; max_pos = None; ranges = [] }
let rec insert ranges ((node_name, node_loc) as node) =
match ranges with
| [] -> [ node ]
| ((x_name, x_loc) as x) :: xs ->
let open Location in
if compare_pos node_loc.loc_start x_loc.loc_end >= 0 then
node :: x :: xs
else if compare_pos x_loc.loc_start node_loc.loc_end >= 0 then
x :: insert xs node
else
raise_errorf ~loc:node_loc
"invalid output from ppx, %s overlaps with %s at location:@.%a"
node_name x_name Location.print x_loc
let min_pos p1 p2 =
match (p1, p2) with
| None, None -> None
| (Some _ as p), None | None, (Some _ as p) -> p
| Some p1, Some p2 -> Some (Location.min_pos p1 p2)
let max_pos p1 p2 =
match (p1, p2) with
| None, None -> None
| (Some _ as p), None | None, (Some _ as p) -> p
| Some p1, Some p2 -> Some (Location.max_pos p1 p2)
let longest_first l1 l2 ~stop_after =
let rec loop xs ys n =
match (xs, ys, n) with
| [], _, _ | _, _, 0 -> (l2, l1)
| _, [], _ -> (l1, l2)
| _ :: xs, _ :: ys, n -> loop xs ys (n - 1)
in
loop l1 l2 stop_after
let union t1 t2 =
let init, l = longest_first t1.ranges t2.ranges ~stop_after:42 in
let ranges = List.fold_left l ~init ~f:insert in
{
min_pos = min_pos t1.min_pos t2.min_pos;
max_pos = max_pos t1.max_pos t2.max_pos;
ranges;
}
let insert ~node_name loc t =
{
min_pos = min_pos (Some loc.loc_start) t.min_pos;
max_pos = max_pos (Some loc.loc_end) t.max_pos;
ranges = insert t.ranges (node_name, loc);
}
let covered_by t ~loc =
match (t.min_pos, t.max_pos) with
| None, None -> true
| Some min_pos, Some max_pos ->
Location.compare_pos min_pos loc.loc_start >= 0
&& Location.compare_pos max_pos loc.loc_end <= 0
| _, _ ->
(* there are no open ranges *)
assert false
let find_outside loc t =
List.find t.ranges ~f:(fun (_, l) ->
Location.compare_pos loc.loc_start l.loc_start > 0
|| Location.compare_pos loc.loc_end l.loc_end < 0)
end
let reloc_pmty_functors x =
let outmost_loc = x.pmty_loc in
let rec aux x =
match x.pmty_desc with
| Pmty_functor (Unit, initial_res) ->
let res = aux initial_res in
if res == initial_res then x
else { x with pmty_desc = Pmty_functor (Unit, res) }
| Pmty_functor (Named (id, mty), initial_res) ->
let res = aux initial_res in
if Location.compare outmost_loc res.pmty_loc = 0 then
let loc_start = mty.pmty_loc.loc_end in
let res = { res with pmty_loc = { res.pmty_loc with loc_start } } in
{ x with pmty_desc = Pmty_functor (Named (id, mty), res) }
else if res == initial_res then x
else { x with pmty_desc = Pmty_functor (Named (id, mty), res) }
| _ -> x
in
aux x
let reloc_pmod_functors x =
let outmost_loc = x.pmod_loc in
let rec aux x =
match x.pmod_desc with
| Pmod_functor (Unit, initial_res) ->
let res = aux initial_res in
if res == initial_res then x
else { x with pmod_desc = Pmod_functor (Unit, res) }
| Pmod_functor (Named (id, mty), initial_res) ->
let res = aux initial_res in
if Location.compare outmost_loc res.pmod_loc = 0 then
let loc_start = mty.pmty_loc.loc_end in
let res = { res with pmod_loc = { res.pmod_loc with loc_start } } in
{ x with pmod_desc = Pmod_functor (Named (id, mty), res) }
else if res == initial_res then x
else { x with pmod_desc = Pmod_functor (Named (id, mty), res) }
| _ -> x
in
aux x
let all_payloads_inside_parent ~loc =
List.for_all ~f:(fun attr ->
Location.compare_pos loc.loc_end attr.attr_loc.loc_end >= 0)
let file : string option ref = ref None
let same_file_so_far = ref true
let stayed_in_the_same_file fname =
(* TODO: remove uses of Location.none from the ppxes. *)
if String.equal fname "_none_" then true (* do nothing for now. *)
else
match !file with
| None ->
file := Some fname;
true
| Some orig_fname ->
String.equal orig_fname fname
||
(same_file_so_far := false;
false)
let should_ignore loc attrs =
(* If the filename changed, then there were line directives, and the locations
are all messed up. *)
(not (stayed_in_the_same_file loc.loc_start.pos_fname))
||
(* Ignore things explicitly marked. *)
List.exists
~f:(fun attr ->
String.equal attr.attr_name.txt
Merlin_helpers.hide_attribute.attr_name.txt)
attrs
let rec extract_constraint e =
match e.pexp_desc with
| Pexp_constraint (e, ct) | Pexp_coerce (e, _, ct) -> Some (e, ct)
| Pexp_newtype (name, exp) ->
Option.map (extract_constraint exp) ~f:(fun (exp, ct) ->
( {
e with
pexp_desc = Pexp_newtype (name, exp);
pexp_loc = { e.pexp_loc with loc_ghost = true };
},
ct ))
| _ -> None
let do_check ~node_name node_loc childrens_locs siblings_locs =
if not !same_file_so_far then Non_intersecting_ranges.empty
else if node_loc.loc_ghost then
Non_intersecting_ranges.union childrens_locs siblings_locs
else if Non_intersecting_ranges.covered_by childrens_locs ~loc:node_loc then
Non_intersecting_ranges.insert ~node_name node_loc siblings_locs
else
let child_name, child_loc =
Non_intersecting_ranges.find_outside node_loc childrens_locs
in
Location.raise_errorf ~loc:node_loc
"invalid output from ppx:@ this %s is built from a%s whose location is \
outside of this node's.@.Child %s found at:@ %a"
node_name
((match String.unsafe_get child_name 0 with
| 'a' | 'e' | 'i' | 'o' | 'u' -> "n "
| _ -> " ")
^ child_name)
child_name Location.print child_loc
let enforce_invariants fname =
let () = file := fname in
object (self)
inherit [Non_intersecting_ranges.t] Ast_traverse.fold as super
(* TODO: we should generate a class which enforces the location invariant.
And then we should only override the methods where we need an escape
hatch because the parser isn't doing the right thing.
That would ensure that we stay up to date as the AST changes. *)
method! longident_loc x siblings =
if x.loc.loc_ghost then siblings
else Non_intersecting_ranges.insert ~node_name:"ident" x.loc siblings
method! row_field x siblings_locs =
if should_ignore x.prf_loc x.prf_attributes then siblings_locs
else
let childrens_locs = super#row_field x Non_intersecting_ranges.empty in
do_check ~node_name:"row field" x.prf_loc childrens_locs siblings_locs
method! object_field x siblings_locs =
if should_ignore x.pof_loc x.pof_attributes then siblings_locs
else
let childrens_locs =
super#object_field x Non_intersecting_ranges.empty
in
do_check ~node_name:"object field" x.pof_loc childrens_locs
siblings_locs
method! binding_op x siblings_locs =
let childrens_locs = super#binding_op x Non_intersecting_ranges.empty in
do_check ~node_name:"binding operator" x.pbop_loc childrens_locs
siblings_locs
method! value_description x siblings_locs =
if should_ignore x.pval_loc x.pval_attributes then siblings_locs
else
let childrens_locs =
super#value_description x Non_intersecting_ranges.empty
in
do_check ~node_name:"value description" x.pval_loc childrens_locs
siblings_locs
method! type_declaration x siblings_locs =
if should_ignore x.ptype_loc x.ptype_attributes then siblings_locs
else
let childrens_locs =
super#type_declaration x Non_intersecting_ranges.empty
in
do_check ~node_name:"type declaration" x.ptype_loc childrens_locs
siblings_locs
method! label_declaration x siblings_locs =
if should_ignore x.pld_loc x.pld_attributes then siblings_locs
else
let childrens_locs =
super#label_declaration x Non_intersecting_ranges.empty
in
do_check ~node_name:"label declaration" x.pld_loc childrens_locs
siblings_locs
method! constructor_declaration x siblings_locs =
if should_ignore x.pcd_loc x.pcd_attributes then siblings_locs
else
let childrens_locs =
super#constructor_declaration x Non_intersecting_ranges.empty
in
do_check ~node_name:"constructor declaration" x.pcd_loc childrens_locs
siblings_locs
method! type_extension x siblings_locs =
if should_ignore x.ptyext_loc x.ptyext_attributes then siblings_locs
else
let childrens_locs =
super#type_extension x Non_intersecting_ranges.empty
in
do_check ~node_name:"type extension" x.ptyext_loc childrens_locs
siblings_locs
method! extension_constructor x siblings_locs =
if should_ignore x.pext_loc x.pext_attributes then siblings_locs
else
let childrens_locs =
super#extension_constructor x Non_intersecting_ranges.empty
in
do_check ~node_name:"extension constructor" x.pext_loc childrens_locs
siblings_locs
method! class_type x siblings_locs =
if should_ignore x.pcty_loc x.pcty_attributes then siblings_locs
else
let childrens_locs = super#class_type x Non_intersecting_ranges.empty in
do_check ~node_name:"class type" x.pcty_loc childrens_locs siblings_locs
method! class_type_field x siblings_locs =
if should_ignore x.pctf_loc x.pctf_attributes then siblings_locs
else
let childrens_locs =
super#class_type_field x Non_intersecting_ranges.empty
in
do_check ~node_name:"class type field" x.pctf_loc childrens_locs
siblings_locs
method! class_infos f x siblings_locs =
if should_ignore x.pci_loc x.pci_attributes then siblings_locs
else
let childrens_locs =
super#class_infos f x Non_intersecting_ranges.empty
in
do_check ~node_name:"class" x.pci_loc childrens_locs siblings_locs
method! class_expr x siblings_locs =
if should_ignore x.pcl_loc x.pcl_attributes then siblings_locs
else
let childrens_locs = super#class_expr x Non_intersecting_ranges.empty in
do_check ~node_name:"class expression" x.pcl_loc childrens_locs
siblings_locs
method! class_field x siblings_locs =
if should_ignore x.pcf_loc x.pcf_attributes then siblings_locs
else
let childrens_locs =
super#class_field x Non_intersecting_ranges.empty
in
do_check ~node_name:"class field" x.pcf_loc childrens_locs siblings_locs
method! signature_item x siblings_locs =
if should_ignore x.psig_loc [] then siblings_locs
else
let childrens_locs =
super#signature_item x Non_intersecting_ranges.empty
in
do_check ~node_name:"signature item" x.psig_loc childrens_locs
siblings_locs
method! module_declaration x siblings_locs =
if should_ignore x.pmd_loc x.pmd_attributes then siblings_locs
else
let childrens_locs =
super#module_declaration x Non_intersecting_ranges.empty
in
do_check ~node_name:"module declaration" x.pmd_loc childrens_locs
siblings_locs
method! module_substitution x siblings_locs =
if should_ignore x.pms_loc x.pms_attributes then siblings_locs
else
let childrens_locs =
super#module_substitution x Non_intersecting_ranges.empty
in
do_check ~node_name:"module substitution" x.pms_loc childrens_locs
siblings_locs
method! module_type_declaration x siblings_locs =
if should_ignore x.pmtd_loc x.pmtd_attributes then siblings_locs
else
let childrens_locs =
super#module_type_declaration x Non_intersecting_ranges.empty
in
do_check ~node_name:"module type declaration" x.pmtd_loc childrens_locs
siblings_locs
method! open_infos f x siblings_locs =
if should_ignore x.popen_loc x.popen_attributes then siblings_locs
else
let childrens_locs =
super#open_infos f x Non_intersecting_ranges.empty
in
do_check ~node_name:"open" x.popen_loc childrens_locs siblings_locs
method! include_infos f x siblings_locs =
if should_ignore x.pincl_loc x.pincl_attributes then siblings_locs
else
let childrens_locs =
super#include_infos f x Non_intersecting_ranges.empty
in
do_check ~node_name:"include" x.pincl_loc childrens_locs siblings_locs
method! structure_item x siblings_locs =
if should_ignore x.pstr_loc [] then siblings_locs
else
let childrens_locs =
super#structure_item x Non_intersecting_ranges.empty
in
do_check ~node_name:"structure item" x.pstr_loc childrens_locs
siblings_locs
method! module_binding x siblings_locs =
if should_ignore x.pmb_loc x.pmb_attributes then siblings_locs
else
let childrens_locs =
super#module_binding x Non_intersecting_ranges.empty
in
do_check ~node_name:"module binding" x.pmb_loc childrens_locs
siblings_locs
(******************************************)
(* The following is special cased because *)
(* the type constraint is duplicated. *)
(******************************************)
method! value_binding x siblings_locs =
if should_ignore x.pvb_loc x.pvb_attributes then siblings_locs
else
let childrens_locs =
match (x.pvb_pat.ppat_desc, extract_constraint x.pvb_expr) with
(* let x : type a b c. ct = e *)
| ( Ppat_constraint
(pvb_pat, { ptyp_desc = Ptyp_poly (_ :: _, ctp); _ }),
Some (pvb_expr, cte) )
(* let x : ct = e
let x :> ct = e *)
| ( Ppat_constraint (pvb_pat, { ptyp_desc = Ptyp_poly ([], ctp); _ }),
Some (pvb_expr, cte) )
when Location.compare ctp.ptyp_loc cte.ptyp_loc = 0 ->
let acc = Non_intersecting_ranges.empty in
let acc = self#pattern pvb_pat acc in
let _acc = self#core_type ctp acc in
let acc = self#expression pvb_expr acc in
let acc = self#attributes x.pvb_attributes acc in
acc
| _ -> super#value_binding x Non_intersecting_ranges.empty
in
do_check ~node_name:"value binding" x.pvb_loc childrens_locs
siblings_locs
(**********************************************)
(* The following is special cased because of: *)
(* MT [@attr payload] *)
(* where the loc of payload is outside the *)
(* loc of the module type.... *)
(* and *)
(* functor (A : S) (B : S) ... *)
(* where the loc of [(B : S) ...] is the same *)
(* as the loc of the outermost module type. *)
(**********************************************)
method! module_type x siblings_locs =
if should_ignore x.pmty_loc x.pmty_attributes then siblings_locs
else
let x = reloc_pmty_functors x in
let childrens_locs =
if all_payloads_inside_parent ~loc:x.pmty_loc x.pmty_attributes then
super#module_type x Non_intersecting_ranges.empty
else
let acc =
self#module_type_desc x.pmty_desc Non_intersecting_ranges.empty
in
let _ = self#attributes x.pmty_attributes acc in
acc
in
do_check ~node_name:"module type" x.pmty_loc childrens_locs
siblings_locs
(**********************************************)
(* The following is special cased because of: *)
(* ME [@attr payload] *)
(* where the loc of payload is outside the *)
(* loc of the module expr.... *)
(* and *)
(* functor (A : S) (B : S) ... *)
(* where the loc of [(B : S) ...] is the same *)
(* as the loc of the outermost module expr. *)
(**********************************************)
method! module_expr x siblings_locs =
if should_ignore x.pmod_loc x.pmod_attributes then siblings_locs
else
let x = reloc_pmod_functors x in
let childrens_locs =
if all_payloads_inside_parent ~loc:x.pmod_loc x.pmod_attributes then
super#module_expr x Non_intersecting_ranges.empty
else
let acc =
self#module_expr_desc x.pmod_desc Non_intersecting_ranges.empty
in
let _ = self#attributes x.pmod_attributes acc in
acc
in
do_check ~node_name:"module expression" x.pmod_loc childrens_locs
siblings_locs
(*********************)
(* Same as above ... *)
(*********************)
method! core_type x siblings_locs =
if should_ignore x.ptyp_loc x.ptyp_attributes then siblings_locs
else
let childrens_locs =
if all_payloads_inside_parent ~loc:x.ptyp_loc x.ptyp_attributes then
super#core_type x Non_intersecting_ranges.empty
else
let acc =
self#core_type_desc x.ptyp_desc Non_intersecting_ranges.empty
in
let _ = self#attributes x.ptyp_attributes acc in
acc
in
do_check ~node_name:"core type" x.ptyp_loc childrens_locs siblings_locs
(*****************)
(* And again ... *)
(*****************)
method! expression x siblings_locs =
if should_ignore x.pexp_loc x.pexp_attributes then siblings_locs
else
let childrens_locs =
if all_payloads_inside_parent ~loc:x.pexp_loc x.pexp_attributes then
super#expression x Non_intersecting_ranges.empty
else
let acc =
self#expression_desc x.pexp_desc Non_intersecting_ranges.empty
in
let _ = self#attributes x.pexp_attributes acc in
acc
in
do_check ~node_name:"expression" x.pexp_loc childrens_locs siblings_locs
(*****************)
(* ... and again *)
(*****************)
method! pattern x siblings_locs =
if should_ignore x.ppat_loc x.ppat_attributes then siblings_locs
else
let childrens_locs =
if all_payloads_inside_parent ~loc:x.ppat_loc x.ppat_attributes then
super#pattern x Non_intersecting_ranges.empty
else
let acc =
self#pattern_desc x.ppat_desc Non_intersecting_ranges.empty
in
let _ = self#attributes x.ppat_attributes acc in
acc
in
do_check ~node_name:"pattern" x.ppat_loc childrens_locs siblings_locs
(***********************************************************)
(* The following is special cased because the location of *)
(* the construct equals the location of the type_exception *)
(* (and so covers the location of the attributes). *)
(***********************************************************)
method! type_exception x siblings_locs =
if should_ignore x.ptyexn_loc x.ptyexn_attributes then siblings_locs
else
let init = Non_intersecting_ranges.empty in
let childs_locs =
self#extension_constructor x.ptyexn_constructor init
in
let attrs_locs = self#attributes x.ptyexn_attributes init in
ignore
(do_check ~node_name:"exception" x.ptyexn_loc attrs_locs siblings_locs);
do_check ~node_name:"exception" x.ptyexn_loc childs_locs siblings_locs
(*******************************************)
(* The following is overridden because the *)
(* lhs is sometimes included in the rhs. *)
(*******************************************)
method! with_constraint x siblings_loc =
match x with
| Pwith_type (_, tdecl) | Pwith_typesubst (_, tdecl) ->
self#type_declaration tdecl siblings_loc
| _ -> super#with_constraint x siblings_loc
(******************************************)
(* The following is overridden because of:*)
(* - Foo.{ bar; ... } *)
(* - Foo.[ bar; ... ] *)
(* - Foo.( bar; ... ) *)
(* - method x : type a. ... = ... *)
(* - foo.@(bar) *)
(* - foo.@(bar) <- baz *)
(* - foo.%.{bar} *)
(* - foo.%.{bar} <- baz *)
(* - foo.%.[bar] *)
(* - foo.%.[bar] <- baz *)
(******************************************)
method! expression_desc x acc =
match x with
| Pexp_record (labels, expr_o) ->
let acc =
self#list
(fun (lid, e) acc ->
if
Location.compare_pos lid.loc.loc_start e.pexp_loc.loc_start
= 0
then
if Location.compare lid.loc e.pexp_loc = 0 then
(* punning. *)
self#longident_loc lid acc
else
match e.pexp_desc with
| Pexp_constraint (e, c) ->
(* { foo : int } and { foo : int = x } ... *)
let _ = self#core_type c acc in
self#expression e acc
| _ ->
(* No idea what's going on there. *)
self#expression e acc
else
let acc = self#longident_loc lid acc in
let acc = self#expression e acc in
acc)
labels acc
in
self#option self#expression expr_o acc
| Pexp_open
(({ popen_expr = { pmod_desc = Pmod_ident lid; _ }; _ } as opn), e)
when Location.compare_pos lid.loc.loc_start e.pexp_loc.loc_start = 0
&& Location.compare_pos lid.loc.loc_end e.pexp_loc.loc_end <> 0 ->
(* let's relocate ... *)
let e_loc = { e.pexp_loc with loc_start = lid.loc.loc_end } in
super#expression_desc
(Pexp_open (opn, { e with pexp_loc = e_loc }))
acc
| Pexp_poly (e, Some { ptyp_desc = Ptyp_poly (_, ct); _ }) -> (
match extract_constraint e with
| Some (e, cte) when Location.compare cte.ptyp_loc ct.ptyp_loc = 0 ->
let acc = self#expression e acc in
let acc = self#core_type ct acc in
acc
| _ -> super#expression_desc x acc)
| Pexp_apply ({ pexp_desc = Pexp_ident { txt = lid; _ }; _ }, args) -> (
match Longident.last_exn lid with
| id
when String.is_prefix id ~prefix:"."
&& (String.is_suffix id ~suffix:"()"
|| String.is_suffix id ~suffix:"()<-"
|| String.is_suffix id ~suffix:"[]"
|| String.is_suffix id ~suffix:"[]<-"
|| String.is_suffix id ~suffix:"{}"
|| String.is_suffix id ~suffix:"{}<-") ->
self#list (fun (_, e) -> self#expression e) args acc
| exception _ -> super#expression_desc x acc
| _ -> super#expression_desc x acc)
| _ -> super#expression_desc x acc
(*******************************************************)
(* The following is overridden because of: *)
(* - punning. *)
(* - record field with type constraint. *)
(* - unpack locations being incorrect when constrained *)
(*******************************************************)
method! pattern_desc x acc =
match x with
| Ppat_record (labels, _) ->
self#list
(fun (lid, pat) acc ->
if
Location.compare_pos lid.loc.loc_start pat.ppat_loc.loc_start
= 0
then
if Location.compare lid.loc pat.ppat_loc = 0 then
(* simple punning! *)
self#longident_loc lid acc
else
match pat.ppat_desc with
| Ppat_constraint (p, c) ->
(* { foo : int } and { foo : int = x } ... *)
let _ = self#core_type c acc in
self#pattern p acc
| _ ->
(* No idea what's going on there. *)
self#pattern pat acc
else
let acc = self#longident_loc lid acc in
let acc = self#pattern pat acc in
acc)
labels acc
| Ppat_constraint ({ ppat_desc = Ppat_unpack a; _ }, b) ->
let acc = self#loc (self#option self#string) a acc in
self#core_type b acc
| _ -> super#pattern_desc x acc
(***********************************************************)
(* The following is overridden because the location of the *)
(* fake structure for a generative argument covers the *)
(* location of the functor. *)
(***********************************************************)
method! module_expr_desc x acc =
match x with
| Pmod_apply (m, { pmod_desc = Pmod_structure []; pmod_loc; _ })
when Location.compare_pos m.pmod_loc.loc_start pmod_loc.loc_start = 0 ->
super#module_expr m acc
| _ -> super#module_expr_desc x acc
(***********************************************************)
(* The following is overridden because the location of the *)
(* open_infos for Pcl_open only covers the "open" keyword *)
(* and not the module opened. *)
(***********************************************************)
method! class_expr_desc x acc =
match x with
| Pcl_open (od, ce) ->
(* inline of open_description (which effectively makes that node
disappear) *)
let acc = self#longident_loc od.popen_expr acc in
let acc = self#override_flag od.popen_override acc in
let acc = self#location od.popen_loc acc in
let acc = self#attributes od.popen_attributes acc in
(* continue *)
let acc = self#class_expr ce acc in
acc
| _ -> super#class_expr_desc x acc
(*********************)
(* Same as above ... *)
(*********************)
method! class_type_desc x acc =
match x with
| Pcty_open (od, ct) ->
(* inline of open_description (which effectively makes that node
disappear) *)
let acc = self#longident_loc od.popen_expr acc in
let acc = self#override_flag od.popen_override acc in
let acc = self#location od.popen_loc acc in
let acc = self#attributes od.popen_attributes acc in
(* continue *)
let acc = self#class_type ct acc in
acc
| _ -> super#class_type_desc x acc
(**********************************************************)
(* The following is overridden because docstrings have *)
(* the same location as the item they get attached to. *)
(**********************************************************)
method! attribute x acc =
match x.attr_name.txt with
| "ocaml.doc" | "ocaml.text" -> acc
| _ -> super#attribute x acc
end