-
Notifications
You must be signed in to change notification settings - Fork 58
/
Copy pathProcessExtra.ml
429 lines (405 loc) · 16.4 KB
/
ProcessExtra.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
open SharedTypes
let addLocItem extra loc locType =
if not loc.Warnings.loc_ghost then
extra.locItems <- {loc; locType} :: extra.locItems
let addReference ~extra stamp loc =
Hashtbl.replace extra.internalReferences stamp
(loc
::
(if Hashtbl.mem extra.internalReferences stamp then
Hashtbl.find extra.internalReferences stamp
else []))
let extraForFile ~(file : File.t) =
let extra = initExtra () in
file.stamps
|> Stamps.iterModules (fun stamp (d : Module.t Declared.t) ->
addLocItem extra d.name.loc (LModule (Definition (stamp, Module)));
addReference ~extra stamp d.name.loc);
file.stamps
|> Stamps.iterValues (fun stamp (d : Types.type_expr Declared.t) ->
addLocItem extra d.name.loc
(Typed (d.name.txt, d.item, Definition (stamp, Value)));
addReference ~extra stamp d.name.loc);
file.stamps
|> Stamps.iterTypes (fun stamp (d : Type.t Declared.t) ->
addLocItem extra d.name.loc
(TypeDefinition (d.name.txt, d.item.Type.decl, stamp));
addReference ~extra stamp d.name.loc;
match d.item.Type.kind with
| Record labels ->
labels
|> List.iter (fun {stamp; fname; typ} ->
addReference ~extra stamp fname.loc;
addLocItem extra fname.loc
(Typed
(d.name.txt, typ, Definition (d.stamp, Field fname.txt))))
| Variant constructors ->
constructors
|> List.iter (fun {Constructor.stamp; cname} ->
addReference ~extra stamp cname.loc;
let t =
{
Types.id = 0;
level = 0;
desc =
Tconstr
( Path.Pident
{Ident.stamp; name = d.name.txt; flags = 0},
[],
ref Types.Mnil );
}
in
addLocItem extra cname.loc
(Typed
( d.name.txt,
t,
Definition (d.stamp, Constructor cname.txt) )))
| _ -> ());
extra
let addExternalReference ~extra moduleName path tip loc =
(* TODO need to follow the path, and be able to load the files to follow module references... *)
Hashtbl.replace extra.externalReferences moduleName
((path, tip, loc)
::
(if Hashtbl.mem extra.externalReferences moduleName then
Hashtbl.find extra.externalReferences moduleName
else []))
let addFileReference ~extra moduleName loc =
let newLocs =
match Hashtbl.find_opt extra.fileReferences moduleName with
| Some oldLocs -> LocationSet.add loc oldLocs
| None -> LocationSet.singleton loc
in
Hashtbl.replace extra.fileReferences moduleName newLocs
let handleConstructor txt =
match txt with
| Longident.Lident name -> name
| Ldot (_left, name) -> name
| Lapply (_, _) -> assert false
let rec lidIsComplex (lid : Longident.t) =
match lid with
| Lapply _ -> true
| Ldot (lid, _) -> lidIsComplex lid
| _ -> false
let extraForStructureItems ~(iterator : Tast_iterator.iterator)
(items : Typedtree.structure_item list) =
items |> List.iter (iterator.structure_item iterator)
let extraForSignatureItems ~(iterator : Tast_iterator.iterator)
(items : Typedtree.signature_item list) =
items |> List.iter (iterator.signature_item iterator)
let extraForCmt ~(iterator : Tast_iterator.iterator)
({cmt_annots} : Cmt_format.cmt_infos) =
let extraForParts parts =
parts
|> Array.iter (fun part ->
match part with
| Cmt_format.Partial_signature str -> iterator.signature iterator str
| Partial_signature_item str -> iterator.signature_item iterator str
| Partial_expression expression -> iterator.expr iterator expression
| Partial_pattern pattern -> iterator.pat iterator pattern
| Partial_class_expr _ -> ()
| Partial_module_type module_type ->
iterator.module_type iterator module_type
| Partial_structure _ | Partial_structure_item _ -> ())
in
match cmt_annots with
| Implementation structure ->
extraForStructureItems ~iterator structure.str_items
| Partial_implementation parts ->
let items =
parts |> Array.to_list
|> Utils.filterMap (fun (p : Cmt_format.binary_part) ->
match p with
| Partial_structure str -> Some str.str_items
| Partial_structure_item str -> Some [str]
(* | Partial_expression(exp) => Some([ str]) *)
| _ -> None)
|> List.concat
in
extraForStructureItems ~iterator items;
extraForParts parts
| Interface signature -> extraForSignatureItems ~iterator signature.sig_items
| Partial_interface parts ->
let items =
parts |> Array.to_list
|> Utils.filterMap (fun (p : Cmt_format.binary_part) ->
match p with
| Partial_signature s -> Some s.sig_items
| Partial_signature_item str -> Some [str]
| _ -> None)
|> List.concat
in
extraForSignatureItems ~iterator items;
extraForParts parts
| _ -> extraForStructureItems ~iterator []
let addForPath ~env ~extra path lident loc typ tip =
let identName = Longident.last lident in
let identLoc = Utils.endOfLocation loc (String.length identName) in
let locType =
match ResolvePath.fromCompilerPath ~env path with
| Stamp stamp ->
addReference ~extra stamp identLoc;
LocalReference (stamp, tip)
| NotFound -> NotFound
| Global (moduleName, path) ->
addExternalReference ~extra moduleName path tip identLoc;
GlobalReference (moduleName, path, tip)
| Exported (env, name) -> (
match
match tip with
| Type -> Exported.find env.exported Exported.Type name
| _ -> Exported.find env.exported Exported.Value name
with
| Some stamp ->
addReference ~extra stamp identLoc;
LocalReference (stamp, tip)
| None -> NotFound)
| GlobalMod _ -> NotFound
in
addLocItem extra loc (Typed (identName, typ, locType))
let addForPathParent ~env ~extra path loc =
let locType =
match ResolvePath.fromCompilerPath ~env path with
| GlobalMod moduleName ->
addFileReference ~extra moduleName loc;
TopLevelModule moduleName
| Stamp stamp ->
addReference ~extra stamp loc;
LModule (LocalReference (stamp, Module))
| NotFound -> LModule NotFound
| Global (moduleName, path) ->
addExternalReference ~extra moduleName path Module loc;
LModule (GlobalReference (moduleName, path, Module))
| Exported (env, name) -> (
match Exported.find env.exported Exported.Module name with
| Some stamp ->
addReference ~extra stamp loc;
LModule (LocalReference (stamp, Module))
| None -> LModule NotFound)
in
addLocItem extra loc locType
let getTypeAtPath ~env path =
match ResolvePath.fromCompilerPath ~env path with
| GlobalMod _ -> `Not_found
| Global (moduleName, path) -> `Global (moduleName, path)
| NotFound -> `Not_found
| Exported (env, name) -> (
match Exported.find env.exported Exported.Type name with
| None -> `Not_found
| Some stamp -> (
let declaredType = Stamps.findType env.file.stamps stamp in
match declaredType with
| Some declaredType -> `Local declaredType
| None -> `Not_found))
| Stamp stamp -> (
let declaredType = Stamps.findType env.file.stamps stamp in
match declaredType with
| Some declaredType -> `Local declaredType
| None -> `Not_found)
let addForField ~env ~extra ~recordType ~fieldType {Asttypes.txt; loc} =
match (Shared.dig recordType).desc with
| Tconstr (path, _args, _memo) ->
let t = getTypeAtPath ~env path in
let name = handleConstructor txt in
let nameLoc = Utils.endOfLocation loc (String.length name) in
let locType =
match t with
| `Local {stamp; item = {kind = Record fields}} -> (
match fields |> List.find_opt (fun f -> f.fname.txt = name) with
| Some {stamp = astamp} ->
addReference ~extra astamp nameLoc;
LocalReference (stamp, Field name)
| None -> NotFound)
| `Global (moduleName, path) ->
addExternalReference ~extra moduleName path (Field name) nameLoc;
GlobalReference (moduleName, path, Field name)
| _ -> NotFound
in
addLocItem extra nameLoc (Typed (name, fieldType, locType))
| _ -> ()
let addForRecord ~env ~extra ~recordType items =
match (Shared.dig recordType).desc with
| Tconstr (path, _args, _memo) ->
let t = getTypeAtPath ~env path in
items
|> List.iter (fun ({Asttypes.txt; loc}, _, _) ->
(* let name = Longident.last(txt); *)
let name = handleConstructor txt in
let nameLoc = Utils.endOfLocation loc (String.length name) in
let locType =
match t with
| `Local {stamp; item = {kind = Record fields}} -> (
match fields |> List.find_opt (fun f -> f.fname.txt = name) with
| Some {stamp = astamp} ->
addReference ~extra astamp nameLoc;
LocalReference (stamp, Field name)
| None -> NotFound)
| `Global (moduleName, path) ->
addExternalReference ~extra moduleName path (Field name) nameLoc;
GlobalReference (moduleName, path, Field name)
| _ -> NotFound
in
addLocItem extra nameLoc (Typed (name, recordType, locType)))
| _ -> ()
let addForConstructor ~env ~extra constructorType {Asttypes.txt; loc}
{Types.cstr_name} =
match (Shared.dig constructorType).desc with
| Tconstr (path, _args, _memo) ->
let name = handleConstructor txt in
let nameLoc = Utils.endOfLocation loc (String.length name) in
let t = getTypeAtPath ~env path in
let locType =
match t with
| `Local {stamp; item = {kind = Variant constructors}} -> (
match
constructors
|> List.find_opt (fun c -> c.Constructor.cname.txt = cstr_name)
with
| Some {stamp = cstamp} ->
addReference ~extra cstamp nameLoc;
LocalReference (stamp, Constructor name)
| None -> NotFound)
| `Global (moduleName, path) ->
addExternalReference ~extra moduleName path (Constructor name) nameLoc;
GlobalReference (moduleName, path, Constructor name)
| _ -> NotFound
in
addLocItem extra nameLoc (Typed (name, constructorType, locType))
| _ -> ()
let rec addForLongident ~env ~extra top (path : Path.t) (txt : Longident.t) loc
=
if (not loc.Location.loc_ghost) && not (lidIsComplex txt) then (
let idLength = String.length (String.concat "." (Longident.flatten txt)) in
let reportedLength = loc.loc_end.pos_cnum - loc.loc_start.pos_cnum in
let isPpx = idLength <> reportedLength in
if isPpx then
match top with
| Some (t, tip) -> addForPath ~env ~extra path txt loc t tip
| None -> addForPathParent ~env ~extra path loc
else
let l = Utils.endOfLocation loc (String.length (Longident.last txt)) in
(match top with
| Some (t, tip) -> addForPath ~env ~extra path txt l t tip
| None -> addForPathParent ~env ~extra path l);
match (path, txt) with
| Pdot (pinner, _pname, _), Ldot (inner, name) ->
addForLongident ~env ~extra None pinner inner
(Utils.chopLocationEnd loc (String.length name + 1))
| Pident _, Lident _ -> ()
| _ -> ())
let rec handle_module_expr ~env ~extra expr =
match expr with
| Typedtree.Tmod_constraint (expr, _, _, _) ->
handle_module_expr ~env ~extra expr.mod_desc
| Tmod_ident (path, {txt; loc}) ->
if not (lidIsComplex txt) then
Log.log ("Ident!! " ^ String.concat "." (Longident.flatten txt));
addForLongident ~env ~extra None path txt loc
| Tmod_functor (_ident, _argName, _maybeType, resultExpr) ->
handle_module_expr ~env ~extra resultExpr.mod_desc
| Tmod_apply (obj, arg, _) ->
handle_module_expr ~env ~extra obj.mod_desc;
handle_module_expr ~env ~extra arg.mod_desc
| _ -> ()
let structure_item ~env ~extra (iter : Tast_iterator.iterator) item =
(match item.Typedtree.str_desc with
| Tstr_include {incl_mod = expr} ->
handle_module_expr ~env ~extra expr.mod_desc
| Tstr_module {mb_expr} -> handle_module_expr ~env ~extra mb_expr.mod_desc
| Tstr_open {open_path; open_txt = {txt; loc}} ->
(* Log.log("Have an open here"); *)
addForLongident ~env ~extra None open_path txt loc
| _ -> ());
Tast_iterator.default_iterator.structure_item iter item
let signature_item ~(file : File.t) ~extra (iter : Tast_iterator.iterator) item
=
(match item.Typedtree.sig_desc with
| Tsig_value {val_id; val_loc; val_name = name; val_desc; val_attributes} ->
let stamp = Ident.binding_time val_id in
if Stamps.findValue file.stamps stamp = None then (
let declared =
ProcessAttributes.newDeclared ~name ~stamp ~extent:val_loc
~modulePath:NotVisible ~item:val_desc.ctyp_type false val_attributes
in
Stamps.addValue file.stamps stamp declared;
addReference ~extra stamp name.loc;
addLocItem extra name.loc
(Typed (name.txt, val_desc.ctyp_type, Definition (stamp, Value))))
| _ -> ());
Tast_iterator.default_iterator.signature_item iter item
let typ ~env ~extra (iter : Tast_iterator.iterator) (item : Typedtree.core_type)
=
(match item.ctyp_desc with
| Ttyp_constr (path, {txt; loc}, _args) ->
addForLongident ~env ~extra (Some (item.ctyp_type, Type)) path txt loc
| _ -> ());
Tast_iterator.default_iterator.typ iter item
let pat ~(file : File.t) ~env ~extra (iter : Tast_iterator.iterator)
(pattern : Typedtree.pattern) =
let addForPattern stamp name =
if Stamps.findValue file.stamps stamp = None then (
let declared =
ProcessAttributes.newDeclared ~name ~stamp ~modulePath:NotVisible
~extent:pattern.pat_loc ~item:pattern.pat_type false
pattern.pat_attributes
in
Stamps.addValue file.stamps stamp declared;
addReference ~extra stamp name.loc;
addLocItem extra name.loc
(Typed (name.txt, pattern.pat_type, Definition (stamp, Value))))
in
(* Log.log("Entering pattern " ++ Utils.showLocation(pat_loc)); *)
(match pattern.pat_desc with
| Tpat_record (items, _) ->
addForRecord ~env ~extra ~recordType:pattern.pat_type items
| Tpat_construct (lident, constructor, _) ->
addForConstructor ~env ~extra pattern.pat_type lident constructor
| Tpat_alias (_inner, ident, name) ->
let stamp = Ident.binding_time ident in
addForPattern stamp name
| Tpat_var (ident, name) ->
(* Log.log("Pattern " ++ name.txt); *)
let stamp = Ident.binding_time ident in
addForPattern stamp name
| _ -> ());
Tast_iterator.default_iterator.pat iter pattern
let expr ~env ~(extra : extra) (iter : Tast_iterator.iterator)
(expression : Typedtree.expression) =
(match expression.exp_desc with
| Texp_ident (path, {txt; loc}, _) when not (JsxHacks.pathIsFragment path) ->
addForLongident ~env ~extra (Some (expression.exp_type, Value)) path txt loc
| Texp_record {fields} ->
addForRecord ~env ~extra ~recordType:expression.exp_type
(fields |> Array.to_list
|> Utils.filterMap (fun (desc, item) ->
match item with
| Typedtree.Overridden (loc, _) -> Some (loc, desc, ())
| _ -> None))
| Texp_constant constant ->
addLocItem extra expression.exp_loc (Constant constant)
(* Skip unit and list literals *)
| Texp_construct ({txt = Lident ("()" | "::"); loc}, _, _args)
when loc.loc_end.pos_cnum - loc.loc_start.pos_cnum <> 2 ->
()
| Texp_construct (lident, constructor, _args) ->
addForConstructor ~env ~extra expression.exp_type lident constructor
| Texp_field (inner, lident, _label_description) ->
addForField ~env ~extra ~recordType:inner.exp_type
~fieldType:expression.exp_type lident
| _ -> ());
Tast_iterator.default_iterator.expr iter expression
let getExtra ~file ~infos =
let extra = extraForFile ~file in
let env = QueryEnv.fromFile file in
let iterator =
{
Tast_iterator.default_iterator with
expr = expr ~env ~extra;
pat = pat ~env ~extra ~file;
signature_item = signature_item ~file ~extra;
structure_item = structure_item ~env ~extra;
typ = typ ~env ~extra;
}
in
extraForCmt ~iterator infos;
extra