Skip to content

Commit 6e5c3a7

Browse files
committed
Hide functions after @doc false or @impl true
1 parent 4667a77 commit 6e5c3a7

File tree

2 files changed

+93
-10
lines changed

2 files changed

+93
-10
lines changed

lib/elixir_analyzer/exercise_test/common_checks/private_helper_functions.ex

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ defmodule ElixirAnalyzer.ExerciseTest.CommonChecks.PrivateHelperFunctions do
1212
def run(_ast, nil), do: []
1313

1414
def run(code_ast, exemploid_ast) do
15-
{_, code_definitions} = Macro.prewalk(code_ast, [], &traverse/2)
16-
{_, exemploid_definitions} = Macro.prewalk(exemploid_ast, [], &traverse/2)
15+
{_, code_hidden} = Macro.prewalk(code_ast, [], &find_hidden/2)
16+
{_, code_def} = Macro.prewalk(code_ast, [], &traverse/2)
17+
{_, exemploid_def} = Macro.prewalk(exemploid_ast, [], &traverse/2)
1718

18-
case Enum.reverse(find_public_helpers(code_definitions, exemploid_definitions)) do
19+
case find_public_helpers(code_def, code_hidden, exemploid_def) |> Enum.reverse() do
1920
[] ->
2021
[]
2122

@@ -35,23 +36,55 @@ defmodule ElixirAnalyzer.ExerciseTest.CommonChecks.PrivateHelperFunctions do
3536
end
3637
end
3738

38-
defp traverse({op, _meta, [{:when, _, [{name, _, args} | _]} | _]} = ast, names)
39+
defp find_hidden({_, _, args} = ast, names) when is_list(args) do
40+
{ast, do_find_hidden(args, names)}
41+
end
42+
43+
defp find_hidden(ast, names), do: {ast, names}
44+
45+
defp do_find_hidden([doc, function | rest], names) do
46+
hidden? =
47+
match?({:@, _, [{:doc, _, [false]}]}, doc) or match?({:@, _, [{:impl, _, [true]}]}, doc)
48+
49+
def? = function_def?(function)
50+
51+
if hidden? and def? do
52+
names = [get_function_data(function) | names]
53+
do_find_hidden(rest, names)
54+
else
55+
do_find_hidden([function | rest], names)
56+
end
57+
end
58+
59+
defp do_find_hidden(_, names), do: names
60+
61+
defp function_def?({_op, _, [{:when, _, [{_name, _, _args} | _]} | _]}), do: true
62+
defp function_def?({_op, _, [{_name, _, _args} | _]}), do: true
63+
defp function_def?(_node), do: false
64+
65+
defp get_function_data({op, _, [{:when, _, [{name, _, args} | _]} | _]}) do
66+
{op, name, if(is_atom(args), do: 0, else: length(args))}
67+
end
68+
69+
defp get_function_data({op, _, [{name, _, args} | _]}) do
70+
{op, name, if(is_atom(args), do: 0, else: length(args))}
71+
end
72+
73+
defp traverse({op, _, [{:when, _, [{name, _, args} | _]} | _]} = ast, names)
3974
when op in @public_ops do
4075
definition = {op, name, if(is_atom(args), do: 0, else: length(args))}
4176
{ast, [definition | names]}
4277
end
4378

44-
defp traverse({op, _meta, [{name, _, args} | _]} = ast, names) when op in @public_ops do
79+
defp traverse({op, _, [{name, _, args} | _]} = ast, names) when op in @public_ops do
4580
definition = {op, name, if(is_atom(args), do: 0, else: length(args))}
4681
{ast, [definition | names]}
4782
end
4883

49-
defp traverse(ast, names) do
50-
{ast, names}
51-
end
84+
defp traverse(ast, names), do: {ast, names}
5285

53-
defp find_public_helpers(code_definitions, exemploid_definitions) do
54-
(Enum.uniq(code_definitions) -- exemploid_definitions)
86+
defp find_public_helpers(code_def, code_hidden, exemploid_def) do
87+
((Enum.uniq(code_def) -- exemploid_def) -- code_hidden)
5588
|> Enum.map(&print_definition/1)
5689
end
5790

test/elixir_analyzer/exercise_test/common_checks/private_helper_functions_test.exs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,56 @@ defmodule ElixirAnalyzer.ExerciseTest.CommonChecks.PrivateHelperFunctionsTest do
204204

205205
assert PrivateHelperFunctions.run(code, @pacman_exemplar) == [{:fail, comment}]
206206
end
207+
208+
test "@doc false hides the function" do
209+
code =
210+
quote do
211+
defmodule Rules do
212+
defmodule BooleanLogic do
213+
@doc false
214+
def do_or(left, right), do: left or right
215+
216+
@doc false
217+
def do_and(left, right) when is_boolean(left), do: left and right
218+
end
219+
220+
def score?(touching_power_pellet, touching_dot) do
221+
BooleanLogic.do_or(touching_power_pellet, touching_dot)
222+
end
223+
224+
def eat_ghost?(power_pellet_active, touching_ghost) do
225+
BooleanLogic.do_and(power_pellet_active, touching_ghost)
226+
end
227+
end
228+
end
229+
230+
assert PrivateHelperFunctions.run(code, @pacman_exemplar) == []
231+
end
232+
233+
test "@impl true hides the function" do
234+
code =
235+
quote do
236+
defmodule Rules do
237+
defmodule BooleanLogic do
238+
@impl true
239+
def do_or(left, right), do: left or right
240+
241+
@impl true
242+
def do_and(left, right) when is_boolean(left), do: left and right
243+
end
244+
245+
def score?(touching_power_pellet, touching_dot) do
246+
BooleanLogic.do_or(touching_power_pellet, touching_dot)
247+
end
248+
249+
def eat_ghost?(power_pellet_active, touching_ghost) do
250+
BooleanLogic.do_and(power_pellet_active, touching_ghost)
251+
end
252+
end
253+
end
254+
255+
assert PrivateHelperFunctions.run(code, @pacman_exemplar) == []
256+
end
207257
end
208258

209259
describe "practice exercise with square-root" do

0 commit comments

Comments
 (0)