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