collector.ex (2411B)
1 defmodule Credo.Check.Consistency.UnusedVariableNames.Collector do 2 @moduledoc false 3 4 use Credo.Check.Consistency.Collector 5 6 def collect_matches(source_file, _params) do 7 unused_variable_recorder = &record_unused_variable/2 8 9 Credo.Code.prewalk(source_file, &traverse(unused_variable_recorder, &1, &2), %{}) 10 end 11 12 def find_locations_not_matching(expected, source_file) do 13 location_recorder = &record_not_matching(expected, &1, &2) 14 15 source_file 16 |> Credo.Code.prewalk(&traverse(location_recorder, &1, &2), []) 17 |> Enum.reverse() 18 end 19 20 defp traverse(callback, {:=, _, params} = ast, acc) do 21 {ast, reduce_unused_variables(params, callback, acc)} 22 end 23 24 defp traverse(callback, {def, _, [{_, _, params} | _]} = ast, acc) 25 when def in [:def, :defp] do 26 {ast, reduce_unused_variables(params, callback, acc)} 27 end 28 29 defp traverse(callback, {:->, _, [params | _]} = ast, acc) do 30 {ast, reduce_unused_variables(params, callback, acc)} 31 end 32 33 defp traverse(_callback, ast, acc), do: {ast, acc} 34 35 defp reduce_unused_variables(nil, _callback, acc), do: acc 36 37 defp reduce_unused_variables(ast, callback, acc) do 38 Enum.reduce(ast, acc, fn 39 {_, _, params}, param_acc when is_list(params) -> 40 reduce_unused_variables(params, callback, param_acc) 41 42 param_ast, param_acc -> 43 if unused_variable_ast?(param_ast) do 44 callback.(param_ast, param_acc) 45 else 46 param_acc 47 end 48 end) 49 end 50 51 defp unused_variable_ast?({:_, _, _}), do: true 52 53 defp unused_variable_ast?({name, _, _}) when is_atom(name) do 54 name 55 |> Atom.to_string() 56 |> unused_variable_name?() 57 end 58 59 defp unused_variable_ast?(_), do: false 60 61 defp unused_variable_name?("__" <> _rest), do: false 62 63 defp unused_variable_name?("_" <> _rest), do: true 64 65 defp unused_variable_name?(_rest), do: false 66 67 defp record_unused_variable({:_, _, _}, acc), do: Map.update(acc, :anonymous, 1, &(&1 + 1)) 68 defp record_unused_variable(_, acc), do: Map.update(acc, :meaningful, 1, &(&1 + 1)) 69 70 defp record_not_matching(expected, {name, meta, _}, acc) do 71 case {expected, Atom.to_string(name)} do 72 {:anonymous, "_" <> rest = trigger} when rest != "" -> 73 [[line_no: meta[:line], trigger: trigger] | acc] 74 75 {:meaningful, "_" = trigger} -> 76 [[line_no: meta[:line], trigger: trigger] | acc] 77 78 _ -> 79 acc 80 end 81 end 82 end