Skip to content

Commit da7e830

Browse files
committed
Disallow &-partial calls on the right side of |>
1 parent c78d3b3 commit da7e830

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2950,7 +2950,7 @@ defmodule Kernel do
29502950
`|>` is called the pipeline operator as it is useful
29512951
to write pipeline style expressions. This operator
29522952
introduces the expression on the left as the first
2953-
argument to the expression on the right.
2953+
argument to the function call on the right.
29542954
29552955
## Examples
29562956
@@ -2961,8 +2961,8 @@ defmodule Kernel do
29612961
29622962
Enum.map(List.flatten([1,[2],3]), &1 * 2)
29632963
2964-
Please be aware of operator precendence, when using
2965-
this operator. For example, the following expression:
2964+
Be aware of operator precendence when using this operator.
2965+
For example, the following expression:
29662966
29672967
String.graphemes "Hello" |> Enum.reverse
29682968
@@ -2972,7 +2972,7 @@ defmodule Kernel do
29722972
29732973
Which will result in an error as Enumerable protocol
29742974
is not defined for binaries. Adding explicit parenthesis
2975-
is recommended:
2975+
resolves the ambiguity:
29762976
29772977
String.graphemes("Hello") |> Enum.reverse
29782978
@@ -2989,7 +2989,11 @@ defmodule Kernel do
29892989
{ call, line, [left] }
29902990
end
29912991

2992-
defp pipeline_op(left, { call, line, args }) when is_list(args) do
2992+
defp pipeline_op(left, { call, line, args }=right) when is_list(args) do
2993+
case validate_pipeline_args(args) do
2994+
:error -> pipeline_error(right)
2995+
_ -> nil
2996+
end
29932997
{ call, line, [left|args] }
29942998
end
29952999

@@ -2998,7 +3002,17 @@ defmodule Kernel do
29983002
end
29993003

30003004
defp pipeline_op(_, other) do
3001-
raise ArgumentError, message: "Unsupported expression in pipeline |> operator: #{inspect other}"
3005+
pipeline_error(other)
3006+
end
3007+
3008+
defp validate_pipeline_args([]), do: nil
3009+
defp validate_pipeline_args([ {:&,_,_ } | _ ]), do: :error
3010+
defp validate_pipeline_args([_|t]) do
3011+
validate_pipeline_args(t)
3012+
end
3013+
3014+
defp pipeline_error(arg) do
3015+
raise ArgumentError, message: "Unsupported expression in pipeline |> operator: #{Macro.to_binary arg}"
30023016
end
30033017

30043018
@doc """

lib/elixir/test/elixir/kernel_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,27 @@ defmodule KernelTest do
227227
assert __MODULE__ |> :constant == 13
228228
end
229229

230+
test "non-call" do
231+
assert 1 |> (&1*2).() == 2
232+
assert [1] |> hd(&1).() == 1
233+
234+
# FIXME: this mustn't work, but it doesn't call pipeline_op at all
235+
#assert "Unsupported expression in pipeline |> operator: &1" = format_rescue("1 |> &1")
236+
assert "Unsupported expression in pipeline |> operator: &1 * 2" = format_rescue("1 |> &1*2")
237+
assert "Unsupported expression in pipeline |> operator: hd(&1)" = format_rescue("[1] |> hd(&1)")
238+
end
239+
240+
defp format_rescue(expr) do
241+
result = try do
242+
:elixir.eval(to_char_list(expr), [])
243+
nil
244+
rescue
245+
error -> error.message
246+
end
247+
248+
result || raise(ExUnit.AssertionError, message: "Expected function given to format_rescue to fail")
249+
end
250+
230251
def constant, do: 13
231252

232253
defp twice(a), do: a * 2

0 commit comments

Comments
 (0)