Skip to content

Commit a38a5bd

Browse files
authoredMay 28, 2024··
Use identifier loc in certain scenarios (#993)
* use identifier location to look up type information in certain scenarios, when in incremental type checking mode * changelog * fix * fix
1 parent fc89007 commit a38a5bd

11 files changed

+272
-43
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
- Hover: print signature above docstrings. https://github.com/rescript-lang/rescript-vscode/pull/969
2828
- Adjust function template snippet return. https://github.com/rescript-lang/rescript-vscode/pull/985
2929
- Don't expand `type t` maker functions in patterns. https://github.com/rescript-lang/rescript-vscode/pull/986
30+
- Use `loc` for identifiers to get more and better completions in certain scenarios with type parameters. https://github.com/rescript-lang/rescript-vscode/pull/993
3031

3132
#### :rocket: New Feature
3233

‎analysis/src/Cfg.ml

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
let debugFollowCtxPath = ref false
22

33
let isDocGenFromCompiler = ref false
4+
5+
let inIncrementalTypecheckingMode =
6+
ref
7+
(try
8+
match Sys.getenv "RESCRIPT_INCREMENTAL_TYPECHECKING" with
9+
| "true" -> true
10+
| _ -> false
11+
with _ -> false)

‎analysis/src/Cmt.ml

+12-7
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@ let fullFromUri ~uri =
1616
let moduleName =
1717
BuildSystem.namespacedName package.namespace (FindFiles.getName path)
1818
in
19-
let incrementalCmtPath =
20-
package.rootPath ^ "/lib/bs/___incremental" ^ "/" ^ moduleName
21-
^
22-
match Files.classifySourceFile path with
23-
| Resi -> ".cmti"
24-
| _ -> ".cmt"
19+
let incremental =
20+
if !Cfg.inIncrementalTypecheckingMode then
21+
let incrementalCmtPath =
22+
package.rootPath ^ "/lib/bs/___incremental" ^ "/" ^ moduleName
23+
^
24+
match Files.classifySourceFile path with
25+
| Resi -> ".cmti"
26+
| _ -> ".cmt"
27+
in
28+
fullForCmt ~moduleName ~package ~uri incrementalCmtPath
29+
else None
2530
in
26-
match fullForCmt ~moduleName ~package ~uri incrementalCmtPath with
31+
match incremental with
2732
| Some cmtInfo ->
2833
if Debug.verbose () then Printf.printf "[cmt] Found incremental cmt\n";
2934
Some cmtInfo

‎analysis/src/Commands.ml

+2
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ let test ~path =
344344
| "db-" -> Log.verbose := false
345345
| "dv+" -> Debug.debugLevel := Verbose
346346
| "dv-" -> Debug.debugLevel := Off
347+
| "in+" -> Cfg.inIncrementalTypecheckingMode := true
348+
| "in-" -> Cfg.inIncrementalTypecheckingMode := false
347349
| "ve+" -> (
348350
let version = String.sub rest 3 (String.length rest - 3) in
349351
let version = String.trim version in

‎analysis/src/CompletionBackEnd.ml

+41-11
Original file line numberDiff line numberDiff line change
@@ -869,11 +869,45 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
869869
| Some (Tpromise (env, typ), _env) ->
870870
[Completion.create "dummy" ~env ~kind:(Completion.Value typ)]
871871
| _ -> [])
872-
| CPId (path, completionContext) ->
872+
| CPId {path; completionContext; loc} ->
873873
if Debug.verbose () then print_endline "[ctx_path]--> CPId";
874-
path
875-
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~completionContext
876-
~env ~scope
874+
(* Looks up the type of an identifier.
875+
876+
Because of reasons we sometimes don't get enough type
877+
information when looking up identifiers where the type
878+
has type parameters. This in turn means less completions.
879+
880+
There's a heuristic below that tries to look up the type
881+
of the ID in the usual way first. But if the type found
882+
still has uninstantiated type parameters, we check the
883+
location for the identifier from the compiler type artifacts.
884+
That type usually has the type params instantiated, if they are.
885+
This leads to better completion.
886+
887+
However, we only do it in incremental type checking mode,
888+
because more type information is always available in that mode. *)
889+
let useTvarLookup = !Cfg.inIncrementalTypecheckingMode in
890+
let byPath =
891+
path
892+
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
893+
~completionContext ~env ~scope
894+
in
895+
let hasTvars =
896+
if useTvarLookup then
897+
match byPath with
898+
| [{kind = Value typ}] when TypeUtils.hasTvar typ -> true
899+
| _ -> false
900+
else false
901+
in
902+
let result =
903+
if hasTvars then
904+
let byLoc = TypeUtils.findTypeViaLoc loc ~full ~debug in
905+
match (byLoc, byPath) with
906+
| Some t, [({kind = Value _} as item)] -> [{item with kind = Value t}]
907+
| _ -> byPath
908+
else byPath
909+
in
910+
result
877911
| CPApply (cp, labels) -> (
878912
if Debug.verbose () then print_endline "[ctx_path]--> CPApply";
879913
match
@@ -916,7 +950,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
916950
[Completion.create "dummy" ~env ~kind:(Completion.Value retType)]
917951
| _ -> [])
918952
| _ -> [])
919-
| CPField (CPId (path, Module), fieldName) ->
953+
| CPField (CPId {path; completionContext = Module}, fieldName) ->
920954
if Debug.verbose () then print_endline "[ctx_path]--> CPField: M.field";
921955
(* M.field *)
922956
path @ [fieldName]
@@ -1271,13 +1305,9 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
12711305
| None -> [])
12721306
| CTypeAtPos loc -> (
12731307
if Debug.verbose () then print_endline "[ctx_path]--> CTypeAtPos";
1274-
match
1275-
References.getLocItem ~full ~pos:(Pos.ofLexing loc.loc_start) ~debug
1276-
with
1308+
match TypeUtils.findTypeViaLoc loc ~full ~debug with
12771309
| None -> []
1278-
| Some {locType = Typed (_, typExpr, _)} ->
1279-
[Completion.create "dummy" ~env ~kind:(Value typExpr)]
1280-
| _ -> [])
1310+
| Some typExpr -> [Completion.create "dummy" ~env ~kind:(Value typExpr)])
12811311

