package merlin-lib

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file polarity_search.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
open Std

type t = Trie of (string * Longident.t * t list lazy_t)

module PathSet = Set.Make(Path)

type query = {
  positive: PathSet.t;
  pos_fun: int;

  negative: PathSet.t;
  neg_fun: int;
}

let remove cost set path =
  if PathSet.mem path !set then (
    decr cost;
    set := PathSet.remove path !set
  )

let rec normalize_path env path =
  match Env.find_type path env with
  | exception Not_found -> path
  | decl ->
    match decl.Types.type_manifest with
    | Some body when decl.Types.type_private = Asttypes.Public
                  || decl.Types.type_kind <> Types.Type_abstract ->
      begin match Types.get_desc body with
      | Types.Tconstr (path, _, _) -> normalize_path env path
      | _ -> path
      end
    | _ -> path

let match_query env query t =
  let cost = ref 0 in
  let rec traverse neg neg_fun pos pos_fun t =
    incr cost;
    incr cost;
    match Types.get_desc t with
    | Types.Tconstr (path, params, _) ->
      remove cost pos (normalize_path env path);
      begin match Env.find_type path env with
      | exception Not_found -> ()
      | { Types.type_variance; _ } ->
        List.iter2 type_variance params ~f:(fun var arg ->
          if Types.Variance.mem Types.Variance.Inj var then (
            if Types.Variance.mem Types.Variance.Pos var then
              traverse neg neg_fun pos pos_fun arg;
            if Types.Variance.mem Types.Variance.Neg var then
              traverse pos pos_fun neg neg_fun arg
          )
        )
      end

    | Types.Tarrow (_, t1, t2, _) ->
      decr pos_fun;
      traverse neg neg_fun pos pos_fun t2;
      traverse pos pos_fun neg neg_fun t1

    | Types.Ttuple ts ->
      List.iter ~f:(traverse neg neg_fun pos pos_fun) ts

    | Types.Tvar _ | Types.Tunivar _ ->
      decr cost (* Favor polymorphic defs *)

    | _ -> ()
  in
  let neg     = ref query.negative and pos     = ref query.positive in
  let neg_fun = ref query.neg_fun  and pos_fun = ref query.pos_fun  in
  traverse neg neg_fun pos pos_fun t;
  if PathSet.is_empty !pos
     && PathSet.is_empty !neg
     && !neg_fun <= 0
     && !pos_fun <= 0
  then
    Some !cost
  else
    None

let build_query ~positive ~negative env =
  let prepare r l =
    if l = Longident.Lident "fun" then (incr r; None) else
    let set, _ = Env.find_type_by_name l env in
    Some (normalize_path env set)
  in
  let pos_fun = ref 0 and neg_fun = ref 0 in
  let positive = List.filter_map positive ~f:(prepare pos_fun) in
  let negative = List.filter_map negative ~f:(prepare neg_fun) in
  {
    positive = PathSet.of_list positive;
    negative = PathSet.of_list negative;
    neg_fun = !neg_fun; pos_fun = !pos_fun;
  }

let directories ~global_modules env =
  let rec explore lident env =
    let add_module name _ md l =
      match md.Types.md_type with
      | Types.Mty_alias _ -> l
      | _ ->
        let lident = Longident.Ldot (lident, name) in
        Trie (name, lident, lazy (explore lident env)) :: l
    in
    Env.fold_modules add_module (Some lident) env []
  in
  List.fold_left ~f:(fun l name ->
      let lident = Longident.Lident name in
      match Env.find_module_by_name lident env with
      | exception _ -> l
      | _ -> Trie (name, lident, lazy (explore lident env)) :: l
    ) ~init:[] global_modules
  (*Env.fold_modules (fun name _ _ l ->
      ignore (seen name);
      let lident = Longident.Lident name in
      Trie (name, lident, lazy (explore lident env)) :: l
    ) None env []*)

let execute_query query env dirs =
  let direct dir acc =
    Env.fold_values (fun _ path desc acc ->
        match match_query env query desc.Types.val_type with
        | Some cost -> (cost, path, desc) :: acc
        | None -> acc
      ) dir env acc
  in
  let rec recurse acc (Trie (_, dir, children)) =
    match
      ignore (Env.find_module_by_name dir env);
      Lazy.force children
    with
    | children ->
      List.fold_left ~f:recurse ~init:(direct (Some dir) acc) children
    | exception Not_found ->
      Logger.notify ~section:"polarity-search" "%S not found"
        (String.concat ~sep:"." (Longident.flatten dir));
      acc
  in
  List.fold_left dirs ~init:(direct None []) ~f:recurse
OCaml

Innovation. Community. Security.