%% Code for testing during development. Finds misspelled function
%% names and incorrect arity usage. It requiers that the modules
%% tested, have already been debug compiled - use erlc with the
%% +debug_info flag, e.g. :
%%
%% erlc +debug_info xxxx.erl
%%
%% This code assumes that the current directory (when calling
%% xref_test:run) is the build folder (the directory where the .beam
%% files are).
%% This code has been tested on my (hsten) local machine, with a
%%
%% > cd src
%% > erlc -W +debug_info *.erl
%% > erl
%% 1> xref_test:run().
%%
%% in the source directory, but should also work in the build
%% directory using the yxa Makefile to compile it.
%%--------------------------------------------------------------------
%% LICENSE: This file is part of the YXA open-source project at:
%% http://www.stacken.kth.se/project/yxa/
%%--------------------------------------------------------------------
-module(xref_test).
%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
run/0,
run/1,
run/2,
run_shell/0
]).
%%--------------------------------------------------------------------
%% Function: run()
%% Descrip.: run xref on yxa code, to look for bugs
%% Returns : -
%%--------------------------------------------------------------------
run() ->
run([]).
run(Dirs) when is_list(Dirs) ->
run(Dirs, []).
run(Dirs, AddAnalysis) when is_list(Dirs) ->
Xref = foobar,
%% stop any old xref process
try xref:stop(Xref)
catch
throw: _ -> ok;
error: _ -> ok;
exit: _ -> ok
end,
%% start new "empty" xref process
xref:start(Xref, {xref_mode, functions}),
%% add path to OTP modules - they should not be detected as unkown modules
OTP = code:get_path(),
xref:set_library_path(Xref, OTP, [{verbose, true}]),
AddOptions = [
{builtins, false},
{recurse, false},
{verbose, true},
{warnings, true}
],
Dir =
case filelib:wildcard("*.beam") of
[] ->
case filelib:is_dir("../ebin") of
true -> "../ebin";
false -> "."
end;
_ ->
"."
end,
%% tell xref where to look for modules to check
Res = lists:foldl(fun(D, Acc) ->
case xref:add_directory(Xref, D, AddOptions) of
{ok, Mods} ->
Mods ++ Acc;
_ ->
Acc
end
end, [], [Dir] ++ Dirs),
io:format("add_directory:~n ~p~n", [Res]),
%% determine which properties to check with xref
Analysis = [
undefined_function_calls,
undefined_functions,
locals_not_used,
%% this lists lots of functions - some are exported
%% behaviour callbacks, others are unused functions intended
%% for future use (to expose a useful interface to the module)
%% and some are probably callback functions not related to
%% behaviours.
%% exports_not_used,
deprecated_function_calls,
deprecated_functions
%% {deprecated_function_calls, DeprFlag},
%% {deprecated_functions, DeprFlag},
%% {call, FuncSpec},
%% {use, FuncSpec},
%% {module_call, ModSpec},
%% {module_use, ModSpec},
%% {application_call, AppSpec},
%% {application_use, AppSpec},
%% {release_call, RelSpec},
%% {release_use, RelSpec}
] ++ AddAnalysis,
%% format analysis results
Options = [{verbose, true}],
F = fun(AnalysisKind) ->
ARes = filter(xref:analyze(Xref, AnalysisKind, Options), AnalysisKind),
case ARes of
{ok, L} -> L;
L -> L
end,
io:format("~n----------------------------------------------------"),
io:format("~n- ANALYSIS ~p", [AnalysisKind]),
io:format("~n----------------------------------------------------"),
io:format("~n~p~n", [L]),
L
end,
lists:append(lists:map(F, Analysis)) =:= [].
run_shell() ->
case run() of
true ->
erlang:halt(0);
false ->
erlang:halt(1)
end.
%%====================================================================
%% Behaviour functions
%%====================================================================
%%====================================================================
%% Internal functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: filter(Res, AnalysisKind)
%% Res = term(), return value of xref:analyze
%% AnalysisKind = atom(), the xref:analyze kind
%% Descrip.: remove certain xref:analyze output that only appears
%% to be wrong
%% Returns : list() of term()
%%--------------------------------------------------------------------
%% filter out the all calls to local:xxxx/yyy
filter(Res, undefined_function_calls) ->
{ok, L} = Res,
F = fun(E, Acc) ->
case E of
{_, {local,_,_}} -> Acc;
_ -> [E | Acc]
end
end,
lists:reverse(lists:foldl(F, [], L));
%% filter out the all calls to local:xxxx/yyy
filter(Res, undefined_functions) ->
{ok, L} = Res,
F = fun(E, Acc) ->
case E of
{local,_,_} -> Acc;
_ -> [E | Acc]
end
end,
lists:reverse(lists:foldl(F, [], L));
filter(Res, _AnalysisKind) ->
Res.