Change import metadata to imports as a list
This is preparation to better ambiguity solving of quoted expressions.
This commit is contained in:
@@ -187,7 +187,7 @@ defmodule Macro do
|
||||
|
||||
* `:alias` - Used for alias hygiene.
|
||||
* `:ambiguous_op` - Used for improved error messages in the compiler.
|
||||
* `:import` - Used for import hygiene.
|
||||
* `:imports` - Used for import hygiene.
|
||||
* `:var` - Used for improved error messages on undefined variables.
|
||||
|
||||
Do not rely on them as they may change or be fully removed in future versions
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
require_function/5, import_function/4,
|
||||
expand_import/7, expand_require/6,
|
||||
default_functions/0, default_macros/0, default_requires/0,
|
||||
find_import/4, format_error/1]).
|
||||
find_import/4, find_imports/4, format_error/1]).
|
||||
-include("elixir.hrl").
|
||||
-import(ordsets, [is_element/2]).
|
||||
-define(kernel, 'Elixir.Kernel').
|
||||
@@ -36,6 +36,20 @@ find_import(Meta, Name, Arity, E) ->
|
||||
false
|
||||
end.
|
||||
|
||||
find_imports(Meta, Name, Arity, E) ->
|
||||
Tuple = {Name, Arity},
|
||||
|
||||
case find_dispatch(Meta, Tuple, [], E) of
|
||||
{function, Receiver} ->
|
||||
elixir_env:trace({imported_function, Meta, Receiver, Name, Arity}, E),
|
||||
[{Receiver, Arity}];
|
||||
{macro, Receiver} ->
|
||||
elixir_env:trace({imported_macro, Meta, Receiver, Name, Arity}, E),
|
||||
[{Receiver, Arity}];
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
%% Function retrieval
|
||||
|
||||
import_function(Meta, Name, Arity, E) ->
|
||||
@@ -233,8 +247,8 @@ caller(Line, E) ->
|
||||
required(Meta) ->
|
||||
lists:keyfind(required, 1, Meta) == {required, true}.
|
||||
|
||||
find_dispatch(Meta, Tuple, Extra, E) ->
|
||||
case is_import(Meta) of
|
||||
find_dispatch(Meta, {_Name, Arity} = Tuple, Extra, E) ->
|
||||
case is_import(Meta, Arity) of
|
||||
{import, _} = Import ->
|
||||
Import;
|
||||
false ->
|
||||
@@ -258,11 +272,15 @@ find_dispatch(Meta, Tuple, Extra, E) ->
|
||||
find_dispatch(Tuple, List) ->
|
||||
[Receiver || {Receiver, Set} <- List, is_element(Tuple, Set)].
|
||||
|
||||
is_import(Meta) ->
|
||||
case lists:keyfind(import, 1, Meta) of
|
||||
{import, _} = Import ->
|
||||
is_import(Meta, Arity) ->
|
||||
case lists:keyfind(imports, 1, Meta) of
|
||||
{imports, Imports} ->
|
||||
case lists:keyfind(context, 1, Meta) of
|
||||
{context, _} -> Import;
|
||||
{context, _} ->
|
||||
case lists:keyfind(Arity, 2, Imports) of
|
||||
{Receiver, Arity} -> {import, Receiver};
|
||||
false -> false
|
||||
end;
|
||||
false -> false
|
||||
end;
|
||||
false -> false
|
||||
|
||||
@@ -66,7 +66,6 @@ Right 80 assoc_op_eol. %% =>
|
||||
Nonassoc 90 capture_op_eol. %% &
|
||||
Right 100 match_op_eol. %% =
|
||||
Left 110 or_op_eol. %% ||, |||, or
|
||||
% Left 120 xor_op_eol. %% ^^^
|
||||
Left 130 and_op_eol. %% &&, &&&, and
|
||||
Left 140 comp_op_eol. %% ==, !=, =~, ===, !==
|
||||
Left 150 rel_op_eol. %% <, >, <=, >=
|
||||
|
||||
@@ -290,7 +290,8 @@ do_quote({unquote, _Meta, [Expr]}, #elixir_quote{unquote=true}, _) ->
|
||||
|
||||
%% Aliases
|
||||
|
||||
do_quote({'__aliases__', Meta, [H | T]} = Alias, #elixir_quote{aliases_hygiene=true} = Q, E) when is_atom(H) and (H /= 'Elixir') ->
|
||||
do_quote({'__aliases__', Meta, [H | T]} = Alias, #elixir_quote{aliases_hygiene=true} = Q, E)
|
||||
when is_atom(H), H /= 'Elixir' ->
|
||||
Annotation =
|
||||
case elixir_aliases:expand(Alias, E) of
|
||||
Atom when is_atom(Atom) -> Atom;
|
||||
@@ -301,12 +302,14 @@ do_quote({'__aliases__', Meta, [H | T]} = Alias, #elixir_quote{aliases_hygiene=t
|
||||
|
||||
%% Vars
|
||||
|
||||
do_quote({Name, Meta, nil}, #elixir_quote{vars_hygiene=true, imports_hygiene=true} = Q, E) when is_atom(Name) ->
|
||||
ImportMeta = import_meta(Meta, Name, 0, Q, E),
|
||||
{'{}', [], [Name, meta(ImportMeta, Q), Q#elixir_quote.context]};
|
||||
do_quote({Name, Meta, nil}, #elixir_quote{vars_hygiene=true} = Q, E)
|
||||
when is_atom(Name), is_list(Meta) ->
|
||||
ImportMeta = if
|
||||
Q#elixir_quote.imports_hygiene -> import_meta(Meta, Name, 0, Q, E);
|
||||
true -> Meta
|
||||
end,
|
||||
|
||||
do_quote({Name, Meta, nil}, #elixir_quote{vars_hygiene=true} = Q, _E) when is_atom(Name) ->
|
||||
{'{}', [], [Name, meta(Meta, Q), Q#elixir_quote.context]};
|
||||
{'{}', [], [Name, meta(ImportMeta, Q), Q#elixir_quote.context]};
|
||||
|
||||
%% Unquote
|
||||
|
||||
@@ -320,13 +323,26 @@ do_quote({{'.', Meta, [Left, unquote]}, _, [Expr]}, #elixir_quote{unquote=true}
|
||||
|
||||
do_quote({'&', Meta, [{'/', _, [{F, _, C}, A]}] = Args},
|
||||
#elixir_quote{imports_hygiene=true} = Q, E) when is_atom(F), is_integer(A), is_atom(C) ->
|
||||
do_quote_fa('&', Meta, Args, F, A, Q, E);
|
||||
NewMeta =
|
||||
case elixir_dispatch:find_import(Meta, F, A, E) of
|
||||
false ->
|
||||
Meta;
|
||||
|
||||
do_quote({Name, Meta, Args}, #elixir_quote{imports_hygiene=true} = Q, E) when is_atom(Name), is_list(Args) ->
|
||||
do_quote_import(Meta, Name, length(Args), Args, Q, E);
|
||||
Receiver ->
|
||||
keystore(context, keystore(imports, Meta, [{Receiver, A}]), Q#elixir_quote.context)
|
||||
end,
|
||||
do_quote_tuple('&', NewMeta, Args, Q, E);
|
||||
|
||||
do_quote({Name, Meta, Context}, #elixir_quote{imports_hygiene=true} = Q, E) when is_atom(Name), is_atom(Context) ->
|
||||
do_quote_import(Meta, Name, 0, Context, Q, E);
|
||||
do_quote({Name, Meta, ArgsOrContext}, #elixir_quote{imports_hygiene=true} = Q, E)
|
||||
when is_atom(Name), is_list(Meta), is_list(ArgsOrContext) or is_atom(ArgsOrContext) ->
|
||||
Arity = if
|
||||
is_atom(ArgsOrContext) -> 0;
|
||||
true -> length(ArgsOrContext)
|
||||
end,
|
||||
|
||||
ImportMeta = import_meta(Meta, Name, Arity, Q, E),
|
||||
Annotated = annotate({Name, ImportMeta, ArgsOrContext}, Q#elixir_quote.context),
|
||||
do_quote_tuple(Annotated, Q, E);
|
||||
|
||||
%% Two-element tuples
|
||||
|
||||
@@ -424,40 +440,24 @@ bad_escape(Arg) ->
|
||||
|
||||
import_meta(Meta, Name, Arity, Q, E) ->
|
||||
case (keyfind(import, Meta) == false) andalso
|
||||
elixir_dispatch:find_import(Meta, Name, Arity, E) of
|
||||
false ->
|
||||
elixir_dispatch:find_imports(Meta, Name, Arity, E) of
|
||||
[] ->
|
||||
case (Arity == 1) andalso keyfind(ambiguous_op, Meta) of
|
||||
{ambiguous_op, nil} -> keystore(ambiguous_op, Meta, Q#elixir_quote.context);
|
||||
_ -> Meta
|
||||
end;
|
||||
Receiver ->
|
||||
keystore(import, keystore(context, Meta, Q#elixir_quote.context), Receiver)
|
||||
|
||||
Imports ->
|
||||
keystore(imports, keystore(context, Meta, Q#elixir_quote.context), Imports)
|
||||
end.
|
||||
|
||||
%% do_quote_*
|
||||
|
||||
do_quote_import(Meta, Name, Arity, ArgsOrAtom, Q, E) ->
|
||||
ImportMeta = import_meta(Meta, Name, Arity, Q, E),
|
||||
Annotated = annotate({Name, ImportMeta, ArgsOrAtom}, Q#elixir_quote.context),
|
||||
do_quote_tuple(Annotated, Q, E).
|
||||
|
||||
do_quote_call(Left, Meta, Expr, Args, Q, E) ->
|
||||
All = [Left, {unquote, Meta, [Expr]}, Args, Q#elixir_quote.context],
|
||||
TAll = [do_quote(X, Q, E) || X <- All],
|
||||
{{'.', Meta, [elixir_quote, dot]}, Meta, [meta(Meta, Q) | TAll]}.
|
||||
|
||||
do_quote_fa(Target, Meta, Args, F, A, Q, E) ->
|
||||
NewMeta =
|
||||
case elixir_dispatch:find_import(Meta, F, A, E) of
|
||||
false -> Meta;
|
||||
Receiver ->
|
||||
lists:keystore(context, 1,
|
||||
lists:keystore(import, 1, Meta, {import, Receiver}),
|
||||
{context, Q#elixir_quote.context}
|
||||
)
|
||||
end,
|
||||
do_quote_tuple(Target, NewMeta, Args, Q, E).
|
||||
|
||||
do_quote_tuple({Left, Meta, Right}, Q, E) ->
|
||||
do_quote_tuple(Left, Meta, Right, Q, E).
|
||||
|
||||
|
||||
@@ -418,7 +418,7 @@ defmodule Kernel.ExpansionTest do
|
||||
test "in guards" do
|
||||
code = quote(do: fn pid when :erlang.==(pid, self) -> pid end)
|
||||
expanded_code = quote(do: fn pid when :erlang.==(pid, :erlang.self()) -> pid end)
|
||||
assert clean_meta(expand(code), [:import, :context]) == expanded_code
|
||||
assert clean_meta(expand(code), [:imports, :context]) == expanded_code
|
||||
|
||||
message = ~r"cannot find or invoke local foo/1"
|
||||
|
||||
@@ -1061,10 +1061,11 @@ defmodule Kernel.ExpansionTest do
|
||||
|
||||
test "expands remotes" do
|
||||
assert expand(quote(do: &List.flatten/2)) ==
|
||||
quote(do: &:"Elixir.List".flatten/2) |> clean_meta([:import, :context, :no_parens])
|
||||
quote(do: &:"Elixir.List".flatten/2)
|
||||
|> clean_meta([:imports, :context, :no_parens])
|
||||
|
||||
assert expand(quote(do: &Kernel.is_atom/1)) ==
|
||||
quote(do: &:erlang.is_atom/1) |> clean_meta([:import, :context, :no_parens])
|
||||
quote(do: &:erlang.is_atom/1) |> clean_meta([:imports, :context, :no_parens])
|
||||
end
|
||||
|
||||
test "expands macros" do
|
||||
|
||||
@@ -631,6 +631,6 @@ defmodule Kernel.QuoteTest.ImportsHygieneTest do
|
||||
test "checks the context also for variables to zero-arity functions" do
|
||||
import BinaryUtils
|
||||
{:int32, meta, __MODULE__} = quote(do: int32)
|
||||
assert meta[:import] == BinaryUtils
|
||||
assert meta[:imports] == [{BinaryUtils, 0}]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,7 +131,10 @@ defmodule Protocol.ConsolidationTest do
|
||||
end
|
||||
|
||||
test "consolidation errors on missing BEAM files" do
|
||||
defprotocol(NoBeam, do: nil)
|
||||
defprotocol NoBeam do
|
||||
def example(arg)
|
||||
end
|
||||
|
||||
assert Protocol.consolidate(String, []) == {:error, :not_a_protocol}
|
||||
assert Protocol.consolidate(NoBeam, []) == {:error, :no_beam_info}
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user