zf

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

match_in_condition.ex (3829B)


      1 defmodule Credo.Check.Refactor.MatchInCondition do
      2   use Credo.Check,
      3     param_defaults: [
      4       allow_tagged_tuples: false
      5     ],
      6     explanations: [
      7       check: """
      8       Pattern matching should only ever be used for simple assignments
      9       inside `if` and `unless` clauses.
     10 
     11       While this fine:
     12 
     13           # okay, simple wildcard assignment:
     14 
     15           if contents = File.read!("foo.txt") do
     16             do_something(contents)
     17           end
     18 
     19       the following should be avoided, since it mixes a pattern match with a
     20       condition and do/else blocks.
     21 
     22           # considered too "complex":
     23 
     24           if {:ok, contents} = File.read("foo.txt") do
     25             do_something(contents)
     26           end
     27 
     28           # also considered "complex":
     29 
     30           if allowed? && ( contents = File.read!("foo.txt") ) do
     31             do_something(contents)
     32           end
     33 
     34       If you want to match for something and execute another block otherwise,
     35       consider using a `case` statement:
     36 
     37           case File.read("foo.txt") do
     38             {:ok, contents} ->
     39               do_something()
     40             _ ->
     41               do_something_else()
     42           end
     43 
     44       """,
     45       params: [
     46         allow_tagged_tuples:
     47           "Allow tagged tuples in conditions, e.g. `if {:ok, contents} = File.read( \"foo.txt\") do`"
     48       ]
     49     ]
     50 
     51   @condition_ops [:if, :unless]
     52   @trigger "="
     53 
     54   @doc false
     55   @impl true
     56   def run(%SourceFile{} = source_file, params) do
     57     issue_meta = IssueMeta.for(source_file, params)
     58     allow_tagged_tuples = Params.get(params, :allow_tagged_tuples, __MODULE__)
     59 
     60     Credo.Code.prewalk(source_file, &traverse(&1, &2, allow_tagged_tuples, issue_meta))
     61   end
     62 
     63   # Skip if arguments is not enumerable
     64   defp traverse({_op, _meta, nil} = ast, issues, _allow_tagged_tuples, _source_file) do
     65     {ast, issues}
     66   end
     67 
     68   # TODO: consider for experimental check front-loader (ast)
     69   # NOTE: we have to exclude the cases matching the above
     70   for op <- @condition_ops do
     71     defp traverse({unquote(op), _meta, arguments} = ast, issues, allow_tagged_tuples, issue_meta) do
     72       # remove do/else blocks
     73       condition_arguments = Enum.reject(arguments, &Keyword.keyword?/1)
     74 
     75       new_issues =
     76         Credo.Code.prewalk(
     77           condition_arguments,
     78           &traverse_condition(
     79             &1,
     80             &2,
     81             unquote(op),
     82             condition_arguments,
     83             allow_tagged_tuples,
     84             issue_meta
     85           )
     86         )
     87 
     88       {ast, issues ++ new_issues}
     89     end
     90   end
     91 
     92   defp traverse(ast, issues, _allow_tagged_tuples, _source_file) do
     93     {ast, issues}
     94   end
     95 
     96   defp traverse_condition(
     97          {:=, meta, arguments} = ast,
     98          issues,
     99          op,
    100          op_arguments,
    101          allow_tagged_tuples?,
    102          issue_meta
    103        ) do
    104     assignment_in_body? = Enum.member?(op_arguments, ast)
    105 
    106     case arguments do
    107       [{atom, _, nil}, _right] when is_atom(atom) ->
    108         if assignment_in_body? do
    109           {ast, issues}
    110         else
    111           new_issue = issue_for(op, meta[:line], issue_meta)
    112 
    113           {ast, issues ++ [new_issue]}
    114         end
    115 
    116       [{tag_atom, {atom, _, nil}}, _right] when is_atom(atom) and is_atom(tag_atom) ->
    117         if allow_tagged_tuples? do
    118           {ast, issues}
    119         else
    120           new_issue = issue_for(op, meta[:line], issue_meta)
    121 
    122           {ast, issues ++ [new_issue]}
    123         end
    124 
    125       _ ->
    126         new_issue = issue_for(op, meta[:line], issue_meta)
    127         {ast, issues ++ [new_issue]}
    128     end
    129   end
    130 
    131   defp traverse_condition(ast, issues, _op, _op_arguments, _allow_tagged_tuples, _issue_meta) do
    132     {ast, issues}
    133   end
    134 
    135   defp issue_for(op, line_no, issue_meta) do
    136     format_issue(
    137       issue_meta,
    138       message: "There should be no matches in `#{op}` conditions.",
    139       trigger: @trigger,
    140       line_no: line_no
    141     )
    142   end
    143 end