zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

collector.ex (2603B)


      1 defmodule Credo.Check.Consistency.MultiAliasImportRequireUse.Collector do
      2   @moduledoc false
      3 
      4   use Credo.Check.Consistency.Collector
      5 
      6   @directives [:alias, :import, :require, :use]
      7 
      8   def collect_matches(source_file, _params) do
      9     source_file
     10     |> Credo.Code.prewalk(&traverse/2, [])
     11     |> group_usages
     12     |> count_occurrences
     13   end
     14 
     15   def find_locations_not_matching(expected, source_file) do
     16     source_file
     17     |> Credo.Code.prewalk(&traverse/2, [])
     18     |> group_usages
     19     |> drop_locations(expected)
     20   end
     21 
     22   defp traverse({directive, meta, arguments} = ast, acc)
     23        when directive in @directives do
     24     aliases =
     25       case arguments do
     26         [{:__aliases__, _, nested_modules}] when length(nested_modules) > 1 ->
     27           base_name = Enum.slice(nested_modules, 0..-2)
     28           {:single, base_name}
     29 
     30         [{{:., _, [{:__aliases__, _, _namespaces}, :{}]}, _, _nested_aliases}] ->
     31           :multi
     32 
     33         _ ->
     34           nil
     35       end
     36 
     37     if aliases do
     38       {ast, [{directive, aliases, meta[:line]} | acc]}
     39     else
     40       {ast, acc}
     41     end
     42   end
     43 
     44   defp traverse(ast, acc), do: {ast, acc}
     45 
     46   defp group_usages(usages) do
     47     split_with(usages, fn
     48       {_directive, :multi, _line_no} -> true
     49       _ -> false
     50     end)
     51   end
     52 
     53   defp count_occurrences({multi, single}) do
     54     stats = [
     55       multi: Enum.count(multi),
     56       single: single |> multiple_single_locations |> Enum.count()
     57     ]
     58 
     59     stats
     60     |> Enum.filter(fn {_, count} -> count > 0 end)
     61     |> Enum.into(%{})
     62   end
     63 
     64   defp drop_locations({_, single}, :multi), do: multiple_single_locations(single)
     65 
     66   defp drop_locations({multi, _}, :single), do: multi_locations(multi)
     67 
     68   defp multi_locations(multi_usages) do
     69     Enum.map(multi_usages, fn {_directive, :multi, line_no} -> line_no end)
     70   end
     71 
     72   defp multiple_single_locations(single_usages) do
     73     single_usages
     74     |> Enum.group_by(fn {directive, base_name, _line_no} ->
     75       {directive, base_name}
     76     end)
     77     |> Enum.filter(fn {_grouped_by, occurrences} ->
     78       Enum.count(occurrences) > 1
     79     end)
     80     |> Enum.map(fn {_grouped_by, [{_, _, line_no} | _]} -> line_no end)
     81   end
     82 
     83   # Enum.split_with/2 is not available on Elixir < 1.4
     84   # see https://github.com/elixir-lang/elixir/blob/v1.4.4/lib/elixir/lib/enum.ex#L1620
     85   defp split_with(enumerable, fun) when is_function(fun, 1) do
     86     {acc1, acc2} =
     87       Enum.reduce(enumerable, {[], []}, fn entry, {acc1, acc2} ->
     88         if fun.(entry) do
     89           {[entry | acc1], acc2}
     90         else
     91           {acc1, [entry | acc2]}
     92         end
     93       end)
     94 
     95     {:lists.reverse(acc1), :lists.reverse(acc2)}
     96   end
     97 end