Packages

Credo custom checks plugin to comply with Nimble Compass conventions

Current section

Files

Jump to
compass_credo_plugin lib check defdelegate_order.ex
Raw

lib/check/defdelegate_order.ex

defmodule CompassCredoPlugin.Check.DefdelegateOrder do
use Credo.Check,
base_priority: :normal,
category: :readability,
explanations: [
check: """
Prefer placing defdelegate before public and private functions.
# Less preferred
defmodule App.UserView do
alias App.SharedView
# All the other module attributes, directives, and macros go here ...
# All the normal functions go here ...
defdelegate formatted_username(user), to: SharedView
end
# Preferred
defmodule App.UserView do
alias App.SharedView
# All the other module attributes, directives, and macros go here ...
defdelegate formatted_username(user), to: SharedView
# All the normal functions go here ...
end
"""
]
def run(%SourceFile{} = source_file, params) do
issue_meta = IssueMeta.for(source_file, params)
{_, attributes_after_first_function} =
source_file
|> extract_functions()
|> split_by_first_function()
attributes_after_first_function
|> Enum.filter(fn {attribute, _} -> attribute == :defdelegate end)
|> Enum.map(fn {_, meta} ->
format_issue(
issue_meta,
message:
"Prefer placing defdelegate functions at the top of the module, just before the normal functions.",
line_no: meta[:line]
)
end)
end
defp extract_functions(source_file) do
source_file
|> Credo.Code.prewalk(
fn
{attribute, meta, _} = ast, functions when attribute in [:def, :defp, :defdelegate] ->
{ast, [{attribute, meta} | functions]}
ast, functions ->
{ast, functions}
end,
[]
)
|> Enum.reverse()
end
defp split_by_first_function(functions) do
Enum.split_while(functions, fn {attribute, _} -> attribute not in [:def, :defp] end)
end
end