forked from rabbitmq/rabbitmq-public-umbrella
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck_xref
executable file
·77 lines (70 loc) · 3.13 KB
/
check_xref
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/env escript
%% -*- erlang -*-
-mode(compile).
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2010-2014 GoPivotal, Inc. All rights reserved.
%%
main([PackageDir, TmpDir]) ->
{ok, _Pid} = xref:start(?MODULE),
ok = xref:set_default(?MODULE, [{verbose,false}, {warnings,false}]),
{ok, PackageModules} = add_dir(PackageDir, "ebin"),
%% Remove them to avoid clash when we add them via the TmpDir
ok = xref:remove_module(?MODULE, PackageModules),
{ok, _} = xref:add_directory(?MODULE, TmpDir, {recurse, true}),
{ok, otp} = xref:add_release(?MODULE, code:lib_dir(), {name, otp}),
ModulesQ =
"(" ++ lists:nthtail(
2, lists:flatten(
[io_lib:format(" + \"~s\" : Mod", [atom_to_list(Mod)])
|| Mod <- PackageModules])) ++
")",
%% 'U' is the set of unknown Functions
%% '*' is set intersection
%% 'range' takes the RHS in all call pairs (From, To)
%% 'closure' gives the transitive closure
%% 'E' is all edges (i.e. all calls)
%% '|' is the subset of calls from any of the vertices
%%
%% I.e. we construct the transitive closure of all calls from any
%% of the plugin's modules, look at the ranges there of
%% (i.e. callees, not callers), and see if they're unknown
Q = "U * range (closure E | " ++ ModulesQ ++")",
{ok, MFAs} = xref:q(?MODULE, Q),
case filter_noise(MFAs, PackageModules) of
[] -> ok;
PrMFAs -> io:format("Undefined functions:~n"),
[io:format("\t~s:~s/~w~n", [M, F, A]) || {M, F, A} <- PrMFAs],
exit(undefined_functions)
end.
add_dir(PackageDir, Dir) ->
Path = filename:join(PackageDir, Dir),
case filelib:is_dir(Path) of
true -> {ok, _} = xref:add_directory(?MODULE, Path, {recurse, true});
false -> {ok, []}
end.
filter_noise(MFAs, Modules) ->
[MFA || MFA <- MFAs, is_interesting(MFA, Modules)].
is_interesting({'$M_EXPR', _, _}, _Ms) -> false;
is_interesting({_, '$F_EXPR', _}, _Ms) -> false;
is_interesting({_, _, -1}, _Ms) -> false;
%% Cowboy invokes file:sendfile/2 which is only on very recent
%% Erlang. However it is guarded by a call to
%% erlang:function_exported/3.
is_interesting({file, sendfile, 2}, Ms) -> not lists:member(cowboy, Ms);
%% Tests need this function, which technically makes it part of a
%% plugin. But it's only in newer Erlangs than we test with.
is_interesting({cover, flush, 1}, Ms) -> not lists:member(
rabbit_test_runner, Ms);
is_interesting(_, _Ms) -> true.