From 1037edf659b401be872034285318cf7fd0fe9508 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Mon, 3 Feb 2020 19:34:49 +0100 Subject: [PATCH] fix invalid vars suggestions in guards --- lib/elixir_sense/core/metadata_builder.ex | 9 +++- .../core/metadata_builder_test.exs | 49 ++++++++++++++++++- test/elixir_sense/suggestions_test.exs | 16 ++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/lib/elixir_sense/core/metadata_builder.ex b/lib/elixir_sense/core/metadata_builder.ex index b1310ada..7238200e 100644 --- a/lib/elixir_sense/core/metadata_builder.ex +++ b/lib/elixir_sense/core/metadata_builder.ex @@ -237,7 +237,7 @@ defmodule ElixirSense.Core.MetadataBuilder do |> result(ast) end - defp pre_clause({_, [line: line, column: _column], _} = ast, state, lhs) do + defp pre_clause({_clause, [line: line, column: _column], _} = ast, state, lhs) do state |> new_alias_scope |> new_import_scope @@ -408,7 +408,7 @@ defmodule ElixirSense.Core.MetadataBuilder do ]}, state ) - when def_name in [:defguard, :defguardp] do + when def_name in @defs do ast_without_params = {def_name, meta, [{name, add_no_call(meta2), []}, body]} pre_func(ast_without_params, state, %{line: line, col: column}, name, params) end @@ -1017,6 +1017,11 @@ defmodule ElixirSense.Core.MetadataBuilder do {ast, [var_info | vars]} end + # drop right side of guard expression as guards cannot define vars + defp match_var({:when, _, [left, _right]}, vars) do + match_var(left, vars) + end + defp match_var(ast, vars) do {ast, vars} end diff --git a/test/elixir_sense/core/metadata_builder_test.exs b/test/elixir_sense/core/metadata_builder_test.exs index 3093bd90..783b175f 100644 --- a/test/elixir_sense/core/metadata_builder_test.exs +++ b/test/elixir_sense/core/metadata_builder_test.exs @@ -121,7 +121,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do """ defmodule MyModule do var_out1 = 1 - def func(%{key1: par1, key2: [par2|[par3, _]]}, par4) do + def func(%{key1: par1, key2: [par2|[par3, _]]}, par4, _par5) do var_in1 = 1 var_in2 = 1 IO.puts "" @@ -150,6 +150,53 @@ defmodule ElixirSense.Core.MetadataBuilderTest do ] end + test "guards do not define vars" do + state = + """ + defmodule MyModule do + def func1(a) when is_integer(b) do + IO.puts("") + end + def func2(a) when is_integer(b) or is_list(c) do + IO.puts("") + end + def func3(a) when is_integer(b) when is_list(c) do + IO.puts("") + end + + case x do + y when is_integer(z) -> + IO.puts("") + end + + with x when is_integer(y) <- z do + IO.puts("") + end + + def func3(a) when is_integer(b) + end + """ + |> string_to_state + + vars = state |> get_line_vars(3) + assert vars == [%VarInfo{is_definition: false, name: :a, positions: [{2, 13}], scope_id: 2}] + + vars = state |> get_line_vars(6) + assert vars == [%VarInfo{is_definition: false, name: :a, positions: [{5, 13}], scope_id: 2}] + + vars = state |> get_line_vars(9) + assert vars == [%VarInfo{is_definition: false, name: :a, positions: [{8, 13}], scope_id: 2}] + + vars = state |> get_line_vars(14) + assert vars == [%VarInfo{is_definition: false, name: :y, positions: [{13, 5}], scope_id: 7}] + + vars = state |> get_line_vars(18) + assert vars == [%VarInfo{is_definition: false, name: :x, positions: [{17, 8}], scope_id: 2}] + + vars = state |> get_line_vars(21) + assert vars == [%VarInfo{is_definition: false, name: :a, positions: [{21, 13}], scope_id: 2}] + end + test "rebinding vars" do state = """ diff --git a/test/elixir_sense/suggestions_test.exs b/test/elixir_sense/suggestions_test.exs index 493995ad..467d7215 100644 --- a/test/elixir_sense/suggestions_test.exs +++ b/test/elixir_sense/suggestions_test.exs @@ -843,6 +843,22 @@ defmodule ElixirSense.SuggestionsTest do ] end + test "only list defined params in guard" do + buffer = """ + defmodule MyServer do + def new(my_var) when is_integer(my + end + """ + + list = + ElixirSense.suggestions(buffer, 2, 37) + |> Enum.filter(fn s -> s.type == :variable end) + + # TODO hint my_var instead of my + + assert list == [%{name: "my_var", type: :variable}] + end + test "lists attributes" do buffer = """ defmodule MyModule do