Skip to content

Handle result type as a builtin #860

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

Merged
merged 2 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@

## master

#### :bug: Bug Fix

- Treat `result` type as a proper built in type. https://github.com/rescript-lang/rescript-vscode/pull/860

#### :nail_care: Polish

- Change end position of cursor when completing `Some(<fieldName>)` in patterns. https://github.com/rescript-lang/rescript-vscode/pull/857


#### :bug: Bug Fix

- Add support for detecting dead fields inside inline records. https://github.com/rescript-lang/rescript-vscode/pull/858
Expand Down
67 changes: 67 additions & 0 deletions analysis/src/CompletionBackEnd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,71 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
| _ -> [noneCase; someAnyCase]
in
completions @ expandedCompletions |> filterItems ~prefix
| Tresult {env; okType; errorType} ->
let okInnerType =
okType |> TypeUtils.extractType ~env ~package:full.package
in
let errorInnerType =
errorType |> TypeUtils.extractType ~env ~package:full.package
in
let expandedOkCompletions =
match okInnerType with
| None -> []
| Some innerType ->
innerType
|> completeTypedValue ~full ~prefix ~completionContext ~mode
|> List.map (fun (c : Completion.t) ->
{
c with
name = "Ok(" ^ c.name ^ ")";
sortText = None;
insertText =
(match c.insertText with
| None -> None
| Some insertText -> Some ("Ok(" ^ insertText ^ ")"));
})
in
let expandedErrorCompletions =
match errorInnerType with
| None -> []
| Some innerType ->
innerType
|> completeTypedValue ~full ~prefix ~completionContext ~mode
|> List.map (fun (c : Completion.t) ->
{
c with
name = "Error(" ^ c.name ^ ")";
sortText = None;
insertText =
(match c.insertText with
| None -> None
| Some insertText -> Some ("Error(" ^ insertText ^ ")"));
})
in
let okAnyCase =
Completion.createWithSnippet ~name:"Ok(_)" ~kind:(Value okType) ~env
~insertText:"Ok(${1:_})" ()
in
let errorAnyCase =
Completion.createWithSnippet ~name:"Error(_)" ~kind:(Value errorType) ~env
~insertText:"Error(${1:_})" ()
in
let completions =
match completionContext with
| Some (Completable.CameFromRecordField fieldName) ->
[
Completion.createWithSnippet
~name:("Ok(" ^ fieldName ^ ")")
~kind:(Value okType) ~env
~insertText:("Ok(" ^ fieldName ^ ")$0")
();
okAnyCase;
errorAnyCase;
]
| _ -> [okAnyCase; errorAnyCase]
in
completions @ expandedOkCompletions @ expandedErrorCompletions
|> filterItems ~prefix
| Tuple (env, exprs, typ) ->
let numExprs = List.length exprs in
[
Expand Down Expand Up @@ -1715,6 +1780,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
| _ -> "(_)"))
| Some (Toption (_env, _typ)) ->
withExhaustiveItem c ~cases:["Some($1)"; "None"] ~startIndex:1
| Some (Tresult _) ->
withExhaustiveItem c ~cases:["Ok($1)"; "Error($1)"] ~startIndex:1
| Some (Tbool _) -> withExhaustiveItem c ~cases:["true"; "false"]
| _ -> [c])
| _ -> [c])
Expand Down
5 changes: 5 additions & 0 deletions analysis/src/SharedTypes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,11 @@ and completionType =
| Texn of QueryEnv.t
| Tpromise of QueryEnv.t * Types.type_expr
| Toption of QueryEnv.t * innerType
| Tresult of {
env: QueryEnv.t;
okType: Types.type_expr;
errorType: Types.type_expr;
}
| Tbool of QueryEnv.t
| Tarray of QueryEnv.t * innerType
| Tstring of QueryEnv.t
Expand Down
55 changes: 55 additions & 0 deletions analysis/src/TypeUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ let rec extractType ~env ~package (t : Types.type_expr) =
Some (Tpromise (env, payloadTypeExpr))
| Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) ->
Some (Tarray (env, TypeExpr payloadTypeExpr))
| Tconstr (Path.Pident {name = "result"}, [okType; errorType], _) ->
Some (Tresult {env; okType; errorType})
| Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env)
| Tconstr (Path.Pident {name = "string"}, [], _) -> Some (Tstring env)
| Tconstr (Path.Pident {name = "exn"}, [], _) -> Some (Texn env)
Expand Down Expand Up @@ -362,6 +364,15 @@ let rec resolveNested ~env ~full ~nested ?ctx (typ : completionType) =
typ
|> extractType ~env ~package:full.package
|> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested)
| NVariantPayload {constructorName = "Ok"; itemNum = 0}, Tresult {okType} ->
okType
|> extractType ~env ~package:full.package
|> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested)
| ( NVariantPayload {constructorName = "Error"; itemNum = 0},
Tresult {errorType} ) ->
errorType
|> extractType ~env ~package:full.package
|> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested)
| ( NVariantPayload {constructorName; itemNum},
Tvariant {env; constructors; typeParams; typeArgs} ) -> (
match
Expand Down Expand Up @@ -481,6 +492,12 @@ let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested =
| ( NVariantPayload {constructorName = "Some"; itemNum = 0},
Toption (env, typ) ) ->
Some (typ, env)
| ( NVariantPayload {constructorName = "Ok"; itemNum = 0},
Tresult {env; okType} ) ->
Some (TypeExpr okType, env)
| ( NVariantPayload {constructorName = "Error"; itemNum = 0},
Tresult {env; errorType} ) ->
Some (TypeExpr errorType, env)
| NArray, Tarray (env, typ) -> Some (typ, env)
| _ -> None))
| patternPath :: nested -> (
Expand Down Expand Up @@ -527,6 +544,12 @@ let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested =
| ( NVariantPayload {constructorName = "Some"; itemNum = 0},
Toption (env, typ) ) ->
typ |> resolveNestedPatternPath ~env ~full ~nested
| ( NVariantPayload {constructorName = "Ok"; itemNum = 0},
Tresult {env; okType} ) ->
TypeExpr okType |> resolveNestedPatternPath ~env ~full ~nested
| ( NVariantPayload {constructorName = "Error"; itemNum = 0},
Tresult {env; errorType} ) ->
TypeExpr errorType |> resolveNestedPatternPath ~env ~full ~nested
| NArray, Tarray (env, typ) ->
typ |> resolveNestedPatternPath ~env ~full ~nested
| _ -> None))
Expand Down Expand Up @@ -613,6 +636,10 @@ let rec extractedTypeToString ?(inner = false) = function
"array<" ^ extractedTypeToString ~inner:true innerTyp ^ ">"
| Toption (_, TypeExpr innerTyp) ->
"option<" ^ Shared.typeToString innerTyp ^ ">"
| Tresult {okType; errorType} ->
"result<" ^ Shared.typeToString okType ^ ", "
^ Shared.typeToString errorType
^ ">"
| Toption (_, ExtractedType innerTyp) ->
"option<" ^ extractedTypeToString ~inner:true innerTyp ^ ">"
| Tpromise (_, innerTyp) -> "promise<" ^ Shared.typeToString innerTyp ^ ">"
Expand Down Expand Up @@ -687,6 +714,34 @@ module Codegen = struct
@ (expandedBranches
|> List.map (fun (pat : Parsetree.pattern) ->
mkConstructPat ~payload:pat "Some")))
| Tresult {okType; errorType} ->
let extractedOkType = okType |> extractType ~env ~package:full.package in
let extractedErrorType =
errorType |> extractType ~env ~package:full.package
in
let expandedOkBranches =
match extractedOkType with
| None -> []
| Some extractedType -> (
match extractedTypeToExhaustivePatterns ~env ~full extractedType with
| None -> []
| Some patterns -> patterns)
in
let expandedErrorBranches =
match extractedErrorType with
| None -> []
| Some extractedType -> (
match extractedTypeToExhaustivePatterns ~env ~full extractedType with
| None -> []
| Some patterns -> patterns)
in
Some
((expandedOkBranches
|> List.map (fun (pat : Parsetree.pattern) ->
mkConstructPat ~payload:pat "Ok"))
@ (expandedErrorBranches
|> List.map (fun (pat : Parsetree.pattern) ->
mkConstructPat ~payload:pat "Error")))
| Tbool _ -> Some [mkConstructPat "true"; mkConstructPat "false"]
| _ -> None