12821312
let getOpens ~debug ~rawOpens ~package ~env =
12831313
if debug && rawOpens <> [] then

‎analysis/src/CompletionFrontEnd.ml

+72-21
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,24 @@ let rec exprToContextPathInner (e : Parsetree.expression) =
219219
| [] -> None
220220
| exp :: _ -> exprToContextPath exp))
221221
| Pexp_ident {txt = Lident ("|." | "|.u")} -> None
222-
| Pexp_ident {txt} -> Some (CPId (Utils.flattenLongIdent txt, Value))
222+
| Pexp_ident {txt; loc} ->
223+
Some
224+
(CPId {path = Utils.flattenLongIdent txt; completionContext = Value; loc})
223225
| Pexp_field (e1, {txt = Lident name}) -> (
224226
match exprToContextPath e1 with
225227
| Some contextPath -> Some (CPField (contextPath, name))
226228
| _ -> None)
227-
| Pexp_field (_, {txt = Ldot (lid, name)}) ->
229+
| Pexp_field (_, {loc; txt = Ldot (lid, name)}) ->
228230
(* Case x.M.field ignore the x part *)
229-
Some (CPField (CPId (Utils.flattenLongIdent lid, Module), name))
231+
Some
232+
(CPField
233+
( CPId
234+
{
235+
path = Utils.flattenLongIdent lid;
236+
completionContext = Module;
237+
loc;
238+
},
239+
name ))
230240
| Pexp_send (e1, {txt}) -> (
231241
match exprToContextPath e1 with
232242
| None -> None
@@ -411,8 +421,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
411421
let ctxPath =
412422
if contextPathToSave = None then
413423
match p with
414-
| {ppat_desc = Ppat_var {txt}} ->
415-
Some (Completable.CPId ([txt], Value))
424+
| {ppat_desc = Ppat_var {txt; loc}} ->
425+
Some
426+
(Completable.CPId {path = [txt]; completionContext = Value; loc})
416427
| _ -> None
417428
else None
418429
in
@@ -1055,12 +1066,16 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
10551066
setResult
10561067
(Cpath
10571068
(CPId
1058-
( lidPath,
1059-
if
1060-
isLikelyModulePath
1061-
&& expr |> Res_parsetree_viewer.isBracedExpr
1062-
then ValueOrField
1063-
else Value )))
1069+
{
1070+
loc = lid.loc;
1071+
path = lidPath;
1072+
completionContext =
1073+
(if
1074+
isLikelyModulePath
1075+
&& expr |> Res_parsetree_viewer.isBracedExpr
1076+
then ValueOrField
1077+
else Value);
1078+
}))
10641079
| Pexp_construct ({txt = Lident ("::" | "()")}, _) ->
10651080
(* Ignore list expressions, used in JSX, unit, and more *) ()
10661081
| Pexp_construct (lid, eOpt) -> (
@@ -1075,7 +1090,11 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
10751090
if
10761091
eOpt = None && (not lid.loc.loc_ghost)
10771092
&& lid.loc |> Loc.hasPos ~pos:posBeforeCursor
1078-
then setResult (Cpath (CPId (lidPath, Value)))
1093+
then
1094+
setResult
1095+
(Cpath
1096+
(CPId
1097+
{loc = lid.loc; path = lidPath; completionContext = Value}))
10791098
else
10801099
match eOpt with
10811100
| Some e when locHasCursor e.pexp_loc -> (
@@ -1106,7 +1125,12 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11061125
(* Case x.M.field ignore the x part *)
11071126
let contextPath =
11081127
Completable.CPField
1109-
( CPId (Utils.flattenLongIdent id, Module),
1128+
( CPId
1129+
{
1130+
loc = fieldName.loc;
1131+
path = Utils.flattenLongIdent id;
1132+
completionContext = Module;
1133+
},
11101134
if blankAfterCursor = Some '.' then
11111135
(* x.M. field ---> M. *) ""
11121136
else if name = "_" then ""
@@ -1149,7 +1173,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
11491173
(match compNamePath with
11501174
| [prefix] when Char.lowercase_ascii prefix.[0] = prefix.[0] ->
11511175
ChtmlElement {prefix}
1152-
| _ -> Cpath (CPId (compNamePath, Module)))
1176+
| _ ->
1177+
Cpath
1178+
(CPId
1179+
{
1180+
loc = compName.loc;
1181+
path = compNamePath;
1182+
completionContext = Module;
1183+
}))
11531184
else iterateJsxProps ~iterator jsxProps
11541185
| Pexp_apply
11551186
( {pexp_desc = Pexp_ident {txt = Lident ("|." | "|.u")}},
@@ -1356,7 +1387,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13561387
(lidPath |> String.concat ".")
13571388
(Loc.toString lid.loc);
13581389
if lid.loc |> Loc.hasPos ~pos:posBeforeCursor then
1359-
setResult (Cpath (CPId (lidPath, Type)))
1390+
setResult
1391+
(Cpath
1392+
(CPId {loc = lid.loc; path = lidPath; completionContext = Type}))
13601393
| _ -> ());
13611394
Ast_iterator.default_iterator.typ iterator core_type
13621395
in
@@ -1374,7 +1407,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13741407
Printf.printf "Ppat_construct %s:%s\n"
13751408
(lidPath |> String.concat ".")
13761409
(Loc.toString lid.loc);
1377-
let completion = Completable.Cpath (CPId (lidPath, Value)) in
1410+
let completion =
1411+
Completable.Cpath
1412+
(CPId {loc = lid.loc; path = lidPath; completionContext = Value})
1413+
in
13781414
match !result with
13791415
| Some (Completable.Cpattern p, scope) ->
13801416
result := Some (Cpattern {p with fallback = Some completion}, scope)
@@ -1392,7 +1428,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
13921428
(lidPath |> String.concat ".")
13931429
(Loc.toString lid.loc);
13941430
found := true;
1395-
setResult (Cpath (CPId (lidPath, Module)))
1431+
setResult
1432+
(Cpath
1433+
(CPId {loc = lid.loc; path = lidPath; completionContext = Module}))
13961434
| _ -> ());
13971435
Ast_iterator.default_iterator.module_expr iterator me
13981436
in
@@ -1406,7 +1444,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
14061444
(lidPath |> String.concat ".")
14071445
(Loc.toString lid.loc);
14081446
found := true;
1409-
setResult (Cpath (CPId (lidPath, Module)))
1447+
setResult
1448+
(Cpath
1449+
(CPId {loc = lid.loc; path = lidPath; completionContext = Module}))
14101450
| _ -> ());
14111451
Ast_iterator.default_iterator.module_type iterator mt
14121452
in
@@ -1422,7 +1462,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
14221462
Printf.printf "Ptype_variant unary %s:%s\n" decl.pcd_name.txt
14231463
(Loc.toString decl.pcd_name.loc);
14241464
found := true;
1425-
setResult (Cpath (CPId ([decl.pcd_name.txt], Value)))
1465+
setResult
1466+
(Cpath
1467+
(CPId
1468+
{
1469+
loc = decl.pcd_name.loc;
1470+
path = [decl.pcd_name.txt];
1471+
completionContext = Value;
1472+
}))
14261473
| _ -> ());
14271474
Ast_iterator.default_iterator.type_kind iterator type_kind
14281475
in
@@ -1459,7 +1506,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
14591506
iterator.structure iterator str |> ignore;
14601507
if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then (
14611508
scope := !lastScopeBeforeCursor;
1462-
setResult (Cpath (CPId ([""], Value))));
1509+
setResult
1510+
(Cpath
1511+
(CPId {loc = Location.none; path = [""]; completionContext = Value})));
14631512
if !found = false then if debug then Printf.printf "XXX Not found!\n";
14641513
!result)
14651514
else if Filename.check_suffix path ".resi" then (
@@ -1468,7 +1517,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor
14681517
iterator.signature iterator signature |> ignore;
14691518
if blankAfterCursor = Some ' ' || blankAfterCursor = Some '\n' then (
14701519
scope := !lastScopeBeforeCursor;
1471-
setResult (Cpath (CPId ([""], Type))));
1520+
setResult
1521+
(Cpath
1522+
(CPId {loc = Location.none; path = [""]; completionContext = Type})));
14721523
if !found = false then if debug then Printf.printf "XXX Not found!\n";
14731524
!result)
14741525
else None

