Make static hooks analyzer working again

This commit is contained in:
Evgeny Khramtsov 2019-05-15 21:40:36 +03:00
parent 2aa181658a
commit 7a03a125aa
1 changed files with 62 additions and 91 deletions

View File

@ -12,14 +12,12 @@
main([Dir]) -> main([Dir]) ->
State = State =
filelib:fold_files( fold_beams(
Dir, ".+\.[eh]rl\$", false, fun(File0, Tree, Acc0) ->
fun(FileIn, Res) -> BareName = filename:rootname(filename:basename(File0)),
case get_forms(FileIn) of Mod = list_to_atom(BareName),
{ok, Forms} -> File = BareName ++ ".erl",
Tree = erl_syntax:form_list(Forms), Acc1 = Acc0#state{file = File, module = Mod},
Mod = list_to_atom(filename:rootname(filename:basename(FileIn))),
Acc0 = analyze_form(Tree, Res#state{module = Mod, file = FileIn}),
erl_syntax_lib:fold( erl_syntax_lib:fold(
fun(Form, Acc) -> fun(Form, Acc) ->
case erl_syntax:type(Form) of case erl_syntax:type(Form) of
@ -50,31 +48,13 @@ main([Dir]) ->
_ -> _ ->
Acc Acc
end end
end, Acc0, Tree); end, Acc1, Tree)
_Err -> end, #state{}, Dir),
Res
end
end, #state{}),
report_orphaned_funs(State), report_orphaned_funs(State),
RunDeps = build_deps(State#state.run_hooks, State#state.hooked_funs), RunDeps = build_deps(State#state.run_hooks, State#state.hooked_funs),
RunFoldDeps = build_deps(State#state.run_fold_hooks, State#state.hooked_funs), RunFoldDeps = build_deps(State#state.run_fold_hooks, State#state.hooked_funs),
emit_module(RunDeps, RunFoldDeps, State#state.specs, Dir, hooks_type_test). emit_module(RunDeps, RunFoldDeps, State#state.specs, Dir, hooks_type_test).
analyze_form(_Form, State) ->
%% case catch erl_syntax_lib:analyze_forms(Form) of
%% Props when is_list(Props) ->
%% M = State#state.module,
%% MFAs = lists:foldl(
%% fun({F, A}, Acc) ->
%% dict:append({M, F}, A, Acc)
%% end, State#state.mfas,
%% proplists:get_value(functions, Props, [])),
%% State#state{mfas = MFAs};
%% _ ->
%% State
%% end.
State.
analyze_run_hook(Form, State) -> analyze_run_hook(Form, State) ->
[Hook|Tail] = erl_syntax:application_arguments(Form), [Hook|Tail] = erl_syntax:application_arguments(Form),
case atom_value(Hook, State) of case atom_value(Hook, State) of
@ -145,7 +125,7 @@ analyze_iq_handler(Form, State) ->
code:ensure_loaded(Mod), code:ensure_loaded(Mod),
case erlang:function_exported(Mod, Fun, 1) of case erlang:function_exported(Mod, Fun, 1) of
false -> false ->
log("~s:~p: Error: function ~s:~s/1 is registered " err("~s:~p: Error: function ~s:~s/1 is registered "
"as iq handler, but is not exported~n", "as iq handler, but is not exported~n",
[State#state.file, erl_syntax:get_pos(Form), [State#state.file, erl_syntax:get_pos(Form),
Mod, Fun]); Mod, Fun]);
@ -179,7 +159,7 @@ build_deps(Hooks, Hooked) ->
code:ensure_loaded(M), code:ensure_loaded(M),
case erlang:function_exported(M, F, Arity) of case erlang:function_exported(M, F, Arity) of
false -> false ->
log("~s:~p: Error: function ~s:~s/~p " err("~s:~p: Error: function ~s:~s/~p "
"is hooked on ~s/~p, but is not " "is hooked on ~s/~p, but is not "
"exported~n", "exported~n",
[FunFile, FunLineNo, M, F, [FunFile, FunLineNo, M, F,
@ -204,7 +184,7 @@ report_orphaned_funs(State) ->
fun({M, F, _, {File, Line}}) -> fun({M, F, _, {File, Line}}) ->
case get_fun_arities(M, F, State) of case get_fun_arities(M, F, State) of
[] -> [] ->
log("~s:~p: Error: function ~s:~s is " err("~s:~p: Error: function ~s:~s is "
"hooked on hook ~s, but is not exported~n", "hooked on hook ~s, but is not exported~n",
[File, Line, M, F, Hook]); [File, Line, M, F, Hook]);
Arities -> Arities ->
@ -219,7 +199,7 @@ report_orphaned_funs(State) ->
end, Arities) of end, Arities) of
false -> false ->
Arity = hd(Arities), Arity = hd(Arities),
log("~s:~p: Error: function ~s:~s/~p is hooked" err("~s:~p: Error: function ~s:~s/~p is hooked"
" on non-existent hook ~s/~p~n", " on non-existent hook ~s/~p~n",
[File, Line, M, F, Arity, Hook, Arity]); [File, Line, M, F, Arity, Hook, Arity]);
true -> true ->
@ -277,7 +257,7 @@ emit_module(RunDeps, RunFoldDeps, Specs, Dir, Module) ->
file:close(Fd), file:close(Fd),
log("Module written to file ~s~n", [File]) log("Module written to file ~s~n", [File])
catch _:{badmatch, {error, Reason}} -> catch _:{badmatch, {error, Reason}} ->
log("writing to ~s failed: ~s", [File, file:format_error(Reason)]) err("writing to ~s failed: ~s", [File, file:format_error(Reason)])
end. end.
emit_run_hooks(Fd, Deps, Specs) -> emit_run_hooks(Fd, Deps, Specs) ->
@ -352,41 +332,32 @@ emit_specs(Fd, Funs, Specs) ->
end end
end, lists:keysort(2, Funs)). end, lists:keysort(2, Funs)).
get_forms(Path) -> fold_beams(Fun, State, Dir) ->
case file:open(Path, [read]) of filelib:fold_files(
{ok, Fd} -> Dir, ".+\.beam\$", false,
parse(Path, Fd, 1, []); fun(File, Acc) ->
Err -> AbsCode = get_code_from_beam(File),
Err lists:foldl(
end. fun(Form, Acc1) ->
Fun(File, Form, Acc1)
end, Acc, AbsCode)
end, State).
parse(Path, Fd, Line, Acc) -> get_code_from_beam(File) ->
{ok, Pos} = file:position(Fd, cur), try
case epp_dodger:parse_form(Fd, Line) of {ok, {_, List}} = beam_lib:chunks(File, [abstract_code]),
{ok, Form, NewLine} -> {_, {raw_abstract_v1, Forms}} = lists:keyfind(abstract_code, 1, List),
{ok, NewPos} = file:position(Fd, cur), Forms
{ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), catch _:{badmatch, _} ->
file:position(Fd, {bof, NewPos}), err("no abstract code found in ~s~n", [File])
AnnForm = erl_syntax:set_ann(Form, RawForm),
parse(Path, Fd, NewLine, [AnnForm|Acc]);
{eof, _} ->
{ok, NewPos} = file:position(Fd, cur),
if NewPos > Pos ->
{ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos),
Form = erl_syntax:text(""),
AnnForm = erl_syntax:set_ann(Form, RawForm),
{ok, lists:reverse([AnnForm|Acc])};
true ->
{ok, lists:reverse(Acc)}
end;
{error, {_, _, ErrDesc}, LineNo} = Err ->
log("~s:~p: Error: ~s~n",
[Path, LineNo, erl_parse:format_error(ErrDesc)]),
Err
end. end.
log(Format, Args) -> log(Format, Args) ->
io:format(standard_io, Format, Args). io:format(standard_io, Format, Args).
err(Format, Args) ->
io:format(standard_error, "Error: " ++ Format, Args),
halt(1).
write(Fd, Format, Args) -> write(Fd, Format, Args) ->
file:write(Fd, io_lib:format(Format, Args)). file:write(Fd, io_lib:format(Format, Args)).