Clean up Macro.Env and hide private fields

This commit is contained in:
José Valim
2019-07-25 22:07:51 +02:00
parent 87ccbafced
commit 6c5ad7b661
8 changed files with 74 additions and 57 deletions
+8 -7
View File
@@ -276,14 +276,15 @@ validate_spec_arg(_Meta, _Key, _Value, _E, _OriginalE) ->
ok.
is_valid_spec_arg_var(Var, E, #{context := match} = OriginalE) ->
case ?key(OriginalE, prematch_vars) of
#{Var := _} ->
true;
_ ->
maps:is_key(Var, ?key(E, current_vars)) andalso
not maps:is_key(Var, ?key(OriginalE, current_vars))
case OriginalE of
#{prematch_vars := #{Var := _}} -> true;
_ -> is_var(Var, E) andalso not is_var(Var, OriginalE)
end;
is_valid_spec_arg_var(_Var, _E, _OriginalE) -> true.
is_valid_spec_arg_var(_Var, _E, _OriginalE) ->
true.
is_var(Var, #{current_vars := {Current, _}}) ->
maps:is_key(Var, Current).
validate_size_required(Meta, true, default, Type, default, E) when Type == binary; Type == bitstring ->
form_error(Meta, E, ?MODULE, unsized_binary);
+1 -1
View File
@@ -9,7 +9,7 @@
match(Fun, Expr, #{context := match} = E) ->
Fun(Expr, E);
match(Fun, Expr, #{context := Context, prematch_vars := Prematch, current_vars := Current} = E) ->
match(Fun, Expr, #{context := Context, prematch_vars := Prematch, current_vars := {Current, _}} = E) ->
{EExpr, EE} = Fun(Expr, E#{context := match, prematch_vars := Current}),
{EExpr, EE#{context := Context, prematch_vars := Prematch}}.
+15 -17
View File
@@ -21,8 +21,7 @@ new() ->
macro_aliases => [], %% keep aliases defined inside a macro
context_modules => [], %% modules defined in the current context
vars => [], %% a set of defined variables
unused_vars => #{}, %% a map with unused variables
current_vars => #{}, %% a map with current variables
current_vars => {#{}, #{}}, %% a tuple with maps of current and unused variables
prematch_vars => warn, %% behaviour outside and inside matches
lexical_tracker => nil, %% holds the lexical tracker PID
contextual_vars => []}. %% holds available contextual variables
@@ -34,7 +33,7 @@ linify(#{} = Env) ->
with_vars(Env, Vars) ->
CurrentVars = maps:from_list([{Var, 0} || Var <- Vars]),
Env#{vars := Vars, current_vars := CurrentVars}.
Env#{vars := Vars, current_vars := {CurrentVars, #{}}}.
env_to_scope(#{context := Context}) ->
#elixir_erl{context=Context}.
@@ -46,28 +45,27 @@ env_to_scope_with_vars(Env, Vars) ->
}.
reset_vars(Env) ->
Env#{vars := [], current_vars := #{}, unused_vars := #{}}.
Env#{vars := [], current_vars := {#{}, #{}}}.
%% SCOPE MERGING
%% Receives two scopes and return a new scope based on the second
%% with their variables merged.
%% Unrolled for performance reasons.
mergev(#{unused_vars := U1, current_vars := C1},
#{unused_vars := U2, current_vars := C2} = E2) ->
mergev(#{current_vars := {C1, U1}}, #{current_vars := {C2, U2}} = E2) ->
if
C1 =/= C2 ->
if
U1 =/= U2 ->
C = merge_vars(C1, C2),
E2#{vars := maps:keys(C), unused_vars := merge_vars(U1, U2), current_vars := C};
E2#{vars := maps:keys(C), current_vars := {C, merge_vars(U1, U2)}};
true ->
C = merge_vars(C1, C2),
E2#{vars := maps:keys(C), current_vars := C}
E2#{vars := maps:keys(C), current_vars := {C, U2}}
end;
U1 =/= U2 ->
E2#{unused_vars := merge_vars(U1, U2)};
E2#{current_vars := {C2, merge_vars(U1, U2)}};
true ->
E2
@@ -77,18 +75,18 @@ mergev(#{unused_vars := U1, current_vars := C1},
%% keeping the variables from the first (imports
%% and everything else are passed forward).
%% Unrolled for performance reasons.
mergea(#{unused_vars := U1, current_vars := C1, vars := V1},
#{unused_vars := U2, current_vars := C2} = E2) ->
mergea(#{current_vars := {C1, U1}, vars := V1},
#{current_vars := {C2, U2}} = E2) ->
if
C1 =/= C2 ->
if
U1 =/= U2 ->
E2#{vars := V1, unused_vars := U1, current_vars := C1};
E2#{vars := V1, current_vars := {C1, U1}};
true ->
E2#{vars := V1, current_vars := C1}
E2#{vars := V1, current_vars := {C1, U2}}
end;
U1 =/= U2 ->
E2#{unused_vars := U1};
E2#{current_vars := {C2, U1}};
true ->
E2
end.
@@ -104,13 +102,13 @@ merge_vars(V1, V2) ->
%% UNUSED VARS
check_unused_vars(#{unused_vars := Unused} = E) ->
check_unused_vars(#{current_vars := {_, Unused}} = E) ->
[elixir_errors:form_warn([{line, Line}], E, ?MODULE, {unused_var, Name}) ||
{{{Name, _}, _}, Line} <- maps:to_list(Unused), Line /= false, not_underscored(Name)],
E.
merge_and_check_unused_vars(#{unused_vars := Unused} = E, #{unused_vars := ClauseUnused}) ->
E#{unused_vars := merge_and_check_unused_vars(Unused, ClauseUnused, E)}.
merge_and_check_unused_vars(#{current_vars := {C, Unused}} = E, #{current_vars := {_, ClauseUnused}}) ->
E#{current_vars := {C, merge_and_check_unused_vars(Unused, ClauseUnused, E)}}.
merge_and_check_unused_vars(Unused, ClauseUnused, E) ->
maps:fold(fun(Key, ClauseValue, Acc) ->
+15 -13
View File
@@ -310,17 +310,19 @@ expand({super, Meta, Args}, E) when is_list(Args) ->
%% Vars
expand({'^', Meta, [Arg]}, #{context := match} = E) ->
#{current_vars := Current, prematch_vars := Prematch} = E,
#{current_vars := {Current, Unused}, prematch_vars := Prematch} = E,
%% We need to rollback to a no match context.
NoMatchE = E#{context := nil, current_vars := Prematch, prematch_vars := pin},
NoMatchE = E#{context := nil, current_vars := {Prematch, Unused}, prematch_vars := pin},
case expand(Arg, NoMatchE) of
{{Name, _, Kind} = Var, ExpandedE} when is_atom(Name), is_atom(Kind) ->
#{current_vars := {NewPrematch, NewUnused}} = ExpandedE,
EA = ExpandedE#{
context := match,
current_vars := Current,
prematch_vars := ?key(ExpandedE, current_vars)
current_vars := {Current, NewUnused},
prematch_vars := NewPrematch
},
{{'^', Meta, [Var]}, EA};
@@ -336,7 +338,7 @@ expand({'_', Meta, Kind}, E) when is_atom(Kind) ->
form_error(Meta, E, ?MODULE, unbound_underscore);
expand({Name, Meta, Kind} = Var, #{context := match} = E) when is_atom(Name), is_atom(Kind) ->
#{unused_vars := Unused, current_vars := Current, prematch_vars := Prematch} = E,
#{current_vars := {Current, Unused}, prematch_vars := Prematch} = E,
Pair = {Name, elixir_utils:var_context(Meta, Kind)},
PrematchVersion = var_version(Prematch, Pair),
@@ -346,25 +348,25 @@ expand({Name, Meta, Kind} = Var, #{context := match} = E) when is_atom(Name), is
#{Pair := PrematchVersion} ->
NewUnused = var_unused(Pair, Meta, PrematchVersion + 1, Unused),
NewCurrent = Current#{Pair => PrematchVersion + 1},
E#{unused_vars := NewUnused, current_vars := NewCurrent};
E#{current_vars := {NewCurrent, NewUnused}};
%% Variable was already overriden
#{Pair := CurrentVersion} ->
maybe_warn_underscored_var_repeat(Meta, Name, Kind, E),
NewUnused = Unused#{{Pair, CurrentVersion} => false},
E#{unused_vars := NewUnused};
E#{current_vars := {Current, NewUnused}};
%% Variable defined for the first time
_ ->
NewVars = ordsets:add_element(Pair, ?key(E, vars)),
NewUnused = var_unused(Pair, Meta, 0, Unused),
NewCurrent = Current#{Pair => 0},
E#{vars := NewVars, unused_vars := NewUnused, current_vars := NewCurrent}
E#{vars := NewVars, current_vars := {NewCurrent, NewUnused}}
end,
{Var, EE};
expand({Name, Meta, Kind} = Var, E) when is_atom(Name), is_atom(Kind) ->
#{unused_vars := Unused, current_vars := Current} = E,
#{current_vars := {Current, Unused}} = E,
Pair = {Name, elixir_utils:var_context(Meta, Kind)},
case Current of
@@ -374,7 +376,7 @@ expand({Name, Meta, Kind} = Var, E) when is_atom(Name), is_atom(Kind) ->
case Unused of
#{UnusedKey := Entry} when Entry /= false ->
{Var, E#{unused_vars := Unused#{UnusedKey := false}}};
{Var, E#{current_vars := {Current, Unused#{UnusedKey := false}}}};
_ ->
{Var, E}
end;
@@ -484,12 +486,12 @@ expand(Other, E) ->
%% Helpers
escape_env_entries(Meta, #{unused_vars := Unused, current_vars := Current} = Env0) ->
escape_env_entries(Meta, #{current_vars := {Current, Unused}} = Env0) ->
Env1 = case Env0 of
#{function := nil} -> Env0;
_ -> Env0#{lexical_tracker := nil}
end,
Env2 = Env1#{unused_vars := escape_map(Unused), current_vars := escape_map(Current)},
Env2 = Env1#{current_vars := {escape_map(Current), escape_map(Unused)}},
Env3 = elixir_env:linify({?line(Meta), Env2}),
Env3.
@@ -754,7 +756,7 @@ assert_no_ambiguous_op(Name, Meta, [Arg], E) ->
{ambiguous_op, Kind} ->
Pair = {Name, Kind},
case ?key(E, current_vars) of
#{Pair := _} ->
{#{Pair := _}, _} ->
form_error(Meta, E, ?MODULE, {op_ambiguity, Name, Arg});
_ ->
ok
+3 -3
View File
@@ -56,13 +56,13 @@ next_counter(Module) ->
compile(Module, _Block, _Vars, #{line := Line, file := File}) when Module == nil; is_boolean(Module) ->
elixir_errors:form_error([{line, Line}], File, ?MODULE, {invalid_module, Module});
compile(Module, Block, Vars, #{line := Line} = Env) when is_atom(Module) ->
compile(Module, Block, Vars, #{line := Line, current_vars := {Current, _}} = Env) when is_atom(Module) ->
%% In case we are generating a module from inside a function,
%% we get rid of the lexical tracker information as, at this
%% point, the lexical tracker process is long gone.
LexEnv = case ?key(Env, function) of
nil -> Env#{module := Module, unused_vars := #{}};
_ -> Env#{lexical_tracker := nil, function := nil, module := Module, unused_vars := #{}}
nil -> Env#{module := Module, current_vars := {Current, #{}}};
_ -> Env#{lexical_tracker := nil, function := nil, module := Module, current_vars := {Current, #{}}}
end,
case ?key(LexEnv, lexical_tracker) of