zf

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

parentheses_on_zero_arity_defs.ex (3195B)


      1 defmodule Credo.Check.Readability.ParenthesesOnZeroArityDefs do
      2   use Credo.Check,
      3     base_priority: :low,
      4     param_defaults: [parens: false],
      5     explanations: [
      6       check: """
      7       Either use parentheses or not when defining a function with no arguments.
      8 
      9       By default, this check enforces no parentheses, so zero-arity function
     10       and macro definitions should look like this:
     11 
     12           def summer? do
     13             # ...
     14           end
     15 
     16       If the `:parens` param is set to `true` for this check, then the check
     17       enforces zero-arity function and macro definitions to have parens:
     18 
     19           def summer?() do
     20             # ...
     21           end
     22 
     23       Like all `Readability` issues, this one is not a technical concern.
     24       But you can improve the odds of others reading and liking your code by making
     25       it easier to follow.
     26       """
     27     ]
     28 
     29   alias Credo.Check.Params
     30 
     31   @def_ops [:def, :defp, :defmacro, :defmacrop]
     32 
     33   @doc false
     34   @impl true
     35   def run(%SourceFile{} = source_file, params) do
     36     parens? = Params.get(params, :parens, __MODULE__)
     37     issue_meta = IssueMeta.for(source_file, params)
     38 
     39     Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta, parens?))
     40   end
     41 
     42   # TODO: consider for experimental check front-loader (ast)
     43   for op <- @def_ops do
     44     # catch variables named e.g. `defp`
     45     defp traverse({unquote(op), _, nil} = ast, issues, _issue_meta, _parens?) do
     46       {ast, issues}
     47     end
     48 
     49     defp traverse({unquote(op), _, body} = ast, issues, issue_meta, parens?) do
     50       function_head = Enum.at(body, 0)
     51 
     52       {ast, issues_for_definition(function_head, issues, issue_meta, parens?)}
     53     end
     54   end
     55 
     56   defp traverse(ast, issues, _issue_meta, _parens?) do
     57     {ast, issues}
     58   end
     59 
     60   # skip the false positive for a metaprogrammed definition
     61   defp issues_for_definition({{:unquote, _, _}, _, _}, issues, _, _parens?) do
     62     issues
     63   end
     64 
     65   defp issues_for_definition({_, _, args}, issues, _, _parens?) when length(args) > 0 do
     66     issues
     67   end
     68 
     69   defp issues_for_definition({name, meta, _}, issues, issue_meta, enforce_parens?) do
     70     line_no = meta[:line]
     71     text = remaining_line_after(issue_meta, line_no, name)
     72     parens? = String.match?(text, ~r/^\((\w*)\)(.)*/)
     73 
     74     cond do
     75       parens? and not enforce_parens? ->
     76         issues ++ [issue_for(issue_meta, line_no, :present)]
     77 
     78       not parens? and enforce_parens? ->
     79         issues ++ [issue_for(issue_meta, line_no, :missing)]
     80 
     81       true ->
     82         issues
     83     end
     84   end
     85 
     86   defp remaining_line_after(issue_meta, line_no, text) do
     87     source_file = IssueMeta.source_file(issue_meta)
     88     line = SourceFile.line_at(source_file, line_no)
     89     name_size = text |> to_string |> String.length()
     90     skip = (SourceFile.column(source_file, line_no, text) || -1) + name_size - 1
     91 
     92     String.slice(line, skip..-1)
     93   end
     94 
     95   defp issue_for(issue_meta, line_no, kind) do
     96     message =
     97       case kind do
     98         :present ->
     99           "Do not use parentheses when defining a function which has no arguments."
    100 
    101         :missing ->
    102           "Use parentheses () when defining a function which has no arguments."
    103       end
    104 
    105     format_issue(issue_meta, message: message, line_no: line_no)
    106   end
    107 end