CredoNoUnless

A Credo check that flags every use of unless.

Prefer usage of if with a negated condition.

Co-mingling unless and if is hard to read. The implicit negation of conditionals can be confusing when reading code, especially when if and unless are used in close proximity.

if connected?(socket), do: push(socket, msg)
unless queue_empty?(state), do: drain(state)
if retries_left?(state), do: retry(state)

Written with if throughout, the sequence reads in one direction:

if connected?(socket), do: push(socket, msg)
if !queue_empty?(state), do: drain(state)
if retries_left?(state), do: retry(state)

Installation

Add credo_no_unless to your deps in mix.exs, alongside credo itself:

def deps do
[
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:credo_no_unless, "~> 0.1", only: [:dev, :test], runtime: false}
]
end

Usage

Enable the check in your .credo.exs:

%{
configs: [
%{
name: "default",
files: %{included: ["lib/"]},
checks: %{
enabled: [
{CredoNoUnless.Check.Readability.NoUnless, []}
]
}
}
]
}

Then run mix credo as usual.

What it catches

All three spellings of unless are flagged:

# block form
unless Enum.empty?(list) do
process(list)
end
# keyword form
unless valid?(changeset), do: reject(changeset)
# fully qualified
Kernel.unless(message == "", do: IO.puts(message))

Each is reported as a readability issue pointing at the unless keyword. Rewrite it as an if with the condition negated:

if !Enum.empty?(list) do
process(list)
end

License

MIT. See LICENSE.