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

Find definition to respect function arity #173

Merged
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
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