Skip to content

Commit dd06da7

Browse files
authored
Merge pull request #137 from rescript-lang/find_definitions
Add a command to find all the references to an item.
2 parents a66401d + 326599e commit dd06da7

File tree

6 files changed

+247
-15
lines changed

6 files changed

+247
-15
lines changed

analysis/src/Cli.ml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ Options:
2222

2323
./run.exe hover src/Foo.res 10 2
2424

25-
definition: get inferred type for Foo.res at line 10 column 2:
25+
definition: get definition for item in Foo.res at line 10 column 2:
2626

27-
./run.exe definition src/Foo.res 10 2|}
27+
./run.exe definition src/Foo.res 10 2
28+
29+
references: get references to item in Foo.res at line 10 column 2:
30+
31+
./run.exe references src/Foo.res 10 2|}
2832

2933
let main () =
3034
match Array.to_list Sys.argv with
@@ -36,6 +40,9 @@ let main () =
3640
| [_; "definition"; path; line; col] ->
3741
Commands.definition ~path ~line:(int_of_string line)
3842
~col:(int_of_string col)
43+
| [_; "references"; path; line; col] ->
44+
Commands.references ~path ~line:(int_of_string line)
45+
~col:(int_of_string col)
3946
| _ :: "dump" :: files -> Commands.dump files
4047
| [_; "test"; path] -> Commands.test ~path
4148
| args when List.mem "-h" args || List.mem "--help" args -> prerr_endline help

analysis/src/Commands.ml

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ let hover state ~file ~line ~col ~extra ~package =
103103
in
104104
match hoverText with
105105
| None -> Protocol.null
106-
| Some s -> Protocol.stringifyHover {contents = s} )
106+
| Some s -> Protocol.stringifyHover {contents = s})
107107

108108
let hover ~path ~line ~col =
109109
let state = TopTypes.empty () in
@@ -124,6 +124,7 @@ let definition state ~file ~line ~col ~extra ~package =
124124
|> List.filter (fun (l, _) -> not l.Location.loc_ghost)
125125
in
126126
let pos = Utils.protocolLineColToCmtLoc ~line ~col in
127+
127128
match References.locForPos ~extra:{extra with locations} pos with
128129
| None -> Protocol.null
129130
| Some (_, loc) -> (
@@ -151,7 +152,7 @@ let definition state ~file ~line ~col ~extra ~package =
151152
if skipZero then Protocol.null
152153
else
153154
Protocol.stringifyLocation
154-
{uri = Uri2.toString uri2; range = Utils.cmtLocToRange loc} )
155+
{uri = Uri2.toString uri2; range = Utils.cmtLocToRange loc})
155156

156157
let definition ~path ~line ~col =
157158
let state = TopTypes.empty () in
@@ -165,6 +166,53 @@ let definition ~path ~line ~col =
165166
in
166167
print_endline result
167168

169+
let references state ~file ~line ~col ~extra ~package =
170+
let open TopTypes in
171+
let locations =
172+
extra.SharedTypes.locations
173+
|> List.filter (fun (l, _) -> not l.Location.loc_ghost)
174+
in
175+
let pos = Utils.protocolLineColToCmtLoc ~line ~col in
176+
177+
match References.locForPos ~extra:{extra with locations} pos with
178+
| None -> Protocol.null
179+
| Some (_, loc) ->
180+
let allReferences =
181+
References.allReferencesForLoc ~pathsForModule:package.pathsForModule
182+
~file ~extra ~allModules:package.localModules
183+
~getUri:(State.fileForUri state)
184+
~getModule:(State.fileForModule state ~package)
185+
~getExtra:(State.extraForModule state ~package)
186+
loc
187+
in
188+
let allLocs =
189+
allReferences
190+
|> List.fold_left
191+
(fun acc (uri2, references) ->
192+
(references
193+
|> List.map (fun loc ->
194+
Protocol.stringifyLocation
195+
{
196+
uri = Uri2.toString uri2;
197+
range = Utils.cmtLocToRange loc;
198+
}))
199+
@ acc)
200+
[]
201+
in
202+
"[\n" ^ (allLocs |> String.concat ",\n") ^ "\n]"
203+
204+
let references ~path ~line ~col =
205+
let state = TopTypes.empty () in
206+
let filePath = Files.maybeConcat (Unix.getcwd ()) path in
207+
let uri = Uri2.fromPath filePath in
208+
let result =
209+
match State.getFullFromCmt ~state ~uri with
210+
| Error _message -> Protocol.null
211+
| Ok (package, {file; extra}) ->
212+
references state ~file ~line ~col ~extra ~package
213+
in
214+
print_endline result
215+
168216
let test ~path =
169217
Uri2.stripPath := true;
170218
match Files.readFile path with
@@ -180,22 +228,27 @@ let test ~path =
180228
let line = i - 1 in
181229
let col = mlen - 1 in
182230
if mlen >= 3 then (
183-
( match String.sub rest 0 3 with
231+
(match String.sub rest 0 3 with
184232
| "def" ->
185233
print_endline
186-
( "Definition " ^ path ^ " " ^ string_of_int line ^ ":"
187-
^ string_of_int col );
234+
("Definition " ^ path ^ " " ^ string_of_int line ^ ":"
235+
^ string_of_int col);
188236
definition ~path ~line ~col
189237
| "hov" ->
190238
print_endline
191-
( "Hover " ^ path ^ " " ^ string_of_int line ^ ":"
192-
^ string_of_int col );
239+
("Hover " ^ path ^ " " ^ string_of_int line ^ ":"
240+
^ string_of_int col);
193241

194242
hover ~path ~line ~col
243+
| "ref" ->
244+
print_endline
245+
("References " ^ path ^ " " ^ string_of_int line ^ ":"
246+
^ string_of_int col);
247+
references ~path ~line ~col
195248
| "com" ->
196249
print_endline
197-
( "Complete " ^ path ^ " " ^ string_of_int line ^ ":"
198-
^ string_of_int col );
250+
("Complete " ^ path ^ " " ^ string_of_int line ^ ":"
251+
^ string_of_int col);
199252
let currentFile, cout = Filename.open_temp_file "def" "txt" in
200253
lines
201254
|> List.iteri (fun j l ->
@@ -208,7 +261,7 @@ let test ~path =
208261
close_out cout;
209262
complete ~path ~line ~col ~currentFile;
210263
Sys.remove currentFile
211-
| _ -> () );
212-
print_newline () )
264+
| _ -> ());
265+
print_newline ())
213266
in
214267
lines |> List.iteri processLine

