Clean up Macro.Env and hide private fields
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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}}.
|
||||
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user