‎analysis/src/SharedTypes.ml

+7-3
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,11 @@ module Completable = struct
606606
| CPBool
607607
| CPOption of contextPath
608608
| CPApply of contextPath * Asttypes.arg_label list
609-
| CPId of string list * completionContext
609+
| CPId of {
610+
path: string list;
611+
completionContext: completionContext;
612+
loc: Location.t;
613+
}
610614
| CPField of contextPath * string
611615
| CPObj of contextPath * string
612616
| CPAwait of contextPath
@@ -689,8 +693,8 @@ module Completable = struct
689693
^ ")"
690694
| CPArray (Some ctxPath) -> "array<" ^ contextPathToString ctxPath ^ ">"
691695
| CPArray None -> "array"
692-
| CPId (sl, completionContext) ->
693-
completionContextToString completionContext ^ list sl
696+
| CPId {path; completionContext} ->
697+
completionContextToString completionContext ^ list path
694698
| CPField (cp, s) -> contextPathToString cp ^ "." ^ str s
695699
| CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]"
696700
| CPPipe {contextPath; id; inJsx} ->

‎analysis/src/TypeUtils.ml

+35-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ let debugLogTypeArgContext {env; typeArgs; typeParams} =
66
(typeArgs |> List.map Shared.typeToString |> String.concat ", ")
77
(typeParams |> List.map Shared.typeToString |> String.concat ", ")
88

