Skip to content

Commit 88ac1f6

Browse files
author
José Valim
committed
Improve coverage for lexical scope on try and case
1 parent 5b2f44d commit 88ac1f6

File tree

5 files changed

+109
-83
lines changed

5 files changed

+109
-83
lines changed

lib/elixir/src/elixir_macros.erl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-module(elixir_macros).
44
-export([translate/2]).
55
-import(elixir_translator, [translate_each/2, translate_args/2, translate_apply/7]).
6-
-import(elixir_scope, [umergec/2]).
6+
-import(elixir_scope, [umergec/2, umergea/2]).
77
-import(elixir_errors, [syntax_error/3, syntax_error/4,
88
assert_no_function_scope/3, assert_module_scope/3, assert_no_match_or_guard_scope/3]).
99

@@ -154,13 +154,13 @@ translate({'try', Meta, [Clauses]}, S) ->
154154
{ TDo, SB } = elixir_translator:translate_each(Do, S#elixir_scope{noname=true}),
155155

156156
Catch = [Tuple || { X, _ } = Tuple <- Clauses, X == 'rescue' orelse X == 'catch'],
157-
{ TCatch, SC } = elixir_try:clauses(Meta, Catch, umergec(S, SB)),
157+
{ TCatch, SC } = elixir_try:clauses(Meta, Catch, umergea(S, SB)),
158158

159159
After = proplists:get_value('after', Clauses, nil),
160-
{ TAfter, SA } = elixir_translator:translate_each(After, umergec(S, SC)),
160+
{ TAfter, SA } = elixir_translator:translate_each(After, umergea(S, SC)),
161161

162162
Else = elixir_clauses:get_pairs(Meta, else, Clauses, S),
163-
{ TElse, SE } = elixir_clauses:match(Meta, Else, umergec(S, SA)),
163+
{ TElse, SE } = elixir_clauses:match(Meta, Else, umergea(S, SA)),
164164

165165
SF = (umergec(S, SE))#elixir_scope{noname=S#elixir_scope.noname},
166166
{ { 'try', ?line(Meta), pack(TDo), TElse, TCatch, pack(TAfter) }, SF };

lib/elixir/src/elixir_scope.erl

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
serialize/1, deserialize/1,
66
serialize_with_vars/2, deserialize_with_vars/2,
77
to_erl_env/1, to_ex_env/1,
8-
umergev/2, umergec/2, merge_clause_vars/2
8+
umergev/2, umergec/2, umergea/2, merge_clause_vars/2
99
]).
1010
-include("elixir.hrl").
1111

@@ -140,8 +140,8 @@ deserialize_with_vars({ File, Functions, Requires, Macros,
140140
counter=[{'',length(Vars)}]
141141
}.
142142

143-
% Receives two scopes and return a new scope based on the second
144-
% with their variables merged.
143+
%% Receives two scopes and return a new scope based on the second
144+
%% with their variables merged.
145145

146146
umergev(S1, S2) ->
147147
V1 = S1#elixir_scope.vars,
@@ -153,11 +153,22 @@ umergev(S1, S2) ->
153153
clause_vars=merge_clause_vars(C1, C2)
154154
}.
155155

156-
% Receives two scopes and return the later scope
157-
% keeping the variables from the first (counters,
158-
% imports and everything else are passed forward).
156+
%% Receives two scopes and return the first scope with
157+
%% counters and flags from the later.
159158

160159
umergec(S1, S2) ->
160+
S1#elixir_scope{
161+
counter=S2#elixir_scope.counter,
162+
extra_guards=S2#elixir_scope.extra_guards,
163+
super=S2#elixir_scope.super,
164+
caller=S2#elixir_scope.caller
165+
}.
166+
167+
%% Receives two scopes and return the later scope
168+
%% keeping the variables from the first (counters,
169+
%% imports and everything else are passed forward).
170+
171+
umergea(S1, S2) ->
161172
S2#elixir_scope{
162173
vars=S1#elixir_scope.vars,
163174
temp_vars=S1#elixir_scope.temp_vars,

lib/elixir/src/elixir_tracker.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ format_error({import_conflict,{Receivers, Name, Arity}}) ->
122122
io_lib:format("imported ~ts.~ts/~B conflicts with local function",
123123
[elixir_errors:inspect(hd(Receivers)), Name, Arity]);
124124

