apply.ex (1895B)
1 defmodule Credo.Check.Refactor.Apply do 2 use Credo.Check, 3 base_priority: :low, 4 explanations: [ 5 check: """ 6 Prefer calling functions directly if the number of arguments is known 7 at compile time instead of using `apply/2` and `apply/3`. 8 9 Example: 10 11 # preferred 12 13 fun.(arg_1, arg_2, ..., arg_n) 14 15 module.function(arg_1, arg_2, ..., arg_n) 16 17 # NOT preferred 18 19 apply(fun, [arg_1, arg_2, ..., arg_n]) 20 21 apply(module, :function, [arg_1, arg_2, ..., arg_n]) 22 """ 23 ] 24 25 @doc false 26 @impl true 27 def run(%SourceFile{} = source_file, params) do 28 Credo.Code.prewalk(source_file, &traverse(&1, &2, IssueMeta.for(source_file, params))) 29 end 30 31 defp traverse(ast, issues, issue_meta) do 32 case issue(ast, issue_meta) do 33 nil -> {ast, issues} 34 issue -> {ast, [issue | issues]} 35 end 36 end 37 38 defp issue({:apply, _meta, [{:__MODULE__, _, _}, _fun, _args]}, _issue_meta), do: nil 39 40 defp issue({:apply, meta, [fun, args]}, issue_meta) do 41 do_issue(:apply2, fun, args, meta, issue_meta) 42 end 43 44 defp issue({:apply, meta, [_module, fun, args]}, issue_meta) do 45 do_issue(:apply3, fun, args, meta, issue_meta) 46 end 47 48 defp issue(_ast, _issue_meta), do: nil 49 50 defp do_issue(_apply, _fun, [{:|, _, _}], _meta, _issue_meta), do: nil 51 52 defp do_issue(:apply2, {name, _meta, nil}, args, meta, issue_meta) 53 when is_atom(name) and is_list(args) do 54 issue_for(meta, issue_meta) 55 end 56 57 defp do_issue(:apply3, fun, args, meta, issue_meta) 58 when is_atom(fun) and is_list(args) do 59 issue_for(meta, issue_meta) 60 end 61 62 defp do_issue(_apply, _fun, _args, _meta, _issue_meta), do: nil 63 64 defp issue_for(meta, issue_meta) do 65 format_issue( 66 issue_meta, 67 message: "Avoid `apply/2` and `apply/3` when the number of arguments is known", 68 line_no: meta[:line] 69 ) 70 end 71 end