Source file stack_or_heap_enclosing.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
open Std
let log_section = "stack-or-heap-enclosing"
let { Logger.log = _ } = Logger.for_section log_section
type stack_or_heap =
| Alloc_mode of Mode.Alloc.r
| No_alloc of { reason : string }
| Unexpected_no_alloc
type stack_or_heap_enclosings = (Location.t * stack_or_heap) list
let from_nodes ~lsp_compat ~pos ~path =
let[@tail_mod_cons] rec with_parents = function
| node :: parent :: rest ->
(node, Some parent) :: with_parents (parent :: rest)
| [ node ] -> [ (node, None) ]
| [] -> []
in
let cursor_is_inside ({ loc_start; loc_end; _ } : Location.t) =
Lexing.compare_pos pos loc_start >= 0 && Lexing.compare_pos pos loc_end <= 0
in
let aux (node, parent) =
let open Browse_raw in
let ret ?(loc = Mbrowse.node_loc node) mode_result =
Some (loc, mode_result)
in
let ret_alloc ?loc alloc_mode = ret ?loc (Alloc_mode alloc_mode) in
let ret_no_alloc ?loc reason = ret ?loc (No_alloc { reason }) in
let ret_maybe_alloc ?loc reason = function
| Some alloc_mode -> ret_alloc ?loc alloc_mode
| None -> ret_no_alloc ?loc reason
in
match (node, parent) with
| ( Pattern { pat_desc = Tpat_var _; _ },
Some
(Value_binding
{ vb_expr = { exp_desc = Texp_function { alloc_mode; _ }; _ };
vb_loc;
_
}) ) ->
let loc = if lsp_compat then None else Some vb_loc in
ret ?loc (Alloc_mode alloc_mode)
| Expression { exp_desc; _ }, _ -> (
match exp_desc with
| Texp_function { alloc_mode; body; _ } -> (
let body_loc =
match body with
| Tfunction_body { exp_loc; _ } -> Some exp_loc
| Tfunction_cases { fc_cases; _ } -> (
match fc_cases with
| first_case :: remaining_cases ->
let { Typedtree.c_lhs = { pat_loc = first_pat; _ }; _ } =
first_case
and { Typedtree.c_rhs = { exp_loc = last_expr; _ }; _ } =
List.last remaining_cases |> Option.value ~default:first_case
in
Some
{ loc_start = first_pat.loc_start;
loc_end = last_expr.loc_end;
loc_ghost = true
}
| [] -> None)
in
match body_loc with
| Some loc when cursor_is_inside loc -> None
| _ -> ret (Alloc_mode alloc_mode))
| Texp_array (_, _, _, alloc_mode) -> ret (Alloc_mode alloc_mode)
| Texp_construct
({ loc; txt = _lident }, { cstr_repr; _ }, args, maybe_alloc_mode)
-> (
let loc =
if lsp_compat && cursor_is_inside loc then Some loc else None
in
match maybe_alloc_mode with
| Some alloc_mode -> ret ?loc (Alloc_mode alloc_mode)
| None -> (
match args with
| [] -> ret_no_alloc ?loc "constructor without arguments"
| _ :: _ -> (
match cstr_repr with
| Variant_unboxed | Variant_with_null ->
ret_no_alloc ?loc "unboxed constructor"
| Variant_extensible | Variant_boxed _ ->
ret ?loc Unexpected_no_alloc)))
| Texp_record { representation; alloc_mode = maybe_alloc_mode; _ } -> (
match (maybe_alloc_mode, representation) with
| _, (Record_inlined _ | Record_dummy _) -> None
| Some alloc_mode, _ -> ret_alloc alloc_mode
| None, Record_unboxed -> ret_no_alloc "unboxed record"
| None, (Record_boxed | Record_float | Record_ufloat | Record_mixed _)
-> ret Unexpected_no_alloc)
| Texp_field (_, _, _, _, boxed_or_unboxed, _) -> (
match boxed_or_unboxed with
| Boxing (alloc_mode, _) -> ret_alloc alloc_mode
| Non_boxing _ -> None)
| Texp_variant (_, maybe_exp_and_alloc_mode) ->
maybe_exp_and_alloc_mode
|> Option.map ~f:(fun (_, (alloc_mode : Typedtree.alloc_mode)) ->
alloc_mode)
|> ret_maybe_alloc "variant without argument"
| _ -> None)
| _ -> None
in
path
|> List.map ~f:(fun (_, node, _) -> node)
|> with_parents |> List.filter_map ~f:aux