Skip to content

Commit c6f9287

Browse files
author
José Valim
committed
Only raise on local macro conflicts if it was previously defined
1 parent 09b1216 commit c6f9287

File tree

5 files changed

+55
-29
lines changed

5 files changed

+55
-29
lines changed

lib/elixir/src/elixir_def_local.erl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
macro_for(_Tuple, _All, #elixir_scope{module=nil}) -> false;
77

8-
macro_for(Tuple, All, #elixir_scope{module=Module,function=Function}) ->
8+
macro_for(Tuple, All, #elixir_scope{module=Module}) ->
99
try elixir_def:lookup_definition(Module, Tuple) of
10-
{ { Tuple, Kind, Line, _, _, _, _ }, Clauses } when Kind == defmacro; All, Kind == defmacrop ->
11-
elixir_tracker:record_local(Tuple, Module, Function),
12-
get_function(Line, Module, Clauses);
10+
{ { Tuple, Kind, Line, _, _, _, _ }, Clauses } when Kind == defmacro, Clauses /= [];
11+
All, Kind == defmacrop, Clauses /= [] ->
12+
fun() -> get_function(Line, Module, Clauses) end;
1313
_ ->
1414
false
1515
catch
@@ -19,7 +19,7 @@ macro_for(Tuple, All, #elixir_scope{module=Module,function=Function}) ->
1919
function_for(Module, Name, Arity) ->
2020
Tuple = { Name, Arity },
2121
case elixir_def:lookup_definition(Module, Tuple) of
22-
{ { Tuple, _, Line, _, _, _, _ }, Clauses } ->
22+
{ { Tuple, _, Line, _, _, _, _ }, Clauses } when Clauses /= [] ->
2323
%% There is no need to record such calls
2424
%% since they come from funtions that were
2525
%% already analyzed at compilation time.

lib/elixir/src/elixir_dispatch.erl

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import_function(Meta, Name, Arity, S) ->
4545
false;
4646
{ import, Receiver } ->
4747
require_function(Meta, Receiver, Name, Arity, S);
48-
nomatch ->
48+
false ->
4949
case elixir_import:special_form(Name, Arity) of
5050
true -> false;
5151
false ->
@@ -123,6 +123,28 @@ expand_import(Meta, Tuple, Args, Module, Extra, S) ->
123123
do_expand_import(Meta, Tuple, Args, Module, S, Result).
124124

125125
do_expand_import(Meta, { Name, Arity } = Tuple, Args, Module, S, Result) ->
126+
Function = S#elixir_scope.function,
127+
128+
Fun = (Function /= nil) andalso (Function /= Tuple) andalso
129+
elixir_def_local:macro_for(Tuple, true, S),
130+
131+
case Fun of
132+
false ->
133+
do_expand_import_no_local(Meta, Tuple, Args, Module, S, Result);
134+
_ ->
135+
case Result of
136+
{ Kind, Receiver } when Kind == import; Receiver == Module ->
137+
do_expand_import_no_local(Meta, Tuple, Args, Module, S, Result);
138+
{ _, Receiver } ->
139+
Error = { macro_conflict, { Receiver, Name, Arity } },
140+
elixir_errors:form_error(Meta, S#elixir_scope.file, ?MODULE, Error);
141+
false ->
142+
elixir_tracker:record_local(Tuple, Module, S#elixir_scope.function),
143+
{ ok, Module, expand_macro_fun(Meta, Fun(), Module, Name, Args, Module, S) }
144+
end
145+
end.
146+
147+
do_expand_import_no_local(Meta, { Name, Arity } = Tuple, Args, Module, S, Result) ->
126148
case Result of
127149
{ macro, ?builtin } ->
128150
case is_element(Tuple, in_erlang_macros()) of
@@ -137,13 +159,7 @@ do_expand_import(Meta, { Name, Arity } = Tuple, Args, Module, S, Result) ->
137159
{ import, Receiver } ->
138160
expand_require(Meta, Receiver, Tuple, Args, Module, S);
139161
_ ->
140-
Fun = (S#elixir_scope.function /= Tuple) andalso
141-
elixir_def_local:macro_for(Tuple, true, S),
142-
case Fun of
143-
false -> { error, noexpansion };
144-
_ ->
145-
{ ok, Module, expand_macro_fun(Meta, Fun, Module, Name, Args, Module, S) }
146-
end
162+
{ error, noexpansion }
147163
end.
148164

149165
expand_require(Meta, ?builtin, { Name, Arity } = Tuple, Args, Module, S) ->
@@ -160,19 +176,22 @@ expand_require(Meta, ?builtin, { Name, Arity } = Tuple, Args, Module, S) ->
160176
end;
161177

162178
expand_require(Meta, Receiver, { Name, Arity } = Tuple, Args, Module, S) ->
163-
Fun = (Module == Receiver) andalso (S#elixir_scope.function /= Tuple) andalso
164-
elixir_def_local:macro_for(Tuple, false, S),
179+
Function = S#elixir_scope.function,
180+
181+
Fun = (Module == Receiver) andalso (Function /= nil) andalso
182+
(Function /= Tuple) andalso elixir_def_local:macro_for(Tuple, false, S),
165183

166184
case Fun of
167185
false ->
168186
case is_element(Tuple, get_optional_macros(Receiver)) of
169187
true ->
170-
elixir_tracker:record_remote(Tuple, Receiver, S#elixir_scope.module, S#elixir_scope.function),
188+
elixir_tracker:record_remote(Tuple, Receiver, Module, Function),
171189
{ ok, Receiver, expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, S) };
172190
false -> { error, noexpansion }
173191
end;
174192
_ ->
175-
{ ok, Receiver, expand_macro_fun(Meta, Fun, Receiver, Name, Args, Module, S) }
193+
elixir_tracker:record_local(Tuple, Module, Function),
194+
{ ok, Receiver, expand_macro_fun(Meta, Fun(), Receiver, Name, Args, Module, S) }
176195
end.
177196

178197
%% Expansion helpers
@@ -250,7 +269,7 @@ find_dispatch(Meta, Tuple, Extra, S) ->
250269
case { FunMatch, MacMatch } of
251270
{ [], [Receiver] } -> { macro, Receiver };
252271
{ [Receiver], [] } -> { function, Receiver };
253-
{ [], [] } -> nomatch;
272+
{ [], [] } -> false;
254273
_ ->
255274
{ Name, Arity } = Tuple,
256275
[First, Second|_] = FunMatch ++ MacMatch,
@@ -322,6 +341,10 @@ format_error({ unrequired_module, { Receiver, Name, Arity, Required }}) ->
322341
io_lib:format("tried to invoke macro ~ts.~ts/~B but module was not required. Required: ~ts",
323342
[elixir_errors:inspect(Receiver), Name, Arity, String]);
324343

344+
format_error({ macro_conflict, { Receiver, Name, Arity } }) ->
345+
io_lib:format("call to local macro ~ts/~B conflicts with imported ~ts.~ts/~B",
346+
[Name, Arity, elixir_errors:inspect(Receiver), Name, Arity]);
347+
325348
format_error({ ambiguous_call, { Mod1, Mod2, Name, Arity }}) ->
326349
io_lib:format("function ~ts/~B imported from both ~ts and ~ts, call is ambiguous",
327350
[Name, Arity, elixir_errors:inspect(Mod1), elixir_errors:inspect(Mod2)]).

lib/elixir/src/elixir_module.erl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,10 @@ compile(Line, Module, Block, Vars, #elixir_scope{context_modules=FileModules} =
7878
[elixir_tracker:record_local(Tuple, Module) || Tuple <- OnLoad]
7979
end,
8080

81-
elixir_tracker:warn_unused_local(File, Module, Private),
82-
elixir_tracker:ensure_no_import_conflict(Line, File, Module, All),
81+
AllFunctions = Def ++ [T || { T, defp, _, _, _ } <- Private],
82+
elixir_tracker:ensure_no_function_conflict(Line, File, Module, AllFunctions),
8383
elixir_tracker:ensure_all_imports_used(Line, File, Module),
84+
elixir_tracker:warn_unused_local(File, Module, Private),
8485
warn_unused_docs(Line, File, Module, All),
8586

8687
Final = [
@@ -293,7 +294,6 @@ warn_unused_docs(_Line, File, Module, All) ->
293294
end
294295
end, ok, docs_table(Module)).
295296

296-
297297
% EXTRA FUNCTIONS
298298

299299
add_info_function(Line, File, Module, Export, Def, Defmacro, C) ->

lib/elixir/src/elixir_tracker.erl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
record_import/4, record_remote/4,
88
record_warn/4, record_definition/3,
99
record_defaults/4, record_alias/2,
10-
ensure_no_import_conflict/4, ensure_all_imports_used/3,
10+
ensure_no_function_conflict/4, ensure_all_imports_used/3,
1111
warn_unused_local/3, format_error/1
1212
]).
1313
-include("elixir.hrl").
@@ -98,10 +98,10 @@ if_tracker(Module, Callback) ->
9898

9999
%% ERROR HANDLING
100100

101-
ensure_no_import_conflict(Meta, File, Module, AllDefined) ->
101+
ensure_no_function_conflict(Meta, File, Module, AllDefined) ->
102102
if_tracker(Module, fun(Pid) ->
103103
[ begin
104-
elixir_errors:form_error(Meta, File, ?MODULE, { import_conflict, Error })
104+
elixir_errors:form_error(Meta, File, ?MODULE, { function_conflict, Error })
105105
end || Error <- ?tracker:collect_imports_conflicts(Pid, AllDefined) ]
106106
end),
107107
ok.
@@ -127,7 +127,7 @@ warn_unused_local(File, Module, Private) ->
127127
end || Error <- Unused ]
128128
end).
129129

130-
format_error({import_conflict,{Receivers, Name, Arity}}) ->
130+
format_error({function_conflict,{Receivers, Name, Arity}}) ->
131131
io_lib:format("imported ~ts.~ts/~B conflicts with local function",
132132
[elixir_errors:inspect(hd(Receivers)), Name, Arity]);
133133

lib/elixir/test/elixir/kernel/errors_test.exs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,21 @@ defmodule Kernel.ErrorsTest do
230230
'''
231231
defmodule ErrorsTest do
232232
1 && 2
233-
def _ && _, do: OMG
233+
def _ && _, do: :error
234234
end
235235
'''
236236
end
237237

238238
test :macro_local_conflict do
239239
assert_compile_fail CompileError,
240-
"nofile:1: imported Kernel.&&/2 conflicts with local function",
240+
"nofile:6: call to local macro &&/2 conflicts with imported Kernel.&&/2",
241241
'''
242242
defmodule ErrorsTest do
243-
1 && 2
244-
defmacro _ && _, do: OMG
243+
def hello, do: 1 || 2
244+
defmacro _ || _, do: :ok
245+
246+
defmacro _ && _, do: :error
247+
def world, do: 1 && 2
245248
end
246249
'''
247250
end

0 commit comments

Comments
 (0)