Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 96 lines (76 sloc) 3.257 kB
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
1 %% Module responsible for local invocation of macros and functions.
2 -module(elixir_def_local).
3 -export([
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
4 build_table/1,
5 delete_table/1,
6fdbb5d @josevalim Invoking an undefined local macro at compilation time raises an error
josevalim authored
6 record/4,
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
7 macro_for/3,
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
8 function_for/3,
9 format_error/1,
6fdbb5d @josevalim Invoking an undefined local macro at compilation time raises an error
josevalim authored
10 check_unused_local_macros/3,
11 check_macros_at_runtime/4
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
12 ]).
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
13 -include("elixir.hrl").
14
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
15 %% Table
16
17 table(Module) -> ?ELIXIR_ATOM_CONCAT([l, Module]).
18
19 build_table(Module) ->
6fdbb5d @josevalim Invoking an undefined local macro at compilation time raises an error
josevalim authored
20 ets:new(table(Module), [duplicate_bag, named_table, private]).
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
21
22 delete_table(Module) ->
23 ets:delete(table(Module)).
24
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
25 record(_Line, _Tuple, _IsMacro, []) -> [];
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
26
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
27 record(Line, Tuple, IsMacro, Module) ->
6fdbb5d @josevalim Invoking an undefined local macro at compilation time raises an error
josevalim authored
28 ets:insert(table(Module), { Tuple, Line, IsMacro }).
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
29
30 %% Reading
31
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
32 macro_for(_Tuple, _All, #elixir_scope{module=[]}) -> false;
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
33
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
34 macro_for(Tuple, All, #elixir_scope{module=Module}) ->
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
35 case ets:lookup(elixir_def:table(Module), Tuple) of
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
36 [{Tuple, Line, Kind, _, Clauses}] when Kind == defmacro; All, Kind == defmacrop ->
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
37 RewrittenClauses = [rewrite_clause(Clause, Module) || Clause <- Clauses],
38 Fun = { 'fun', Line, {clauses, lists:reverse(RewrittenClauses)} },
39 { value, Result, _Binding } = erl_eval:exprs([Fun], []),
40 Result;
41 _ -> false
42 end.
43
44 function_for(Module, Name, Arity) ->
45 Tuple = { Name, Arity },
46 case ets:lookup(elixir_def:table(Module), Tuple) of
47 [{Tuple, Line, _, _, Clauses}] ->
e612fb7 @josevalim Allow macros to be invoked __MODULE__.macro during compilation
josevalim authored
48 % elixir_def_local:record(Line, Tuple, false, Module),
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
49 RewrittenClauses = [rewrite_clause(Clause, Module) || Clause <- Clauses],
50 Fun = { 'fun', Line, {clauses, lists:reverse(RewrittenClauses)} },
51 { value, Result, _Binding } = erl_eval:exprs([Fun], []),
52 Result;
7acf4a5 @josevalim Ensure nice exceptions when a local cannot be found during compilation.
josevalim authored
53 _ ->
54 [_|T] = erlang:get_stacktrace(),
55 erlang:raise(error, undef, [{Module,Name,Arity,[]}|T])
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
56 end.
57
58 %% Helpers
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
59 %% TODO: Consider caching functions in a table for performance.
279357a @josevalim Ensure macros that call locals can be invoked locally without failures.
josevalim authored
60
61 rewrite_clause({ call, Line, { atom, Line, _ } = Atom, Args }, Module) ->
62 Remote = { remote, Line,
63 { atom, Line, ?MODULE },
64 { atom, Line, function_for }
65 },
66 Arity = { integer, Line, length(Args) },
67 FunCall = { call, Line, Remote, [{ atom, Line, Module }, Atom, Arity] },
68 { call, Line, FunCall, Args };
69
70 rewrite_clause(Tuple, Module) when is_tuple(Tuple) ->
71 list_to_tuple(rewrite_clause(tuple_to_list(Tuple), Module));
72
73 rewrite_clause(List, Module) when is_list(List) ->
74 [rewrite_clause(Item, Module) || Item <- List];
75
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
76 rewrite_clause(Else, _) -> Else.
77
78 %% Error handling
79
80 check_unused_local_macros(Filename, Module, PMacros) ->
81 Table = table(Module),
82 [elixir_errors:handle_file_warning(Filename,
83 { Line, ?MODULE, { unused_macro, Fun } }) || { Fun, Line } <- PMacros, not ets:member(Table, Fun)].
84
6fdbb5d @josevalim Invoking an undefined local macro at compilation time raises an error
josevalim authored
85 check_macros_at_runtime(Filename, Module, Macros, PMacros) ->
86 Table = table(Module),
87 [elixir_errors:form_error(Line, Filename, ?MODULE, { runtime_macro, Fun }) ||
88 Fun <- Macros, [Line] <- ets:match(Table, { Fun, '$1', false })],
89 [elixir_errors:form_error(Line, Filename, ?MODULE, { runtime_macro, Fun }) ||
90 { Fun, _ } <- PMacros, [Line] <- ets:match(Table, { Fun, '$1', false })].
91
56de9ad @josevalim Raise when a private macro is unused, closes #188
josevalim authored
92 format_error({unused_macro,{Name, Arity}}) ->
6fdbb5d @josevalim Invoking an undefined local macro at compilation time raises an error
josevalim authored
93 io_lib:format("macro ~s/~B is unused", [Name, Arity]);
94
95 format_error({runtime_macro,{Name, Arity}}) ->
96 io_lib:format("macro ~s/~B is being invoked before it is defined", [Name, Arity]).
Something went wrong with that request. Please try again.