9+
(** Checks whether this type has any uninstantiated type parameters. *)
10+
let rec hasTvar (ty : Types.type_expr) : bool =
11+
match ty.desc with
12+
| Tvar _ -> true
13+
| Tarrow (_, ty1, ty2, _) -> hasTvar ty1 || hasTvar ty2
14+
| Ttuple tyl -> List.exists hasTvar tyl
15+
| Tconstr (_, tyl, _) -> List.exists hasTvar tyl
16+
| Tobject (ty, _) -> hasTvar ty
17+
| Tfield (_, _, ty1, ty2) -> hasTvar ty1 || hasTvar ty2
18+
| Tnil -> false
19+
| Tlink ty -> hasTvar ty
20+
| Tsubst ty -> hasTvar ty
21+
| Tvariant {row_fields; _} ->
22+
List.exists
23+
(function
24+
| _, Types.Rpresent (Some ty) -> hasTvar ty
25+
| _, Reither (_, tyl, _, _) -> List.exists hasTvar tyl
26+
| _ -> false)
27+
row_fields
28+
| Tunivar _ -> true
29+
| Tpoly (ty, tyl) -> hasTvar ty || List.exists hasTvar tyl
30+
| Tpackage (_, _, tyl) -> List.exists hasTvar tyl
31+
32+
let findTypeViaLoc ~full ~debug (loc : Location.t) =
33+
match References.getLocItem ~full ~pos:(Pos.ofLexing loc.loc_end) ~debug with
34+
| Some {locType = Typed (_, typExpr, _)} -> Some typExpr
35+
| _ -> None
36+
937
let rec pathFromTypeExpr (t : Types.type_expr) =
1038
match t.desc with
1139
| Tconstr (Pident {name = "function$"}, [t; _], _) -> pathFromTypeExpr t
@@ -927,7 +955,13 @@ let rec contextPathFromCoreType (coreType : Parsetree.core_type) =
927955
| Ptyp_constr ({txt = Lident "array"}, [innerTyp]) ->
928956
Some (Completable.CPArray (innerTyp |> contextPathFromCoreType))
929957
| Ptyp_constr (lid, _) ->
930-
Some (CPId (lid.txt |> Utils.flattenLongIdent, Type))
958+
Some
959+
(CPId
960+
{
961+
path = lid.txt |> Utils.flattenLongIdent;
962+
completionContext = Type;
963+
loc = lid.loc;
964+
})
931965
| _ -> None
932966

