Packages

Credo custom checks plugin to comply with Nimble Compass conventions

Current section

Files

Jump to
compass_credo_plugin lib check do_single_expression.ex
Raw

lib/check/do_single_expression.ex

defmodule CompassCredoPlugin.Check.DoSingleExpression do
use Credo.Check,
base_priority: :normal,
category: :readability,
explanations: [
check: """
Use do: for single line if/unless statements
Prefer using the single-line function as long as mix format command satisfies to keep the function body just in one line.
# Less Preferred
if some_condition do
"some_stuff"
end
def validate_coupon() do
:ok
end
# Preferred
if some_condition, do: "some_stuff"
def validate_coupon(), do: :ok
"""
]
@matching_definition_types [:def, :defp, :if, :unless]
@min_required_body_lines 2
@impl true
def run(source_file, params) do
ast =
source_file
|> Credo.SourceFile.source()
|> Code.string_to_quoted!(token_metadata: true)
issue_meta = IssueMeta.for(source_file, params)
ast
|> Credo.Code.prewalk(&traverse(&1, &2, issue_meta))
|> Enum.reverse()
end
defp traverse({definition_type, meta, [_definition, body]} = ast, issues, issue_meta)
when definition_type in @matching_definition_types do
if contains_single_expression?(body) and contains_do_and_end?(meta) and
total_body_lines(meta) < @min_required_body_lines do
{ast, [issue_for(meta[:line], issue_meta) | issues]}
else
{ast, issues}
end
end
defp traverse(ast, issues, _issue_meta), do: {ast, issues}
defp contains_single_expression?(body) do
case body[:do] do
{:__block__, _, _} ->
false
_ ->
true
end
end
defp contains_do_and_end?(meta), do: !is_nil(meta[:do]) and !is_nil(meta[:end])
defp total_body_lines(meta), do: meta[:end][:line] - meta[:do][:line] - 1
defp issue_for(line_no, issue_meta) do
format_issue(
issue_meta,
message: "Single expression and single line in a do ... end block. Use do: instead",
line_no: line_no
)
end
end