Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support type instantiation during autocompletion. #561

Merged
merged 9 commits into from
Sep 1, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#### :bug: Bug Fix

- Fix issue where autocomplete would not perform type instantiation https://github.com/rescript-lang/rescript-vscode/pull/561
- Fix issue where hovering over a field in record construction would show the type without instantiating its type arguments https://github.com/rescript-lang/rescript-vscode/pull/560
- Fix Incorrect semantic highlighting of `external` declarations https://github.com/rescript-lang/rescript-vscode/pull/517
- Fix issue where doc comment with nested comments inside is not shown properly on hover https://github.com/rescript-lang/rescript-vscode/pull/526
Expand Down
107 changes: 93 additions & 14 deletions analysis/src/CompletionBackEnd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -980,14 +980,75 @@ let findLocalCompletionsWithOpens ~pos ~(env : QueryEnv.t) ~prefix ~exact ~opens
(* There's no local completion for fields *)
[]

let instantiateType ~typeParams ~typeArgs (t : Types.type_expr) =
if typeParams = [] || typeArgs = [] then t
else
let rec applySub tp ta t =
match (tp, ta) with
| t1 :: tRest1, t2 :: tRest2 ->
if t1 = t then t2 else applySub tRest1 tRest2 t
| [], _ | _, [] -> assert false
in
let rec loop (t : Types.type_expr) =
match t.desc with
| Tlink t -> loop t
| Tvar _ -> applySub typeParams typeArgs t
| Tunivar _ -> t
| Tconstr (path, args, memo) ->
{t with desc = Tconstr (path, args |> List.map loop, memo)}
| Tsubst t -> loop t
| Tvariant rd -> {t with desc = Tvariant (rowDesc rd)}
| Tnil -> t
| Tarrow (lbl, t1, t2, c) ->
{t with desc = Tarrow (lbl, loop t1, loop t2, c)}
| Ttuple tl -> {t with desc = Ttuple (tl |> List.map loop)}
| Tobject (t, r) -> {t with desc = Tobject (loop t, r)}
| Tfield (n, k, t1, t2) -> {t with desc = Tfield (n, k, loop t1, loop t2)}
| Tpoly (t, []) -> loop t
| Tpoly (t, tl) -> {t with desc = Tpoly (loop t, tl |> List.map loop)}
| Tpackage (p, l, tl) ->
{t with desc = Tpackage (p, l, tl |> List.map loop)}
and rowDesc (rd : Types.row_desc) =
let row_fields =
rd.row_fields |> List.map (fun (l, rf) -> (l, rowField rf))
in
let row_more = loop rd.row_more in
let row_name =
match rd.row_name with
| None -> None
| Some (p, tl) -> Some (p, tl |> List.map loop)
in
{rd with row_fields; row_more; row_name}
and rowField (rf : Types.row_field) =
match rf with
| Rpresent None -> rf
| Rpresent (Some t) -> Rpresent (Some (loop t))
| Reither (b1, tl, b2, r) -> Reither (b1, tl |> List.map loop, b2, r)
| Rabsent -> Rabsent
in
loop t

let rec extractRecordType ~env ~package (t : Types.type_expr) =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractRecordType ~env ~package t1
| Tconstr (path, _, _) -> (
| Tconstr (path, typeArgs, _) -> (
match References.digConstructor ~env ~package path with
| Some (env, ({item = {kind = Record fields}} as typ)) ->
let typeParams = typ.item.decl.type_params in
let fields =
fields
|> List.map (fun field ->
let fieldTyp =
field.typ |> instantiateType ~typeParams ~typeArgs
in
{field with typ = fieldTyp})
in
Some (env, fields, typ)
| Some (env, {item = {decl = {type_manifest = Some t1}}}) ->
| Some
( env,
{item = {decl = {type_manifest = Some t1; type_params = typeParams}}}
) ->
let t1 = t1 |> instantiateType ~typeParams ~typeArgs in
extractRecordType ~env ~package t1
| _ -> None)
| _ -> None
Expand All @@ -996,9 +1057,13 @@ let rec extractObjectType ~env ~package (t : Types.type_expr) =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractObjectType ~env ~package t1
| Tobject (tObj, _) -> Some (env, tObj)
| Tconstr (path, _, _) -> (
| Tconstr (path, typeArgs, _) -> (
match References.digConstructor ~env ~package path with
| Some (env, {item = {decl = {type_manifest = Some t1}}}) ->
| Some
( env,
{item = {decl = {type_manifest = Some t1; type_params = typeParams}}}
) ->
let t1 = t1 |> instantiateType ~typeParams ~typeArgs in
extractObjectType ~env ~package t1
| _ -> None)
| _ -> None
Expand All @@ -1008,9 +1073,14 @@ let extractFunctionType ~env ~package typ =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> loop ~env acc t1
| Tarrow (label, tArg, tRet, _) -> loop ~env ((label, tArg) :: acc) tRet
| Tconstr (path, _, _) -> (
| Tconstr (path, typeArgs, _) -> (
match References.digConstructor ~env ~package path with
| Some (env, {item = {decl = {type_manifest = Some t1}}}) ->
| Some
( env,
{
item = {decl = {type_manifest = Some t1; type_params = typeParams}};
} ) ->
let t1 = t1 |> instantiateType ~typeParams ~typeArgs in
loop ~env acc t1
| _ -> (List.rev acc, t))
| _ -> (List.rev acc, t)
Expand Down Expand Up @@ -1234,17 +1304,17 @@ let rec getCompletionsForContextPath ~package ~opens ~rawOpens ~allFiles ~pos
| _ :: rest -> List.rev rest
| [] -> [])
in
let getConstr typ =
let getConstrPath typ =
match typ.Types.desc with
| Tconstr (path, _, _)
| Tlink {desc = Tconstr (path, _, _)}
| Tsubst {desc = Tconstr (path, _, _)}
| Tpoly ({desc = Tconstr (path, _, _)}, []) ->
| Tconstr (path, _typeArgs, _)
| Tlink {desc = Tconstr (path, _typeArgs, _)}
| Tsubst {desc = Tconstr (path, _typeArgs, _)}
| Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) ->
Some path
| _ -> None
in
let fromType typ =
match getConstr typ with
match getConstrPath typ with
| None -> None
| Some path -> Some (getModulePath path)
in
Expand Down Expand Up @@ -1668,9 +1738,18 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i
| Tarrow ((Labelled l | Optional l), tArg, tRet, _) ->
(l, tArg) :: getLabels ~env tRet
| Tarrow (Nolabel, _, tRet, _) -> getLabels ~env tRet
| Tconstr (path, _, _) -> (
| Tconstr (path, typeArgs, _) -> (
match References.digConstructor ~env ~package path with
| Some (env, {item = {decl = {type_manifest = Some t1}}}) ->
| Some
( env,
{
item =
{
decl =
{type_manifest = Some t1; type_params = typeParams};
};
} ) ->
let t1 = t1 |> instantiateType ~typeParams ~typeArgs in
getLabels ~env t1
| _ -> [])
| _ -> []
Expand Down
18 changes: 18 additions & 0 deletions analysis/tests/src/Hover.res
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,22 @@ module TypeSubstitutionRecords = {
// ^hov
let x2: foobar = {content: {age: 42}, zzz: ""}
// ^hov

// x1.content.
// ^com

// x2.content.
// ^com

type foo2<'b> = foo<'b>
type foobar2 = foo2<bar>

let y1: foo2<bar> = {content: {age: 42}, zzz: ""}
let y2: foobar2 = {content: {age: 42}, zzz: ""}

// y1.content.
// ^com

// y2.content.
// ^com
}
48 changes: 48 additions & 0 deletions analysis/tests/src/expected/Hover.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,51 @@ Hover src/Hover.res 163:23
Hover src/Hover.res 165:22
{"contents": "```rescript\nfoobar\n```\n\n```rescript\ntype foobar = foo<bar>\n```"}

Complete src/Hover.res 168:16
posCursor:[168:16] posNoWhite:[168:15] Found expr:[168:5->168:16]
Pexp_field [168:5->168:15] _:[174:2->168:16]
Completable: Cpath Value[x1].content.""
[{
"label": "age",
"kind": 5,
"tags": [],
"detail": "age: int\n\ntype bar = {age: int}",
"documentation": null
}]

Complete src/Hover.res 171:16
posCursor:[171:16] posNoWhite:[171:15] Found expr:[171:5->171:16]
Pexp_field [171:5->171:15] _:[174:2->171:16]
Completable: Cpath Value[x2].content.""
[{
"label": "age",
"kind": 5,
"tags": [],
"detail": "age: int\n\ntype bar = {age: int}",
"documentation": null
}]

Complete src/Hover.res 180:16
posCursor:[180:16] posNoWhite:[180:15] Found expr:[180:5->180:16]
Pexp_field [180:5->180:15] _:[185:0->180:16]
Completable: Cpath Value[y1].content.""
[{
"label": "age",
"kind": 5,
"tags": [],
"detail": "age: int\n\ntype bar = {age: int}",
"documentation": null
}]

Complete src/Hover.res 183:16
posCursor:[183:16] posNoWhite:[183:15] Found expr:[183:5->183:16]
Pexp_field [183:5->183:15] _:[185:0->183:16]
Completable: Cpath Value[y2].content.""
[{
"label": "age",
"kind": 5,
"tags": [],
"detail": "age: int\n\ntype bar = {age: int}",
"documentation": null
}]