Skip to content

Commit

Permalink
Find definition to respect function arity (#173)
Browse files Browse the repository at this point in the history
* Go to definition respects arity of function

* Refactor code for finding function definition

* Remove unrequired TODO

* Remove unrequired column arg to Definition.find

* Simplify test case for find definition with arity

Co-authored-by: Tim Gent <[email protected]>
  • Loading branch information
timgent and timgent committed Jan 8, 2023
1 parent 7b99c28 commit ed87526
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/elixir_sense.ex
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ defmodule ElixirSense do

Definition.find(
subject,
line,
env,
buffer_file_metadata.mods_funs_to_positions,
calls,
Expand Down
41 changes: 40 additions & 1 deletion lib/elixir_sense/providers/definition.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ defmodule ElixirSense.Providers.Definition do
"""
@spec find(
String.t(),
pos_integer,
State.Env.t(),
State.mods_funs_to_positions_t(),
list(State.CallInfo.t()),
State.types_t()
) :: %Location{} | nil
def find(
subject,
line,
%State.Env{
aliases: aliases,
module: module,
Expand Down Expand Up @@ -63,6 +65,8 @@ defmodule ElixirSense.Providers.Definition do
subject
|> Source.split_module_and_func(module, aliases)
|> find_function_or_module(
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand Down Expand Up @@ -95,6 +99,8 @@ defmodule ElixirSense.Providers.Definition do

defp find_function_or_module(
{module, function},
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand All @@ -104,6 +110,8 @@ defmodule ElixirSense.Providers.Definition do
unless {module, function} in visited do
do_find_function_or_module(
{module, function},
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand All @@ -115,6 +123,8 @@ defmodule ElixirSense.Providers.Definition do

defp do_find_function_or_module(
{{:attribute, _attr} = type, function},
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand All @@ -125,6 +135,8 @@ defmodule ElixirSense.Providers.Definition do
{:atom, module} ->
do_find_function_or_module(
{Introspection.expand_alias(module, env.aliases), function},
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand All @@ -139,6 +151,8 @@ defmodule ElixirSense.Providers.Definition do

defp do_find_function_or_module(
{nil, :super},
line,
calls,
mods_funs_to_positions,
%State.Env{scope: {function, arity}, module: module} = env,
metadata_types,
Expand All @@ -150,6 +164,8 @@ defmodule ElixirSense.Providers.Definition do
# overridable function is most likely defined by __using__ macro
do_find_function_or_module(
{origin, :__using__},
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand All @@ -164,6 +180,8 @@ defmodule ElixirSense.Providers.Definition do

defp do_find_function_or_module(
{module, function},
line,
calls,
mods_funs_to_positions,
env,
metadata_types,
Expand All @@ -188,7 +206,10 @@ defmodule ElixirSense.Providers.Definition do
nil

{mod, fun, true} ->
case mods_funs_to_positions[{mod, fun, nil}] || metadata_types[{mod, fun, nil}] do
fn_definition = find_fn_definition(mod, fun, line, mods_funs_to_positions, calls)

case fn_definition || mods_funs_to_positions[{mod, fun, nil}] ||
metadata_types[{mod, fun, nil}] do
nil ->
Location.find_source({mod, fun}, current_module)

Expand Down Expand Up @@ -216,4 +237,22 @@ defmodule ElixirSense.Providers.Definition do
end
end
end

defp find_fn_definition(mod, fun, line, mods_funs_to_positions, calls) do
mods_funs_to_positions
|> Enum.find(fn
{{^mod, ^fun, fn_arity}, %{positions: fn_positions}} when not is_nil(fn_arity) ->
case calls do
[] -> Enum.any?(fn_positions, fn {fn_line, _fn_col} -> fn_line == line end)
[%{arity: call_arity} | _] -> fn_arity == call_arity
end

_ ->
false
end)
|> case do
{_, mod_fun_info} -> mod_fun_info
nil -> nil
end
end
end
26 changes: 26 additions & 0 deletions test/elixir_sense/definition_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,32 @@ defmodule ElixirSense.Providers.DefinitionTest do
assert read_line(file, {line, column}) =~ "ElixirSenseExample.Macros.go"
end

test "find definition for the correct arity of function - on fn call" do
buffer = """
defmodule MyModule do
def main, do: my_func("a", "b")
# ^
def my_func, do: "not this one"
def my_func(a, b), do: a <> b
end
"""

assert %Location{type: :function, file: nil, line: 5, column: 7} =
ElixirSense.definition(buffer, 2, 18)
end

test "find definition for the correct arity of function - on fn definition" do
buffer = """
defmodule MyModule do
def my_func, do: "not this one"
def my_func(a, b), do: a <> b
end
"""

assert %Location{type: :function, file: nil, line: 3, column: 7} =
ElixirSense.definition(buffer, 3, 9)
end

test "find definition of delegated functions" do
buffer = """
defmodule MyModule do
Expand Down

0 comments on commit ed87526

Please sign in to comment.