125-
format_error({ unused_import, Module }) ->
125+
format_error({unused_import, Module}) ->
126126
io_lib:format("unused import ~ts", [elixir_errors:inspect(Module)]);
127127

128128
format_error({unused_args,{Name, Arity}}) ->

lib/elixir/src/elixir_translator.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
-export([forms/4, 'forms!'/4]).
55
-export([translate/2, translate_each/2, translate_arg/2,
66
translate_args/2, translate_apply/7, translate_fn/3]).
7-
-import(elixir_scope, [umergev/2, umergec/2]).
7+
-import(elixir_scope, [umergev/2, umergec/2, umergea/2]).
88
-import(elixir_errors, [syntax_error/3, syntax_error/4, parse_error/4,
99
assert_function_scope/3, assert_module_scope/3, assert_no_guard_scope/3,
1010
assert_no_match_or_guard_scope/3]).
@@ -607,14 +607,14 @@ translate_local(Meta, Name, Args, S) ->
607607

608608
translate_arg(Arg, { Acc, S }) ->
609609
{ TArg, TAcc } = translate_each(Arg, Acc),
610-
{ TArg, { umergec(Acc, TAcc), umergev(S, TAcc) } }.
610+
{ TArg, { umergea(Acc, TAcc), umergev(S, TAcc) } }.
611611

