Skip to content

Commit e0c004f

Browse files
committed
Merge pull request #3759 from lexmag/duplicated-clauses
Raise `CompileError` in case of duplicated clauses given for `try` and `receive`
2 parents 34c37ab + ac9a9ab commit e0c004f

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

lib/elixir/src/elixir_exp_clauses.erl

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ guard(Other, E) ->
4747
'case'(Meta, KV, E) when not is_list(KV) ->
4848
compile_error(Meta, ?m(E, file), "invalid arguments for case");
4949
'case'(Meta, KV, E) ->
50+
ok = assert_at_most_once('do', KV, 0, fun(Kind) ->
51+
compile_error(Meta, ?m(E, file), "duplicated ~ts clauses given for case", [Kind])
52+
end),
5053
EE = E#{export_vars := []},
5154
{EClauses, EVars} = lists:mapfoldl(fun(X, Acc) -> do_case(Meta, X, Acc, EE) end, [], KV),
5255
{EClauses, elixir_env:mergev(EVars, E)}.
@@ -64,6 +67,9 @@ do_case(Meta, {Key, _}, _Acc, E) ->
6467
'cond'(Meta, KV, E) when not is_list(KV) ->
6568
compile_error(Meta, ?m(E, file), "invalid arguments for cond");
6669
'cond'(Meta, KV, E) ->
70+
ok = assert_at_most_once('do', KV, 0, fun(Kind) ->
71+
compile_error(Meta, ?m(E, file), "duplicated ~ts clauses given for cond", [Kind])
72+
end),
6773
EE = E#{export_vars := []},
6874
{EClauses, EVars} = lists:mapfoldl(fun(X, Acc) -> do_cond(Meta, X, Acc, EE) end, [], KV),
6975
{EClauses, elixir_env:mergev(EVars, E)}.
@@ -81,6 +87,11 @@ do_cond(Meta, {Key, _}, _Acc, E) ->
8187
'receive'(Meta, KV, E) when not is_list(KV) ->
8288
compile_error(Meta, ?m(E, file), "invalid arguments for receive");
8389
'receive'(Meta, KV, E) ->
90+
RaiseError = fun(Kind) ->
91+
compile_error(Meta, ?m(E, file), "duplicated ~ts clauses given for receive", [Kind])
92+
end,
93+
ok = assert_at_most_once('do', KV, 0, RaiseError),
94+
ok = assert_at_most_once('after', KV, 0, RaiseError),
8495
EE = E#{export_vars := []},
8596
{EClauses, EVars} = lists:mapfoldl(fun(X, Acc) -> do_receive(Meta, X, Acc, EE) end, [], KV),
8697
{EClauses, elixir_env:mergev(EVars, E)}.
@@ -105,8 +116,16 @@ do_receive(Meta, {Key, _}, _Acc, E) ->
105116
'try'(Meta, [{do, _}], E) ->
106117
compile_error(Meta, ?m(E, file), "missing catch/rescue/after/else keyword in try");
107118
'try'(Meta, KV, E) when not is_list(KV) ->
108-
elixir_errors:compile_error(Meta, ?m(E, file), "invalid arguments for try");
119+
compile_error(Meta, ?m(E, file), "invalid arguments for try");
109120
'try'(Meta, KV, E) ->
121+
RaiseError = fun(Kind) ->
122+
compile_error(Meta, ?m(E, file), "duplicated ~ts clauses given for try", [Kind])
123+
end,
124+
ok = assert_at_most_once('do', KV, 0, RaiseError),
125+
ok = assert_at_most_once('rescue', KV, 0, RaiseError),
126+
ok = assert_at_most_once('catch', KV, 0, RaiseError),
127+
ok = assert_at_most_once('else', KV, 0, RaiseError),
128+
ok = assert_at_most_once('after', KV, 0, RaiseError),
110129
{lists:map(fun(X) -> do_try(Meta, X, E) end, KV), E}.
111130

