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

Disable redundant type annotation #1037

Merged
merged 7 commits into from Mar 6, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -3,6 +3,9 @@
## Fixes

- Fix a document syncing issue when utf-16 is the position encoding (#1004)
- Disable "Type-annotate" action for code that is already annotated.
([#1037](https://github.com/ocaml/ocaml-lsp/pull/1037)), fixes
[#1036](https://github.com/ocaml/ocaml-lsp/issues/1036)

# 1.15.1

Expand Down
18 changes: 17 additions & 1 deletion ocaml-lsp-server/src/code_actions/action_type_annotate.ml
Expand Up @@ -7,8 +7,24 @@ let check_typeable_context pipeline pos_start =
let pos_start = Mpipeline.get_lexing_pos pipeline pos_start in
let typer = Mpipeline.typer_result pipeline in
let browse = Mbrowse.of_typedtree (Mtyper.get_typedtree typer) in
let is_exp_constrained = function
| Typedtree.Texp_constraint _, _, _ -> true
| Typedtree.Texp_coerce (Some _, _), _, _ -> true
| _ -> false
in
let is_pat_constrained = function
| Typedtree.Tpat_constraint _, _, _ -> true
| _ -> false
in
let is_valid p extras =
if List.exists ~f:p extras then `Invalid else `Valid
in
match Mbrowse.enclosing pos_start [ browse ] with
| (_, (Expression _ | Pattern _)) :: _ -> `Valid
| (_, Expression e) :: _ -> is_valid is_exp_constrained e.exp_extra
| (_, Pattern { pat_desc = Typedtree.Tpat_any; _ })
:: (_, Pattern { pat_desc = Typedtree.Tpat_alias _; pat_extra; _ })
:: _ -> is_valid is_pat_constrained pat_extra
| (_, Pattern p) :: _ -> is_valid is_pat_constrained p.pat_extra
| _ :: _ | [] -> `Invalid

let get_source_text doc (loc : Loc.t) =
Expand Down
69 changes: 58 additions & 11 deletions ocaml-lsp-server/test/e2e-new/code_actions.ml
Expand Up @@ -47,18 +47,28 @@ let iter_code_actions ?(path = "foo.ml") ~source range k =
Fiber.fork_and_join_unit run_client (fun () ->
run >>> Fiber.Ivar.read diagnostics >>> Client.stop client)

let print_code_actions ?(path = "foo.ml") source range =
let print_code_actions ?(path = "foo.ml") ?(filter = fun _ -> true) source range
=
iter_code_actions ~path ~source range (function
| None -> print_endline "no code actions"
| Some code_actions ->
print_endline "Code actions:";
List.iter code_actions ~f:(fun ca ->
let json =
match ca with
| `Command command -> Command.yojson_of_t command
| `CodeAction ca -> CodeAction.yojson_of_t ca
in
Yojson.Safe.pretty_to_string ~std:false json |> print_endline))
| None -> print_endline "No code actions"
| Some code_actions -> (
code_actions |> List.filter ~f:filter |> function
| [] -> print_endline "No code actions"
| actions ->
print_endline "Code actions:";
List.iter actions ~f:(fun ca ->
let json =
match ca with
| `Command command -> Command.yojson_of_t command
| `CodeAction ca -> CodeAction.yojson_of_t ca
in
Yojson.Safe.pretty_to_string ~std:false json |> print_endline)))

let find_annotate_action =
let open CodeAction in
function
| `CodeAction { kind = Some (Other "type-annotate"); _ } -> true
| _ -> false

let%expect_test "code actions" =
let source = {ocaml|
Expand Down Expand Up @@ -106,3 +116,40 @@ let foo = 123
"kind": "switch",
"title": "Create foo.mli"
} |}]

let%expect_test "does not type-annotate already annotated argument" =
let source = {ocaml|
let f (x : int) = 1
|ocaml} in
let range =
let start = Position.create ~line:1 ~character:7 in
let end_ = Position.create ~line:1 ~character:8 in
Range.create ~start ~end_
in
print_code_actions source range ~filter:find_annotate_action;
[%expect {| No code actions |}]

let%expect_test "does not type-annotate already annotated expression" =
let source = {ocaml|
let f x = (1 : int)
|ocaml} in
let range =
let start = Position.create ~line:1 ~character:11 in
let end_ = Position.create ~line:1 ~character:12 in
Range.create ~start ~end_
in
print_code_actions source range ~filter:find_annotate_action;
[%expect {| No code actions |}]

let%expect_test "does not type-annotate already annotated and coerced \
expression" =
let source = {ocaml|
let f x = (1 : int :> int)
|ocaml} in
let range =
let start = Position.create ~line:1 ~character:11 in
let end_ = Position.create ~line:1 ~character:12 in
Range.create ~start ~end_
in
print_code_actions source range ~filter:find_annotate_action;
[%expect {| No code actions |}]