612612
translate_args(Args, #elixir_scope{context=match} = S) ->
613613
translate(Args, S);
614614

615615
translate_args(Args, S) ->
616616
{ TArgs, { SC, SV } } = lists:mapfoldl(fun translate_arg/2, {S, S}, Args),
617-
{ TArgs, umergec(SV, SC) }.
617+
{ TArgs, umergea(SV, SC) }.
618618

619619
%% Translate apply
620620
%% Used by both apply and external function invocation macros.
Lines changed: 84 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,149 @@
11
Code.require_file "../test_helper.exs", __DIR__
22

3-
defmodule Kernel.ImportAvailable do
4-
defmacro flatten do
5-
[flatten: 1]
6-
end
7-
end
8-
9-
defmodule Kernel.ImportOnlyTest do
3+
defmodule Kernel.ImportTest do
104
use ExUnit.Case, async: true
115

12-
test :import_with_only do
13-
require Kernel.ImportAvailable
14-
import :lists, only: Kernel.ImportAvailable.flatten
15-
assert flatten([1, [2], 3]) == [1, 2, 3]
6+
defmodule ImportAvailable do
7+
defmacro flatten do
8+
[flatten: 1]
9+
end
1610
end
1711

18-
test :import_all do
12+
13+
test "import all" do
1914
import :lists
2015
assert flatten([1, [2], 3]) == [1, 2, 3]
2116
end
2217

23-
test :import_except_none do
18+
test "import except none" do
2419
import :lists, except: []
2520
assert flatten([1, [2], 3]) == [1, 2, 3]
2621
end
2722

28-
test :import_with_except_erlang do
23+
test "import except one" do
2924
import :lists, except: [each: 2]
3025
assert flatten([1, [2], 3]) == [1, 2, 3]
3126
end
3227

28+
test "import only via macro" do
29+
require ImportAvailable
30+
import :lists, only: ImportAvailable.flatten
31+
assert flatten([1, [2], 3]) == [1, 2, 3]
32+
end
33+
3334
defmacrop dynamic_opts do
34-
[except: [each: 2]]
35+
[only: [flatten: 1]]
3536
end
3637

37-
test :import_with_dynamic_opts do
38+
test "import with options via macro" do
3839
import :lists, dynamic_opts
3940
assert flatten([1, [2], 3]) == [1, 2, 3]
4041
end
41-
end
42-
43-
defmodule Kernel.DoubleImportTest do
44-
use ExUnit.Case, async: true
4542

46-
test :import_double_except do
47-
import :lists, except: [flatten: 1]
43+
test "import with double except" do
44+
import :lists, except: [duplicate: 2]
4845
import :lists, except: [each: 2]
4946
assert append([1], [2, 3]) == [1, 2, 3]
50-
assert flatten([1, [2], 3]) == [1, [2], 3]
47+
# Buggy local duplicate is untouched
48+
assert duplicate([1], 2) == [1]
5149
end
5250

53-
def flatten(list), do: list
54-
end
55-
56-
defmodule Kernel.MessedBitwise do
57-
defmacro bnot(x), do: x
58-
defmacro bor(x, _), do: x
59-
end
60-
61-
defmodule Kernel.Underscored do
62-
def hello(x), do: x
63-
def __underscore__(x), do: x
64-
end
65-
66-
defmodule Kernel.ExplicitUnderscored do
67-
def __underscore__(x), do: x * 2
68-
end
51+
defmodule Underscored do
52+
def hello(x), do: x
53+
def __underscore__(x), do: x
54+
end
6955

70-
defmodule Kernel.ImportUnderscoreTest do
71-
use ExUnit.Case, async: true
56+
defmodule ExplicitUnderscored do
57+
def __underscore__(x), do: x * 2
58+
end
7259

73-
test :includes_only_underscore do
74-
import Kernel.Underscored, only: [__underscore__: 1]
60+
test "import only with underscore" do
61+
import Underscored, only: [__underscore__: 1]
7562
assert __underscore__(3) == 3
7663
end
7764

78-
import :all, Kernel.ExplicitUnderscored
79-
80-
test :does_not_include_underscored do
81-
import Kernel.Underscored
65+
test "import all includes underscores" do
66+
import :all, ExplicitUnderscored
67+
import Underscored
8268
assert __underscore__(2) == 4
8369
end
8470

85-
test :includes_remaining do
86-
import Kernel.Underscored
71+
test "import non underscored" do
72+
import :all, ExplicitUnderscored
73+
import Underscored
8774
assert hello(2) == 2
8875
end
89-
end
9076

91-
defmodule Kernel.ImportMacrosTest do
92-
use ExUnit.Case, async: true
77+
defmodule MessedBitwise do
78+
defmacro bnot(x), do: x
79+
defmacro bor(x, _), do: x
80+
end
9381

9482
import :macros, Bitwise
9583

96-
test :import_true do
97-
assert band(1, 1) == 1
98-
assert bor(0, 1) == 1
99-
assert bnot(0) == -1
100-
end
101-
102-
test :function_import_with_only do
84+
test "conflicing imports with only and except" do
10385
import :macros, Bitwise, except: [bnot: 1]
104-
import :macros, Kernel.MessedBitwise, only: [bnot: 1]
86+
import :macros, MessedBitwise, only: [bnot: 1]
10587
assert bnot(0) == 0
10688
assert bor(0, 1) == 1
10789
end
10890

109-
# This test is asserting that the requires done
110-
# inside the function do not affect outer ones.
111-
test :import_true_not_affected do
91+
# This test is asserting that the imports in the
92+
# test above do not affect this test.
93+
test "imports from other functions do not leak" do
11294
assert band(1, 1) == 1
11395
assert bor(0, 1) == 1
11496
assert bnot(0) == -1
11597
end
116-
end
11798

118-
defmodule Kernel.MultipleImportTest do
119-
use ExUnit.Case, async: true
120-
121-
test :import_ambiguous do
99+
test "import ambiguous" do
122100
# Simply make sure that we can indeed import functions with
123101
# the same name and arity from different modules without the
124102
# import itself causing any errors.
125103
import List
126104
import String
127105
end
128106

129-
test :import_many do
107+
test "import many" do
130108
[import(List), import(String)]
131109
assert capitalize("foo") == "Foo"
132110
assert flatten([1, [2], 3]) == [1, 2, 3]
133111
end
112+
113+
test "import lexical on if" do
114+
if false do
115+
import :lists
116+
flatten([1, [2], 3])
117+
flunk
118+
else
119+
# Buggy local duplicate is untouched
120+
assert duplicate([1], 2) == [1]
121+
end
122+
end
123+
124+
test "import lexical on case" do
125+
case true do
126+
false ->
127+
import :lists
128+
flatten([1, [2], 3])
129+
flunk
130+
true ->
131+
# Buggy local duplicate is untouched
132+
assert duplicate([1], 2) == [1]
133+
end
134+
end
135+
136+
test "import lexical on try" do
137+
try do
138+
import :lists
139+
flatten([1, [2], 3])
140+
catch
141+
_ -> flatten([:a, [:b], :c])
142+
end
143+
144+
# Buggy local duplicate is untouched
145+
assert duplicate([1], 2) == [1]
146+
end
147+
148+
defp duplicate(list, _), do: list
134149
end

0 commit comments

Comments
 (0)