Current section
Files
Jump to
Current section
Files
lib/pattern/no_anon_fn_application_in_pipe.ex
defmodule Credence.Pattern.NoAnonFnApplicationInPipe do
@moduledoc """
Readability rule: Detects anonymous functions applied with `.()` inside
a pipeline.
Applying an anonymous function directly in a pipe (e.g.
`|> (fn x -> ... end).()`) is non-idiomatic and hard to read.
Use `then/2` instead, which was added in Elixir 1.12 specifically
for this purpose.
## Bad
list
|> Enum.sort()
|> (fn s -> [1 | s] end).()
## Good
list
|> Enum.sort()
|> then(fn s -> [1 | s] end)
# Or with capture syntax:
|> then(&[1 | &1])
"""
use Credence.Pattern.Rule
alias Credence.Issue
@impl true
def check(ast, _opts) do
{_ast, issues} =
Macro.prewalk(ast, [], fn
# Match: ... |> (fn ... end).()
{:|>, meta, [_left, {{:., _, [{:fn, _, _}]}, _, []}]} = node, issues ->
issue = %Issue{
rule: :no_anon_fn_application_in_pipe,
message:
"Avoid applying anonymous functions with `.()` inside a pipeline. " <>
"Use `then/2` instead: `|> then(fn x -> ... end)`.",
meta: %{line: Keyword.get(meta, :line)}
}
{node, [issue | issues]}
node, issues ->
{node, issues}
end)
Enum.reverse(issues)
end
@impl true
def fix_patches(ast, _opts) do
Credence.RuleHelpers.patches_from_postwalk(ast, fn
# |> (fn ... end).() → |> then(fn ... end)
{:|>, pipe_meta, [left, {{:., _, [{:fn, _, _} = fn_node]}, _, []}]} ->
{:|>, pipe_meta, [left, {:then, [], [fn_node]}]}
node ->
node
end)
end
end