variable_names.ex (3272B)
1 defmodule Credo.Check.Readability.VariableNames do 2 use Credo.Check, 3 base_priority: :high, 4 explanations: [ 5 check: """ 6 Variable names are always written in snake_case in Elixir. 7 8 # snake_case: 9 10 incoming_result = handle_incoming_message(message) 11 12 # not snake_case 13 14 incomingResult = handle_incoming_message(message) 15 16 Like all `Readability` issues, this one is not a technical concern. 17 But you can improve the odds of others reading and liking your code by making 18 it easier to follow. 19 """ 20 ] 21 22 alias Credo.Code.Name 23 24 @special_var_names [:__CALLER__, :__DIR__, :__ENV__, :__MODULE__] 25 26 @doc false 27 @impl true 28 def run(%SourceFile{} = source_file, params) do 29 issue_meta = IssueMeta.for(source_file, params) 30 31 Credo.Code.prewalk(source_file, &traverse(&1, &2, issue_meta)) 32 end 33 34 defp traverse({:=, _meta, [lhs, _rhs]} = ast, issues, issue_meta) do 35 {ast, issues_for_lhs(lhs, issues, issue_meta)} 36 end 37 38 defp traverse({:->, _meta, [lhs, _rhs]} = ast, issues, issue_meta) do 39 {ast, issues_for_lhs(lhs, issues, issue_meta)} 40 end 41 42 defp traverse( 43 {:<-, _meta, [{:|, _comp_meta, [_lhs, rhs]}, _comp_rhs]} = ast, 44 issues, 45 issue_meta 46 ) do 47 {ast, issues_for_lhs(rhs, issues, issue_meta)} 48 end 49 50 defp traverse({:<-, _meta, [lhs, _rhs]} = ast, issues, issue_meta) do 51 {ast, issues_for_lhs(lhs, issues, issue_meta)} 52 end 53 54 defp traverse( 55 {:def, _meta, [{_fun, _fun_meta, [lhs, _rhs]}, _fun_rhs]} = ast, 56 issues, 57 issue_meta 58 ) do 59 {ast, issues_for_lhs(lhs, issues, issue_meta)} 60 end 61 62 defp traverse( 63 {:defp, _meta, [{_fun, _fun_meta, [lhs, _rhs]}, _fun_rhs]} = ast, 64 issues, 65 issue_meta 66 ) do 67 {ast, issues_for_lhs(lhs, issues, issue_meta)} 68 end 69 70 defp traverse(ast, issues, _issue_meta) do 71 {ast, issues} 72 end 73 74 for op <- [:{}, :%{}, :^, :|, :<>] do 75 defp issues_for_lhs({unquote(op), _meta, parameters}, issues, issue_meta) do 76 issues_for_lhs(parameters, issues, issue_meta) 77 end 78 end 79 80 defp issues_for_lhs({_name, _meta, nil} = value, issues, issue_meta) do 81 case issue_for_name(value, issue_meta) do 82 nil -> 83 issues 84 85 new_issue -> 86 [new_issue | issues] 87 end 88 end 89 90 defp issues_for_lhs(list, issues, issue_meta) when is_list(list) do 91 Enum.reduce(list, issues, &issues_for_lhs(&1, &2, issue_meta)) 92 end 93 94 defp issues_for_lhs(tuple, issues, issue_meta) when is_tuple(tuple) do 95 Enum.reduce( 96 Tuple.to_list(tuple), 97 issues, 98 &issues_for_lhs(&1, &2, issue_meta) 99 ) 100 end 101 102 defp issues_for_lhs(_, issues, _issue_meta) do 103 issues 104 end 105 106 for name <- @special_var_names do 107 defp issue_for_name({unquote(name), _, nil}, _), do: nil 108 end 109 110 defp issue_for_name({name, meta, nil}, issue_meta) do 111 string_name = to_string(name) 112 113 unless Name.snake_case?(string_name) or Name.no_case?(string_name) do 114 issue_for(issue_meta, meta[:line], name) 115 end 116 end 117 118 defp issue_for(issue_meta, line_no, trigger) do 119 format_issue( 120 issue_meta, 121 message: "Variable names should be written in snake_case.", 122 trigger: trigger, 123 line_no: line_no 124 ) 125 end 126 end