zf

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

operation_on_same_values.ex (2770B)


      1 defmodule Credo.Check.Warning.OperationOnSameValues do
      2   use Credo.Check,
      3     base_priority: :high,
      4     explanations: [
      5       check: """
      6       Operations on the same values always yield the same result and therefore make
      7       little sense in production code.
      8 
      9       Examples:
     10 
     11           x == x  # always returns true
     12           x <= x  # always returns true
     13           x >= x  # always returns true
     14           x != x  # always returns false
     15           x > x   # always returns false
     16           y / y   # always returns 1
     17           y - y   # always returns 0
     18 
     19       In practice they are likely the result of a debugging session or were made by
     20       mistake.
     21       """
     22     ]
     23 
     24   @def_ops [:def, :defp, :defmacro]
     25   @ops ~w(== >= <= != > < / -)a
     26   @ops_and_constant_results [
     27     {:==, "Comparison", true},
     28     {:>=, "Comparison", true},
     29     {:<=, "Comparison", true},
     30     {:!=, "Comparison", false},
     31     {:>, "Comparison", false},
     32     {:<, "Comparison", false},
     33     {:/, "Operation", 1},
     34     {:-, "Operation", 0}
     35   ]
     36 
     37   @doc false
     38   @impl true
     39   def run(%SourceFile{} = source_file, params) do
     40     issue_meta = IssueMeta.for(source_file, params)
     41 
     42     Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta))
     43   end
     44 
     45   # TODO: consider for experimental check front-loader (ast)
     46   for op <- @def_ops do
     47     # exclude def arguments for operators
     48     defp traverse(
     49            {unquote(op), _meta, [{op, _, _} | rest]},
     50            issues,
     51            _issue_meta
     52          )
     53          when op in @ops do
     54       {rest, issues}
     55     end
     56   end
     57 
     58   for {op, operation_name, constant_result} <- @ops_and_constant_results do
     59     defp traverse({unquote(op), meta, [lhs, rhs]} = ast, issues, issue_meta) do
     60       if variable_or_mod_attribute?(lhs) &&
     61            Credo.Code.remove_metadata(lhs) == Credo.Code.remove_metadata(rhs) do
     62         new_issue =
     63           issue_for(
     64             issue_meta,
     65             meta[:line],
     66             unquote(op),
     67             unquote(operation_name),
     68             unquote(constant_result)
     69           )
     70 
     71         {ast, issues ++ [new_issue]}
     72       else
     73         {ast, issues}
     74       end
     75     end
     76   end
     77 
     78   defp variable_or_mod_attribute?({atom, _meta, nil}) when is_atom(atom), do: true
     79   defp variable_or_mod_attribute?({:@, _meta, list}) when is_list(list), do: true
     80   defp variable_or_mod_attribute?(_), do: false
     81 
     82   # exclude @spec definitions
     83   defp traverse({:@, _meta, [{:spec, _, _} | _]}, issues, _issue_meta) do
     84     {nil, issues}
     85   end
     86 
     87   defp traverse(ast, issues, _issue_meta) do
     88     {ast, issues}
     89   end
     90 
     91   defp issue_for(issue_meta, line_no, trigger, operation, constant_result) do
     92     format_issue(
     93       issue_meta,
     94       message: "#{operation} will always return #{constant_result}.",
     95       trigger: trigger,
     96       line_no: line_no
     97     )
     98   end
     99 end