Skip to content

Commit c82ad63

Browse files
committed
Only add if_undefined: :apply if necessary
1 parent d71de51 commit c82ad63

File tree

5 files changed

+78
-57
lines changed

5 files changed

+78
-57
lines changed

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1320,7 +1320,7 @@ defmodule Kernel.SpecialForms do
13201320
Which the argument for the `:sum` function call is not the
13211321
expected result:
13221322
1323-
{:sum, [], [1, {:value, [if_undefined: :apply], Elixir}, 3]}
1323+
{:sum, [], [1, {:value, [], Elixir}, 3]}
13241324
13251325
For this, we use `unquote`:
13261326

lib/elixir/lib/macro.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ defmodule Macro do
162162
* `:generated` - Whether the code should be considered as generated by
163163
the compiler or not. This means the compiler and tools like Dialyzer may not
164164
emit certain warnings.
165+
* `:if_undefined` - How to expand a variable that is undefined. Set it to
166+
`:apply` if you want a variable to become a nullary call without warning.
165167
* `:keep` - Used by `quote/2` with the option `location: :keep` to annotate
166168
the file and the line number of the quoted source.
167169
* `:line` - The line number of the AST node.

lib/iex/lib/iex/evaluator.ex

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,9 @@ defmodule IEx.Evaluator do
324324
end
325325

326326
defp eval_and_inspect(forms, line, state) do
327-
forms = add_if_undefined_apply_to_vars(forms)
328-
{result, binding, env} = eval_expr_by_expr(forms, state.binding, state.env)
327+
%{env: env, binding: binding} = state
328+
forms = add_if_undefined_apply_to_vars(forms, env)
329+
{result, binding, env} = eval_expr_by_expr(forms, binding, env)
329330

330331
unless result == IEx.dont_display_result() do
331332
io_inspect(result)
@@ -335,10 +336,14 @@ defmodule IEx.Evaluator do
335336
%{state | env: env, binding: binding, history: history}
336337
end
337338