933967
let unwrapCompletionTypeIfOption (t : SharedTypes.completionType) =
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// let dict = Js.Dict.fromArray([])
2+
// ^com
3+
4+
// let dict = Js.Dict.fromArray([()])
5+
// ^com
6+
7+
// let dict = Js.Dict.fromArray([("key", )])
8+
// ^com
9+
10+
// ^in+
11+
let dict = Js.Dict.fromArray([
12+
("key", true),
13+
// ("key2", )
14+
// ^com
15+
])
16+
// ^in-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Complete src/CompletionDicts.res 0:33
2+
posCursor:[0:33] posNoWhite:[0:32] Found expr:[0:14->0:35]
3+
Pexp_apply ...[0:14->0:31] (...[0:32->0:34])
4+
Completable: Cexpression CArgument Value[Js, Dict, fromArray]($0)->array
5+
Package opens Pervasives.JsxModules.place holder
6+
Resolved opens 1 pervasives
7+
ContextPath CArgument Value[Js, Dict, fromArray]($0)
8+
ContextPath Value[Js, Dict, fromArray]
9+
Path Js.Dict.fromArray
10+
[{
11+
"label": "(_, _)",
12+
"kind": 12,
13+
"tags": [],
14+
"detail": "(key, 'a)",
15+
"documentation": null,
16+
"insertText": "(${1:_}, ${2:_})",
17+
"insertTextFormat": 2
18+
}]
19+
20+
Complete src/CompletionDicts.res 3:34
21+
posCursor:[3:34] posNoWhite:[3:33] Found expr:[3:14->3:37]
22+
Pexp_apply ...[3:14->3:31] (...[3:32->3:36])
23+
Completable: Cexpression CArgument Value[Js, Dict, fromArray]($0)->array
24+
Package opens Pervasives.JsxModules.place holder
25+
Resolved opens 1 pervasives
26+
ContextPath CArgument Value[Js, Dict, fromArray]($0)
27+
ContextPath Value[Js, Dict, fromArray]
28+
Path Js.Dict.fromArray
29+
[{
30+
"label": "(_, _)",
31+
"kind": 12,
32+
"tags": [],
33+
"detail": "(key, 'a)",
34+
"documentation": null,
35+
"insertText": "(${1:_}, ${2:_})",
36+
"insertTextFormat": 2
37+
}]
38+
39+
Complete src/CompletionDicts.res 6:40
40+
posCursor:[6:40] posNoWhite:[6:39] Found expr:[6:14->6:44]
41+
Pexp_apply ...[6:14->6:31] (...[6:32->6:43])
42+
Completable: Cexpression CArgument Value[Js, Dict, fromArray]($0)->array, tuple($1)
43+
Package opens Pervasives.JsxModules.place holder
44+
Resolved opens 1 pervasives
45+
ContextPath CArgument Value[Js, Dict, fromArray]($0)
46+
ContextPath Value[Js, Dict, fromArray]
47+
Path Js.Dict.fromArray
48+
[]
49+
50+
51+
Complete src/CompletionDicts.res 12:14
52+
posCursor:[12:14] posNoWhite:[12:13] Found expr:[10:11->14:2]
53+
Pexp_apply ...[10:11->10:28] (...[10:29->14:1])
54+
Completable: Cexpression CArgument Value[Js, Dict, fromArray]($0)->array, tuple($1)
55+
Package opens Pervasives.JsxModules.place holder
56+
Resolved opens 1 pervasives
57+
ContextPath CArgument Value[Js, Dict, fromArray]($0)
58+
ContextPath Value[Js, Dict, fromArray]
59+
Path Js.Dict.fromArray
60+
[{
61+
"label": "true",
62+
"kind": 4,
63+
"tags": [],
64+
"detail": "bool",
65+
"documentation": null
66+
}, {
67+
"label": "false",
68+
"kind": 4,
69+
"tags": [],
70+
"detail": "bool",
71+
"documentation": null
72+
}]
73+
74+

‎server/src/utils.ts

+4
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ export let runAnalysisAfterSanityCheck = (
186186
env: {
187187
...process.env,
188188
RESCRIPT_VERSION: rescriptVersion,
189+
RESCRIPT_INCREMENTAL_TYPECHECKING:
190+
config.extensionConfiguration.incrementalTypechecking?.enabled === true
191+
? "true"
192+
: undefined,
189193
},
190194
};
191195
try {

0 commit comments

Comments
 (0)
Please sign in to comment.