zf

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

expensive_empty_enum_check.ex (2240B)


      1 defmodule Credo.Check.Warning.ExpensiveEmptyEnumCheck do
      2   use Credo.Check,
      3     base_priority: :high,
      4     explanations: [
      5       # TODO: improve checkdoc
      6       check: """
      7       Checking if the size of the enum is `0` can be very expensive, since you are
      8       determining the exact count of elements.
      9 
     10       Checking if an enum is empty should be done by using
     11 
     12           Enum.empty?(enum)
     13 
     14       or
     15 
     16           list == []
     17 
     18 
     19       For Enum.count/2: Checking if an enum doesn't contain specific elements should
     20       be done by using
     21 
     22           not Enum.any?(enum, condition)
     23 
     24       """
     25     ]
     26 
     27   @doc false
     28   @impl true
     29   def run(%SourceFile{} = source_file, params) do
     30     issue_meta = IssueMeta.for(source_file, params)
     31 
     32     Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta))
     33   end
     34 
     35   @enum_count_pattern quote do: {
     36                               {:., _, [{:__aliases__, _, [:Enum]}, :count]},
     37                               _,
     38                               _
     39                             }
     40   @length_pattern quote do: {:length, _, [_]}
     41   @comparisons [
     42     {@enum_count_pattern, 0},
     43     {0, @enum_count_pattern},
     44     {@length_pattern, 0},
     45     {0, @length_pattern}
     46   ]
     47   @operators [:==, :===]
     48 
     49   for {lhs, rhs} <- @comparisons,
     50       operator <- @operators do
     51     defp traverse(
     52            {unquote(operator), meta, [unquote(lhs), unquote(rhs)]} = ast,
     53            issues,
     54            issue_meta
     55          ) do
     56       {ast, issues_for_call(meta, issues, issue_meta, ast)}
     57     end
     58   end
     59 
     60   defp traverse(ast, issues, _issue_meta) do
     61     {ast, issues}
     62   end
     63 
     64   defp issues_for_call(meta, issues, issue_meta, ast) do
     65     [issue_for(issue_meta, meta[:line], Macro.to_string(ast), suggest(ast)) | issues]
     66   end
     67 
     68   defp suggest({_op, _, [0, {_pattern, _, args}]}), do: suggest_for_arity(Enum.count(args))
     69   defp suggest({_op, _, [{_pattern, _, args}, 0]}), do: suggest_for_arity(Enum.count(args))
     70 
     71   defp suggest_for_arity(2), do: "`not Enum.any?/2`"
     72   defp suggest_for_arity(1), do: "Enum.empty?/1 or list == []"
     73 
     74   defp issue_for(issue_meta, line_no, trigger, suggestion) do
     75     format_issue(
     76       issue_meta,
     77       message: "#{trigger} is expensive. Prefer #{suggestion}",
     78       trigger: trigger,
     79       line_no: line_no
     80     )
     81   end
     82 end