338-
defp add_if_undefined_apply_to_vars(forms) do
339+
defp add_if_undefined_apply_to_vars(forms, env) do
339340
Macro.prewalk(forms, fn
340341
{var, meta, context} when is_atom(var) and is_atom(context) ->
341-
{var, Keyword.put_new(meta, :if_undefined, :apply), context}
342+
if Macro.Env.lookup_import(env, {var, 0}) != [] do
343+
{var, Keyword.put_new(meta, :if_undefined, :apply), context}
344+
else
345+
{var, meta, context}
346+
end
342347

343348
other ->
344349
other

lib/iex/test/iex/interaction_test.exs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,10 @@ defmodule IEx.InteractionTest do
198198

199199
describe ".iex" do
200200
test "no .iex" do
201-
assert "** (CompileError) iex:1: undefined function my_variable/0" <> _ =
202-
capture_iex("my_variable")
201+
capture_io(:stderr, fn ->
202+
assert "** (CompileError) iex:1: undefined function my_variable/0" <> _ =
203+
capture_iex("my_variable")
204+
end)
203205
end
204206

205207
@tag :tmp_dir

lib/iex/test/iex/server_test.exs

Lines changed: 62 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@ defmodule IEx.ServerTest do
4242
test "outside of the evaluator with refusal", config do
4343
Process.register(self(), config.test)
4444

45-
{server, evaluator} = pry_session(config.test, "N\niex_context")
46-
client = pry_request([server])
47-
send(evaluator, :run)
45+
capture_io(:stderr, fn ->
46+
{server, evaluator} = pry_session(config.test, "N\niex_context")
47+
client = pry_request([server])
48+
send(evaluator, :run)
4849

49-
assert Task.await(client) == {:error, :refused}
50-
assert Task.await(server) =~ "undefined function iex_context"
50+
assert Task.await(client) == {:error, :refused}
51+
assert Task.await(server) =~ "undefined function iex_context"
52+
end)
5153
end
5254

5355
test "outside of the evaluator with crash", config do
@@ -63,71 +65,79 @@ defmodule IEx.ServerTest do
6365
test "outside of the evaluator with double acceptance", config do
6466
Process.register(self(), config.test)
6567

66-
{server1, evaluator1} = pry_session(config.test, "Y\niex_context")
67-
{server2, evaluator2} = pry_session(config.test, "Y\niex_context")
68-
client = pry_request([server1, server2])
68+
capture_io(:stderr, fn ->
69+
{server1, evaluator1} = pry_session(config.test, "Y\niex_context")
70+
{server2, evaluator2} = pry_session(config.test, "Y\niex_context")
71+
client = pry_request([server1, server2])
6972

70-
send(evaluator1, :run)
71-
send(evaluator2, :run)
72-
reply1 = Task.await(server1)
73-
reply2 = Task.await(server2)
73+
send(evaluator1, :run)
74+
send(evaluator2, :run)
75+
reply1 = Task.await(server1)
76+
reply2 = Task.await(server2)
7477

75-
{accepted, refused} =
76-
if reply1 =~ ":inside_pry", do: {reply1, reply2}, else: {reply2, reply1}
78+
{accepted, refused} =
79+
if reply1 =~ ":inside_pry", do: {reply1, reply2}, else: {reply2, reply1}
7780

78-
assert accepted =~ ":inside_pry"
79-
assert refused =~ "** session was already accepted elsewhere"
80-
assert refused =~ "undefined function iex_context"
81+
assert accepted =~ ":inside_pry"
82+
assert refused =~ "** session was already accepted elsewhere"
83+
assert refused =~ "undefined function iex_context"
8184

82-
assert Task.await(client) == {:ok, false}
85+
assert Task.await(client) == {:ok, false}
86+
end)
8387
end
8488

8589
test "outside of the evaluator with double refusal", config do
8690
Process.register(self(), config.test)
8791

88-
{server1, evaluator1} = pry_session(config.test, "N\niex_context")
89-
{server2, evaluator2} = pry_session(config.test, "N\niex_context")
90-
client = pry_request([server1, server2])
92+
capture_io(:stderr, fn ->
93+
{server1, evaluator1} = pry_session(config.test, "N\niex_context")
94+
{server2, evaluator2} = pry_session(config.test, "N\niex_context")
95+
client = pry_request([server1, server2])
9196

92-
send(evaluator1, :run)
93-
send(evaluator2, :run)
94-
reply1 = Task.await(server1)
95-
reply2 = Task.await(server2)
97+
send(evaluator1, :run)
98+
send(evaluator2, :run)
99+
reply1 = Task.await(server1)
100+
reply2 = Task.await(server2)
96101

97-
assert reply1 =~ "undefined function iex_context"
98-
assert reply2 =~ "undefined function iex_context"
102+
assert reply1 =~ "undefined function iex_context"
103+
assert reply2 =~ "undefined function iex_context"
99104

100-
assert Task.await(client) == {:error, :refused}
105+
assert Task.await(client) == {:error, :refused}
106+
end)
101107
end
102108

103109
test "outside of the evaluator with acceptance and then refusal", config do
104110
Process.register(self(), config.test)
105111

106-
{server1, evaluator1} = pry_session(config.test, "Y\niex_context")
107-
{server2, evaluator2} = pry_session(config.test, "N\niex_context")
108-
client = pry_request([server1, server2])
112+
capture_io(:stderr, fn ->
113+
{server1, evaluator1} = pry_session(config.test, "Y\niex_context")
114+
{server2, evaluator2} = pry_session(config.test, "N\niex_context")
115+
client = pry_request([server1, server2])
109116

110-
send(evaluator1, :run)
111-
send(evaluator2, :run)
112-
assert Task.await(server1) =~ ":inside_pry"
113-
assert Task.await(server2) =~ "undefined function iex_context"
117+
send(evaluator1, :run)
118+
send(evaluator2, :run)
119+
assert Task.await(server1) =~ ":inside_pry"
120+
assert Task.await(server2) =~ "undefined function iex_context"
114121

115-
assert Task.await(client) == {:ok, false}
122+
assert Task.await(client) == {:ok, false}
123+
end)
116124
end
117125

118126
test "outside of the evaluator with refusal and then acceptance", config do
119127
Process.register(self(), config.test)
120128

121-
{server1, evaluator1} = pry_session(config.test, "N\niex_context")
122-
{server2, evaluator2} = pry_session(config.test, "Y\niex_context")
123-
client = pry_request([server1, server2])
129+
capture_io(:stderr, fn ->
130+
{server1, evaluator1} = pry_session(config.test, "N\niex_context")
131+
{server2, evaluator2} = pry_session(config.test, "Y\niex_context")
132+
client = pry_request([server1, server2])
124133

125-
send(evaluator1, :run)
126-
send(evaluator2, :run)
127-
assert Task.await(server1) =~ "undefined function iex_context"
128-
assert Task.await(server2) =~ ":inside_pry"
134+
send(evaluator1, :run)
135+
send(evaluator2, :run)
136+
assert Task.await(server1) =~ "undefined function iex_context"
137+
assert Task.await(server2) =~ ":inside_pry"
129138

130-
assert Task.await(client) == {:ok, false}
139+
assert Task.await(client) == {:ok, false}
140+
end)
131141
end
132142

133143
@tag :tmp_dir
@@ -136,14 +146,16 @@ defmodule IEx.ServerTest do
136146
path = Path.join(tmp_dir, "dot-iex")
137147
File.write!(path, "my_variable = 144")
138148

139-
{server, evaluator} = pry_session(config.test, "Y\nmy_variable", dot_iex_path: path)
140-
client = pry_request([server])
141-
send(evaluator, :run)
149+
capture_io(:stderr, fn ->
150+
{server, evaluator} = pry_session(config.test, "Y\nmy_variable", dot_iex_path: path)
151+
client = pry_request([server])
152+
send(evaluator, :run)
142153

143-
assert Task.await(server) =~
144-
"** (UndefinedFunctionError) function :erl_eval.my_variable/0 is undefined or private"
154+
assert Task.await(server) =~
155+
"** (UndefinedFunctionError) function :erl_eval.my_variable/0 is undefined or private"
145156

146-
assert Task.await(client) == {:ok, false}
157+
assert Task.await(client) == {:ok, false}
158+
end)
147159
end
148160

149161
@tag :tmp_dir

0 commit comments

Comments
 (0)