zf

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

parentheses_in_condition.ex (5185B)


      1 defmodule Credo.Check.Readability.ParenthesesInCondition do
      2   use Credo.Check,
      3     base_priority: :high,
      4     tags: [:formatter],
      5     explanations: [
      6       check: """
      7       Because `if` and `unless` are macros, the preferred style is to not use
      8       parentheses around conditions.
      9 
     10           # preferred
     11 
     12           if valid?(username) do
     13             # ...
     14           end
     15 
     16           # NOT preferred
     17 
     18           if( valid?(username) ) do
     19             # ...
     20           end
     21 
     22       Like all `Readability` issues, this one is not a technical concern.
     23       But you can improve the odds of others reading and liking your code by making
     24       it easier to follow.
     25       """
     26     ]
     27 
     28   @doc false
     29   @impl true
     30   # TODO: consider for experimental check front-loader (tokens)
     31   def run(%SourceFile{} = source_file, params) do
     32     issue_meta = IssueMeta.for(source_file, params)
     33 
     34     source_file
     35     |> Credo.Code.to_tokens()
     36     |> collect_parenthetical_tokens([], nil)
     37     |> find_issues([], issue_meta)
     38   end
     39 
     40   defp collect_parenthetical_tokens([], acc, _), do: acc
     41 
     42   defp collect_parenthetical_tokens([head | t], acc, prev_head) do
     43     acc =
     44       case check_for_opening_paren(head, t, prev_head) do
     45         false -> acc
     46         token -> acc ++ [token]
     47       end
     48 
     49     collect_parenthetical_tokens(t, acc, head)
     50   end
     51 
     52   defp check_for_opening_paren(
     53          {:identifier, _, if_or_unless} = start,
     54          [{:"(", _} = next_token | t],
     55          prev_head
     56        )
     57        when if_or_unless in [:if, :unless] do
     58     check_for_closing_paren(start, next_token, t, prev_head)
     59   end
     60 
     61   defp check_for_opening_paren(
     62          {:paren_identifier, _, if_or_unless},
     63          _,
     64          {:arrow_op, _, :|>}
     65        )
     66        when if_or_unless in [:if, :unless] do
     67     false
     68   end
     69 
     70   defp check_for_opening_paren(
     71          {:paren_identifier, _, if_or_unless} = token,
     72          [{:"(", _} | t],
     73          _
     74        )
     75        when if_or_unless in [:if, :unless] do
     76     if Enum.any?(collect_paren_children(t), &is_do/1) do
     77       false
     78     else
     79       token
     80     end
     81   end
     82 
     83   defp check_for_opening_paren(_, _, _), do: false
     84 
     85   # matches:  if( something ) do
     86   #                         ^^^^
     87   defp check_for_closing_paren(start, {:do, _}, _tail, {:")", _}) do
     88     start
     89   end
     90 
     91   # matches:  if( something ) == something_else do
     92   #                           ^^
     93   defp check_for_closing_paren(_start, {:")", _}, [{:comp_op, _, _} | _tail], _prev_head) do
     94     false
     95   end
     96 
     97   # matches:  if( something ) or something_else do
     98   #                           ^^
     99   defp check_for_closing_paren(_start, {:")", _}, [{:or_op, _, _} | _tail], _prev_head) do
    100     false
    101   end
    102 
    103   # matches:  if( something ) and something_else do
    104   #                           ^^^
    105   defp check_for_closing_paren(_start, {:")", _}, [{:and_op, _, _} | _tail], _prev_head) do
    106     false
    107   end
    108 
    109   # matches:  if( something ) in something_else do
    110   #                           ^^
    111   defp check_for_closing_paren(_start, {:")", _}, [{:in_op, _, _} | _tail], _prev_head) do
    112     false
    113   end
    114 
    115   # matches:  if( 1 + foo ) / bar > 0 do
    116   #                         ^
    117   defp check_for_closing_paren(_start, {:")", _}, [{:mult_op, _, _} | _tail], _prev_head) do
    118     false
    119   end
    120 
    121   # matches:  if( 1 + foo ) + bar > 0 do
    122   #                         ^
    123   defp check_for_closing_paren(_start, {:")", _}, [{:dual_op, _, _} | _tail], _prev_head) do
    124     false
    125   end
    126 
    127   # matches:  if( 1 &&& foo ) > bar do
    128   #                           ^
    129   defp check_for_closing_paren(_start, {:")", _}, [{:rel_op, _, _} | _tail], _prev_head) do
    130     false
    131   end
    132 
    133   # matches:  if( something ), do:
    134   #                         ^^
    135   defp check_for_closing_paren(start, {:",", _}, _, {:")", _}) do
    136     start
    137   end
    138 
    139   defp check_for_closing_paren(_, {:or_op, _, _}, [{:"(", _} | _], _) do
    140     false
    141   end
    142 
    143   defp check_for_closing_paren(_, {:and_op, _, _}, [{:"(", _} | _], _) do
    144     false
    145   end
    146 
    147   defp check_for_closing_paren(_, {:comp_op, _, _}, [{:"(", _} | _], _) do
    148     false
    149   end
    150 
    151   defp check_for_closing_paren(start, token, [next_token | t], _prev_head) do
    152     check_for_closing_paren(start, next_token, t, token)
    153   end
    154 
    155   defp check_for_closing_paren(_, _, _, _), do: false
    156 
    157   defp is_do({_, _, :do}), do: true
    158   defp is_do(_), do: false
    159 
    160   defp collect_paren_children(x) do
    161     {_, children} = Enum.reduce(x, {0, []}, &collect_paren_child/2)
    162     children
    163   end
    164 
    165   defp collect_paren_child({:"(", _}, {nest_level, tokens}), do: {nest_level + 1, tokens}
    166 
    167   defp collect_paren_child({:")", _}, {nest_level, tokens}), do: {nest_level - 1, tokens}
    168 
    169   defp collect_paren_child(token, {0, tokens}), do: {0, tokens ++ [token]}
    170   defp collect_paren_child(_, {_, _} = state), do: state
    171 
    172   defp find_issues([], acc, _issue_meta) do
    173     acc
    174   end
    175 
    176   defp find_issues([{_, {line_no, _, _}, trigger} | t], acc, issue_meta) do
    177     new_issue = issue_for(issue_meta, line_no, trigger)
    178 
    179     acc = acc ++ [new_issue]
    180 
    181     find_issues(t, acc, issue_meta)
    182   end
    183 
    184   defp issue_for(issue_meta, line_no, trigger) do
    185     format_issue(
    186       issue_meta,
    187       message: "The condition of `#{trigger}` should not be wrapped in parentheses.",
    188       trigger: trigger,
    189       line_no: line_no
    190     )
    191   end
    192 end