Skip to content

Commit 2b5e69a

Browse files
committed
Make sure with/1 does not leak variables to else (#4804)
Conflicts: lib/elixir/src/elixir_scope.erl
1 parent a56606d commit 2b5e69a

File tree

3 files changed

+33
-21
lines changed

3 files changed

+33
-21
lines changed

lib/elixir/src/elixir_scope.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ format_error({unused_match, Name, Kind}) ->
212212

213213
format_error({unsafe_var, Name}) ->
214214
io_lib:format("the variable \"~ts\" is unsafe as it has been set inside "
215-
"a case/cond/receive/if/&&/||. Please explicitly return the "
216-
"variable value instead. For example:\n\n"
215+
"a case/cond/receive/if/&&/||. Please explicitly return "
216+
"the variable value instead. For example:\n\n"
217217
" case int do\n"
218218
" 1 -> atom = :one\n"
219219
" 2 -> atom = :two\n"

lib/elixir/src/elixir_with.erl

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,23 @@ expand_else(nil, E) ->
6565

6666
translate(Meta, Args, S) ->
6767
{Parts, [{do, Expr} | ExprList]} = elixir_utils:split_last(Args),
68-
CaseExpr =
69-
case ExprList of
70-
[{else, ElseExpr}] ->
71-
build_else(Meta, build_case(Parts, {ok, Expr}, fun(X) -> {error, X} end), ElseExpr);
72-
[] ->
73-
build_case(Parts, Expr, fun(X) -> X end)
74-
end,
75-
{TC, TS} = elixir_translator:translate(CaseExpr, S#elixir_scope{extra=nil}),
76-
{TC, elixir_scope:mergec(S, TS)}.
68+
case ExprList of
69+
[{else, ElseExpr}] ->
70+
{TCases, TS} = translate_case(Parts, {ok, Expr}, fun(X) -> {error, X} end, S),
71+
translate_else(Meta, TCases, ElseExpr, TS);
72+
[] ->
73+
translate_case(Parts, Expr, fun(X) -> X end, S)
74+
end.
75+
76+
translate_case(Parts, DoExpr, Wrapper, S) ->
77+
Cases = build_case(Parts, DoExpr, Wrapper),
78+
{TCases, TS} = elixir_translator:translate(Cases, S#elixir_scope{extra=nil}),
79+
{TCases, elixir_scope:mergec(S, TS)}.
80+
81+
translate_else(Meta, WithCases, ElseExpr, S) ->
82+
ElseClauses = build_else(Meta, ElseExpr),
83+
{TClauses, TS} = elixir_clauses:clauses(Meta, ElseClauses, S#elixir_scope{extra=nil}),
84+
{{'case', ?ann(Meta), WithCases, TClauses}, elixir_scope:mergec(S, TS)}.
7785

7886
build_case([{'<-', Meta, [{Name, _, Ctx}, _] = Args} | Rest], DoExpr, Wrapper)
7987
when is_atom(Name) andalso is_atom(Ctx) ->
@@ -90,18 +98,15 @@ build_case([Expr | Rest], DoExpr, Wrapper) ->
9098
build_case([], DoExpr, _Wrapper) ->
9199
DoExpr.
92100

93-
build_else(Meta, WithCases, ElseClauses) ->
101+
build_else(Meta, ElseClauses) ->
94102
Result = {result, Meta, ?MODULE},
95-
Clauses = [
96-
{'->', Meta, [[{ok, Result}], Result]}
97-
| else_to_error_clause(ElseClauses)
98-
] ++ [build_raise(Meta)],
99-
{'case', Meta, [WithCases, [{do, Clauses}]]}.
103+
[{match, Meta, [{ok, Result}], Result} |
104+
each_clause_to_error_match(ElseClauses)] ++ [build_raise(Meta)].
100105

101-
else_to_error_clause(Clauses) ->
102-
[{'->', Meta, [[{error, Match}], Expr]} ||
106+
each_clause_to_error_match(Clauses) ->
107+
[{match, Meta, [{error, Match}], Expr} ||
103108
{'->', Meta, [[Match], Expr]} <- Clauses].
104109

105110
build_raise(Meta) ->
106-
Other = {raise, Meta, ?MODULE},
107-
{'->', ?generated, [[{error, Other}], {{'.', Meta, [erlang, error]}, Meta, [{with_clause, Other}]}]}.
111+
Other = {other, Meta, ?MODULE},
112+
{match, ?generated, [{error, Other}], {{'.', Meta, [erlang, error]}, Meta, [{with_clause, Other}]}}.

lib/elixir/test/elixir/kernel/with_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ defmodule Kernel.WithTest do
4747
assert result == :error
4848
end
4949

50+
test "does not leak variables to else" do
51+
state = 1
52+
result = with 1 <- state, state = 2, :ok <- error(), do: state, else: (_ -> state)
53+
assert result == 1
54+
assert state == 1
55+
end
56+
5057
test "errors in with" do
5158
assert_raise RuntimeError, fn ->
5259
with({:ok, res} <- oops(), do: res)

0 commit comments

Comments
 (0)