Files
n8n-openai-adapter/lib/elixir/src/elixir_locals.erl
T
José Valim 219c6cc867 Move unused_vars check to Elixir land
This gives us better control over when and how unused variables are printed.

As a result, we are able to emit unused variable warnings in situations
we could not before. This also opens up the way for us to remove a
dependency on erl_lint and track types information, which allows us to
speed up compilation times about 5% and allow us to emit more performant
code in some situations.
2018-01-20 19:45:48 +01:00

119 lines
3.9 KiB
Erlang

%% Module responsible for tracking invocations of module calls.
-module(elixir_locals).
-export([
setup/1, cleanup/1, cache_env/1, get_cached_env/1,
record_local/3, record_import/4, record_defaults/4,
yank/2, reattach/5,
ensure_no_import_conflict/3, warn_unused_local/4, format_error/1
]).
-include("elixir.hrl").
-define(attr, {elixir, locals_tracker}).
-define(cache, {elixir, cache_env}).
-define(tracker, 'Elixir.Module.LocalsTracker').
setup(Module) ->
case elixir_config:get(bootstrap) of
false ->
Table = ?tracker:init(),
ets:insert(elixir_module:data_table(Module), {?attr, Table}),
ok;
true ->
ok
end.
cleanup(Module) ->
if_tracker(Module, fun(Tracker) -> ?tracker:delete(Tracker), ok end).
yank(Tuple, Module) ->
if_tracker(Module, fun(Tracker) -> ?tracker:yank(Tracker, Tuple) end).
reattach(Tuple, Kind, Module, Function, Neighbours) ->
if_tracker(Module, fun(Tracker) -> ?tracker:reattach(Tracker, Tuple, Kind, Function, Neighbours) end).
record_local(Tuple, _Module, Function)
when Function == nil; Function == Tuple -> ok;
record_local(Tuple, Module, Function) ->
if_tracker(Module, fun(Tracker) -> ?tracker:add_local(Tracker, Function, Tuple), ok end).
record_import(_Tuple, Receiver, Module, Function)
when Function == nil; Module == Receiver -> false;
record_import(Tuple, Receiver, Module, Function) ->
if_tracker(Module, fun(Tracker) -> ?tracker:add_import(Tracker, Function, Receiver, Tuple), ok end).
record_defaults(_Tuple, _Kind, _Module, 0) ->
ok;
record_defaults(Tuple, Kind, Module, Defaults) ->
if_tracker(Module, fun(Tracker) -> ?tracker:add_defaults(Tracker, Kind, Tuple, Defaults), ok end).
if_tracker(Module, Callback) ->
if_tracker(Module, ok, Callback).
if_tracker(Module, Default, Callback) ->
try ets:lookup_element(elixir_module:data_table(Module), ?attr, 2) of
Tracker -> Callback(Tracker)
catch
error:badarg -> Default
end.
%% CACHING
cache_env(#{line := Line, module := Module} = E) ->
Table = elixir_module:data_table(Module),
Cache = elixir_env:reset_vars(E#{line := nil}),
Pos =
case ets:lookup(Table, ?cache) of
[{_, Key, Cache}] ->
Key;
[{_, PrevKey, _}] ->
Key = PrevKey + 1,
ets:insert(Table, {{cache_env, Key}, Cache}),
ets:insert(Table, {?cache, Key, Cache}),
Key
end,
{Module, {Line, Pos}}.
get_cached_env({Module, {Line, Pos}}) ->
(ets:lookup_element(elixir_module:data_table(Module), {cache_env, Pos}, 2))#{line := Line};
get_cached_env(Env) ->
Env.
%% ERROR HANDLING
ensure_no_import_conflict(_File, 'Elixir.Kernel', _All) ->
ok;
ensure_no_import_conflict(File, Module, All) ->
if_tracker(Module, ok, fun(Tracker) ->
[elixir_errors:form_error(Meta, File, ?MODULE, {function_conflict, Error})
|| {Meta, Error} <- ?tracker:collect_imports_conflicts(Tracker, All)],
ok
end).
warn_unused_local(File, Module, All, Private) ->
if_tracker(Module, [], fun(Tracker) ->
{Unreachable, Warnings} = ?tracker:collect_unused_locals(Tracker, All, Private),
[elixir_errors:form_warn(Meta, File, ?MODULE, Error) || {Meta, Error} <- Warnings],
Unreachable
end).
format_error({function_conflict, {Receivers, Name, Arity}}) ->
io_lib:format("imported ~ts.~ts/~B conflicts with local function",
[elixir_aliases:inspect(hd(Receivers)), Name, Arity]);
format_error({unused_args, {Name, Arity}}) ->
io_lib:format("default arguments in ~ts/~B are never used", [Name, Arity]);
format_error({unused_args, {Name, Arity}, 1}) ->
io_lib:format("the first default argument in ~ts/~B is never used", [Name, Arity]);
format_error({unused_args, {Name, Arity}, Count}) ->
io_lib:format("the first ~B default arguments in ~ts/~B are never used", [Count, Name, Arity]);
format_error({unused_def, {Name, Arity}, defp}) ->
io_lib:format("function ~ts/~B is unused", [Name, Arity]);
format_error({unused_def, {Name, Arity}, defmacrop}) ->
io_lib:format("macro ~ts/~B is unused", [Name, Arity]).