Skip to content

Commit bf1d993

Browse files
author
José Valim
committed
Ensure proper precedence between & and operators, closes #7412
Signed-off-by: José Valim <[email protected]>
1 parent 755639f commit bf1d993

File tree

4 files changed

+58
-8
lines changed

4 files changed

+58
-8
lines changed

lib/elixir/lib/code/formatter.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule Code.Formatter do
1010
@min_line 0
1111
@max_line 9_999_999
1212
@empty empty()
13+
@ampersand_prec Code.Identifier.unary_op(:&) |> elem(1)
1314

1415
# Operators that do not have space between operands
1516
@no_space_binary_operators [:..]
@@ -788,10 +789,11 @@ defmodule Code.Formatter do
788789
end
789790

790791
defp binary_operand_to_algebra(operand, context, state, parent_op, parent_info, side, nesting) do
792+
{parent_assoc, parent_prec} = parent_info
793+
791794
with {op, meta, [left, right]} <- operand,
792795
op_info = Code.Identifier.binary_op(op),
793796
{_assoc, prec} <- op_info do
794-
{parent_assoc, parent_prec} = parent_info
795797
op_string = Atom.to_string(op)
796798

797799
cond do
@@ -817,7 +819,9 @@ defmodule Code.Formatter do
817819
binary_op_to_algebra(op, op_string, meta, left, right, context, state, 2)
818820
end
819821
else
820-
{:&, _, [arg]} when not is_integer(arg) and side == :left ->
822+
{:&, _, [arg]}
823+
when not is_integer(arg) and side == :left
824+
when not is_integer(arg) and parent_assoc == :left and parent_prec > @ampersand_prec ->
821825
{doc, state} = quoted_to_algebra(operand, context, state)
822826
{wrap_in_parens(doc), state}
823827

lib/elixir/test/elixir/code_formatter/integration_test.exs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,50 @@ defmodule Code.Formatter.IntegrationTest do
453453
| :other
454454
"""
455455
end
456+
457+
test "capture with operators" do
458+
assert_same """
459+
"this works" |> (&String.upcase/1) |> (&String.downcase/1)
460+
"""
461+
462+
assert_same """
463+
"this works" || (&String.upcase/1) || (&String.downcase/1)
464+
"""
465+
466+
assert_same """
467+
"this works" == (&String.upcase/1) == (&String.downcase/1)
468+
"""
469+
470+
bad = """
471+
"this works" = (&String.upcase/1) = (&String.downcase/1)
472+
"""
473+
474+
assert_format bad, """
475+
"this works" = (&String.upcase/1) = &String.downcase/1
476+
"""
477+
478+
bad = """
479+
"this works" ++ (&String.upcase/1) ++ (&String.downcase/1)
480+
"""
481+
482+
assert_format bad, """
483+
"this works" ++ (&String.upcase/1) ++ &String.downcase/1
484+
"""
485+
486+
bad = """
487+
"this works" | (&String.upcase/1) | (&String.downcase/1)
488+
"""
489+
490+
assert_format bad, """
491+
"this works" | (&String.upcase/1) | &String.downcase/1
492+
"""
493+
494+
bad = ~S"""
495+
"this works" \\ (&String.upcase/1) \\ (&String.downcase/1)
496+
"""
497+
498+
assert_format bad, ~S"""
499+
"this works" \\ &String.upcase/1 \\ &String.downcase/1
500+
"""
501+
end
456502
end

lib/elixir/test/elixir/code_formatter/operators_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -812,9 +812,9 @@ defmodule Code.Formatter.OperatorsTest do
812812
end
813813

814814
test "with operators outside" do
815-
assert_same "(& &1) == & &2"
816-
assert_same "(& &1) and & &2"
817-
assert_same "(&foo/1) and &bar/1"
815+
assert_same "(& &1) == (& &2)"
816+
assert_same "(& &1) and (& &2)"
817+
assert_same "(&foo/1) and (&bar/1)"
818818
assert_same "[(&IO.puts/1) | &IO.puts/2]"
819819
end
820820

lib/elixir/test/elixir/kernel/fn_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ defmodule Kernel.FnTest do
5858
assert (&List.flatten/1).([[0]]) == [0]
5959
assert (&List.flatten/1).([[0]]) == [0]
6060
assert (&List.flatten(&1)).([[0]]) == [0]
61-
assert (&List.flatten(&1)) == &List.flatten/1
61+
assert (&List.flatten(&1)) == (&List.flatten/1)
6262
end
6363

6464
test "capture local" do
@@ -77,7 +77,7 @@ defmodule Kernel.FnTest do
7777
assert (&is_atom/1).(:a)
7878
assert (&is_atom/1).(:a)
7979
assert (&is_atom(&1)).(:a)
80-
assert (&is_atom(&1)) == &is_atom/1
80+
assert (&is_atom(&1)) == (&is_atom/1)
8181
end
8282

8383
test "capture macro" do
@@ -102,7 +102,7 @@ defmodule Kernel.FnTest do
102102
mod = List
103103
assert (&mod.flatten(&1)).([1, [2], 3]) == [1, 2, 3]
104104
assert (&mod.flatten/1).([1, [2], 3]) == [1, 2, 3]
105-
assert (&mod.flatten/1) == &List.flatten/1
105+
assert (&mod.flatten/1) == (&List.flatten/1)
106106
end
107107

108108
test "local partial application" do

0 commit comments

Comments
 (0)