predicate_function_names.ex (2804B)
1 defmodule Credo.Check.Readability.PredicateFunctionNames do 2 use Credo.Check, 3 base_priority: :high, 4 explanations: [ 5 check: """ 6 Predicate functions/macros should be named accordingly: 7 8 * For functions, they should end in a question mark. 9 10 # preferred 11 12 defp user?(cookie) do 13 end 14 15 defp has_attachment?(mail) do 16 end 17 18 # NOT preferred 19 20 defp is_user?(cookie) do 21 end 22 23 defp is_user(cookie) do 24 end 25 26 * For guard-safe macros they should have the prefix `is_` and not end in a question mark. 27 28 # preferred 29 30 defmacro is_user(cookie) do 31 end 32 33 # NOT preferred 34 35 defmacro is_user?(cookie) do 36 end 37 38 defmacro user?(cookie) do 39 end 40 41 Like all `Readability` issues, this one is not a technical concern. 42 But you can improve the odds of others reading and liking your code by making 43 it easier to follow. 44 """ 45 ] 46 47 @def_ops [:def, :defp, :defmacro] 48 49 @doc false 50 @impl true 51 def run(%SourceFile{} = source_file, params) do 52 issue_meta = IssueMeta.for(source_file, params) 53 54 Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) 55 end 56 57 # TODO: consider for experimental check front-loader (ast) 58 # NOTE: see below for how we want to avoid `defp = "my_variable"` definitions 59 for op <- @def_ops do 60 # catch variables named e.g. `defp` 61 defp traverse({unquote(op), _meta, nil} = ast, issues, _issue_meta) do 62 {ast, issues} 63 end 64 65 defp traverse( 66 {unquote(op) = op, _meta, arguments} = ast, 67 issues, 68 issue_meta 69 ) do 70 {ast, issues_for_definition(op, arguments, issues, issue_meta)} 71 end 72 end 73 74 defp traverse(ast, issues, _issue_meta) do 75 {ast, issues} 76 end 77 78 defp issues_for_definition(op, body, issues, issue_meta) do 79 case Enum.at(body, 0) do 80 {name, meta, nil} -> 81 issues_for_name(op, name, meta, issues, issue_meta) 82 83 _ -> 84 issues 85 end 86 end 87 88 defp issues_for_name(_op, name, meta, issues, issue_meta) do 89 name = to_string(name) 90 91 cond do 92 String.starts_with?(name, "is_") && String.ends_with?(name, "?") -> 93 [ 94 issue_for(issue_meta, meta[:line], name, :predicate_and_question_mark) 95 | issues 96 ] 97 98 String.starts_with?(name, "is_") -> 99 [issue_for(issue_meta, meta[:line], name, :only_predicate) | issues] 100 101 true -> 102 issues 103 end 104 end 105 106 defp issue_for(issue_meta, line_no, trigger, _) do 107 format_issue( 108 issue_meta, 109 message: 110 "Predicate function names should not start with 'is', and should end in a question mark.", 111 trigger: trigger, 112 line_no: line_no 113 ) 114 end 115 end