%% 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.