Skip to content

Commit 34b6e17

Browse files
author
José Valim
committed
Fix shadowing warning in case with function, closes #1524
1 parent 31818d6 commit 34b6e17

File tree

7 files changed

+47
-9
lines changed

7 files changed

+47
-9
lines changed

lib/elixir/include/elixir.hrl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
-record(elixir_scope, {
1212
context=nil, %% can be assign, guards or nil
13+
extra=nil, %% extra information about the context, like fn_match for fns
1314
noname=false, %% when true, don't add new names (used by try)
1415
super=false, %% when true, it means super was invoked
1516
caller=false, %% when true, it means caller was invoked

lib/elixir/src/elixir_errors.erl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,15 @@ handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour,Module}}) ->
141141
handle_file_warning(_, _File, {Line,erl_lint,{unused_var,_Var}}) when Line =< 0 ->
142142
[];
143143

144+
%% Ignore shadowed vars as we guarantee no conflicts ourselves
145+
handle_file_warning(_, _File, {_Line,erl_lint,{shadowed_var,_Var,_Where}}) ->
146+
[];
147+
144148
%% Properly format other unused vars
145149
handle_file_warning(_, File, {Line,erl_lint,{unused_var,Var}}) ->
146150
Message = format_error(erl_lint, { unused_var, format_var(Var) }),
147151
warn(file_format(Line, File, Message));
148152

149-
%% Properly format shadowed vars
150-
handle_file_warning(_, File, {Line,erl_lint,{shadowed_var,Var,Where}}) ->
151-
Message = format_error(erl_lint, { shadowed_var, format_var(Var), Where }),
152-
warn(file_format(Line, File, Message));
153-
154153
%% Default behavior
155154
handle_file_warning(_, File, {Line,Module,Desc}) ->
156155
Message = format_error(Module, Desc),

lib/elixir/src/elixir_fn.erl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
fn(Meta, Clauses, S) ->
88
Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) ->
99
{ Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards),
10-
elixir_clauses:assigns_block(?line(CMeta), fun elixir_translator:translate/2, Args, [Expr], Guards, umergec(S, Acc))
10+
elixir_clauses:assigns_block(?line(CMeta), fun translate_fn_match/2, Args, [Expr], Guards, umergec(S, Acc))
1111
end,
1212

1313
{ TClauses, NS } = lists:mapfoldl(Transformer, S, Clauses),
@@ -21,6 +21,10 @@ fn(Meta, Clauses, S) ->
2121
"cannot mix clauses with different arities in function definition")
2222
end.
2323

24+
translate_fn_match(Arg, S) ->
25+
{ TArg, TS } = elixir_translator:translate(Arg, S#elixir_scope{extra=fn_match}),
26+
{ TArg, TS#elixir_scope{extra=S#elixir_scope.extra} }.
27+
2428
capture(Meta, { '/', _, [{ { '.', _, [M, F] }, _ , [] }, A] }, S) when is_atom(F), is_integer(A) ->
2529
{ [MF, FF, AF], SF } = elixir_translator:translate_args([M, F, A], S),
2630
{ { 'fun', ?line(Meta), { function, MF, FF, AF } }, SF };

lib/elixir/src/elixir_macros.erl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ translate_in(Meta, Left, Right, S) ->
281281
false -> { TLeft, SR }
282282
end,
283283

284-
{ InGuard, TExpr } = case TRight of
284+
{ TCache, TExpr } = case TRight of
285285
{ nil, _ } ->
286286
Expr = { atom, Line, false },
287287
{ Cache, Expr };
@@ -322,7 +322,7 @@ translate_in(Meta, Left, Right, S) ->
322322
end
323323
end,
324324

325-
case InGuard of
325+
case TCache of
326326
true -> { Var, { block, Line, [ { match, Line, Var, TLeft }, TExpr ] }, SV };
327327
false -> { Var, TExpr, SV }
328328
end.

lib/elixir/src/elixir_translator.erl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,22 @@ translate_each({ 'super?', Meta, [] }, S) ->
346346

347347
%% Variables
348348

349+
translate_each({ '^', Meta, [ { Name, _, Kind } = Var ] },
350+
#elixir_scope{extra=fn_match, extra_guards=Extra} = S) when is_atom(Name), is_atom(Kind) ->
351+
case orddict:find({ Name, Kind }, S#elixir_scope.backup_vars) of
352+
{ ok, Value } ->
353+
Line = ?line(Meta),
354+
{ TVar, TS } = translate_each(Var, S),
355+
Guard = { op, Line, '=:=', { var, ?line(Meta), Value }, TVar },
356+
{ TVar, TS#elixir_scope{extra_guards=[Guard|Extra]} };
357+
error ->
358+
compile_error(Meta, S#elixir_scope.file, "unbound variable ^~ts", [Name])
359+
end;
360+
349361
translate_each({ '^', Meta, [ { Name, _, Kind } ] }, #elixir_scope{context=match} = S) when is_atom(Name), is_atom(Kind) ->
350362
case orddict:find({ Name, Kind }, S#elixir_scope.backup_vars) of
351363
{ ok, Value } ->
352-
{ { var, Meta, Value }, S };
364+
{ { var, ?line(Meta), Value }, S };
353365
error ->
354366
compile_error(Meta, S#elixir_scope.file, "unbound variable ^~ts", [Name])
355367
end;

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ defmodule Kernel.FnTest do
44
use ExUnit.Case, async: true
55
import CompileAssertion
66

7+
test "clause with ^" do
8+
x = 1
9+
assert (fn ^x -> :ok; _ -> :error end).(1) == :ok
10+
end
11+
712
test "capture remote" do
813
assert (&:erlang.atom_to_list/1).(:a) == 'a'
914
assert (&Kernel.atom_to_list/1).(:a) == 'a'

lib/elixir/test/elixir/kernel/warning_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@ defmodule Kernel.WarningTest do
7878
purge Sample
7979
end
8080

81+
test :shadowing do
82+
assert capture_err(fn ->
83+
Code.eval_string """
84+
defmodule Sample do
85+
def test(x) do
86+
case x do
87+
{:file, fid} -> fid
88+
{:path, _} -> fn(fid) -> fid end
89+
end
90+
end
91+
end
92+
"""
93+
end) == nil
94+
after
95+
purge Sample
96+
end
97+
8198
test :unused_default_args do
8299
assert capture_err(fn ->
83100
Code.eval_string """

0 commit comments

Comments
 (0)