From 9dc73645bed20c43cfb5def1233012f00e1a7f11 Mon Sep 17 00:00:00 2001 From: Matt Enlow Date: Thu, 7 Nov 2024 12:34:17 -0700 Subject: [PATCH] handle more false positive cases --- lib/style/pipes.ex | 42 +++++++++++-- test/style/pipes_test.exs | 122 ++++++++++++++++++++------------------ 2 files changed, 101 insertions(+), 63 deletions(-) diff --git a/lib/style/pipes.ex b/lib/style/pipes.ex index dffc18e9..76a97b44 100644 --- a/lib/style/pipes.ex +++ b/lib/style/pipes.ex @@ -131,12 +131,44 @@ defmodule Styler.Style.Pipes do end # a(b |> c[, ...args]) - # maybe pipeify it + # The first argument to a function-looking node is a pipe. + # Maybe pipe the whole thing? def run({{f, m, [{:|>, _, _} = pipe | args]}, _} = zipper, ctx) do - if Enum.any?(args, &Style.do_block?/1) do - {:cont, zipper, ctx} - else - {:cont, Zipper.replace(zipper, {:|>, m, [pipe, {f, m, args}]}), ctx} + parent = + case Zipper.up(zipper) do + {{parent, _, _}, _} -> parent + _ -> nil + end + + stringified = is_atom(f) && to_string(f) + + cond do + # this is likely a macro + # assert a |> b() |> c() + !m[:closing] -> + {:cont, zipper, ctx} + + # leave bools alone as they often read better coming first, like when prepended with `not` + # [not ]is_nil(a |> b() |> c()) + stringified && (String.starts_with?(stringified, "is_") or String.ends_with?(stringified, "?")) -> + {:cont, zipper, ctx} + + # string interpolation, module attribute assignment, or prettier bools with not + parent in [:"::", :@, :not] -> + {:cont, zipper, ctx} + + # double down on being good to exunit macros, and any other special ops + # ..., do: assert(a |> b |> c) + # not (a |> b() |> c()) + f in [:assert, :refute | @special_ops] -> + {:cont, zipper, ctx} + + # if a |> b() |> c(), do: ... + Enum.any?(args, &Style.do_block?/1) -> + {:cont, zipper, ctx} + + true -> + {:cont, Zipper.replace(zipper, {:|>, m, [pipe, {f, m, args}]}), ctx} end end diff --git a/test/style/pipes_test.exs b/test/style/pipes_test.exs index 77d41c4e..069e23ec 100644 --- a/test/style/pipes_test.exs +++ b/test/style/pipes_test.exs @@ -90,7 +90,7 @@ defmodule Styler.Style.PipesTest do y end - a(foo(if_result), b) + if_result |> foo() |> a(b) """ ) end @@ -853,69 +853,69 @@ defmodule Styler.Style.PipesTest do test "optimizing" do assert_style( - """ - a - |> Enum.map(fn b -> - c - # a comment - d - end) - |> Enum.join(x) - |> Enum.each(...) - """, - """ - a - |> Enum.map_join(x, fn b -> - c - # a comment - d - end) - |> Enum.each(...) - """ + """ + a + |> Enum.map(fn b -> + c + # a comment + d + end) + |> Enum.join(x) + |> Enum.each(...) + """, + """ + a + |> Enum.map_join(x, fn b -> + c + # a comment + d + end) + |> Enum.each(...) + """ ) assert_style( - """ - a - |> Enum.map(fn b -> - c - # a comment - d - end) - |> Enum.into(x) - |> Enum.each(...) - """, - """ - a - |> Enum.into(x, fn b -> - c - # a comment - d - end) - |> Enum.each(...) - """ + """ + a + |> Enum.map(fn b -> + c + # a comment + d + end) + |> Enum.into(x) + |> Enum.each(...) + """, + """ + a + |> Enum.into(x, fn b -> + c + # a comment + d + end) + |> Enum.each(...) + """ ) assert_style( - """ - a - |> Enum.map(fn b -> - c - # a comment - d - end) - |> Keyword.new() - |> Enum.each(...) - """, - """ - a - |> Keyword.new(fn b -> - c - # a comment - d - end) - |> Enum.each(...) - """ + """ + a + |> Enum.map(fn b -> + c + # a comment + d + end) + |> Keyword.new() + |> Enum.each(...) + """, + """ + a + |> Keyword.new(fn b -> + c + # a comment + d + end) + |> Enum.each(...) + """ ) end end @@ -923,9 +923,15 @@ defmodule Styler.Style.PipesTest do describe "pipifying" do test "no false positives" do pipe = "a() |> b() |> c()" + assert_style pipe + assert_style String.replace(pipe, " |>", "\n|>") assert_style "fn -> #{pipe} end" assert_style "if #{pipe}, do: ..." assert_style "x\n\n#{pipe}" + assert_style "@moduledoc #{pipe}" + assert_style "!(#{pipe})" + assert_style "not foo(#{pipe})" + assert_style ~s<"\#{#{pipe}}"> end test "pipifying" do