-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathast_analysis.ml
1331 lines (1306 loc) · 56.2 KB
/
ast_analysis.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
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
open Core
open Generic_types
let rec unwrap_longident (li: Longident.t) = match li with
| Longident.Lident l -> [l]
| Longident.Lapply (l1, l2) ->
unwrap_longident l1 @ unwrap_longident l2
| Longident.Ldot (li, s) -> unwrap_longident li @ [s]
(** given a list of items, removes all elements *)
let subtract base removal =
List.filter ~f:(fun v -> not (List.mem ~equal:String.equal removal v))
base
let dedup = List.dedup_and_sort ~compare:String.compare
let rec find_variables_exp ({
pexp_desc; _
}: Parsetree.expression) : string list =
match pexp_desc with
| Parsetree.Pexp_ident { txt; _ } ->
unwrap_longident txt
| Parsetree.Pexp_constant _ -> []
| Parsetree.Pexp_let (_, vbs, exp) ->
let (bound_variables, used_variables) =
List.fold_right ~f:(fun (vb: Parsetree.value_binding) ((bound: _ list),used) ->
let (new_bound, new_used) = find_variables_vb vb in
(* remove any previously bound variables from new_used: *)
let new_used = subtract new_used bound in
((bound @ new_bound), (used @ new_used) )
) vbs ~init:([],[]) in
let bound_variables = dedup bound_variables in
let used_variables_in_bindings = List.dedup_and_sort ~compare:String.compare used_variables in
let used_variables_in_expr = find_variables_exp exp in
let used_variables_in_expr = subtract used_variables_in_expr bound_variables in
let all_variables =
used_variables_in_bindings
@
used_variables_in_expr
in
List.dedup_and_sort ~compare:String.compare all_variables
| Parsetree.Pexp_function cases ->
List.concat_map ~f:find_variables_case cases
| Parsetree.Pexp_fun (lab, eval, ppat, exp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [string] in
let bound_variables = bound_variables @ (find_variables_pat ppat) in
let variables_used_in_default = Option.map ~f:find_variables_exp eval |> Option.value ~default:[] in
let variables_used_in_exp = find_variables_exp exp in
let variables_used_in_exp = subtract variables_used_in_exp bound_variables in
let all_bindings = variables_used_in_default @ variables_used_in_exp in
dedup all_bindings
| Parsetree.Pexp_apply (e1, e2) ->
let expression_variables = find_variables_exp e1 in
let apply_exprs = List.map ~f:snd e2 in
let apply_variables = List.concat_map ~f:find_variables_exp apply_exprs in
let all_bindings = expression_variables @ apply_variables in
dedup all_bindings
| Parsetree.Pexp_try (match_exp, cases)
| Parsetree.Pexp_match (match_exp, cases) ->
let variables_in_match_exp = find_variables_exp match_exp in
let variables_in_cases = List.concat_map ~f:find_variables_case cases in
let all_bindings = variables_in_match_exp @ variables_in_cases in
dedup all_bindings
| Parsetree.Pexp_array exps
| Parsetree.Pexp_tuple exps ->
let all_bindings = List.concat_map ~f:find_variables_exp exps in
dedup all_bindings
| Parsetree.Pexp_variant (_, args)
| Parsetree.Pexp_construct (_, args) ->
Option.map ~f:find_variables_exp args |> Option.value ~default:[]
| Parsetree.Pexp_record (fields, base) ->
let fields = List.map ~f:snd fields in
let variables_in_fields = List.concat_map ~f:find_variables_exp fields in
let variables_in_base = Option.map ~f:find_variables_exp base |> Option.value ~default:[] in
let all_bindings = variables_in_fields @ variables_in_base in
dedup all_bindings
| Parsetree.Pexp_field (ex, _) -> find_variables_exp ex
| Parsetree.Pexp_setfield (e1, _, e2) ->
let e1_variables = find_variables_exp e1 in
let e2_variables = find_variables_exp e2 in
let all_bindings = e1_variables @ e2_variables in
dedup all_bindings
| Parsetree.Pexp_ifthenelse (e1, e2, e3) ->
let e1_vars = find_variables_exp e1 in
let e2_vars = find_variables_exp e2 in
let e3_vars = Option.map ~f:find_variables_exp e3 |> Option.value ~default:[] in
dedup (e1_vars @ e2_vars @ e3_vars)
| Parsetree.Pexp_while (e1, e2)
| Parsetree.Pexp_sequence (e1, e2) ->
let e1_vars = find_variables_exp e1 in
let e2_vars = find_variables_exp e2 in
dedup (e1_vars @ e2_vars)
| Parsetree.Pexp_for (pat, e1, e2, _, e3) ->
let e1_vars = find_variables_exp e1 in
let e2_vars = find_variables_exp e2 in
let e3_vars = find_variables_exp e3 in
let for_index_pattern = find_variables_pat pat in
let e3_vars = subtract e3_vars for_index_pattern in
let vars = e1_vars @ e2_vars @ e3_vars in
dedup vars
| Parsetree.Pexp_assert e1
| Parsetree.Pexp_newtype (_, e1)
| Parsetree.Pexp_poly (e1, _)
| Parsetree.Pexp_lazy e1
| Parsetree.Pexp_setinstvar (_, e1)
| Parsetree.Pexp_send (e1, _)
| Parsetree.Pexp_coerce (e1, _, _)
| Parsetree.Pexp_constraint (e1, _) ->
find_variables_exp e1
| Parsetree.Pexp_new _ -> []
| Parsetree.Pexp_override fields ->
let fields = List.map ~f:snd fields in
let vars = List.concat_map ~f:find_variables_exp fields in
dedup vars
| Parsetree.Pexp_letop { let_; ands; body } ->
let (bound_variables, used_variables) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (b, u) = find_variables_bop bop in
let u = subtract u bound in
let bound = bound @ b in
let used = used @ u in
(bound,used)
) (let_ :: ands)
in
let expr_variables = find_variables_exp body in
let expr_variables = subtract expr_variables bound_variables in
let all_variables = used_variables @ expr_variables in
dedup all_variables
| Parsetree.Pexp_open (od, expr) ->
let od_vari = find_variables_od od in
let expr_vari = find_variables_exp expr in
let all_variables = od_vari @ expr_vari in
dedup all_variables
| Parsetree.Pexp_letmodule (_, mexpr, expr) ->
let mexpr_vari = find_variables_mexp mexpr in
let expr_vari = find_variables_exp expr in
let all_variables = mexpr_vari @ expr_vari in
dedup all_variables
| Parsetree.Pexp_letexception (_, expr) -> find_variables_exp expr
| Parsetree.Pexp_object cs -> find_variables_cs cs
| Parsetree.Pexp_pack mexpr -> find_variables_mexp mexpr
| Parsetree.Pexp_extension ext -> find_variables_ext ext
| Parsetree.Pexp_unreachable -> []
and find_variables_od ({ popen_expr; _ }: Parsetree.open_declaration) =
find_variables_mexp popen_expr
and find_variables_mexp ({ pmod_desc; _ }: Parsetree.module_expr) =
match pmod_desc with
| Parsetree.Pmod_ident _ -> []
| Parsetree.Pmod_structure str -> List.concat_map ~f:find_variables_si str
| Parsetree.Pmod_functor (omt, mexp) ->
let variables =
match omt with
| Parsetree.Unit -> []
| Parsetree.Named (_, mt) -> find_variables_mt mt in
let expr = find_variables_mexp mexp in
let all_variables = variables @ expr in
dedup all_variables
| Parsetree.Pmod_apply (mexp, mexp2) ->
find_variables_mexp mexp @ find_variables_mexp mexp2
| Parsetree.Pmod_constraint (mexp, mt) ->
find_variables_mexp mexp @ find_variables_mt mt
| Parsetree.Pmod_unpack expr -> find_variables_exp expr
| Parsetree.Pmod_extension ext -> find_variables_ext ext
and find_variables_si ({ pstr_desc; _ }: Parsetree.structure_item) =
match pstr_desc with
| Parsetree.Pstr_eval (expr, _) -> find_variables_exp expr
| Parsetree.Pstr_value (_, vbs) ->
let (bound, unbound) = List.map ~f:find_variables_vb vbs
|> List.unzip in
let bound = List.concat bound in
let unbound = List.concat unbound in
let bindings = subtract unbound bound in
dedup bindings
| Parsetree.Pstr_primitive _ -> []
| Parsetree.Pstr_type (_, _) -> []
| Parsetree.Pstr_typext _ -> []
| Parsetree.Pstr_exception _ -> []
| Parsetree.Pstr_module mb -> find_variables_mb mb
| Parsetree.Pstr_recmodule mbs ->
List.concat_map ~f:find_variables_mb mbs
| Parsetree.Pstr_modtype mt -> find_variables_mtdcl mt
| Parsetree.Pstr_open od -> find_variables_od od
| Parsetree.Pstr_class cls ->
List.concat_map ~f:find_variables_cls cls
| Parsetree.Pstr_class_type ct -> List.concat_map ~f:find_variables_ctdcl ct
| Parsetree.Pstr_include id -> find_variables_id id
| Parsetree.Pstr_attribute _ -> []
| Parsetree.Pstr_extension (ext, _) -> find_variables_ext ext
and find_variables_id ({ pincl_mod; _ }: Parsetree.include_declaration) =
find_variables_mexp pincl_mod
and find_variables_mb ({ pmb_expr; _ }: Parsetree.module_binding) =
find_variables_mexp pmb_expr
and find_variables_cls ({ pci_expr={ pcl_desc; _ }; _ }: Parsetree.class_declaration) =
match pcl_desc with
| Parsetree.Pcl_constr (_, _) -> []
| Parsetree.Pcl_structure cs -> find_variables_cs cs
| Parsetree.Pcl_fun (lab, default_expr, patter, csexp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [string] in
let variables_in_deflt = Option.map ~f:find_variables_exp default_expr
|> Option.value ~default:[] in
let bound_variables = bound_variables @ (find_variables_pat patter) in
let variables_in_csexp = variables_in_deflt @ find_variables_csexp csexp in
let variables = subtract variables_in_csexp bound_variables in
dedup variables
| Parsetree.Pcl_apply (csexp, exprs) ->
let variables_in_csexp = find_variables_csexp csexp in
let exprs = List.map ~f:snd exprs |> List.concat_map ~f:find_variables_exp in
let variables = variables_in_csexp @ exprs in
dedup variables
| Parsetree.Pcl_let (_, vbs, body) ->
let (bound_variables, used_variables) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (b, u) = find_variables_vb bop in
let u = subtract u bound in
let bound = bound @ b in
let used = used @ u in
(bound,used)
) vbs
in
let expr_variables = find_variables_csexp body in
let expr_variables = subtract expr_variables bound_variables in
let all_variables = used_variables @ expr_variables in
dedup all_variables
| Parsetree.Pcl_constraint (csexp, ct) ->
find_variables_csexp csexp @ find_variables_ct ct
| Parsetree.Pcl_extension ext -> find_variables_ext ext
| Parsetree.Pcl_open ( _, csexp) ->
let variables = find_variables_csexp csexp in
dedup variables
and find_variables_ctdcl ({ pci_expr; _ }: Parsetree.class_type_declaration) =
find_variables_ct pci_expr
and find_variables_cs { pcstr_self; pcstr_fields } =
let (bound_variables, used_variables) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (u, b) = find_variables_cf bop in
let u = subtract u bound in
let bound = bound @ b in
let used = used @ u in
(bound,used)
) pcstr_fields
in
let bound_variables = (find_variables_pat pcstr_self) @ bound_variables in
let bound_variables = dedup bound_variables in
let expr_variables = subtract used_variables bound_variables in
dedup expr_variables
and find_variables_cf ({ pcf_desc; _ }: Parsetree.class_field) =
match pcf_desc with
| Parsetree.Pcf_inherit (_, csexp, name) -> find_variables_csexp csexp,
(name
|> Option.map
~f:(fun ({ txt; _ }: string Asttypes.loc) -> txt)
|> Option.to_list)
| Parsetree.Pcf_val ({ txt; _ }, _, cfk) ->
find_variables_cfk cfk, [txt]
| Parsetree.Pcf_method ({ txt; _ }, _, cfk) ->
find_variables_cfk cfk, [txt]
| Parsetree.Pcf_constraint _ -> [],[]
| Parsetree.Pcf_initializer exp -> find_variables_exp exp,[]
| Parsetree.Pcf_attribute _ -> [], []
| Parsetree.Pcf_extension ext -> find_variables_ext ext,[]
and find_variables_ct ({ pcty_desc; _ }: Parsetree.class_type) =
match pcty_desc with
| Parsetree.Pcty_constr (_, _) -> []
| Parsetree.Pcty_signature csi -> find_variables_csi csi
| Parsetree.Pcty_arrow (lab, _, ct) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [string] in
let ct_variables = find_variables_ct ct in
let ct_variables = subtract ct_variables bound_variables in
dedup ct_variables
| Parsetree.Pcty_extension ext -> find_variables_ext ext
| Parsetree.Pcty_open (_, ct) ->
let all_variables = find_variables_ct ct in
dedup all_variables
and find_variables_csi ({ pcsig_fields; _ }: Parsetree.class_signature) =
List.concat_map ~f:find_variables_ctf pcsig_fields
and find_variables_ctf ({ pctf_desc; _ }: Parsetree.class_type_field) =
match pctf_desc with
| Parsetree.Pctf_inherit ct -> find_variables_ct ct
| Parsetree.Pctf_val _ -> []
| Parsetree.Pctf_method _ -> []
| Parsetree.Pctf_constraint _ -> []
| Parsetree.Pctf_attribute _ -> []
| Parsetree.Pctf_extension ext -> find_variables_ext ext
and find_variables_cfk (cfk: Parsetree.class_field_kind) =
match cfk with
| Parsetree.Cfk_virtual _ -> []
| Parsetree.Cfk_concrete (_, exp) -> find_variables_exp exp
and find_variables_csexp ({ pcl_desc; _ }: Parsetree.class_expr) =
match pcl_desc with
| Parsetree.Pcl_constr (_, _) -> []
| Parsetree.Pcl_structure cs -> find_variables_cs cs
| Parsetree.Pcl_fun (lab, default_expr, patter, csexp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [string] in
let variables_in_deflt = Option.map ~f:find_variables_exp default_expr
|> Option.value ~default:[] in
let bound_variables = bound_variables @ (find_variables_pat patter) in
let variables_in_csexp = variables_in_deflt @ find_variables_csexp csexp in
let variables = subtract variables_in_csexp bound_variables in
dedup variables
| Parsetree.Pcl_apply (csexp, exprs) ->
let cs_expr_vari = find_variables_csexp csexp in
let exprs = List.map ~f:snd exprs |> List.concat_map ~f:find_variables_exp in
let all_variables = cs_expr_vari @ exprs in
dedup all_variables
| Parsetree.Pcl_let (_, vbs, body) ->
let (bound_variables, used_variables) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (b, u) = find_variables_vb bop in
let u = subtract u bound in
let bound = bound @ b in
let used = used @ u in
(bound,used)
) vbs
in
let expr_variables = find_variables_csexp body in
let expr_variables = subtract expr_variables bound_variables in
let all_variables = used_variables @ expr_variables in
dedup all_variables
| Parsetree.Pcl_constraint (_, _) -> []
| Parsetree.Pcl_extension ext -> find_variables_ext ext
| Parsetree.Pcl_open (_, cexp) ->
dedup (find_variables_csexp cexp)
and find_variables_mtdcl ({ pmtd_type; _ }: Parsetree.module_type_declaration) =
Option.map pmtd_type ~f:find_variables_mt |> Option.value ~default:[]
and find_variables_mt ({ pmty_desc; _ }: Parsetree.module_type) =
match pmty_desc with
| Parsetree.Pmty_ident _ -> []
| Parsetree.Pmty_signature _ -> []
| Parsetree.Pmty_functor (omt, mt) ->
let param_mt = match omt with
| Parsetree.Unit -> []
| Parsetree.Named (_, omt) -> find_variables_mt omt in
let expr_mt = find_variables_mt mt in
param_mt @ expr_mt
| Parsetree.Pmty_with (mt, _) -> find_variables_mt mt
| Parsetree.Pmty_typeof mexp -> find_variables_mexp mexp
| Parsetree.Pmty_extension ext -> find_variables_ext ext
| Parsetree.Pmty_alias _ -> []
and find_variables_ext ((_, pylod): Parsetree.extension) =
match pylod with
| Parsetree.PStr str -> List.concat_map ~f:find_variables_si str
| Parsetree.PSig _ -> []
| Parsetree.PTyp _ -> []
| Parsetree.PPat (pat, exp) ->
let bound_variables = find_variables_pat pat in
let variables_in_exp = Option.map ~f:find_variables_exp exp |> Option.value ~default:[] in
let all_bindings = subtract variables_in_exp bound_variables in
dedup all_bindings
and find_variables_vb ({ pvb_pat; pvb_expr; _ }: Parsetree.value_binding) =
find_variables_pat pvb_pat, find_variables_exp pvb_expr
and find_variables_bop ({ pbop_pat; pbop_exp; _ }: Parsetree.binding_op) =
find_variables_pat pbop_pat, find_variables_exp pbop_exp
and find_variables_pat ({ ppat_desc; _ }: Parsetree.pattern) : string list =
match ppat_desc with
| Parsetree.Ppat_any -> []
| Parsetree.Ppat_var { txt; _ } -> [txt]
| Parsetree.Ppat_alias (pat, { txt; _ }) -> (* (Some v) as x *)
find_variables_pat pat @ [txt]
| Parsetree.Ppat_constant _ -> []
| Parsetree.Ppat_interval (_, _) -> []
| Parsetree.Ppat_tuple ls ->
List.concat_map ls ~f:(find_variables_pat)
| Parsetree.Ppat_variant (_, popt) ->
(Option.map ~f:(find_variables_pat) popt |> Option.value ~default:[])
| Parsetree.Ppat_construct (_, popt) ->
(Option.map ~f:(fun (_, pat) -> find_variables_pat pat) popt |> Option.value ~default:[])
| Parsetree.Ppat_array fields ->
List.concat_map ~f:(find_variables_pat) fields
| Parsetree.Ppat_record (fields, _) ->
List.map fields ~f:(fun (_,p) -> p) |> List.concat_map ~f:(find_variables_pat)
| Parsetree.Ppat_or (p1, p2) ->
find_variables_pat p1 @ find_variables_pat p2
| Parsetree.Ppat_open (_, p1)
| Parsetree.Ppat_exception p1
| Parsetree.Ppat_lazy p1
| Parsetree.Ppat_constraint (p1, _) ->
find_variables_pat p1
| Parsetree.Ppat_type _ -> []
| Parsetree.Ppat_unpack { txt; _ } ->
txt|> Option.to_list
| Parsetree.Ppat_extension _ -> []
and find_variables_case ({ pc_lhs; pc_guard; pc_rhs }: Parsetree.case) =
let bound_variables = find_variables_pat pc_lhs in
let used_variables_in_guard =
(pc_guard |> Option.map ~f:find_variables_exp |> Option.value ~default:[]) in
let used_variables_in_expr = find_variables_exp pc_rhs in
let used_variables_in_expr = subtract used_variables_in_expr bound_variables in
let all_used = used_variables_in_guard @ used_variables_in_expr in
dedup all_used
let rec find_pattern_scopes_exp ({
pexp_desc; pexp_loc={ loc_start={pos_cnum=s; _}; _ }; _
}: Parsetree.expression) : (int * int ) list =
match pexp_desc with
| Parsetree.Pexp_ident _ -> []
| Parsetree.Pexp_constant _ -> []
| Parsetree.Pexp_let (_, vbs, exp) ->
let bound_variables =
List.fold_right ~init:[] ~f:(fun vb acc ->
let scopes = find_pattern_scopes_vb vb in
(* remove any previously bound variables from new_used: *)
acc @ scopes
) vbs in
let used_variables_in_expr = find_pattern_scopes_exp exp in
let all_variables =
bound_variables @
used_variables_in_expr
in
all_variables
| Parsetree.Pexp_function cases ->
List.concat_map ~f:find_pattern_scopes_case cases
| Parsetree.Pexp_fun (lab, eval, ppat, exp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [(s, s + String.length string)] in
let bound_variables = bound_variables @ (find_pattern_scopes_pat ppat) in
let variables_used_in_default = Option.map ~f:find_pattern_scopes_exp eval |> Option.value ~default:[] in
let variables_used_in_exp = find_pattern_scopes_exp exp in
let all_bindings = bound_variables @ variables_used_in_default @ variables_used_in_exp in
all_bindings
| Parsetree.Pexp_apply (e1, e2) ->
let expression_variables = find_pattern_scopes_exp e1 in
let (pats, apply_exprs) = List.fold ~init:([],[]) ~f:(fun (pats,exps) (labl,expr) ->
let es =
let loc = expr.pexp_loc in
loc.loc_start.pos_cnum
in
let labl = match labl with
| Asttypes.Nolabel -> []
| Asttypes.Labelled string
| Asttypes.Optional string ->
[(es-1 - String.length string, es-1)]
in
let expr_pats = find_pattern_scopes_exp expr in
pats @ labl @ expr_pats, expr :: exps
) e2 in
let apply_variables = List.concat_map ~f:find_pattern_scopes_exp apply_exprs in
let all_bindings = pats @ expression_variables @ apply_variables in
all_bindings
| Parsetree.Pexp_try (match_exp, cases)
| Parsetree.Pexp_match (match_exp, cases) ->
let variables_in_match_exp = find_pattern_scopes_exp match_exp in
let variables_in_cases = List.concat_map ~f:find_pattern_scopes_case cases in
let all_bindings = variables_in_match_exp @ variables_in_cases in
all_bindings
| Parsetree.Pexp_array exps
| Parsetree.Pexp_tuple exps ->
let all_bindings = List.concat_map ~f:find_pattern_scopes_exp exps in
all_bindings
| Parsetree.Pexp_variant (_, args)
| Parsetree.Pexp_construct (_, args) ->
Option.map ~f:find_pattern_scopes_exp args |> Option.value ~default:[]
| Parsetree.Pexp_record (fields, base) ->
let fields = List.map ~f:snd fields in
let variables_in_fields = List.concat_map ~f:find_pattern_scopes_exp fields in
let variables_in_base = Option.map ~f:find_pattern_scopes_exp base |> Option.value ~default:[] in
let all_bindings = variables_in_fields @ variables_in_base in
all_bindings
| Parsetree.Pexp_field (ex, _) -> find_pattern_scopes_exp ex
| Parsetree.Pexp_setfield (e1, _, e2) ->
let e1_variables = find_pattern_scopes_exp e1 in
let e2_variables = find_pattern_scopes_exp e2 in
let all_bindings = e1_variables @ e2_variables in
all_bindings
| Parsetree.Pexp_ifthenelse (e1, e2, e3) ->
let e1_vars = find_pattern_scopes_exp e1 in
let e2_vars = find_pattern_scopes_exp e2 in
let e3_vars = Option.map ~f:find_pattern_scopes_exp e3 |> Option.value ~default:[] in
(e1_vars @ e2_vars @ e3_vars)
| Parsetree.Pexp_while (e1, e2)
| Parsetree.Pexp_sequence (e1, e2) ->
let e1_vars = find_pattern_scopes_exp e1 in
let e2_vars = find_pattern_scopes_exp e2 in
(e1_vars @ e2_vars)
| Parsetree.Pexp_for (pat, e1, e2, _, e3) ->
let e1_vars = find_pattern_scopes_exp e1 in
let e2_vars = find_pattern_scopes_exp e2 in
let e3_vars = find_pattern_scopes_exp e3 in
let for_index_pattern = find_pattern_scopes_pat pat in
let vars = for_index_pattern @ e1_vars @ e2_vars @ e3_vars in
vars
| Parsetree.Pexp_assert e1
| Parsetree.Pexp_newtype (_, e1)
| Parsetree.Pexp_poly (e1, _)
| Parsetree.Pexp_lazy e1
| Parsetree.Pexp_setinstvar (_, e1)
| Parsetree.Pexp_send (e1, _)
| Parsetree.Pexp_coerce (e1, _, _)
| Parsetree.Pexp_constraint (e1, _) ->
find_pattern_scopes_exp e1
| Parsetree.Pexp_new _ -> []
| Parsetree.Pexp_override fields ->
let fields = List.map ~f:snd fields in
let vars = List.concat_map ~f:find_pattern_scopes_exp fields in
vars
| Parsetree.Pexp_letop { let_; ands; body } ->
let (bound_variables, used_variables) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (b, u) = find_pattern_scopes_bop bop in
let bound = bound @ b in
let used = used @ u in
(bound,used)
) (let_ :: ands)
in
let expr_variables = find_pattern_scopes_exp body in
let all_variables = bound_variables @ used_variables @ expr_variables in
all_variables
| Parsetree.Pexp_open (od, expr) ->
let od_vari = find_pattern_scopes_od od in
let expr_vari = find_pattern_scopes_exp expr in
let all_variables = od_vari @ expr_vari in
all_variables
| Parsetree.Pexp_letmodule (_, mexpr, expr) ->
let mexpr_vari = find_pattern_scopes_mexp mexpr in
let expr_vari = find_pattern_scopes_exp expr in
let all_variables = mexpr_vari @ expr_vari in
all_variables
| Parsetree.Pexp_letexception (_, expr) -> find_pattern_scopes_exp expr
| Parsetree.Pexp_object cs -> find_pattern_scopes_cs cs
| Parsetree.Pexp_pack mexpr -> find_pattern_scopes_mexp mexpr
| Parsetree.Pexp_extension ext -> find_pattern_scopes_ext ext
| Parsetree.Pexp_unreachable -> []
and find_pattern_scopes_pat ({ ppat_loc; _ }: Parsetree.pattern) =
[ppat_loc.loc_start.pos_cnum, ppat_loc.loc_end.pos_cnum]
and find_pattern_scopes_od ({ popen_expr; _ }: Parsetree.open_declaration) =
find_pattern_scopes_mexp popen_expr
and find_pattern_scopes_vb ({ pvb_pat; pvb_expr; _ }: Parsetree.value_binding) =
find_pattern_scopes_pat pvb_pat @ find_pattern_scopes_exp pvb_expr
and find_pattern_scopes_mexp ({ pmod_desc; _ }: Parsetree.module_expr) =
match pmod_desc with
| Parsetree.Pmod_ident _ -> []
| Parsetree.Pmod_structure str -> List.concat_map ~f:find_pattern_scopes_si str
| Parsetree.Pmod_functor (omt, mexp) ->
let variables = match omt with
| Parsetree.Unit -> []
| Parsetree.Named (_, omt) -> find_pattern_scopes_mt omt in
let expr = find_pattern_scopes_mexp mexp in
let all_variables = variables @ expr in
all_variables
| Parsetree.Pmod_apply (mexp, mexp2) ->
find_pattern_scopes_mexp mexp @ find_pattern_scopes_mexp mexp2
| Parsetree.Pmod_constraint (mexp, mt) ->
find_pattern_scopes_mexp mexp @ find_pattern_scopes_mt mt
| Parsetree.Pmod_unpack expr -> find_pattern_scopes_exp expr
| Parsetree.Pmod_extension ext -> find_pattern_scopes_ext ext
and find_pattern_scopes_si ({ pstr_desc; _ }: Parsetree.structure_item) =
match pstr_desc with
| Parsetree.Pstr_eval (expr, _) -> find_pattern_scopes_exp expr
| Parsetree.Pstr_value (_, vbs) ->
let bound = List.map ~f:find_pattern_scopes_vb vbs in
let bound = List.concat bound in
bound
| Parsetree.Pstr_primitive _ -> []
| Parsetree.Pstr_type (_, _) -> []
| Parsetree.Pstr_typext _ -> []
| Parsetree.Pstr_exception _ -> []
| Parsetree.Pstr_module mb -> find_pattern_scopes_mb mb
| Parsetree.Pstr_recmodule mbs ->
List.concat_map ~f:find_pattern_scopes_mb mbs
| Parsetree.Pstr_modtype mt -> find_pattern_scopes_mtdcl mt
| Parsetree.Pstr_open od -> find_pattern_scopes_od od
| Parsetree.Pstr_class cls ->
List.concat_map ~f:find_pattern_scopes_cls cls
| Parsetree.Pstr_class_type ct -> List.concat_map ~f:find_pattern_scopes_ctdcl ct
| Parsetree.Pstr_include id -> find_pattern_scopes_id id
| Parsetree.Pstr_attribute _ -> []
| Parsetree.Pstr_extension (ext, _) -> find_pattern_scopes_ext ext
and find_pattern_scopes_id ({ pincl_mod; _ }: Parsetree.include_declaration) =
find_pattern_scopes_mexp pincl_mod
and find_pattern_scopes_mb ({ pmb_expr; _ }: Parsetree.module_binding) =
find_pattern_scopes_mexp pmb_expr
and find_pattern_scopes_cls ({ pci_expr={ pcl_desc; pcl_loc={loc_start={pos_cnum=s; _}; _ };
_}; _ }: Parsetree.class_declaration) =
match pcl_desc with
| Parsetree.Pcl_constr (_, _) -> []
| Parsetree.Pcl_structure cs -> find_pattern_scopes_cs cs
| Parsetree.Pcl_fun (lab, eval, ppat, exp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [(s, s + String.length string)] in
let bound_variables = bound_variables @ (find_pattern_scopes_pat ppat) in
let variables_used_in_default = Option.map ~f:find_pattern_scopes_exp eval |> Option.value ~default:[] in
let variables_used_in_exp = find_pattern_scopes_csexp exp in
let all_bindings = bound_variables @ variables_used_in_default @ variables_used_in_exp in
all_bindings
| Parsetree.Pcl_apply (csexp, exprs) ->
let variables_in_csexp = find_pattern_scopes_csexp csexp in
let exprs = List.map ~f:snd exprs |> List.concat_map ~f:find_pattern_scopes_exp in
let variables = variables_in_csexp @ exprs in
variables
| Parsetree.Pcl_let (_, vbs, body) ->
let bound_variables =
List.fold_right ~init:[] ~f:(fun vb acc ->
let scopes = find_pattern_scopes_vb vb in
(* remove any previously bound variables from new_used: *)
acc @ scopes
) vbs in
let used_variables_in_expr = find_pattern_scopes_csexp body in
let all_variables =
bound_variables @
used_variables_in_expr
in
all_variables
| Parsetree.Pcl_constraint (csexp, ct) ->
find_pattern_scopes_csexp csexp @ find_pattern_scopes_ct ct
| Parsetree.Pcl_extension ext -> find_pattern_scopes_ext ext
| Parsetree.Pcl_open ( _, csexp) ->
let variables = find_pattern_scopes_csexp csexp in
variables
and find_pattern_scopes_ctdcl ({ pci_expr; _ }: Parsetree.class_type_declaration) =
find_pattern_scopes_ct pci_expr
and find_pattern_scopes_cs { pcstr_self; pcstr_fields } =
let bound_variables =
List.fold ~init:[] ~f:(fun acc bop ->
let u = find_pattern_scopes_cf bop in
acc @ u
) pcstr_fields
in
let bound_variables = (find_pattern_scopes_pat pcstr_self) @ bound_variables in
let expr_variables = bound_variables in
expr_variables
and find_pattern_scopes_cf ({ pcf_desc; _ }: Parsetree.class_field) : (int * int) list =
match pcf_desc with
| Parsetree.Pcf_inherit (_, csexp, name) -> find_pattern_scopes_csexp csexp @
(name
|> Option.map
~f:(fun ({ loc={
loc_start={pos_cnum=s;_};
loc_end={pos_cnum=e;_}; _
}; _ }: string Asttypes.loc) -> (s,e))
|> Option.to_list)
| Parsetree.Pcf_method ({ loc={loc_start={pos_cnum=s;_}; loc_end={pos_cnum=e;_};_};_}, _, cfk)
| Parsetree.Pcf_val ({ loc={loc_start={pos_cnum=s;_}; loc_end={pos_cnum=e;_};_};_}, _, cfk) ->
(s,e) :: find_pattern_scopes_cfk cfk
| Parsetree.Pcf_constraint _ -> []
| Parsetree.Pcf_initializer exp -> find_pattern_scopes_exp exp
| Parsetree.Pcf_attribute _ -> []
| Parsetree.Pcf_extension ext -> find_pattern_scopes_ext ext
and find_pattern_scopes_ct ({ pcty_desc; pcty_loc={loc_start={pos_cnum=s;_};_};_ }
: Parsetree.class_type) =
match pcty_desc with
| Parsetree.Pcty_constr (_, _) -> []
| Parsetree.Pcty_signature csi -> find_pattern_scopes_csi csi
| Parsetree.Pcty_arrow (lab, _, ct) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [(s, s + String.length string)] in
let bound_variables = bound_variables @ (find_pattern_scopes_ct ct) in
let all_bindings = bound_variables in
all_bindings
| Parsetree.Pcty_extension ext -> find_pattern_scopes_ext ext
| Parsetree.Pcty_open (_, ct) ->
let all_variables = find_pattern_scopes_ct ct in
all_variables
and find_pattern_scopes_csi ({ pcsig_fields; _ }: Parsetree.class_signature) =
List.concat_map ~f:find_pattern_scopes_ctf pcsig_fields
and find_pattern_scopes_ctf ({ pctf_desc; _ }: Parsetree.class_type_field) =
match pctf_desc with
| Parsetree.Pctf_inherit ct -> find_pattern_scopes_ct ct
| Parsetree.Pctf_val _ -> []
| Parsetree.Pctf_method _ -> []
| Parsetree.Pctf_constraint _ -> []
| Parsetree.Pctf_attribute _ -> []
| Parsetree.Pctf_extension ext -> find_pattern_scopes_ext ext
and find_pattern_scopes_cfk (cfk: Parsetree.class_field_kind) =
match cfk with
| Parsetree.Cfk_virtual _ -> []
| Parsetree.Cfk_concrete (_, exp) -> find_pattern_scopes_exp exp
and find_pattern_scopes_csexp ({ pcl_desc; pcl_loc={loc_start={pos_cnum=s;_};_}; _ }: Parsetree.class_expr) =
match pcl_desc with
| Parsetree.Pcl_constr (_, _) -> []
| Parsetree.Pcl_structure cs -> find_pattern_scopes_cs cs
| Parsetree.Pcl_fun (lab, eval, ppat, exp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [(s, s + String.length string)] in
let bound_variables = bound_variables @ (find_pattern_scopes_pat ppat) in
let variables_used_in_default = Option.map ~f:find_pattern_scopes_exp eval |> Option.value ~default:[] in
let variables_used_in_exp = find_pattern_scopes_csexp exp in
let all_bindings = bound_variables @ variables_used_in_default @ variables_used_in_exp in
all_bindings
| Parsetree.Pcl_apply (csexp, exprs) ->
let variables_in_csexp = find_pattern_scopes_csexp csexp in
let exprs = List.map ~f:snd exprs |> List.concat_map ~f:find_pattern_scopes_exp in
let variables = variables_in_csexp @ exprs in
variables
| Parsetree.Pcl_let (_, vbs, body) ->
let bound_variables =
List.fold_right ~init:[] ~f:(fun vb acc ->
let scopes = find_pattern_scopes_vb vb in
(* remove any previously bound variables from new_used: *)
acc @ scopes
) vbs in
let used_variables_in_expr = find_pattern_scopes_csexp body in
let all_variables =
bound_variables @
used_variables_in_expr
in
all_variables
| Parsetree.Pcl_constraint (csexp, ct) ->
find_pattern_scopes_csexp csexp @ find_pattern_scopes_ct ct
| Parsetree.Pcl_extension ext -> find_pattern_scopes_ext ext
| Parsetree.Pcl_open ( _, csexp) ->
let variables = find_pattern_scopes_csexp csexp in
variables
and find_pattern_scopes_mtdcl ({ pmtd_type; _ }: Parsetree.module_type_declaration) =
Option.map pmtd_type ~f:find_pattern_scopes_mt |> Option.value ~default:[]
and find_pattern_scopes_mt ({ pmty_desc; _ }: Parsetree.module_type) =
match pmty_desc with
| Parsetree.Pmty_ident _ -> []
| Parsetree.Pmty_signature _ -> []
| Parsetree.Pmty_functor (omt, mt) ->
let param_mt = match omt with
| Parsetree.Unit -> []
| Parsetree.Named (_, omt) ->
find_pattern_scopes_mt omt in
let expr_mt = find_pattern_scopes_mt mt in
param_mt @ expr_mt
| Parsetree.Pmty_with (mt, _) -> find_pattern_scopes_mt mt
| Parsetree.Pmty_typeof mexp -> find_pattern_scopes_mexp mexp
| Parsetree.Pmty_extension ext -> find_pattern_scopes_ext ext
| Parsetree.Pmty_alias _ -> []
and find_pattern_scopes_ext ((_, pylod): Parsetree.extension) =
match pylod with
| Parsetree.PStr st -> List.concat_map ~f:find_pattern_scopes_si st
| Parsetree.PSig _ -> []
| Parsetree.PTyp _ -> []
| Parsetree.PPat (pat, exp) ->
let bound_variables = find_pattern_scopes_pat pat in
let variables_in_exp = Option.map ~f:find_pattern_scopes_exp exp |> Option.value ~default:[] in
let all_bindings = bound_variables @ variables_in_exp in
all_bindings
and find_pattern_scopes_bop ({ pbop_pat; pbop_exp; _ }: Parsetree.binding_op) =
find_pattern_scopes_pat pbop_pat, find_pattern_scopes_exp pbop_exp
and find_pattern_scopes_case ({ pc_lhs; pc_guard; pc_rhs }: Parsetree.case) =
let bound_variables = find_pattern_scopes_pat pc_lhs in
let used_variables_in_guard =
(pc_guard |> Option.map ~f:find_pattern_scopes_exp |> Option.value ~default:[]) in
let used_variables_in_expr = find_pattern_scopes_exp pc_rhs in
let used_variables_in_expr = bound_variables @ used_variables_in_expr in
let all_used = used_variables_in_guard @ used_variables_in_expr in
all_used
(* scope - variable, start and end *)
type scope = string * int * int
let rec calculate_str_bounds (str: Parsetree.structure_item list) =
let calc_str_b ({ pstr_loc; _ }: Parsetree.structure_item) =
pstr_loc.loc_start.pos_cnum, pstr_loc.loc_end.pos_cnum
in
let union (s1,e1) (s2,e2) =
let sn = if s1 < s2 then s1 else s2 in
let en = if e1 < e2 then e2 else e1 in
sn,en
in
match str with
| [] -> None
| h :: t ->
let tailb = calculate_str_bounds t in
let currentb = calc_str_b h in
Option.map ~f:(fun tailb -> union tailb currentb) tailb
let rec calculate_cstr_bounds (str: Parsetree.class_field list) =
let calc_str_b ({ pcf_loc=pstr_loc; _ }: Parsetree.class_field) =
pstr_loc.loc_start.pos_cnum, pstr_loc.loc_end.pos_cnum
in
let union (s1,e1) (s2,e2) =
let sn = if s1 < s2 then s1 else s2 in
let en = if e1 < e2 then e2 else e1 in
sn,en
in
match str with
| [] -> None
| h :: t ->
let tailb = calculate_cstr_bounds t in
let currentb = calc_str_b h in
Option.map ~f:(fun tailb -> union tailb currentb) tailb
let rec find_fun_scope ({ pexp_desc; pexp_loc; _ }: Parsetree.expression) =
match pexp_desc with
| Parsetree.Pexp_fun (_, _, _, body) -> find_fun_scope body
| _ -> pexp_loc.loc_start.pos_cnum, pexp_loc.loc_end.pos_cnum
(* returns a list of scopes - *)
let rec find_scopes_exp ({
pexp_desc; _
}: Parsetree.expression) : scope list =
match pexp_desc with
| Parsetree.Pexp_ident _ -> []
| Parsetree.Pexp_constant _ -> []
| Parsetree.Pexp_let (_, vbs, body) ->
let (bound_variables, sub_scopes) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (variables, scopes) = find_scopes_vb bop in
let bound = bound @ variables in
let used = used @ scopes in
(bound,used)
) vbs
in
let let_scopes =
let let_start,let_end =
let e3 = body.pexp_loc in
e3.loc_start.pos_cnum, e3.loc_end.pos_cnum
in
List.map ~f:(fun v -> (v, let_start, let_end)) bound_variables
in
let body_scopes = find_scopes_exp body in
let all_scopes = let_scopes @ sub_scopes @ body_scopes in
all_scopes
| Parsetree.Pexp_function cases ->
List.concat_map ~f:find_scopes_case cases
| Parsetree.Pexp_fun (lab, eval, ppat, exp) ->
let bound_variables = match lab with
| Asttypes.Nolabel -> []
| Asttypes.Optional string
| Asttypes.Labelled string -> [string] in
let bound_variables = bound_variables @ (find_variables_pat ppat) in
let scopes_used_in_default = Option.map ~f:find_scopes_exp eval |> Option.value ~default:[] in
let scopes_made_by_fun =
let fun_start,fun_end =
find_fun_scope exp
in
List.map ~f:(fun v -> (v,fun_start,fun_end)) bound_variables
in
let scopes_used_in_fun = find_scopes_exp exp in
let all_bindings = scopes_used_in_default @ scopes_made_by_fun @ scopes_used_in_fun in
all_bindings
| Parsetree.Pexp_apply (e1, e2) ->
let expression_variables = find_scopes_exp e1 in
let apply_exprs = List.map ~f:snd e2 in
let apply_variables = List.concat_map ~f:find_scopes_exp apply_exprs in
let all_bindings = expression_variables @ apply_variables in
all_bindings
| Parsetree.Pexp_try (match_exp, cases)
| Parsetree.Pexp_match (match_exp, cases) ->
let variables_in_match_exp = find_scopes_exp match_exp in
let variables_in_cases = List.concat_map ~f:find_scopes_case cases in
let all_bindings = variables_in_match_exp @ variables_in_cases in
all_bindings
| Parsetree.Pexp_array exps
| Parsetree.Pexp_tuple exps ->
let all_bindings = List.concat_map ~f:find_scopes_exp exps in
all_bindings
| Parsetree.Pexp_variant (_, args)
| Parsetree.Pexp_construct (_, args) ->
Option.map ~f:find_scopes_exp args |> Option.value ~default:[]
| Parsetree.Pexp_record (fields, base) ->
let fields = List.map ~f:snd fields in
let variables_in_fields = List.concat_map ~f:find_scopes_exp fields in
let variables_in_base = Option.map ~f:find_scopes_exp base |> Option.value ~default:[] in
let all_bindings = variables_in_fields @ variables_in_base in
all_bindings
| Parsetree.Pexp_field (ex, _) -> find_scopes_exp ex
| Parsetree.Pexp_setfield (e1, _, e2) ->
let e1_variables = find_scopes_exp e1 in
let e2_variables = find_scopes_exp e2 in
let all_bindings = e1_variables @ e2_variables in
all_bindings
| Parsetree.Pexp_ifthenelse (e1, e2, e3) ->
let e1_vars = find_scopes_exp e1 in
let e2_vars = find_scopes_exp e2 in
let e3_vars = Option.map ~f:find_scopes_exp e3 |> Option.value ~default:[] in
(e1_vars @ e2_vars @ e3_vars)
| Parsetree.Pexp_while (e1, e2)
| Parsetree.Pexp_sequence (e1, e2) ->
let e1_vars = find_scopes_exp e1 in
let e2_vars = find_scopes_exp e2 in
(e1_vars @ e2_vars)
| Parsetree.Pexp_for (pat, e1, e2, _, e3) ->
let e1_scopes = find_scopes_exp e1 in
let e2_scopes = find_scopes_exp e2 in
let e3_scopes = find_scopes_exp e3 in
let for_index_vars = find_variables_pat pat in
let for_index_scopes =
let for_start,for_end =
let e3 = e3.pexp_loc in
e3.loc_start.pos_cnum, e3.loc_end.pos_cnum
in
List.map ~f:(fun var -> (var, for_start, for_end)) for_index_vars
in
let body_scopes = find_scopes_exp e3 in
let all_scopes = for_index_scopes @ e1_scopes @ e2_scopes @ e3_scopes @ body_scopes in
all_scopes
| Parsetree.Pexp_assert e1
| Parsetree.Pexp_newtype (_, e1)
| Parsetree.Pexp_poly (e1, _)
| Parsetree.Pexp_lazy e1
| Parsetree.Pexp_setinstvar (_, e1)
| Parsetree.Pexp_send (e1, _)
| Parsetree.Pexp_coerce (e1, _, _)
| Parsetree.Pexp_constraint (e1, _) ->
find_scopes_exp e1
| Parsetree.Pexp_new _ -> []
| Parsetree.Pexp_override fields ->
let fields = List.map ~f:snd fields in
let vars = List.concat_map ~f:find_scopes_exp fields in
vars
| Parsetree.Pexp_letop { let_; ands; body } ->
let (bound_variables, sub_scopes) =
List.fold ~init:([],[]) ~f:(fun (bound,used) bop ->
let (variables, scopes) = find_scopes_bop bop in
let bound = bound @ variables in
let used = used @ scopes in
(bound,used)
) (let_ :: ands)
in
let let_scopes =
let let_start,let_end =
let e3 = body.pexp_loc in
e3.loc_start.pos_cnum, e3.loc_end.pos_cnum
in
List.map ~f:(fun v -> (v, let_start, let_end)) bound_variables
in
let body_scopes = find_scopes_exp body in
let all_scopes = let_scopes @ sub_scopes @ body_scopes in
all_scopes
| Parsetree.Pexp_open (od, expr) ->
let od_vari = find_scopes_od od in
let expr_vari = find_scopes_exp expr in
let all_variables = od_vari @ expr_vari in
all_variables
| Parsetree.Pexp_letmodule (_, mexpr, expr) ->
let mexpr_vari = find_scopes_mexp mexpr in
let expr_vari = find_scopes_exp expr in
let all_variables = mexpr_vari @ expr_vari in
all_variables
| Parsetree.Pexp_letexception (_, expr) -> find_scopes_exp expr
| Parsetree.Pexp_object cs -> find_scopes_cs cs
| Parsetree.Pexp_pack mexpr -> find_scopes_mexp mexpr
| Parsetree.Pexp_extension ext -> find_scopes_ext ext
| Parsetree.Pexp_unreachable -> []
and find_scopes_od ({ popen_expr; _ }: Parsetree.open_declaration) =
find_scopes_mexp popen_expr
and find_scopes_mexp ({ pmod_desc; _ }: Parsetree.module_expr) =
match pmod_desc with
| Parsetree.Pmod_ident _ -> []
| Parsetree.Pmod_structure str ->
let rec loop ls acc = match ls with
| [] -> List.rev acc
| h :: t ->
let (bound_variables, sub_scopes) = find_scopes_si h in
let new_scopes =
match calculate_str_bounds t with
| None -> []
| Some (start_scope,end_scopes) ->
List.map ~f:(fun v -> (v, start_scope, end_scopes)) bound_variables
in
let all_scopes = new_scopes @ sub_scopes in
let all_scopes = List.rev all_scopes in
loop t (all_scopes @ acc)
in
loop str []
| Parsetree.Pmod_functor (omt, mexp) ->
let variables = match omt with
| Parsetree.Unit -> []
| Parsetree.Named (_, omt) -> find_scopes_mt omt in
let expr = find_scopes_mexp mexp in
let all_variables = variables @ expr in
all_variables
| Parsetree.Pmod_apply (mexp, mexp2) ->
find_scopes_mexp mexp @ find_scopes_mexp mexp2
| Parsetree.Pmod_constraint (mexp, mt) ->
find_scopes_mexp mexp @ find_scopes_mt mt
| Parsetree.Pmod_unpack expr -> find_scopes_exp expr
| Parsetree.Pmod_extension ext -> find_scopes_ext ext
and find_scopes_si ({ pstr_desc; _ }: Parsetree.structure_item) =
match pstr_desc with
| Parsetree.Pstr_eval (expr, _) -> [], find_scopes_exp expr
| Parsetree.Pstr_value (_, vbs) ->
let (bound_variables, sub_scopes) = List.map ~f:find_scopes_vb vbs
|> List.unzip in
let bound_variables = List.concat bound_variables in
let sub_scopes = List.concat sub_scopes in
bound_variables, sub_scopes
| Parsetree.Pstr_primitive { pval_name={ txt=t; _ }; _ } -> [t],[]
| Parsetree.Pstr_type (_, _) -> [], []
| Parsetree.Pstr_typext _ -> [], []
| Parsetree.Pstr_exception _ -> [], []
| Parsetree.Pstr_module mb -> [], find_scopes_mb mb
| Parsetree.Pstr_recmodule mbs ->
[], List.concat_map ~f:find_scopes_mb mbs