summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFred Hebert <mononcqc@ferd.ca>2017-06-13 15:16:55 -0400
committerGitHub <noreply@github.com>2017-06-13 15:16:55 -0400
commit21a121fa669efb1463ad9160375e715557d83749 (patch)
tree16908378472fc97530593b0e6b63bc4807b2a5ea
parent9b1aa9191027647eb370eb6b0d5fa9cbfacc21d4 (diff)
parent7f1301f635b8c07fd22ffdf1c9dcf6f5cbba8655 (diff)
Merge pull request #1572 from ferd/fix-xref-undef-behaviour
Prevent xref crashes with undef behaviours
-rw-r--r--.gitignore1
-rw-r--r--src/rebar_prv_xref.erl11
-rw-r--r--test/rebar_xref_SUITE.erl46
3 files changed, 54 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 34b6ef0..129c935 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
_checkouts
.rebar3
rebar3
+rebar3.cmd
_build
.depsolver_plt
*.beam
diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl
index 51154b5..e6a074f 100644
--- a/src/rebar_prv_xref.erl
+++ b/src/rebar_prv_xref.erl
@@ -168,8 +168,15 @@ keyall(Key, List) ->
lists:flatmap(fun({K, L}) when Key =:= K -> L; (_) -> [] end, List).
get_behaviour_callbacks(exports_not_used, Attributes) ->
- [B:behaviour_info(callbacks) || B <- keyall(behaviour, Attributes) ++
- keyall(behavior, Attributes)];
+ lists:map(fun(Mod) ->
+ try
+ Mod:behaviour_info(callbacks)
+ catch
+ error:undef ->
+ ?WARN("Behaviour ~p is used but cannot be found.", [Mod]),
+ []
+ end
+ end, keyall(behaviour, Attributes) ++ keyall(behavior, Attributes));
get_behaviour_callbacks(_XrefCheck, _Attributes) ->
[].
diff --git a/test/rebar_xref_SUITE.erl b/test/rebar_xref_SUITE.erl
index d1c071b..f052fa4 100644
--- a/test/rebar_xref_SUITE.erl
+++ b/test/rebar_xref_SUITE.erl
@@ -10,7 +10,8 @@
all/0,
xref_test/1,
xref_ignore_test/1,
- xref_dep_hook/1]).
+ xref_dep_hook/1,
+ xref_undef_behaviour/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -58,7 +59,7 @@ end_per_testcase(_, _Config) ->
ok.
all() ->
- [xref_test, xref_ignore_test, xref_dep_hook].
+ [xref_test, xref_ignore_test, xref_dep_hook, xref_undef_behaviour].
%% ===================================================================
%% Test cases
@@ -83,6 +84,18 @@ xref_ignore_test(Config) ->
xref_dep_hook(Config) ->
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, []}).
+xref_undef_behaviour(Config) ->
+ AppDir = ?config(apps, Config),
+ State = ?config(state, Config),
+ Name = ?config(app_name, Config),
+ RebarConfig = ?config(rebar_config, Config),
+ %% delete one of the behaviours, which should create new warnings
+ delete_src_file(AppDir, Name, behaviour1),
+ %% just ensure this does not crash
+ Result = rebar3:run(rebar_state:new(State, RebarConfig, AppDir), ["xref"]),
+ verify_results(xref_undef_behaviour, Name, Result).
+
+
%% ===================================================================
%% Helper functions
%% ===================================================================
@@ -123,6 +136,31 @@ verify_test_results(xref_test, AppName, XrefResults, _QueryResults) ->
?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)),
?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)),
ok;
+verify_test_results(xref_undef_behaviour, AppName, XrefResults, _QueryResults) ->
+ AppModules = ["behaviour2", "mymod", "othermod", "somemod"],
+ [Behaviour2Mod, MyMod, OtherMod, SomeMod] =
+ [list_to_atom(AppName ++ "_" ++ Mod) || Mod <- AppModules],
+ UndefFuns = proplists:get_value(undefined_functions, XrefResults),
+ UndefFunCalls = proplists:get_value(undefined_function_calls, XrefResults),
+ LocalsNotUsed = proplists:get_value(locals_not_used, XrefResults),
+ ExportsNotUsed = proplists:get_value(exports_not_used, XrefResults),
+ DeprecatedFuns = proplists:get_value(deprecated_functions, XrefResults),
+ DeprecatedFunCalls = proplists:get_value(deprecated_function_calls, XrefResults),
+ ?assert(lists:member({SomeMod, notavailable, 1}, UndefFuns)),
+ ?assert(lists:member({{OtherMod, somefunc, 0}, {SomeMod, notavailable, 1}},
+ UndefFunCalls)),
+ ?assert(lists:member({MyMod, fdeprecated, 0}, DeprecatedFuns)),
+ ?assert(lists:member({{OtherMod, somefunc, 0}, {MyMod, fdeprecated, 0}},
+ DeprecatedFunCalls)),
+ ?assert(lists:member({MyMod, localfunc2, 0}, LocalsNotUsed)),
+ ?assert(lists:member({Behaviour2Mod, behaviour_info, 1}, ExportsNotUsed)),
+ ?assert(lists:member({MyMod, other2, 1}, ExportsNotUsed)),
+ ?assert(lists:member({OtherMod, somefunc, 0}, ExportsNotUsed)),
+ ?assert(lists:member({MyMod, bh1_a, 1}, ExportsNotUsed)),
+ ?assert(lists:member({MyMod, bh1_b, 1}, ExportsNotUsed)),
+ ?assertNot(lists:member({MyMod, bh2_a, 1}, ExportsNotUsed)),
+ ?assertNot(lists:member({MyMod, bh2_b, 1}, ExportsNotUsed)),
+ ok;
verify_test_results(xref_ignore_test, AppName, XrefResults, _QueryResults) ->
AppModules = ["behaviour1", "behaviour2", "mymod", "othermod", "somemod"],
[_Behaviour1Mod, _Behaviour2Mod, _MyMod, _OtherMod, SomeMod] =
@@ -141,6 +179,10 @@ write_src_file(Dir, AppName, Module, IgnoreXref) ->
ok = filelib:ensure_dir(Erl),
ok = ec_file:write(Erl, get_module_body(Module, AppName, IgnoreXref)).
+delete_src_file(Dir, AppName, Module) ->
+ Erl = filename:join([Dir, "src", module_name(AppName, Module)]),
+ ok = file:delete(Erl).
+
module_name(AppName, Module) ->
lists:flatten([AppName, "_", atom_to_list(Module), ".erl"]).