Skip to content

Commit d1f3876

Browse files
authored
fix: correctly process broken code when searching local variables (#282)
* fix error when searching local variables in broken code * fix test name
1 parent 04b9b7e commit d1f3876

File tree

2 files changed

+74
-44
lines changed

2 files changed

+74
-44
lines changed

Diff for: lib/next_ls/helpers/ast_helpers/variables.ex

+51-41
Original file line numberDiff line numberDiff line change
@@ -9,52 +9,62 @@ defmodule NextLS.ASTHelpers.Variables do
99
@spec get_variable_definition(String.t(), {integer(), integer()}) :: {atom(), {Range.t(), Range.t()}} | nil
1010
def get_variable_definition(file, position) do
1111
file = File.read!(file)
12-
ast = Code.string_to_quoted!(file, columns: true)
13-
14-
{_ast, %{vars: vars}} =
15-
Macro.traverse(
16-
ast,
17-
%{vars: [], symbols: %{}, sym_ranges: [], scope: []},
18-
&prewalk/2,
19-
&postwalk/2
20-
)
21-
22-
Enum.find_value(vars, fn %{name: name, sym_range: range, ref_range: ref_range} ->
23-
if position_in_range?(position, ref_range), do: {name, range}, else: nil
24-
end)
12+
13+
case Code.string_to_quoted(file, columns: true) do
14+
{:ok, ast} ->
15+
{_ast, %{vars: vars}} =
16+
Macro.traverse(
17+
ast,
18+
%{vars: [], symbols: %{}, sym_ranges: [], scope: []},
19+
&prewalk/2,
20+
&postwalk/2
21+
)
22+
23+
Enum.find_value(vars, fn %{name: name, sym_range: range, ref_range: ref_range} ->
24+
if position_in_range?(position, ref_range), do: {name, range}, else: nil
25+
end)
26+
27+
_error ->
28+
nil
29+
end
2530
end
2631

2732
@spec list_variable_references(String.t(), {integer(), integer()}) :: [{atom(), {Range.t(), Range.t()}}]
2833
def list_variable_references(file, position) do
2934
file = File.read!(file)
30-
ast = Code.string_to_quoted!(file, columns: true)
31-
32-
{_ast, %{vars: vars}} =
33-
Macro.traverse(
34-
ast,
35-
%{vars: [], symbols: %{}, sym_ranges: [], scope: []},
36-
&prewalk/2,
37-
&postwalk/2
38-
)
39-
40-
symbol =
41-
Enum.find_value(vars, fn %{name: name, sym_range: range, ref_range: ref_range} ->
42-
if position_in_range?(position, ref_range), do: {name, range}, else: nil
43-
end)
44-
45-
position =
46-
case symbol do
47-
nil -> position
48-
{_, {line.._, column.._}} -> {line, column}
49-
end
50-
51-
Enum.reduce(vars, [], fn val, acc ->
52-
if position_in_range?(position, val.sym_range) do
53-
[{val.name, val.ref_range} | acc]
54-
else
55-
acc
56-
end
57-
end)
35+
36+
case Code.string_to_quoted(file, columns: true) do
37+
{:ok, ast} ->
38+
{_ast, %{vars: vars}} =
39+
Macro.traverse(
40+
ast,
41+
%{vars: [], symbols: %{}, sym_ranges: [], scope: []},
42+
&prewalk/2,
43+
&postwalk/2
44+
)
45+
46+
symbol =
47+
Enum.find_value(vars, fn %{name: name, sym_range: range, ref_range: ref_range} ->
48+
if position_in_range?(position, ref_range), do: {name, range}, else: nil
49+
end)
50+
51+
position =
52+
case symbol do
53+
nil -> position
54+
{_, {line.._, column.._}} -> {line, column}
55+
end
56+
57+
Enum.reduce(vars, [], fn val, acc ->
58+
if position_in_range?(position, val.sym_range) do
59+
[{val.name, val.ref_range} | acc]
60+
else
61+
acc
62+
end
63+
end)
64+
65+
_error ->
66+
[]
67+
end
5868
end
5969

6070
# search symbols in function and macro definition args and increase scope

Diff for: test/next_ls/helpers/ast_helpers/variables_test.exs

+23-3
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,17 @@ defmodule NextLS.ASTHelpers.VariablesTest do
143143
end
144144
""")
145145

146-
[source: source]
146+
broken = Path.join(tmp_dir, "my_proj/lib/broken.ex")
147+
148+
File.write!(broken, """
149+
defmodule Broken do
150+
def foo(bar) do
151+
{:ok, bar}
152+
end
153+
# end
154+
""")
155+
156+
[source: source, broken: broken]
147157
end
148158

149159
describe "get_variable_definition/2" do
@@ -154,12 +164,17 @@ defmodule NextLS.ASTHelpers.VariablesTest do
154164

155165
test "returns nil when position is not a variable reference", %{source: source} do
156166
symbol = Variables.get_variable_definition(source, {7, 6})
157-
assert symbol == nil
167+
refute symbol
158168
end
159169

160170
test "returns nil when position is a variable symbol", %{source: source} do
161171
symbol = Variables.get_variable_definition(source, {5, 5})
162-
assert symbol == nil
172+
refute symbol
173+
end
174+
175+
test "returns nil when source code is broken", %{broken: broken} do
176+
symbol = Variables.get_variable_definition(broken, {1, 10})
177+
refute symbol
163178
end
164179
end
165180

@@ -332,5 +347,10 @@ defmodule NextLS.ASTHelpers.VariablesTest do
332347
assert length(refs3) == 1
333348
assert {:var, {127..127, 17..19}} in refs3
334349
end
350+
351+
test "returns empty list when source code is broken", %{broken: broken} do
352+
symbol = Variables.list_variable_references(broken, {2, 10})
353+
assert Enum.empty?(symbol)
354+
end
335355
end
336356
end

0 commit comments

Comments
 (0)