2
2
% % in between local functions and imports.
3
3
% % For imports dispatch, please check elixir_dispatch.
4
4
-module (elixir_import ).
5
- -export ([import /5 , recorded_locals /1 , format_error /1 ,
5
+ -export ([import /5 , record_local / 2 , recorded_locals /1 , format_error /1 ,
6
6
ensure_no_import_conflict /4 , ensure_no_local_conflict /4 ,
7
7
ensure_all_imports_used /3 ,
8
8
build_table /1 , delete_table /1 , record /3 ]).
9
9
-include (" elixir.hrl" ).
10
10
11
+ % % This table keeps:
12
+ % %
13
+ % % * Invoked imports and requires in the format { { Name, Arity }, Module }
14
+ % % * The current warn status imports and requires in the format { Module, Line :: integer }
15
+ % % * Invoked locals in the format { { Name, Arity }, Public :: boolean }
16
+ % %
11
17
table (Module ) -> ? atom_concat ([i , Module ]).
12
18
13
19
build_table (Module ) ->
@@ -16,18 +22,20 @@ build_table(Module) ->
16
22
delete_table (Module ) ->
17
23
ets :delete (table (Module )).
18
24
19
- record (_Tuple , _Receiver , nil ) ->
20
- false ;
21
-
25
+ record (_Tuple , _Receiver , nil ) -> false ;
22
26
record (Tuple , Receiver , Module ) ->
23
27
try
24
28
ets :insert (table (Module ), { Tuple , Receiver })
25
29
catch
26
30
error :badarg -> false
27
31
end .
28
32
29
- record_warn (_Meta , _Ref , _Opts , # elixir_scope {module = nil }) -> false ;
33
+ record_local (Tuple , # elixir_scope {function = Function })
34
+ when Function == nil ; Function == Tuple -> false ;
35
+ record_local (Tuple , # elixir_scope {module = Module }) ->
36
+ record (Tuple , true , Module ).
30
37
38
+ record_warn (_Meta , _Ref , _Opts , # elixir_scope {module = nil }) -> false ;
31
39
record_warn (Meta , Ref , Opts , S ) ->
32
40
Table = table (S # elixir_scope .module ),
33
41
ets :delete (Table , Ref ),
@@ -43,10 +51,15 @@ record_warn(Meta, Ref, Opts, S) ->
43
51
44
52
recorded_locals (Module ) ->
45
53
Table = table (Module ),
46
- Match = { '$1' , Module },
47
- Result = ets :match (Table , Match ),
48
- ets :match_delete (Table , Match ),
49
- lists :append (Result ).
54
+ Match = module_local_spec (),
55
+ Result = ets :select (Table , Match ),
56
+ ets :select_delete (Table , Match ),
57
+ Result .
58
+
59
+ module_local_spec () ->
60
+ [{ { '$1' , '$2' }, [{ 'orelse' , {'==' ,'$2' ,true }, {'==' ,'$2' ,false } }], ['$1' ] }].
61
+
62
+ % % IMPORT HELPERS
50
63
51
64
% % Update the scope to consider the imports for aliases
52
65
% % based on the given options and selector.
@@ -80,16 +93,13 @@ import(Meta, Ref, Opts, Selector, S) ->
80
93
record_warn (Meta , Ref , Opts , S ),
81
94
SM .
82
95
83
- % % IMPORT FUNCTION RELATED HELPERS
84
-
85
96
% % Calculates the imports based on only and except
86
97
87
98
calculate (Meta , Key , Opts , Old , Temp , AvailableFun , S ) ->
88
99
File = S # elixir_scope .file ,
89
100
90
101
New = case keyfind (only , Opts ) of
91
- { only , RawOnly } ->
92
- Only = expand_fun_arity (Meta , only , RawOnly , S ),
102
+ { only , Only } ->
93
103
case Only -- get_exports (Key ) of
94
104
[{Name ,Arity }|_ ] ->
95
105
Tuple = { invalid_import , { Key , Name , Arity } },
@@ -101,8 +111,7 @@ calculate(Meta, Key, Opts, Old, Temp, AvailableFun, S) ->
101
111
case keyfind (except , Opts ) of
102
112
false -> AvailableFun (true );
103
113
{ except , [] } -> AvailableFun (true );
104
- { except , RawExcept } ->
105
- Except = expand_fun_arity (Meta , except , RawExcept , S ),
114
+ { except , Except } ->
106
115
case keyfind (Key , Old ) of
107
116
false -> AvailableFun (true ) -- Except ;
108
117
{Key ,OldImports } -> OldImports -- Except
@@ -134,29 +143,6 @@ if_quoted(Meta, Temp, Callback) ->
134
143
Temp
135
144
end .
136
145
137
- % % Ensure we are expanding macros and stuff
138
-
139
- expand_fun_arity (Meta , Kind , Value , S ) ->
140
- { TValue , _S } = elixir_translator :translate_each (Value , S ),
141
- cons_to_keywords (Meta , Kind , TValue , S ).
142
-
143
- cons_to_keywords (Meta , Kind , { cons , _ , Left , { nil , _ } }, S ) ->
144
- [tuple_to_fun_arity (Meta , Kind , Left , S )];
145
-
146
- cons_to_keywords (Meta , Kind , { cons , _ , Left , Right }, S ) ->
147
- [tuple_to_fun_arity (Meta , Kind , Left , S )|cons_to_keywords (Meta , Kind , Right , S )];
148
-
149
- cons_to_keywords (Meta , Kind , _ , S ) ->
150
- elixir_errors :syntax_error (Meta , S # elixir_scope .file ,
151
- " invalid value for :~ts , expected a list with functions and arities" , [Kind ]).
152
-
153
- tuple_to_fun_arity (_Meta , _Kind , { tuple , _ , [{ atom , _ , Atom }, { integer , _ , Integer }] }, _S ) ->
154
- { Atom , Integer };
155
-
156
- tuple_to_fun_arity (Meta , Kind , _ , S ) ->
157
- elixir_errors :syntax_error (Meta , S # elixir_scope .file ,
158
- " invalid value for :~ts , expected a list with functions and arities" , [Kind ]).
159
-
160
146
% % Retrieve functions and macros from modules
161
147
162
148
get_exports (Module ) ->
@@ -207,26 +193,33 @@ ensure_no_local_conflict(Meta, File, Module, AllDefined) ->
207
193
208
194
ensure_no_import_conflict (Meta , File , Module , AllDefined ) ->
209
195
Table = table (Module ),
210
- Matches = [X || X <- AllDefined , ets :member (Table , X )],
196
+ [ensure_no_import_conflict (Meta , File , Module , Table , X ) || X <- AllDefined ],
197
+ ok .
198
+
199
+ ensure_no_import_conflict (Meta , File , Module , Table , { Name , Arity } = Tuple ) ->
200
+ RawMatches = ets :match (Table , { Tuple , '$1' }),
201
+ Matches = [X || X <- lists :append (RawMatches ), not is_boolean (X ), X /= Module ],
211
202
212
203
case Matches of
213
- [{Name ,Arity }|_ ] ->
214
- Key = ets :lookup_element (Table , { Name , Arity }, 2 ),
215
- Tuple = { import_conflict , { hd (Key ), Name , Arity } },
216
- elixir_errors :form_error (Meta , File , ? MODULE , Tuple );
217
- [] ->
218
- ok
204
+ [] -> ok ;
205
+ Key ->
206
+ Error = { import_conflict , { hd (Key ), Name , Arity } },
207
+ elixir_errors :form_error (Meta , File , ? MODULE , Error )
219
208
end .
220
209
210
+ % % Ensure all imports are used by checking all
211
+ % % enabled warnings in the table with the imported
212
+ % % function calls.
213
+
221
214
ensure_all_imports_used (_Line , File , Module ) ->
222
215
Table = table (Module ),
223
- [begin
224
- elixir_errors :handle_file_warning (File , { L , ? MODULE , { unused_import , M } })
225
- end || [ M , L ] <- ets :select (Table , module_line_spec ()),
226
- ets :match (Table , { '$1' , M }) == []].
216
+ [ begin
217
+ elixir_errors :handle_file_warning (File , { L , ? MODULE , { unused_import , M } })
218
+ end || { M , L } <- ets :select (Table , module_line_spec ()),
219
+ ets :match (Table , { '$1' , M }) == []].
227
220
228
221
module_line_spec () ->
229
- [{ { '$1' , '$2' }, [{ is_integer , '$2' }], ['$$ ' ] }].
222
+ [{ { '$1' , '$2' }, [{ is_integer , '$2' }], ['$_ ' ] }].
230
223
231
224
% % Ensure the given functions don't clash with any
232
225
% % of Elixir non overridable macros.
@@ -287,8 +280,6 @@ intersection([H|T], All) ->
287
280
288
281
intersection ([], _All ) -> [].
289
282
290
- % % INTROSPECTION
291
-
292
283
% % Internal funs that are never imported etc.
293
284
294
285
remove_underscored (default , List ) -> remove_underscored (List );
@@ -308,7 +299,7 @@ remove_internals(Set) ->
308
299
ordsets :del_element ({ module_info , 1 },
309
300
ordsets :del_element ({ module_info , 0 }, Set )).
310
301
311
- % % Macros implemented in Erlang that are not overridable .
302
+ % % Macros implemented in Erlang that are not importable .
312
303
313
304
special_form () ->
314
305
[
0 commit comments