Skip to content

Commit 0c6302f

Browse files
committed
complete creator/maker functions for type t that cannot be resolved further
1 parent a8f2b54 commit 0c6302f

6 files changed

+244
-70
lines changed

analysis/src/CompletionBackEnd.ml

+98-68
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ let getComplementaryCompletionsForTypedValue ~opens ~allFiles ~scope ~env prefix
514514
in
515515
localCompletionsWithOpens @ fileModules
516516

517-
let getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact ~scope
517+
let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope
518518
~completionContext ~env path =
519519
if debug then Printf.printf "Path %s\n" (path |> String.concat ".");
520520
let allFiles = allFilesInPackage full.package in
@@ -541,7 +541,9 @@ let getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact ~scope
541541
localCompletionsWithOpens @ fileModules
542542
| moduleName :: path -> (
543543
Log.log ("Path " ^ pathToString path);
544-
match getEnvWithOpens ~scope ~env ~package ~opens ~moduleName path with
544+
match
545+
getEnvWithOpens ~scope ~env ~package:full.package ~opens ~moduleName path
546+
with
545547
| Some (env, prefix) ->
546548
Log.log "Got the env";
547549
let namesUsed = Hashtbl.create 10 in
@@ -552,8 +554,8 @@ let rec digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
552554
~scope path =
553555
match
554556
path
555-
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true ~package
556-
~opens ~full ~pos ~env ~scope
557+
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true ~opens
558+
~full ~pos ~env ~scope
557559
with
558560
| {kind = Type {kind = Abstract (Some (p, _))}} :: _ ->
559561
(* This case happens when what we're looking for is a type alias.
@@ -769,8 +771,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
769771
| _ -> [])
770772
| CPId (path, completionContext) ->
771773
path
772-
|> getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact
773-
~completionContext ~env ~scope
774+
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~completionContext
775+
~env ~scope
774776
| CPApply (cp, labels) -> (
775777
match
776778
cp
@@ -815,7 +817,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
815817
| CPField (CPId (path, Module), fieldName) ->
816818
(* M.field *)
817819
path @ [fieldName]
818-
|> getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact
820+
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
819821
~completionContext:Field ~env ~scope
820822
| CPField (cp, fieldName) -> (
821823
let completionsForCtxPath =
@@ -933,52 +935,18 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
933935
| Tconstr (path, _typeArgs, _)
934936
| Tlink {desc = Tconstr (path, _typeArgs, _)}
935937
| Tsubst {desc = Tconstr (path, _typeArgs, _)}
936-
| Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) -> (
938+
| Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) ->
937939
if debug then Printf.printf "CPPipe type path:%s\n" (Path.name path);
938-
match Utils.expandPath path with
939-
| _ :: pathRev ->
940-
(* type path is relative to the completion environment
941-
express it from the root of the file *)
942-
let found, pathFromEnv =
943-
QueryEnv.pathFromEnv envFromCompletionItem (List.rev pathRev)
944-
in
945-
if debug then
946-
Printf.printf "CPPipe pathFromEnv:%s found:%b\n"
947-
(pathFromEnv |> String.concat ".")
948-
found;
949-
if pathFromEnv = [] then None
950-
else if
951-
env.file.moduleName <> envFromCompletionItem.file.moduleName
952-
&& found
953-
(* If the module names are different, then one needs to qualify the path.
954-
But only if the path belongs to the env from completion *)
955-
then Some (envFromCompletionItem.file.moduleName :: pathFromEnv)
956-
else Some pathFromEnv
957-
| _ -> None)
940+
TypeUtils.getPathRelativeToEnv ~debug ~env
941+
~envFromItem:envFromCompletionItem (Utils.expandPath path)
958942
| _ -> None)
959943
in
960944
match completionPath with
961945
| Some completionPath -> (
962-
let rec removeRawOpen rawOpen modulePath =
963-
match (rawOpen, modulePath) with
964-
| [_], _ -> Some modulePath
965-
| s :: inner, first :: restPath when s = first ->
966-
removeRawOpen inner restPath
967-
| _ -> None
968-
in
969-
let rec removeRawOpens rawOpens modulePath =
970-
match rawOpens with
971-
| rawOpen :: restOpens -> (
972-
let newModulePath = removeRawOpens restOpens modulePath in
973-
match removeRawOpen rawOpen newModulePath with
974-
| None -> newModulePath
975-
| Some mp -> mp)
976-
| [] -> modulePath
977-
in
978946
let completionPathMinusOpens =
979-
completionPath |> Utils.flattenAnyNamespaceInPath
980-
|> removeRawOpens package.opens
981-
|> removeRawOpens rawOpens |> String.concat "."
947+
TypeUtils.removeOpensFromCompletionPath ~rawOpens ~package
948+
completionPath
949+
|> String.concat "."
982950
in
983951
let completionName name =
984952
if completionPathMinusOpens = "" then name
@@ -987,7 +955,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
987955
let completions =
988956
completionPath @ [funNamePrefix]
989957
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:false
990-
~package ~opens ~full ~pos ~env ~scope
958+
~opens ~full ~pos ~env ~scope
991959
in
992960
let completions =
993961
completions
@@ -1051,7 +1019,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10511019
let findTypeOfValue path =
10521020
path
10531021
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true
1054-
~package ~opens ~full ~pos ~env ~scope
1022+
~opens ~full ~pos ~env ~scope
10551023
|> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos
10561024
in
10571025
let lowercaseComponent =
@@ -1061,16 +1029,25 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10611029
in
10621030
let targetLabel =
10631031
if lowercaseComponent then
1064-
match
1065-
["ReactDOM"; "domProps"]
1066-
|> digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos
1067-
~env ~scope
1068-
with
1069-
| None -> None
1070-
| Some fields -> (
1071-
match fields |> List.find_opt (fun f -> f.fname.txt = propName) with
1072-
| None -> None
1073-
| Some f -> Some (f.fname.txt, f.typ, env))
1032+
let rec digToTypeForCompletion path =
1033+
match
1034+
path
1035+
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true
1036+
~opens ~full ~pos ~env ~scope
1037+
with
1038+
| {kind = Type {kind = Abstract (Some (p, _))}} :: _ ->
1039+
(* This case happens when what we're looking for is a type alias.
1040+
This is the case in newer rescript-react versions where
1041+
ReactDOM.domProps is an alias for JsxEvent.t. *)
1042+
let pathRev = p |> Utils.expandPath in
1043+
pathRev |> List.rev |> digToTypeForCompletion
1044+
| {kind = Type {kind = Record fields}} :: _ -> (
1045+
match fields |> List.find_opt (fun f -> f.fname.txt = propName) with
1046+
| None -> None
1047+
| Some f -> Some (f.fname.txt, f.typ, env))
1048+
| _ -> None
1049+
in
1050+
["ReactDOM"; "domProps"] |> digToTypeForCompletion
10741051
else
10751052
CompletionJsx.getJsxLabels ~componentPath:pathToComponent
10761053
~findTypeOfValue ~package
@@ -1202,14 +1179,67 @@ let printConstructorArgs argsLen ~asSnippet =
12021179

12031180
type completionMode = Pattern of Completable.patternMode | Expression
12041181

1205-
let rec completeTypedValue ~full ~prefix ~completionContext ~mode
1182+
let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
12061183
(t : SharedTypes.completionType) =
12071184
let emptyCase num =
12081185
match mode with
12091186
| Expression -> "$" ^ string_of_int (num - 1)
12101187
| Pattern _ -> "${" ^ string_of_int num ^ ":_}"
12111188
in
12121189
match t with
1190+
| TtypeT {env; path} ->
1191+
(* Find all functions in the module that returns type t *)
1192+
let rec fnReturnsTypeT t =
1193+
match t.Types.desc with
1194+
| Tlink t1
1195+
| Tsubst t1
1196+
| Tpoly (t1, [])
1197+
| Tconstr (Pident {name = "function$"}, [t1; _], _) ->
1198+
fnReturnsTypeT t1
1199+
| Tarrow _ -> (
1200+
match TypeUtils.extractFunctionType ~env ~package:full.package t with
1201+
| ( (Nolabel, {desc = Tconstr (Path.Pident {name = "t"}, _, _)}) :: _,
1202+
{desc = Tconstr (Path.Pident {name = "t"}, _, _)} ) ->
1203+
(* Filter out functions that take type t first. These are often
1204+
@send style functions that we don't want to have here because
1205+
they usually aren't meant to create a type t from scratch. *)
1206+
false
1207+
| _args, {desc = Tconstr (Path.Pident {name = "t"}, _, _)} -> true
1208+
| _ -> false)
1209+
| _ -> false
1210+
in
1211+
let functionsReturningTypeT =
1212+
Hashtbl.create (Hashtbl.length env.exported.values_)
1213+
in
1214+
env.exported.values_
1215+
|> Hashtbl.iter (fun name stamp ->
1216+
match Stamps.findValue env.file.stamps stamp with
1217+
| None -> ()
1218+
| Some {item} -> (
1219+
if fnReturnsTypeT item then
1220+
let fnNname =
1221+
TypeUtils.getPathRelativeToEnv ~debug:false
1222+
~env:(QueryEnv.fromFile full.file)
1223+
~envFromItem:env (Utils.expandPath path)
1224+
in
1225+
1226+
match fnNname with
1227+
| None -> ()
1228+
| Some base ->
1229+
let base =
1230+
TypeUtils.removeOpensFromCompletionPath ~rawOpens
1231+
~package:full.package base
1232+
in
1233+
Hashtbl.add functionsReturningTypeT
1234+
((base |> String.concat ".") ^ "." ^ name)
1235+
item));
1236+
Hashtbl.fold
1237+
(fun fnName typeExpr all ->
1238+
Completion.createWithSnippet
1239+
~name:(Printf.sprintf "%s()" fnName)
1240+
~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env ()
1241+
:: all)
1242+
functionsReturningTypeT []
12131243
| Tbool env ->
12141244
[
12151245
Completion.create "true" ~kind:(Label "bool") ~env;
@@ -1268,7 +1298,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
12681298
| None -> []
12691299
| Some innerType ->
12701300
innerType
1271-
|> completeTypedValue ~full ~prefix ~completionContext ~mode
1301+
|> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
12721302
|> List.map (fun (c : Completion.t) ->
12731303
{
12741304
c with
@@ -1314,7 +1344,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
13141344
| None -> []
13151345
| Some innerType ->
13161346
innerType
1317-
|> completeTypedValue ~full ~prefix ~completionContext ~mode
1347+
|> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
13181348
|> List.map (fun (c : Completion.t) ->
13191349
{
13201350
c with
@@ -1331,7 +1361,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
13311361
| None -> []
13321362
| Some innerType ->
13331363
innerType
1334-
|> completeTypedValue ~full ~prefix ~completionContext ~mode
1364+
|> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
13351365
|> List.map (fun (c : Completion.t) ->
13361366
{
13371367
c with
@@ -1549,8 +1579,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
15491579
let allFiles = allFilesInPackage package in
15501580
let findTypeOfValue path =
15511581
path
1552-
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true
1553-
~package ~opens ~full ~pos ~env ~scope
1582+
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true ~opens
1583+
~full ~pos ~env ~scope
15541584
|> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos
15551585
in
15561586
match completable with
@@ -1781,8 +1811,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
17811811
| Some (typ, _env, completionContext) ->
17821812
let items =
17831813
typ
1784-
|> completeTypedValue ~mode:(Pattern patternMode) ~full ~prefix
1785-
~completionContext
1814+
|> completeTypedValue ~rawOpens ~mode:(Pattern patternMode) ~full
1815+
~prefix ~completionContext
17861816
in
17871817
fallbackOrEmpty ~items ())
17881818
| None -> fallbackOrEmpty ())
@@ -1819,7 +1849,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
18191849
in
18201850
let items =
18211851
typ
1822-
|> completeTypedValue ~mode:Expression ~full ~prefix
1852+
|> completeTypedValue ~rawOpens ~mode:Expression ~full ~prefix
18231853
~completionContext
18241854
|> List.map (fun (c : Completion.t) ->
18251855
if wrapInsertTextInBraces then

analysis/src/SharedTypes.ml

+1
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ and completionType =
331331
| Tbool of QueryEnv.t
332332
| Tarray of QueryEnv.t * innerType
333333
| Tstring of QueryEnv.t
334+
| TtypeT of {env: QueryEnv.t; path: Path.t}
334335
| Tvariant of {
335336
env: QueryEnv.t;
336337
constructors: Constructor.t list;

analysis/src/TypeUtils.ml

+47
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ let rec extractType ~env ~package (t : Types.type_expr) =
149149
})
150150
| Some (env, {item = {kind = Record fields}}) ->
151151
Some (Trecord {env; fields; definition = `TypeExpr t})
152+
| Some (env, {item = {name = "t"}}) -> Some (TtypeT {env; path})
152153
| _ -> None)
153154
| Ttuple expressions -> Some (Tuple (env, expressions, t))
154155
| Tvariant {row_fields} ->
@@ -631,6 +632,7 @@ let rec extractedTypeToString ?(inner = false) = function
631632
else Shared.typeToString typ
632633
| Tbool _ -> "bool"
633634
| Tstring _ -> "string"
635+
| TtypeT _ -> "type t"
634636
| Tarray (_, TypeExpr innerTyp) ->
635637
"array<" ^ Shared.typeToString innerTyp ^ ">"
636638
| Tarray (_, ExtractedType innerTyp) ->
@@ -757,3 +759,48 @@ module Codegen = struct
757759
|> List.map (fun (pat : Parsetree.pattern) ->
758760
Ast_helper.Exp.case pat (mkFailWithExp ())))
759761
end
762+
763+
let getPathRelativeToEnv ~debug ~(env : QueryEnv.t) ~envFromItem path =
764+
match path with
765+
| _ :: pathRev ->
766+
(* type path is relative to the completion environment
767+
express it from the root of the file *)
768+
let found, pathFromEnv =
769+
QueryEnv.pathFromEnv envFromItem (List.rev pathRev)
770+
in
771+
if debug then
772+
Printf.printf "CPPipe pathFromEnv:%s found:%b\n"
773+
(pathFromEnv |> String.concat ".")
774+
found;
775+
if pathFromEnv = [] then None
776+
else if
777+
env.file.moduleName <> envFromItem.file.moduleName && found
778+
(* If the module names are different, then one needs to qualify the path.
779+
But only if the path belongs to the env from completion *)
780+
then Some (envFromItem.file.moduleName :: pathFromEnv)
781+
else Some pathFromEnv
782+
| _ -> None
783+
784+
let removeOpensFromCompletionPath ~rawOpens ~package completionPath =
785+
let rec removeRawOpen rawOpen modulePath =
786+
match (rawOpen, modulePath) with
787+
| [_], _ -> Some modulePath
788+
| s :: inner, first :: restPath when s = first ->
789+
removeRawOpen inner restPath
790+
| _ -> None
791+
in
792+
let rec removeRawOpens rawOpens modulePath =
793+
match rawOpens with
794+
| rawOpen :: restOpens -> (
795+
let newModulePath = removeRawOpens restOpens modulePath in
796+
match removeRawOpen rawOpen newModulePath with
797+
| None -> newModulePath
798+
| Some mp -> mp)
799+
| [] -> modulePath
800+
in
801+
let completionPathMinusOpens =
802+
completionPath |> Utils.flattenAnyNamespaceInPath
803+
|> removeRawOpens package.opens
804+
|> removeRawOpens rawOpens
805+
in
806+
completionPathMinusOpens

analysis/tests/src/CompletionExpressions.res

+26
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,29 @@ let fnTakingPolyVariant = (a: somePolyVariant) => {
287287

288288
// fnTakingPolyVariant(o)
289289
// ^com
290+
291+
module SuperInt: {
292+
type t
293+
let increment: (t, int) => t
294+
let decrement: (t, int => int) => t
295+
let make: int => t
296+
let toInt: t => int
297+
} = {
298+
type t = int
299+
let increment = (t, num) => t + num
300+
let decrement = (t, decrementer) => decrementer(t)
301+
let make = t => t
302+
let toInt = t => t
303+
}
304+
305+
type withIntLocal = {superInt: SuperInt.t}
306+
307+
// let withInt: withIntLocal = {superInt: }
308+
// ^com
309+
310+
// CompletionSupport.makeTestHidden()
311+
// ^com
312+
313+
open CompletionSupport
314+
// CompletionSupport.makeTestHidden()
315+
// ^com

0 commit comments

Comments
 (0)