112131
do_try(_Meta, {'do', Expr}, E) ->
@@ -214,3 +233,11 @@ expand_without_export(Meta, Kind, Fun, {Key, Clauses}, E) when is_list(Clauses)
214233
{Key, lists:map(Transformer, Clauses)};
215234
expand_without_export(Meta, Kind, _Fun, {Key, _}, E) ->
216235
compile_error(Meta, ?m(E, file), "expected -> clauses for ~ts in ~ts", [Key, Kind]).
236+
237+
assert_at_most_once(_Kind, [], _Count, _Fun) -> ok;
238+
assert_at_most_once(Kind, [{Kind, _} | _], 1, ErrorFun) ->
239+
ErrorFun(Kind);
240+
assert_at_most_once(Kind, [{Kind, _} | Rest], Count, Fun) ->
241+
assert_at_most_once(Kind, Rest, Count + 1, Fun);
242+
assert_at_most_once(Kind, [_ | Rest], Count, Fun) ->
243+
assert_at_most_once(Kind, Rest, Count, Fun).

lib/elixir/test/elixir/kernel/expansion_test.exs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,12 @@ defmodule Kernel.ExpansionTest do
326326
quote do: (cond do 1 -> x = 1; 2 -> y = 2 end; :erlang.+(x, y))
327327
end
328328

329+
test "cond: expects at most one do" do
330+
assert_raise CompileError, ~r"duplicated do clauses given for cond", fn ->
331+
expand(quote(do: (cond do: (x -> x), do: (y -> y))))
332+
end
333+
end
334+
329335
## Case
330336

331337
test "case: expands each clause" do
@@ -353,6 +359,12 @@ defmodule Kernel.ExpansionTest do
353359
quote do: (case w() do x -> x = x; y -> y = y end; :erlang.+(x, y))
354360
end
355361

362+
test "case: expects at most one do" do
363+
assert_raise CompileError, ~r"duplicated do clauses given for case", fn ->
364+
expand(quote(do: (case e, do: (x -> x), do: (y -> y))))
365+
end
366+
end
367+
356368
## Receive
357369

358370
test "receive: expands each clause" do
@@ -385,6 +397,16 @@ defmodule Kernel.ExpansionTest do
385397
quote do: (receive do x -> x = x after y() -> y(); w = y() end; :erlang.+(x, w))
386398
end
387399

400+
test "receive: expects at most one clause" do
401+
assert_raise CompileError, ~r"duplicated do clauses given for receive", fn ->
402+
expand(quote(do: (receive do: (x -> x), do: (y -> y))))
403+
end
404+
405+
assert_raise CompileError, ~r"duplicated after clauses given for receive", fn ->
406+
expand(quote(do: (receive do x -> x after y -> y after z -> z end)))
407+
end
408+
end
409+
388410
## Try
389411

390412
test "try: expands catch" do
@@ -413,6 +435,28 @@ defmodule Kernel.ExpansionTest do
413435
end
414436
end
415437

438+
test "try: expects at most one clause" do
439+
assert_raise CompileError, ~r"duplicated do clauses given for try", fn ->
440+
expand(quote(do: (try do: e, do: f)))
441+
end
442+
443+
assert_raise CompileError, ~r"duplicated rescue clauses given for try", fn ->
444+
expand(quote(do: (try do e rescue x -> x rescue y -> y end)))
445+
end
446+
447+
assert_raise CompileError, ~r"duplicated after clauses given for try", fn ->
448+
expand(quote(do: (try do e after x = y after x = y end)))
449+
end
450+
451+
assert_raise CompileError, ~r"duplicated else clauses given for try", fn ->
452+
expand(quote(do: (try do e else x -> x else y -> y end)))
453+
end
454+
455+
assert_raise CompileError, ~r"duplicated catch clauses given for try", fn ->
456+
expand(quote(do: (try do e catch x -> x catch y -> y end)))
457+
end
458+
end
459+
416460
## Binaries
417461

418462
test "bitstrings: size * unit" do

0 commit comments

Comments
 (0)