analysis/src/References.ml

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ let locForPos ~extra pos =
4646
arg has the location range of arg
4747
heuristic for: [Props, arg], give loc of `arg` *)
4848
(* Printf.eprintf "l1 %s\nl2 %s\n"
49-
(SharedTypes.locationToString _l1)
50-
(SharedTypes.locationToString l2); *)
49+
(SharedTypes.locationToString _l1)
50+
(SharedTypes.locationToString l2); *)
5151
Some l2
5252
| [(loc1, _); ((loc2, _) as l); (loc3, _)] when loc1 = loc2 && loc2 = loc3 ->
5353
(* JSX with at most one child
@@ -301,3 +301,149 @@ let definitionForLoc ~pathsForModule ~file ~getUri ~getModule loc =
301301
(* oooh wht do I do if the stamp is inside a pseudo-file? *)
302302
maybeLog ("Got stamp " ^ string_of_int stamp);
303303
definition ~file:env.file ~getModule stamp tip)))
304+
305+
let isVisible (declared : _ SharedTypes.declared) =
306+
declared.exported
307+
&&
308+
let rec loop v =
309+
match v with
310+
| File _ -> true
311+
| NotVisible -> false
312+
| IncludedModule (_, inner) -> loop inner
313+
| ExportedModule (_, inner) -> loop inner
314+
in
315+
loop declared.modulePath
316+
317+
let rec pathFromVisibility visibilityPath current =
318+
match visibilityPath with
319+
| File _ -> Some current
320+
| IncludedModule (_, inner) -> pathFromVisibility inner current
321+
| ExportedModule (name, inner) ->
322+
pathFromVisibility inner (Nested (name, current))
323+
| NotVisible -> None
324+
325+
let pathFromVisibility visibilityPath tipName =
326+
pathFromVisibility visibilityPath (Tip tipName)
327+
328+
let forLocalStamp ~pathsForModule ~file ~extra ~allModules ~getModule ~getUri
329+
~getExtra stamp tip =
330+
let env = Query.fileEnv file in
331+
let open Infix in
332+
match
333+
match tip with
334+
| Constructor name ->
335+
Query.getConstructor file stamp name |?>> fun x -> x.stamp
336+
| Field name -> Query.getField file stamp name |?>> fun x -> x.stamp
337+
| _ -> Some stamp
338+
with
339+
| None -> []
340+
| Some localStamp -> (
341+
match Hashtbl.find_opt extra.internalReferences localStamp with
342+
| None -> []
343+
| Some local ->
344+
maybeLog ("Checking externals: " ^ string_of_int stamp);
345+
let externals =
346+
match Query.declaredForTip ~stamps:env.file.stamps stamp tip with
347+
| None -> []
348+
| Some declared ->
349+
if isVisible declared then (
350+
let alternativeReferences =
351+
match
352+
alternateDeclared ~pathsForModule ~file ~getUri declared tip
353+
with
354+
| None -> []
355+
| Some (file, extra, {stamp}) -> (
356+
match
357+
match tip with
358+
| Constructor name ->
359+
Query.getConstructor file stamp name |?>> fun x -> x.stamp
360+
| Field name ->
361+
Query.getField file stamp name |?>> fun x -> x.stamp
362+
| _ -> Some stamp
363+
with
364+
| None -> []
365+
| Some localStamp -> (
366+
match
367+
Hashtbl.find_opt extra.internalReferences localStamp
368+
with
369+
| None -> []
370+
| Some local -> [(file.uri, local)]))
371+
(* if this file has a corresponding interface or implementation file
372+
also find the references in that file *)
373+
in
374+
match pathFromVisibility declared.modulePath declared.name.txt with
375+
| None -> []
376+
| Some path ->
377+
maybeLog ("Now checking path " ^ pathToString path);
378+
let thisModuleName = file.moduleName in
379+
let externals =
380+
allModules
381+
|> List.filter (fun name -> name <> file.moduleName)
382+
|> Utils.filterMap (fun name ->
383+
match getModule name with
384+
| None -> None
385+
| Some file -> (
386+
match getExtra name with
387+
| None -> None
388+
| Some extra -> (
389+
match
390+
Hashtbl.find_opt extra.externalReferences
391+
thisModuleName
392+
with
393+
| None -> None
394+
| Some refs ->
395+
let refs =
396+
refs
397+
|> Utils.filterMap (fun (p, t, l) ->
398+
match p = path && t = tip with
399+
| true -> Some l
400+
| false -> None)
401+
in
402+
Some (file.uri, refs))))
403+
in
404+
alternativeReferences @ externals)
405+
else (
406+
maybeLog "Not visible";
407+
[])
408+
in
409+
(file.uri, local) :: externals)
410+
411+
let allReferencesForLoc ~pathsForModule ~getUri ~file ~extra ~allModules
412+
~getModule ~getExtra loc =
413+
match loc with
414+
| Explanation _
415+
| Typed (_, NotFound)
416+
| LModule NotFound
417+
| TopLevelModule _ | Constant _ ->
418+
[]
419+
| TypeDefinition (_, _, stamp) ->
420+
forLocalStamp ~pathsForModule ~getUri ~file ~extra ~allModules ~getModule
421+
~getExtra stamp Type
422+
| Typed (_, (LocalReference (stamp, tip) | Definition (stamp, tip)))
423+
| LModule (LocalReference (stamp, tip) | Definition (stamp, tip)) ->
424+
maybeLog
425+
("Finding references for " ^ Uri2.toString file.uri ^ " and stamp "
426+
^ string_of_int stamp ^ " and tip " ^ tipToString tip);
427+
forLocalStamp ~pathsForModule ~getUri ~file ~extra ~allModules ~getModule
428+
~getExtra stamp tip
429+
| LModule (GlobalReference (moduleName, path, tip))
430+
| Typed (_, GlobalReference (moduleName, path, tip)) -> (
431+
match getModule moduleName with
432+
| None -> []
433+
| Some file -> (
434+
let env = Query.fileEnv file in
435+
match Query.resolvePath ~env ~path ~getModule with
436+
| None -> []
437+
| Some (env, name) -> (
438+
match Query.exportedForTip ~env name tip with
439+
| None -> []
440+
| Some stamp -> (
441+
match getUri env.file.uri with
442+
| Error _ -> []
443+
| Ok (file, extra) ->
444+
maybeLog
445+
("Finding references for (global) " ^ Uri2.toString env.file.uri
446+
^ " and stamp " ^ string_of_int stamp ^ " and tip "
447+
^ tipToString tip);
448+
forLocalStamp ~pathsForModule ~getUri ~file ~extra ~allModules
449+
~getModule ~getExtra stamp tip))))

analysis/src/State.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,14 @@ let fileForModule state ~package modname =
7575
match docsForModule modname state ~package with
7676
| None -> None
7777
| Some (file, _) -> Some file
78+
79+
let extraForModule state ~package modname =
80+
if Hashtbl.mem package.TopTypes.pathsForModule modname then
81+
let paths = Hashtbl.find package.pathsForModule modname in
82+
match SharedTypes.getSrc paths with
83+
| None -> None
84+
| Some src -> (
85+
match getFullFromCmt ~state ~uri:(Uri2.fromPath src) with
86+
| Ok (_package, {extra}) -> Some extra
87+
| Error _ -> None)
88+
else None

analysis/tests/src/References.res

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
let x = 12
2+
// ^ref
3+
4+
let a = x
5+
6+
let b = a
7+
8+
let c = x
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
References tests/src/References.res 0:4
2+
[
3+
{"uri": "References.res", "range": {"start": {"line": 7, "character": 8}, "end": {"line": 7, "character": 9}}},
4+
{"uri": "References.res", "range": {"start": {"line": 3, "character": 8}, "end": {"line": 3, "character": 9}}},
5+
{"uri": "References.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 5}}}
6+
]
7+

0 commit comments

Comments
 (0)