Expand Down
3 changes: 3 additions & 0 deletions analysis/tests/src/CompletionPattern.res
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,6 @@ let res: result<someVariant, somePolyVariant> = Ok(One)

// switch res { | Ok() }
// ^com

// switch res { | Error() }
// ^com
36 changes: 36 additions & 0 deletions analysis/tests/src/expected/CompletionPattern.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1141,3 +1141,39 @@ Path res
"insertTextFormat": 2
}]

Complete src/CompletionPattern.res 219:24
posCursor:[219:24] posNoWhite:[219:23] Found pattern:[219:18->219:25]
Ppat_construct Error:[219:18->219:23]
posCursor:[219:24] posNoWhite:[219:23] Found pattern:[219:23->219:25]
Ppat_construct ():[219:23->219:25]
Completable: Cpattern Value[res]->variantPayload::Error($0)
Package opens Pervasives.JsxModules.place holder
Resolved opens 1 pervasives
ContextPath Value[res]
Path res
[{
"label": "#one",
"kind": 4,
"tags": [],
"detail": "#one\n\n[#one | #three(someRecord, bool) | #two(bool)]",
"documentation": null,
"insertText": "#one",
"insertTextFormat": 2
}, {
"label": "#three(_, _)",
"kind": 4,
"tags": [],
"detail": "#three(someRecord, bool)\n\n[#one | #three(someRecord, bool) | #two(bool)]",
"documentation": null,
"insertText": "#three(${1:_}, ${2:_})",
"insertTextFormat": 2
}, {
"label": "#two(_)",
"kind": 4,
"tags": [],
"detail": "#two(bool)\n\n[#one | #three(someRecord, bool) | #two(bool)]",
"documentation": null,
"insertText": "#two(${1:_})",
"insertTextFormat": 2
}]