1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19
20-module(xref_SUITE).
21
22%-define(debug, true).
23
24-ifdef(debug).
25-define(format(S, A), io:format(S, A)).
26-define(line, put(line, ?LINE), ).
27-define(config(X,Y), "./log_dir/").
28-define(t,test_server).
29-define(datadir, "xref_SUITE_data").
30-define(privdir, "xref_SUITE_priv").
31-define(copydir, "xref_SUITE_priv/datacopy").
32-else.
33-include_lib("common_test/include/ct.hrl").
34-define(format(S, A), ok).
35-define(datadir, proplists:get_value(data_dir, Conf)).
36-define(privdir, proplists:get_value(priv_dir, Conf)).
37-define(copydir, proplists:get_value(copy_dir, Conf)).
38-endif.
39
40-export([all/0, suite/0, groups/0,
41         init_per_suite/1, end_per_suite/1]).
42
43-export([addrem/1, convert/1, intergraph/1, lines/1, loops/1,
44         no_data/1, modules/1]).
45
46-export([add/1, default/1, info/1, lib/1, read/1, read2/1, remove/1,
47         replace/1, update/1, deprecated/1, trycatch/1,
48         fun_mfa/1, fun_mfa_r14/1,
49         fun_mfa_vars/1, qlc/1]).
50
51-export([analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1,
52         behaviour/1]).
53
54-export([format_error/1, otp_7423/1, otp_7831/1, otp_10192/1, otp_13708/1,
55         otp_14464/1, otp_14344/1]).
56
57-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
58
59-import(sofs, [converse/1, from_term/1, intersection/2, is_sofs_set/1,
60               range/1, relation_to_family/1, set/1, to_external/1,
61               union/2]).
62
63%% Checks some info counters of a server and some relations that should hold.
64-export([check_count/1, check_state/1]).
65
66-include_lib("kernel/include/file.hrl").
67-include_lib("tools/src/xref.hrl").
68
69suite() ->
70    [{ct_hooks,[ts_install_cth]},
71     {timetrap,{minutes,2}}].
72
73all() ->
74    [{group, xref}, {group, files}, {group, analyses},
75     {group, misc}].
76
77groups() ->
78    [{xref, [],
79      [addrem, convert, intergraph, lines, loops, no_data,
80       modules]},
81     {files, [],
82      [add, default, info, lib, read, read2, remove, replace,
83       update, deprecated, trycatch, fun_mfa,
84       fun_mfa_r14, fun_mfa_vars, qlc]},
85     {analyses, [],
86
87      [analyze, basic, md, q, variables, unused_locals, behaviour]},
88     {misc, [], [format_error, otp_7423, otp_7831, otp_10192, otp_13708,
89                 otp_14464, otp_14344]}].
90
91
92init_per_suite(Conf) when is_list(Conf) ->
93    DataDir = ?datadir,
94    PrivDir = ?privdir,
95    CopyDir = fname(PrivDir, "datacopy"),
96    ok = file:make_dir(CopyDir),
97    TarFile = fname(PrivDir, "datacopy.tgz"),
98    ok = file:set_cwd(DataDir),
99    ok = erl_tar:create(TarFile, ["."], [compressed]),
100    ok = erl_tar:extract(TarFile, [compressed, {cwd,CopyDir}]),
101    ok = file:delete(TarFile),
102    [{copy_dir, CopyDir}|Conf].
103
104end_per_suite(Conf) when is_list(Conf) ->
105    ok.
106
107%% Seems a bit short...
108%% Simple test of removing modules
109addrem(Conf) when is_list(Conf) ->
110    S0 = new(),
111
112    F1 = {m1,f1,1},
113    F2 = {m2,f1,2},
114
115    E1 = {F1,F2},
116    E2 = {F2,F1},
117
118    D1 = {F1,12},
119    DefAt_m1 = [D1],
120    X_m1 = [F1],
121    % L_m1 = [],
122    XC_m1 = [E1],
123    LC_m1 = [],
124    LCallAt_m1 = [],
125    XCallAt_m1 = [{E1,13}],
126    Info1 = #xref_mod{name = m1, app_name = [a1]},
127    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
128                    XC_m1, LC_m1),
129
130    D2 = {F2,7},
131    DefAt_m2 = [D2],
132    X_m2 = [F2],
133    % L_m2 = [],
134    XC_m2 = [E2],
135    LC_m2 = [],
136    LCallAt_m2 = [],
137    XCallAt_m2 = [{E2,96}],
138    Info2 = #xref_mod{name = m2, app_name = [a2]},
139    S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
140                    XC_m2, LC_m2),
141
142    S5 = set_up(S2),
143
144    {ok, XMod1, S6} = remove_module(S5, m1),
145    [a1] = XMod1#xref_mod.app_name,
146    {ok, XMod2, S6a} = remove_module(S6, m2),
147    [a2] = XMod2#xref_mod.app_name,
148    S7 = set_up(S6a),
149
150    AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
151    S9 = add_application(S7, AppInfo1),
152    S10 = set_up(S9),
153    AppInfo2 = #xref_app{name = a2, rel_name = [r1]},
154    _S11 = add_application(S10, AppInfo2),
155    ok.
156
157%% Coercion of data
158convert(Conf) when is_list(Conf) ->
159    S0 = new(),
160
161    F1 = {m1,f1,1},
162    F6 = {m1,f2,6}, % X
163    F2 = {m2,f1,2},
164    F3 = {m2,f2,3}, % X
165    F7 = {m2,f3,7}, % X
166    F4 = {m3,f1,4}, % X
167    F5 = {m3,f2,5},
168
169    UF1 = {m1,f12,17},
170    UF2 = {m17,f17,177},
171
172    E1 = {F1,F3}, % X
173    E2 = {F6,F7}, % X
174    E3 = {F2,F6}, % X
175    E4 = {F1,F4}, % X
176    E5 = {F4,F5},
177    E6 = {F7,F4}, % X
178
179    UE1 = {F2,UF2}, % X
180    UE2 = {F5,UF1}, % X
181
182    D1 = {F1,12},
183    D6 = {F6,3},
184    DefAt_m1 = [D1,D6],
185    X_m1 = [F6],
186    % L_m1 = [F1],
187    XC_m1 = [E1,E2,E4],
188    LC_m1 = [],
189    LCallAt_m1 = [],
190    XCallAt_m1 = [{E1,13},{E2,17},{E4,7}],
191    Info1 = #xref_mod{name = m1, app_name = [a1]},
192    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
193                    XC_m1, LC_m1),
194
195    D2 = {F2,7},
196    D3 = {F3,9},
197    D7 = {F7,19},
198    DefAt_m2 = [D2,D3,D7],
199    X_m2 = [F3,F7],
200    % L_m2 = [F2],
201    XC_m2 = [E3,E6,UE1],
202    LC_m2 = [],
203    LCallAt_m2 = [],
204    XCallAt_m2 = [{E3,96},{E6,12},{UE1,77}],
205    Info2 = #xref_mod{name = m2, app_name = [a2]},
206    S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
207                    XC_m2, LC_m2),
208
209    D4 = {F4,6},
210    D5 = {F5,97},
211    DefAt_m3 = [D4,D5],
212    X_m3 = [F4],
213    % L_m3 = [F5],
214    XC_m3 = [UE2],
215    LC_m3 = [E5],
216    LCallAt_m3 = [{E5,19}],
217    XCallAt_m3 = [{UE2,22}],
218    Info3 = #xref_mod{name = m3, app_name = [a3]},
219    S3 = add_module(S2, Info3, DefAt_m3, X_m3, LCallAt_m3, XCallAt_m3,
220                    XC_m3, LC_m3),
221
222    Info4 = #xref_mod{name = m4, app_name = [a2]},
223    S4 = add_module(S3, Info4, [], [], [], [], [], []),
224
225    AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
226    S9 = add_application(S4, AppInfo1),
227    AppInfo2 = #xref_app{name = a2, rel_name = [r1]},
228    S10 = add_application(S9, AppInfo2),
229    AppInfo3 = #xref_app{name = a3, rel_name = [r2]},
230    S11 = add_application(S10, AppInfo3),
231
232    RelInfo1 = #xref_rel{name = r1},
233    S12 = add_release(S11, RelInfo1),
234    RelInfo2 = #xref_rel{name = r2},
235    S13 = add_release(S12, RelInfo2),
236
237    S = set_up(S13),
238
239    {ok, _} = eval("(Lin)(m1->m1:Mod) * m1->m1", type_error, S),
240    {ok, _} = eval("(XXL)(Lin)(m1->m1:Mod) * m1->m1", type_error, S),
241
242    AllDefAt = eval("(Lin) M", S),
243    AllV = eval("(Fun) M", S),
244    AllCallAt = eval("(XXL)(Lin) E", S),
245    AllE = eval("E", S),
246
247    AM = eval("AM", S),
248    A = eval("A", S),
249    R = eval("R", S),
250
251
252    % vertices
253    % general 1 step
254    {ok, _} = eval("(Fun) (Lin) M", AllV, S),
255    {ok, _} = eval("(Fun) (Lin) (Lin) M", AllV, S),
256    {ok, _} = eval(f("(Fun) (Lin) ~p", [[F1, F3]]), [F1,F3], S),
257    {ok, _} = eval(f("(Mod) ~p", [AllV]), [m1,m17,m2,m3], S),
258    {ok, _} = eval(f("(Mod) ~p", [[F1,F3,F6]]), [m1,m2], S),
259    {ok, _} = eval("(App) M", A, S),
260    {ok, _} = eval(f("(App) ~p", [[m1,m2,m4]]), [a1,a2], S),
261    {ok, _} = eval(f("(Rel) ~p", [A]), R, S),
262    {ok, _} = eval(f("(Rel) ~p", [[a1,a2,a2]]), [r1], S),
263    % general 2 steps
264    {ok, _} = eval("(Mod) (Lin) M", [m1,m17,m2,m3], S),
265    {ok, _} = eval(f("(App) ~p", [AllV]), [a1,a2,a3], S),
266    {ok, _} = eval("(Rel) M", R, S),
267    % general 4 steps
268    {ok, _} = eval("(Rel) (Lin) M", [r1,r2], S),
269
270    % special 1 step
271    {ok, _} = eval(f("(Lin) ~p", [AllV]), AllDefAt, S),
272    {ok, _} = eval(f("(Lin) ~p", [[F1,F3]]), [{F1,12},{F3,9}], S),
273    {ok, _} = eval("(Fun) M", AllV, S),
274    {ok, _} = eval(f("(Fun) ~p", [[m1,m2]]), [F1,F2,F3,F6,F7,UF1], S),
275    {ok, _} = eval(f("(Mod) ~p", [A]), AM, S),
276    {ok, _} = eval(f("(Mod) ~p", [[a1,a2]]), [m1,m2,m4], S),
277    {ok, _} = eval(f("(App) ~p", [R]), A, S),
278    {ok, _} = eval(f("(App) ~p", [[r1]]), [a1,a2], S),
279    % special 2 steps
280    {ok, _} = eval("(Lin) M", AllDefAt, S),
281    AnalyzedV = eval("(Fun) AM", S),
282    {ok, _} = eval(f("(Fun) ~p", [A]), AnalyzedV, S),
283    {ok, _} = eval(f("(Mod) ~p", [R]), AM, S),
284    % special 4 steps
285    AnalyzedAllDefAt = eval("(Lin) AM", S),
286    {ok, _} = eval("(Lin) R", AnalyzedAllDefAt, S),
287
288    % edges
289    Ms = [{m1,m2},{m1,m3},{m2,m1},{m2,m3},{m3,m3}],
290    UMs = [{m2,m17},{m3,m1}],
291    AllMs = append(Ms, UMs),
292    As = [{a1,a2},{a1,a3},{a2,a1},{a2,a3},{a3,a3}],
293    Rs = [{r1,r1},{r1,r2},{r2,r2}],
294
295    % general 1 step
296    {ok, _} = eval("(Fun) (Lin) E", AllE, S),
297    {ok, _}  = eval(f("(Fun)(Lin) ~p", [[E1, E6]]), [E1, E6], S),
298    {ok, _} = eval("(Mod) E", AllMs, S),
299    {ok, _} = eval(f("(Mod) ~p", [[E1, E6]]), [{m1,m2},{m2,m3}], S),
300    {ok, _} = eval(f("(App) ~p", [As]), As, S),
301    {ok, _} = eval("(App) [m1->m2,m2->m3]", [{a1,a2},{a2,a3}], S),
302    {ok, _} = eval(f("(Rel) ~p", [As]), Rs, S),
303    {ok, _} = eval("(Rel) a1->a2", [{r1,r1}], S),
304
305    % special 1 step
306    {ok, _} = eval("(XXL) (Lin) (Fun) E", AllCallAt, S),
307    {ok, _} = eval("(XXL) (XXL) (Lin) (Fun) E", AllCallAt, S),
308
309    {ok, _} = eval(f("(XXL) (Lin) ~p", [[E1, E6]]),
310                   [{{D1,D3},[13]}, {{D7,D4},[12]}], S),
311    {ok, _} = eval(f("(Fun) ~p", [AllMs]), AllE, S),
312    {ok, _} = eval("(Fun) [m1->m2,m2->m3]", [E1,E2,E6], S),
313    {ok, _} = eval(f("(Mod) ~p", [As]), Ms, S),
314    {ok, _} = eval("(Mod) [a1->a2,a2->a3]", [{m1,m2},{m2,m3}], S),
315    {ok, _} = eval(f("(App) ~p", [Rs]), As, S),
316    {ok, _} = eval("(App) r1->r1", [{a1,a2},{a2,a1}], S),
317    ok.
318
319%% Inter Call Graph
320intergraph(Conf) when is_list(Conf) ->
321    S0 = new(),
322
323    F1 = {m1,f1,1}, % X
324    F2 = {m1,f2,2}, % X
325    F3 = {m1,f3,3},
326    F4 = {m1,f4,4},
327    F5 = {m1,f5,5},
328
329    F6 = {m2,f1,6}, % X
330    F7 = {m2,f1,7},
331    F8 = {m2,f1,8},
332    F9 = {m2,f1,9},
333    F10 = {m2,f1,10},
334    F11 = {m2,f1,11},
335
336    % Note: E1 =:= E4!
337    E1 = {F2,F1},
338    E2 = {F2,F3},
339    E3 = {F3,F1},
340    E4 = {F2,F1}, % X
341    E5 = {F4,F2},
342    E6 = {F5,F4},
343    E7 = {F4,F5},
344
345    E8 = {F6,F7},
346    E9 = {F7,F8},
347    E10 = {F8,F1}, % X
348    E11 = {F6,F9},
349    E12 = {F6,F10},
350    E13 = {F9,F11},
351    E14 = {F10,F11},
352    E15 = {F11,F1}, % X
353
354    D1 = {F1,1},
355    D2 = {F2,2},
356    D3 = {F3,3},
357    D4 = {F4,4},
358    D5 = {F5,5},
359    DefAt_m1 = [D1,D2,D3,D4,D5],
360    X_m1 = [F1,F2],
361    % L_m1 = [F3,F4,F5],
362    XC_m1 = [E4],
363    LC_m1 = [E1,E2,E3,E5,E6,E7],
364    % Note: E1 and E4 together!
365    LCallAt_m1 = [{E1,1},{E2,2},{E3,3},{E5,5},{E6,6},{E7,7}],
366    XCallAt_m1 = [{E1,4}],
367    Info1 = #xref_mod{name = m1, app_name = [a1]},
368    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
369                    XC_m1, LC_m1),
370
371    D6 = {F6,6},
372    D7 = {F7,7},
373    D8 = {F8,8},
374    D9 = {F9,9},
375    D10 = {F10,10},
376    D11 = {F11,11},
377    DefAt_m2 = [D6,D7,D8,D9,D10,D11],
378    X_m2 = [F6],
379    % L_m2 = [F7,F8,F9,F10,F11],
380    XC_m2 = [E10,E15],
381    LC_m2 = [E8,E9,E11,E12,E13,E14],
382    LCallAt_m2 = [{E8,8},{E9,9},{E11,11},{E12,12},{E13,13},{E14,14}],
383    XCallAt_m2 = [{E10,10},{E15,15}],
384    Info2 = #xref_mod{name = m2, app_name = [a2]},
385    S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
386                    XC_m2, LC_m2),
387
388    AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
389    S5 = add_application(S2, AppInfo1),
390    AppInfo2 = #xref_app{name = a2, rel_name = [r1]},
391    S6 = add_application(S5, AppInfo2),
392
393    RelInfo = #xref_rel{name = r1},
394    S7 = add_release(S6, RelInfo),
395
396    S = set_up(S7),
397
398    {ok, _} = eval("EE | m1", [E1,E5,E6,E7], S),
399    {ok, _} = eval("EE | m2", [{F6,F1}], S),
400    {ok, _} = eval("EE | m2 + EE | m2", [{F6,F1}], S),
401
402    {ok, _} = eval("(Fun)(Lin)(E | m1)",
403                   to_external(union(set(XC_m1), set(LC_m1))), S),
404    {ok, _} = eval("(XXL)(ELin) (EE | m1)",
405                   [{{D2,D1},[1,2,4]},{{D4,D2},[5]},{{D5,D4},[6]},{{D4,D5},[7]}],
406                   S),
407    {ok, _} = eval("(XXL)(ELin)(EE | m2)", [{{D6,D1},[8,11,12]}], S),
408    {ok, _} = eval("(XXL)(ELin)(ELin)(EE | m2)",
409                   [{{D6,D1},[8,11,12]}], S),
410
411    %% Combining graphs (equal or different):
412    {ok, _} = eval("(XXL)(ELin)(EE | m2 + EE | m2)",
413                   [{{D6,D1},[8,11,12]}], S),
414    {ok, _} = eval("(XXL)(ELin)(EE | m2 * EE | m2)",
415                   [{{D6,D1},[8,11,12]}], S),
416    {ok, _} = eval("(XXL)(ELin)(EE | m2 - EE | m1)",
417                   [{{D6,D1},[8,11,12]}], S),
418    {ok, _} = eval("(XXL)(ELin)(EE | m2 - E | m2)",
419                   [{{D6,D1},[8,11,12]}], S),
420    {ok, _} = eval("(XXL)(ELin)(Fun)(ELin)(EE | m2)",
421                   [{{D6,D1},[8,11,12]}], S),
422    {ok, _} = eval("EE | m1 + E | m1", LC_m1, S),
423    {ok, _} = eval(f("EE | ~p + E | ~p", [F2, F2]), [E1,E2], S),
424    %% [1,4] from 'calls' is a subset of [1,2,4] from Inter Call Graph:
425    {ok, _} = eval(f("(XXL)(Lin) (E | ~p)", [F2]),
426                   [{{D2,D1},[1,4]},{{D2,D3},[2]}], S),
427
428    {ok, _} = eval(f("(XXL)(ELin) (EE | ~p)", [F2]),
429                   [{{D2,D1},[1,2,4]}], S),
430    {ok, _} = eval(f("(XXL)((ELin)(EE | ~p) + (Lin)(E | ~p))", [F2, F2]),
431                   [{{D2,D1},[1,2,4]},{{D2,D3},[2]}], S),
432    {ok, _} =
433    eval(f("(XXL)((ELin) ~p + (Lin) ~p)", [{F2, F1}, {F2, F1}]),
434         [{{D2,D1},[1,2,4]}], S),
435    {ok, _} = eval(f("(Fun)(Lin) ~p", [{F2, F1}]), [E1], S),
436    %% The external call E4 is included in the reply:
437    {ok, _} = eval("(XXL)(Lin)(LC | m1)",
438                   [{{D2,D1},[1,4]},{{D2,D3},[2]},{{D3,D1},[3]},
439                    {{D4,D2},[5]},{{D4,D5},[7]},{{D5,D4},[6]}], S),
440    %% The local call E1 is included in the reply:
441    {ok, _} = eval("(XXL)(Lin)(XC | m1)", [{{D2,D1},[1,4]}], S),
442
443    {ok, _} = eval(f("(LLin) (E | ~p || ~p) + (XLin) (E | ~p || ~p)",
444                     [F2, F1, F2, F1]), [{E4,[1,4]}], S),
445
446    {ok, _} = eval("# (ELin) E", 6, S),
447
448    ok.
449
450%% More test of Inter Call Graph, and regular expressions
451lines(Conf) when is_list(Conf) ->
452    S0 = new(),
453
454    F1 = {m1,f1,1}, % X
455    F2 = {m1,f2,2},
456    F3 = {m1,f3,3},
457    F4 = {m2,f4,4}, % X
458    F5 = {m1,f5,5}, % X
459    F6 = {m1,f6,6},
460
461    E1 = {F1,F2},
462    E2 = {F2,F1}, % X
463    E3 = {F3,F2},
464    E4 = {F1,F4}, % X
465    E5 = {F2,F4}, % X
466    E6 = {F5,F6},
467    E7 = {F6,F4}, % X
468
469    D1 = {F1,1},
470    D2 = {F2,2},
471    D3 = {F3,3},
472    D4 = {F4,4},
473    D5 = {F5,5},
474    D6 = {F6,6},
475
476    DefAt_m1 = [D1,D2,D3,D5,D6],
477    X_m1 = [F1,F5],
478    % L_m1 = [F2,F3,F6],
479    XC_m1 = [E4,E5,E7],
480    LC_m1 = [E1,E2,E3,E6],
481    LCallAt_m1 = [{E1,1},{E3,3},{E6,6}],
482    XCallAt_m1 = [{E2,2},{E4,4},{E5,5},{E7,7}],
483    Info1 = #xref_mod{name = m1, app_name = [a1]},
484    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
485                    XC_m1, LC_m1),
486
487    DefAt_m2 = [D4],
488    X_m2 = [F4],
489    % L_m2 = [],
490    XC_m2 = [],
491    LC_m2 = [],
492    LCallAt_m2 = [],
493    XCallAt_m2 = [],
494    Info2 = #xref_mod{name = m2, app_name = [a2]},
495    S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
496                    XC_m2, LC_m2),
497
498    AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
499    S5 = add_application(S2, AppInfo1),
500    AppInfo2 = #xref_app{name = a2, rel_name = [r1]},
501    S6 = add_application(S5, AppInfo2),
502
503    RelInfo = #xref_rel{name = r1},
504    S7 = add_release(S6, RelInfo),
505
506    S = set_up(S7),
507
508    {ok, _} = eval("(XXL) (ELin) (EE | m1)",
509                   [{{D1,D1},[1]},{{D1,D4},[1,4]},{{D3,D1},[3]},{{D3,D4},[3]},
510                    {{D5,D4},[6]}], S),
511    {ok, _} = eval("(XXL)(Lin) (E | m1)",
512                   [{{D1,D2},[1]},{{D1,D4},[4]},{{D2,D1},[2]},
513                    {{D2,D4},[5]},{{D3,D2},[3]},{{D5,D6},[6]},{{D6,D4},[7]}],
514                   S),
515    {ok, _} = eval("(E | m1) + (EE | m1)",
516                   [E1,E2,E3,E4,E5,E6,E7,{F1,F1},{F3,F1},{F3,F4},{F5,F4}],
517                   S),
518    {ok, _} = eval("(Lin)(E | m1)",
519                   [{E4,[4]},{E1,[1]},{E2,[2]},{E5,[5]},
520                    {E3,[3]},{E7,[7]},{E6,[6]}], S),
521    {ok, _} = eval("(ELin)(EE | m1)",
522                   [{{F1,F1},[1]},{{F1,F4},[1,4]},{{F3,F1},[3]},{{F3,F4},[3]},
523                    {{F5,F4},[6]}], S),
524    {ok, _} = eval("(Lin)(E | m1) + (ELin)(EE | m1)",
525                   [{E4,[1,4]},{E1,[1]},{E2,[2]},{E5,[5]},
526                    {E3,[3]},{E7,[7]},{E6,[6]},
527                    {{F1,F1},[1]},{{F3,F1},[3]},{{F3,F4},[3]},
528                    {{F5,F4},[6]}], S),
529    {ok, _} = eval("(Lin)(E | m1) - (ELin)(EE | m1)",
530                   [{E1,[1]},{E2,[2]},{E5,[5]},
531                    {E3,[3]},{E7,[7]},{E6,[6]}], S),
532    {ok, _} = eval("(Lin)(E | m1) * (ELin)(EE | m1)",
533                   [{E4,[4]}], S),
534    {ok, _} = eval("(XXL)(Lin) (E | m1)",
535                   [{{D1,D4},[4]},{{D1,D2},[1]},{{D2,D1},[2]},{{D2,D4},[5]},
536                    {{D3,D2},[3]},{{D6,D4},[7]},{{D5,D6},[6]}], S),
537    {ok, _} = eval("(XXL)(ELin) (EE | m1)",
538                   [{{D1,D1},[1]},{{D1,D4},[1,4]},{{D3,D1},[3]},{{D3,D4},[3]},
539                    {{D5,D4},[6]}], S),
540    {ok, _} = eval("(XXL)(Lin)(Fun)(Lin) (E | m1)",
541                   [{{D1,D4},[4]},{{D1,D2},[1]},{{D2,D1},[2]},{{D2,D4},[5]},
542                    {{D3,D2},[3]},{{D6,D4},[7]},{{D5,D6},[6]}], S),
543    {ok, _} = eval("(XXL)(ELin)(Fun)(ELin) (EE | m1)",
544                   [{{D1,D1},[1]},{{D1,D4},[1,4]},{{D3,D1},[3]},{{D3,D4},[3]},
545                    {{D5,D4},[6]}], S),
546
547    %% A few tests on regexp.
548    {ok, _} = eval("\"(foo\":Mod", parse_error, S),
549    {ok, _} = eval("_Foo:_/_", parse_error, S),
550    {ok, _} = eval("\".*foo\"", parse_error, S),
551    {ok, _} = eval("_:_/_:Lin", parse_error, S),
552    {ok, _} = eval("_:_/_:Mod", parse_error, S),
553    {ok, _} = eval("_:_/_:App", parse_error, S),
554    {ok, _} = eval("_:_/_:Rel", parse_error, S),
555    {ok, _} = eval("m2:_/4", [F4], S),
556    {ok, _} = eval("m2:_/4:Fun", [F4], S),
557    {ok, _} = eval("\"m.?\":\"f.*\"/\"6\"", [F6], S),
558    {ok, _} = eval("_:_/6", [F6], S),
559    {ok, _} = eval("m1:\"f1\"/_", [F1], S),
560    {ok, _} = eval("\"m1\":f1/_", [F1], S),
561    {ok, _} = eval("\"m1\":Mod", [m1], S),
562    {ok, _} = eval("\"a1\":App", [a1], S),
563    {ok, _} = eval("\"r1\":Rel", [r1], S),
564    {ok, _} = eval("_:_/-1", [], S),
565
566    ok.
567
568%% More Inter Call Graph, loops and "unusual" cases
569loops(Conf) when is_list(Conf) ->
570    S0 = new(),
571
572    F1 = {m1,f1,1}, % X
573    F2 = {m1,f2,2},
574    F3 = {m1,f3,3}, % X
575    F4 = {m1,f4,4},
576    F5 = {m1,f5,5},
577    F6 = {m1,f1,6}, % X
578    F7 = {m1,f1,7},
579
580    E1 = {F1,F1}, % X
581    E2 = {F2,F2},
582    E3 = {F3,F4},
583    E4 = {F4,F5},
584    E5 = {F5,F3}, % X
585
586    D1 = {F1,1},
587    D2 = {F2,2},
588    D3 = {F3,3},
589    D4 = {F4,4},
590    D5 = {F5,5},
591    D6 = {F6,6},
592    D7 = {F7,7},
593    DefAt_m1 = [D1,D2,D3,D4,D5,D6,D7],
594    X_m1 = [F1,F3,F6],
595    % L_m1 = [F2,F4,F5],
596    XC_m1 = [],
597    LC_m1 = [E1,E2,E3,E4,E5],
598    LCallAt_m1 = [{E2,2},{E3,3},{E4,4}],
599    XCallAt_m1 = [{E1,1},{E5,5}],
600    Info1 = #xref_mod{name = m1, app_name = [a1]},
601    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
602                    XC_m1, LC_m1),
603
604    S = set_up(S1),
605
606    % Neither F6 nor F7 is included. Perhaps one should change that?
607    {ok, _} = eval("EE | m1", [E1,E2,{F3,F3}], S),
608    {ok, _} = eval(f("(XXL)(ELin) (EE | ~p)", [F3]), [{{D3,D3},[3]}], S),
609
610    {ok, _} = eval("m1->m1 | m1->m1", type_error, S),
611    {ok, _} = eval(f("~p | ~p", [F2, F1]), type_error, S),
612
613    {ok, _} = eval(f("range (closure EE | ~p)", [F1]), [F1], S),
614    {ok, _} = eval(f("domain (closure EE || ~p)", [F3]), [F3], S),
615
616    {ok, _} = eval(f("domain (closure E || ~p)", [F3]), [F3,F4,F5], S),
617
618    {ok, _} = eval("components E", [[F1],[F2],[F3,F4,F5]], S),
619    {ok, _} = eval("components EE", [[F1],[F2],[F3]], S),
620
621    ok.
622
623%% Simple tests when there is no data
624no_data(Conf) when is_list(Conf) ->
625    S0 = new(),
626    S1 = set_up(S0),
627    {ok, _} = eval("M", [], S1),
628    {ok, _} = eval("A", [], S1),
629    {ok, _} = eval("R", [], S1),
630
631    ModInfo = #xref_mod{name = m, app_name = []},
632    S2 = add_module(S1, ModInfo, [], [], [], [], [], []),
633    AppInfo = #xref_app{name = a, rel_name = []},
634    S3 = add_application(S2, AppInfo),
635    RelInfo = #xref_rel{name = r, dir = ""},
636    S4 = add_release(S3, RelInfo),
637    S5 = set_up(S4),
638    {ok, _} = eval("M", [m], S5),
639    {ok, _} = eval("A", [a], S5),
640    {ok, _} = eval("R", [r], S5),
641    ok.
642
643%% Modules mode
644modules(Conf) when is_list(Conf) ->
645    CopyDir = ?copydir,
646    Dir = fname(CopyDir, "rel2"),
647    X = fname(Dir, "x.erl"),
648    Y = fname(Dir, "y.erl"),
649    A1_1 = fname([Dir,"lib","app1-1.1"]),
650    A2 = fname([Dir,"lib","app2-1.1"]),
651    EB1_1 = fname(A1_1, "ebin"),
652    EB2 = fname(A2, "ebin"),
653    Xbeam = fname(EB2, "x.beam"),
654    Ybeam = fname(EB1_1, "y.beam"),
655
656    {ok, x} = compile:file(X, [debug_info, {outdir,EB2}]),
657    {ok, y} = compile:file(Y, [debug_info, {outdir,EB1_1}]),
658
659    {ok, S0} = xref_base:new([{xref_mode, modules}]),
660    {ok, release2, S1} =
661    xref_base:add_release(S0, Dir, [{name,release2}]),
662    S = set_up(S1),
663    {{error, _, {unavailable_analysis, undefined_function_calls}}, _} =
664    xref_base:analyze(S, undefined_function_calls),
665    {{error, _, {unavailable_analysis, locals_not_used}}, _} =
666    xref_base:analyze(S, locals_not_used),
667    {{error, _, {unavailable_analysis, {call, foo}}}, _} =
668    xref_base:analyze(S, {call, foo}),
669    {{error, _, {unavailable_analysis, {use, foo}}}, _} =
670    xref_base:analyze(S, {use, foo}),
671    analyze(undefined_functions, [{x,undef,0}], S),
672    5 = length(xref_base:info(S)),
673
674    %% More: all info, conversions.
675
676    ok = file:delete(Xbeam),
677    ok = file:delete(Ybeam),
678    ok = xref_base:delete(S),
679    ok.
680
681
682%% Add modules, applications, releases, directories
683add(Conf) when is_list(Conf) ->
684    CopyDir = ?copydir,
685    Dir = fname(CopyDir, "rel2"),
686    UDir = fname([CopyDir,"dir","unreadable"]),
687    DDir = fname(CopyDir,"dir"),
688    UFile = fname([DDir, "dir","unreadable.beam"]),
689    X = fname(Dir, "x.erl"),
690    Y = fname(Dir, "y.erl"),
691    A1_1 = fname([Dir,"lib","app1-1.1"]),
692    A2 = fname([Dir,"lib","app2-1.1"]),
693    EB1_1 = fname(A1_1, "ebin"),
694    EB2 = fname(A2, "ebin"),
695    Xbeam = fname(EB2, "x.beam"),
696    Ybeam = fname(EB1_1, "y.beam"),
697
698    {ok, x} = compile:file(X, [debug_info, {outdir,EB2}]),
699    {ok, y} = compile:file(Y, [debug_info, {outdir,EB1_1}]),
700
701    case os:type() of
702        {unix, _} ->
703            make_udir(UDir),
704            make_ufile(UFile);
705        _ ->
706            true
707    end,
708
709    {error, _, {invalid_options,[not_an_option] }} =
710    xref_base:new([not_an_option]),
711    {error, _, {invalid_options,[{verbose,not_a_value}] }} =
712    xref_base:new([{verbose,not_a_value}]),
713    S = new(),
714    {error, _, {invalid_options,[not_an_option]}} =
715    xref_base:set_up(S, [not_an_option]),
716    {error, _, {invalid_options,[{builtins,true},not_an_option]}} =
717    xref_base:add_directory(S, foo, [{builtins,true},not_an_option]),
718    {error, _, {invalid_options,[{builtins,not_a_value}]}} =
719    xref_base:add_directory(S, foo, [{builtins,not_a_value}]),
720    {error, _, {invalid_filename,{foo,bar}}} =
721    xref_base:add_directory(S, {foo,bar}, []),
722    {error, _, {invalid_options,[{builtins,true},not_an_option]}} =
723    xref_base:add_module(S, foo, [{builtins,true},not_an_option]),
724    {error, _, {invalid_options,[{builtins,not_a_value}]}} =
725    xref_base:add_module(S, foo, [{builtins,not_a_value}]),
726    {error, _, {invalid_filename,{foo,bar}}} =
727    xref_base:add_module(S, {foo,bar}, []),
728    {error, _, {invalid_options,[{builtins,true},not_an_option]}} =
729    xref_base:add_application(S, foo, [{builtins,true},not_an_option]),
730    {error, _, {invalid_options,[{builtins,not_a_value}]}} =
731    xref_base:add_application(S, foo, [{builtins,not_a_value}]),
732    {error, _, {invalid_filename,{foo,bar}}} =
733    xref_base:add_application(S, {foo,bar}, []),
734    {error, _, {invalid_options,[not_an_option]}} =
735    xref_base:add_release(S, foo, [not_an_option]),
736    {error, _, {invalid_options,[{builtins,not_a_value}]}} =
737    xref_base:add_release(S, foo, [{builtins,not_a_value}]),
738    {error, _, {invalid_filename,{foo,bar}}} =
739    xref_base:add_release(S, {foo,bar}, []),
740    {ok, S1} =
741    xref_base:set_default(S, [{verbose,false}, {warnings, false}]),
742    case os:type() of
743        {unix, _} ->
744            {error, _, {file_error, _, _}} =
745            xref_base:add_release(S, UDir);
746        _ ->
747            true
748    end,
749    {error, _, {file_error, _, _}} =
750    xref_base:add_release(S, fname(["/a/b/c/d/e/f","__foo"])),
751    {ok, release2, S2} =
752    xref_base:add_release(S1, Dir, [{name,release2}]),
753    {error, _, {module_clash, {x, _, _}}} =
754    xref_base:add_module(S2, Xbeam),
755    {ok, S3} = xref_base:remove_release(S2, release2),
756    {ok, rel2, S4} = xref_base:add_release(S3, Dir),
757    {error, _, {release_clash, {rel2, _, _}}} =
758    xref_base:add_release(S4, Dir),
759    {ok, S5} = xref_base:remove_release(S4, rel2),
760    %% One unreadable file and one JAM file found (no verification here):
761    {ok, [], S6} = xref_base:add_directory(S5, fname(CopyDir,"dir"),
762                                           [{recurse,true}, {warnings,true}]),
763    case os:type() of
764        {unix, _} ->
765            {error, _, {file_error, _, _}} =
766            xref_base:add_directory(S6, UDir);
767        _ ->
768            true
769    end,
770    {ok, app1, S7} = xref_base:add_application(S6, A1_1),
771    {error, _, {application_clash, {app1, _, _}}} =
772    xref_base:add_application(S7, A1_1),
773    {ok, S8} = xref_base:remove_application(S7, app1),
774    ok = xref_base:delete(S8),
775    ok = file:delete(Xbeam),
776    ok = file:delete(Ybeam),
777    case os:type() of
778        {unix, _} ->
779            ok = file:del_dir(UDir),
780            ok = file:delete(UFile);
781        _ ->
782            true
783    end,
784    ok.
785
786%% Default values of options
787default(Conf) when is_list(Conf) ->
788    S = new(),
789    {error, _, {invalid_options,[not_an_option]}} =
790    xref_base:set_default(S, not_an_option, true),
791    {error, _, {invalid_options,[{builtins, not_a_value}]}} =
792    xref_base:set_default(S, builtins, not_a_value),
793    {error, _, {invalid_options,[not_an_option]}} =
794    xref_base:get_default(S, not_an_option),
795    {error, _, {invalid_options,[not_an_option]}} =
796    xref_base:set_default(S, [not_an_option]),
797
798    D = xref_base:get_default(S),
799    [{builtins,false},{recurse,false},{verbose,false},{warnings,true}] =
800    D,
801
802    ok = xref_base:delete(S),
803    ok.
804
805%% The info functions
806info(Conf) when is_list(Conf) ->
807    CopyDir = ?copydir,
808    Dir = fname(CopyDir,"rel2"),
809    LDir = fname(CopyDir,"lib_test"),
810    X = fname(Dir, "x.erl"),
811    Y = fname(Dir, "y.erl"),
812    A1_1 = fname([Dir,"lib","app1-1.1"]),
813    A2 = fname([Dir,"lib","app2-1.1"]),
814    EB1_1 = fname(A1_1, "ebin"),
815    EB2 = fname(A2, "ebin"),
816    Xbeam = fname(EB2, "x.beam"),
817    Ybeam = fname(EB1_1, "y.beam"),
818
819    {ok, x} = compile:file(X, [debug_info, {outdir,EB2}]),
820    {ok, y} = compile:file(Y, [debug_info, {outdir,EB1_1}]),
821
822    {ok, _} = start(s),
823    {error, _, {no_such_info, release}} = xref:info(s, release),
824    {error, _, {no_such_info, release}} = xref:info(s, release, rel),
825    {error, _, {no_such_module, mod}} = xref:info(s, modules, mod),
826    {error, _, {no_such_application, app}} =
827    xref:info(s, applications, app),
828    {error, _, {no_such_release, rel}} = xref:info(s, releases, rel),
829    ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
830    {ok, rel2} = xref:add_release(s, Dir),
831    9 = length(xref:info(s)),
832    [{x,_}, {y, _}] = xref:info(s, modules),
833    [{app1,_}, {app2, _}] = xref:info(s, applications),
834    [{rel2,_}] = xref:info(s, releases),
835    [] = xref:info(s, libraries),
836    [{x,_}] = xref:info(s, modules, x),
837    [{rel2,_}] = xref:info(s, releases, rel2),
838    {error, _, {no_such_library, foo}} = xref:info(s, libraries, [foo]),
839
840    {ok, lib1} =
841    compile:file(fname(LDir,lib1),[debug_info,{outdir,LDir}]),
842    {ok, lib2} =
843    compile:file(fname(LDir,lib2),[debug_info,{outdir,LDir}]),
844    ok = xref:set_library_path(s, [LDir], [{verbose,false}]),
845    [{lib1,_}, {lib2, _}] = xref:info(s, libraries),
846    [{lib1,_}, {lib2, _}] = xref:info(s, libraries, [lib1,lib2]),
847    ok = file:delete(fname(LDir, "lib1.beam")),
848    ok = file:delete(fname(LDir, "lib2.beam")),
849
850    check_state(s),
851
852    xref:stop(s),
853
854    ok = file:delete(Xbeam),
855    ok = file:delete(Ybeam),
856
857    ok.
858
859%% Library modules
860lib(Conf) when is_list(Conf) ->
861    CopyDir = ?copydir,
862    Dir = fname(CopyDir,"lib_test"),
863    UDir = fname([CopyDir,"dir","non_existent"]),
864
865    {ok, lib1} = compile:file(fname(Dir,lib1),[debug_info,{outdir,Dir}]),
866    {ok, lib2} = compile:file(fname(Dir,lib2),[debug_info,{outdir,Dir}]),
867    {ok, lib3} = compile:file(fname(Dir,lib3),[debug_info,{outdir,Dir}]),
868    {ok, t} = compile:file(fname(Dir,t),[debug_info,{outdir,Dir}]),
869
870    {ok, _} = start(s),
871    ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
872    {ok, t} = xref:add_module(s, fname(Dir,"t.beam")),
873    {error, _, {invalid_options,[not_an_option]}} =
874    xref:set_library_path(s, ["foo"], [not_an_option]),
875    {error, _, {invalid_path,otp}} = xref:set_library_path(s,otp),
876    {error, _, {invalid_path,[""]}} = xref:set_library_path(s,[""]),
877    {error, _, {invalid_path,[[$a | $b]]}} =
878    xref:set_library_path(s,[[$a | $b]]),
879    {error, _, {invalid_path,[otp]}} = xref:set_library_path(s,[otp]),
880    {ok, []} = xref:get_library_path(s),
881    ok = xref:set_library_path(s, [Dir], [{verbose,false}]),
882    {ok, UnknownFunctions} = xref:q(s, "U"),
883    [{lib1,unknown,0}, {lib2,local,0},
884     {lib2,unknown,0}, {unknown,unknown,0}]
885    = UnknownFunctions,
886    {ok, [{lib2,f,0},{lib3,f,0}]} = xref:q(s, "DF"),
887    {ok, []} = xref:q(s, "DF_1"),
888    {ok, [{lib2,f,0}]} = xref:q(s, "DF_2"),
889    {ok, [{lib2,f,0}]} = xref:q(s, "DF_3"),
890
891    {ok, [unknown]} = xref:q(s, "UM"),
892    {ok, UnknownDefAt} = xref:q(s, "(Lin)U"),
893    [{{lib1,unknown,0},0},{{lib2,local,0},0}, {{lib2,unknown,0},0},
894     {{unknown,unknown,0},0}] = UnknownDefAt,
895    {ok, LibFuns} = xref:q(s, "X * LM"),
896    [{lib2,f,0},{lib3,f,0}] = LibFuns,
897    {ok, LibMods} = xref:q(s, "LM"),
898    [lib1,lib2,lib3] = LibMods,
899    {ok, [{{lib2,f,0},0},{{lib3,f,0},0}]} = xref:q(s, "(Lin) (LM * X)"),
900    {ok, [{{lib1,unknown,0},0}, {{lib2,f,0},0}, {{lib2,local,0},0},
901          {{lib2,unknown,0},0}, {{lib3,f,0},0}]} = xref:q(s,"(Lin)LM"),
902    {ok,[lib1,lib2,lib3,t,unknown]} = xref:q(s,"M"),
903    {ok,[{lib2,f,0},{lib3,f,0},{t,t,0}]} = xref:q(s,"X * M"),
904    check_state(s),
905
906    copy_file(fname(Dir, "lib1.erl"), fname(Dir,"lib1.beam")),
907    ok = xref:set_library_path(s, [Dir]),
908    {error, _, _} = xref:q(s, "U"),
909
910    %% OTP-3921. AM and LM not always disjoint.
911    {ok, lib1} = compile:file(fname(Dir,lib1),[debug_info,{outdir,Dir}]),
912    {ok, lib1} = xref:add_module(s, fname(Dir,"lib1.beam")),
913    check_state(s),
914
915    {error, _, {file_error, _, _}} = xref:set_library_path(s, [UDir]),
916
917    xref:stop(s),
918    ok = file:delete(fname(Dir, "lib1.beam")),
919    ok = file:delete(fname(Dir, "lib2.beam")),
920    ok = file:delete(fname(Dir, "lib3.beam")),
921    ok = file:delete(fname(Dir, "t.beam")),
922
923    {ok, cp} = compile:file(fname(Dir,cp),[debug_info,{outdir,Dir}]),
924    {ok, _} = start(s),
925    ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
926    {ok, cp} = xref:add_module(s, fname(Dir,"cp.beam")),
927    {ok, [{lists, sort, 1}]} = xref:q(s, "U"),
928    ok = xref:set_library_path(s, code_path),
929    {ok, []} = xref:q(s, "U"),
930    check_state(s),
931    xref:stop(s),
932    ok = file:delete(fname(Dir, "cp.beam")),
933    ok.
934
935%% Data read from the Abstract Code
936read(Conf) when is_list(Conf) ->
937    CopyDir = ?copydir,
938    Dir = fname(CopyDir,"read"),
939    File = fname(Dir, "read"),
940    Beam = fname(Dir, "read.beam"),
941    {ok, read} = compile:file(File, [debug_info,{outdir,Dir}]),
942    do_read(File, abstract_v2),
943    copy_file(fname(Dir, "read.beam.v1"), Beam),
944    do_read(File, abstract_v1),
945    ok = file:delete(Beam),
946    ok.
947
948do_read(File, Version) ->
949    {ok, _} = start(s),
950    ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
951    {ok, read} = xref:add_module(s, File),
952
953    {U, OK, OKB} = read_expected(Version),
954
955    %% {ok, UC} = xref:q(s, "(Lin) UC"),
956    %% RR = to_external(converse(family_to_relation(family(UC)))),
957    %% lists:foreach(fun(X) -> io:format("~w~n", [X]) end, RR),
958    Unres = to_external(relation_to_family(converse(from_term(U)))),
959    {ok, Unres} =	xref:q(s, "(Lin) UC"),
960
961    %% {ok, EE} = xref:q(s, "(Lin) (E - UC)"),
962    %% AA = to_external(converse(family_to_relation(family(EE)))),
963    %% lists:foreach(fun(X) -> io:format("~w~n", [X]) end, AA),
964    Calls = to_external(relation_to_family(converse(from_term(OK)))),
965    {ok, Calls} = xref:q(s, "(Lin) (E - UC) "),
966
967    ok = check_state(s),
968    {ok, UM} = xref:q(s, "UM"),
969    true = member('$M_EXPR', UM),
970
971    {ok, X} = xref:q(s, "X"),
972    true = member({read, module_info, 0}, X),
973    false = member({foo, module_info, 0}, X),
974    false = member({erlang, module_info, 0}, X),
975    {ok, Unknowns} = xref:q(s, "U"),
976    false = member({read, module_info, 0}, Unknowns),
977    true = member({foo, module_info, 0}, Unknowns),
978    true = member({erlang, module_info, 0}, Unknowns),
979    {ok, LC} = xref:q(s, "LC"),
980    true = member({{read,bi,0},{read,bi,0}}, LC),
981
982    ok = xref:set_library_path(s, add_erts_code_path(fname(code:lib_dir(kernel),ebin))),
983    io:format("~p~n",[(catch xref:get_library_path(s))]),
984    {ok, X2} = xref:q(s, "X"),
985    ok = check_state(s),
986    true = member({read, module_info, 0}, X2),
987    false = member({foo, module_info, 0}, X2),
988    true = member({erlang, module_info, 0}, X2),
989    {ok, Unknowns2} = xref:q(s, "U"),
990    false = member({read, module_info, 0}, Unknowns2),
991    true = member({foo, module_info, 0}, Unknowns2),
992    false = member({erlang, module_info, 0}, Unknowns2),
993
994    ok = xref:remove_module(s, read),
995    {ok, read} = xref:add_module(s, File, [{builtins,true}]),
996
997    UnresB = to_external(relation_to_family(converse(from_term(U)))),
998    {ok, UnresB} = xref:q(s, "(Lin) UC"),
999    CallsB = to_external(relation_to_family(converse(from_term(OKB)))),
1000    {ok, CallsB} = xref:q(s, "(Lin) (E - UC) "),
1001    ok = check_state(s),
1002    {ok, XU} = xref:q(s, "XU"),
1003    Erl = set([{erlang,length,1},{erlang,integer,1},
1004               {erlang,binary_to_term,1}]),
1005    [{erlang,binary_to_term,1},{erlang,length,1}] =
1006    to_external(intersection(set(XU), Erl)),
1007    xref:stop(s).
1008
1009%% What is expected when xref_SUITE_data/read/read.erl is added:
1010read_expected(Version) ->
1011    %% Line positions in xref_SUITE_data/read/read.erl:
1012    POS1 = 28, POS2 = POS1+10, POS3 = POS2+6, POS4 = POS3+6, POS5 = POS4+10,
1013    POS6 = POS5+5, POS7 = POS6+6, POS8 = POS7+6, POS9 = POS8+8,
1014    POS10 = POS9+10, POS11 = POS10+7, POS12 = POS11+8, POS13 = POS12+10,
1015    POS14 = POS13+18, POS15 = POS14+23,
1016
1017    FF = {read,funfuns,0},
1018    U = [{POS1+5,{FF,{dist,'$F_EXPR',0}}},
1019         {POS1+8,{FF,{dist,'$F_EXPR',0}}},
1020         {POS2+8,{{read,funfuns,0},{expr,'$F_EXPR',1}}},
1021         {POS3+4,{FF,{expr,'$F_EXPR',2}}},
1022         {POS4+2,{FF,{modul,'$F_EXPR',1}}},
1023         {POS4+4,{FF,{spm,'$F_EXPR',1}}},
1024         {POS4+6,{FF,{spm,'$F_EXPR',1}}},
1025         {POS4+8,{FF,{spm,'$F_EXPR',1}}},
1026         {POS5+1,{FF,{'$M_EXPR','$F_EXPR',0}}},
1027         {POS5+2,{FF,{'$M_EXPR','$F_EXPR',0}}},
1028         {POS5+3,{FF,{'$M_EXPR','$F_EXPR',0}}},
1029         {POS6+1,{FF,{'$M_EXPR','$F_EXPR',0}}},
1030         {POS6+2,{FF,{'$M_EXPR','$F_EXPR',0}}},
1031         {POS6+4,{FF,{n,'$F_EXPR',-1}}},
1032         {POS7+1,{FF,{'$M_EXPR',f,1}}},
1033         {POS7+2,{FF,{'$M_EXPR',f,1}}},
1034         {POS8+2,{FF,{hej,'$F_EXPR',1}}},
1035         {POS8+3,{FF,{t,'$F_EXPR',1}}},
1036         {POS8+5,{FF,{a,'$F_EXPR',1}}},
1037         {POS8+7,{FF,{m,'$F_EXPR',1}}},
1038         {POS9+1,{FF,{'$M_EXPR',f,1}}},
1039         {POS9+3,{FF,{a,'$F_EXPR',1}}},
1040         {POS10+1,{FF,{'$M_EXPR',foo,1}}},
1041         {POS10+2,{FF,{'$M_EXPR','$F_EXPR',1}}},
1042         {POS10+3,{FF,{'$M_EXPR','$F_EXPR',2}}},
1043         {POS10+4,{FF,{'$M_EXPR','$F_EXPR',1}}},
1044         {POS10+5,{FF,{'$M_EXPR',san,1}}},
1045         {POS10+6,{FF,{'$M_EXPR','$F_EXPR',1}}},
1046         {POS11+1,{FF,{'$M_EXPR','$F_EXPR',1}}},
1047         {POS11+2,{FF,{'$M_EXPR','$F_EXPR',-1}}},
1048         {POS11+3,{FF,{m,f,-1}}},
1049         {POS11+4,{FF,{m,f,-1}}},
1050         {POS11+5,{FF,{'$M_EXPR','$F_EXPR',1}}},
1051         {POS11+6,{FF,{'$M_EXPR','$F_EXPR',1}}},
1052         {POS12+1,{FF,{'$M_EXPR','$F_EXPR',-1}}},
1053         {POS12+4,{FF,{'$M_EXPR','$F_EXPR',2}}},
1054         {POS12+7,{FF,{'$M_EXPR','$F_EXPR',-1}}},
1055         {POS12+8,{FF,{m4,f4,-1}}},
1056         {POS13+2,{FF,{debug,'$F_EXPR',0}}},
1057         {POS13+3,{FF,{'$M_EXPR','$F_EXPR',-1}}},
1058         {POS14+8,{{read,bi,0},{'$M_EXPR','$F_EXPR',1}}}],
1059
1060    O1 = [{20,{{read,lc,0},{ets,new,0}}},
1061          {21,{{read,lc,0},{ets,tab2list,1}}},
1062          {POS1+1,{FF,{erlang,spawn,1}}},
1063          {POS1+1,{FF,{mod17,fun17,0}}},
1064          {POS1+2,{FF,{erlang,spawn,1}}},
1065          {POS1+2,{FF,{read,local,0}}},
1066          {POS1+3,{FF,{erlang,spawn,1}}},
1067          {POS1+4,{FF,{dist,func,0}}},
1068          {POS1+4,{FF,{erlang,spawn,1}}},
1069          {POS1+5,{FF,{erlang,spawn,1}}},
1070          {POS1+6,{FF,{erlang,spawn_link,1}}},
1071          {POS1+6,{FF,{mod17,fun17,0}}},
1072          {POS1+7,{FF,{dist,func,0}}},
1073          {POS1+7,{FF,{erlang,spawn_link,1}}},
1074          {POS1+8,{FF,{erlang,spawn_link,1}}},
1075          {POS2+1,{FF,{d,f,0}}},
1076          {POS2+1,{FF,{dist,func,2}}},
1077          {POS2+1,{FF,{erlang,spawn,2}}},
1078          {POS2+2,{FF,{dist,func,2}}},
1079          {POS2+2,{FF,{erlang,spawn,2}}},
1080          {POS2+2,{FF,{mod42,func,0}}},
1081          {POS2+3,{FF,{d,f,0}}},
1082          {POS2+3,{FF,{dist,func,2}}},
1083          {POS2+3,{FF,{erlang,spawn_link,2}}},
1084          {POS2+4,{FF,{dist,func,2}}},
1085          {POS2+4,{FF,{erlang,spawn_link,2}}},
1086          {POS2+4,{FF,{mod42,func,0}}},
1087          {POS3+1,{FF,{dist,func,2}}},
1088          {POS3+3,{FF,{dist,func,2}}},
1089          {POS4+1,{FF,{erlang,spawn,4}}},
1090          {POS4+1,{FF,{modul,function,0}}},
1091          {POS4+2,{FF,{erlang,spawn,4}}},
1092          {POS4+3,{FF,{dist,func,2}}},
1093          {POS4+3,{FF,{erlang,spawn,4}}},
1094          {POS4+3,{FF,{spm,spf,2}}},
1095          {POS4+4,{FF,{dist,func,2}}},
1096          {POS4+4,{FF,{erlang,spawn,4}}},
1097          {POS4+5,{FF,{dist,func,2}}},
1098          {POS4+5,{FF,{erlang,spawn_link,4}}},
1099          {POS4+5,{FF,{spm,spf,2}}},
1100          {POS4+6,{FF,{dist,func,2}}},
1101          {POS4+6,{FF,{erlang,spawn_link,4}}},
1102          {POS4+7,{FF,{read,bi,0}}},
1103          {POS4+7,{FF,{spm,spf,2}}},
1104          {POS4+8,{FF,{read,bi,0}}},
1105          {POS5+1,{FF,{erlang,spawn,1}}},
1106          {POS5+2,{FF,{erlang,spawn,1}}},
1107          {POS5+3,{FF,{erlang,spawn_link,1}}},
1108          {POS6+1,{FF,{erlang,spawn,2}}},
1109          {POS6+2,{FF,{erlang,spawn_link,2}}},
1110          {POS7+1,{FF,{erlang,spawn,4}}},
1111          {POS8+1,{FF,{hej,san,1}}},
1112          {POS8+4,{FF,{a,b,1}}},
1113          {POS8+6,{FF,{m,f,1}}},
1114          {POS9+1,{FF,{read,bi,0}}},
1115          {POS9+2,{FF,{a,b,1}}},
1116          {POS9+4,{FF,{erlang,not_a_function,1}}},
1117          {POS9+5,{FF,{mod,func,2}}},
1118          {POS9+6,{FF,{erlang,apply,1}}},
1119          {POS9+7,{FF,{math,add3,1}}},
1120          {POS9+8,{FF,{q,f,1}}},
1121          {POS10+5,{FF,{mod1,fun1,1}}},
1122          {POS12+5,{FF,{m3,f3,2}}},
1123          {POS13+1,{FF,{dm,df,1}}},
1124          {POS13+6,{{read,bi,0},{foo,module_info,0}}},
1125          {POS13+7,{{read,bi,0},{read,module_info,0}}},
1126          {POS13+9,{{read,bi,0},{t,foo,1}}},
1127          {POS14+11,{{read,bi,0},{erlang,module_info,0}}},
1128          {POS14+17,{{read,bi,0},{read,bi,0}}}],
1129
1130    OK = case Version of
1131             abstract_v1 ->
1132                 [{0,{FF,{read,'$F_EXPR',178}}},
1133                  {0,{FF,{modul,'$F_EXPR',179}}}]
1134                 ++ O1;
1135             _ ->
1136                 [{16,{FF,{read,'$F_EXPR',178}}},
1137                  {17,{FF,{modul,'$F_EXPR',179}}}]
1138                 ++
1139                 O1
1140         end,
1141
1142    %% When builtins =:= true:
1143    OKB1 = [{POS13+1,{FF,{erts_debug,apply,4}}},
1144            {POS13+2,{FF,{erts_debug,apply,4}}},
1145            {POS13+3,{FF,{erts_debug,apply,4}}},
1146            {POS1+3, {FF,{erlang,binary_to_term,1}}},
1147            {POS3+1, {FF,{erlang,spawn,3}}},
1148            {POS3+2, {FF,{erlang,spawn,3}}},
1149            {POS3+3,  {FF,{erlang,spawn_link,3}}},
1150            {POS3+4, {FF,{erlang,spawn_link,3}}},
1151            {POS4+7, {FF,{erlang,spawn_opt,4}}},
1152            {POS4+8, {FF,{erlang,spawn_opt,4}}},
1153            {POS6+4, {FF,{erlang,spawn,3}}},
1154            {POS7+2, {FF,{erlang,spawn_opt,4}}},
1155            {POS8+4,{FF,{erlang,apply,2}}},
1156            {POS8+5,{FF,{erlang,apply,2}}},
1157            {POS8+6,{FF,{erlang,apply,3}}},
1158            {POS8+7,{FF,{erlang,apply,3}}},
1159            {POS9+1,{FF,{erlang,apply,3}}},
1160            {POS9+2,{FF,{erlang,apply,2}}},
1161            {POS9+3,{FF,{erlang,apply,2}}},
1162            {POS9+4,{FF,{erlang,apply,2}}},
1163            {POS9+5,{FF,{erlang,apply,3}}},
1164            {POS9+7,{FF,{erlang,apply,2}}},
1165            {POS10+4,{FF,{erlang,apply,2}}},
1166            {POS11+1,{FF,{erlang,apply,3}}},
1167            {POS11+2,{FF,{erlang,apply,3}}},
1168            {POS11+3,{FF,{erlang,apply,3}}},
1169            {POS11+4,{FF,{erlang,apply,3}}},
1170            {POS11+6,{FF,{erlang,apply,2}}},
1171            {POS12+1,{FF,{erlang,apply,2}}},
1172            {POS12+4,{FF,{erlang,apply,2}}},
1173            {POS12+5,{FF,{erlang,apply,3}}},
1174            {POS12+7,{FF,{erlang,apply,2}}},
1175            {POS12+8,{FF,{erlang,apply,3}}},
1176            {POS13+5, {{read,bi,0},{erlang,length,1}}},
1177            {POS14+3, {{read,bi,0},{erlang,length,1}}}],
1178
1179    %% Operators (OTP-8647):
1180    OKB = case Version of
1181              abstract_v1 ->
1182                  [{POS8+3, {FF,{erlang,apply,3}}},
1183                   {POS10+1, {FF,{erlang,apply,3}}},
1184                   {POS10+6, {FF,{erlang,apply,3}}}];
1185              _ ->
1186                  [{POS13+16, {{read,bi,0},{erlang,'!',2}}},
1187                   {POS13+16, {{read,bi,0},{erlang,'-',1}}},
1188                   {POS13+16, {{read,bi,0},{erlang,self,0}}},
1189                   {POS15+1,  {{read,bi,0},{erlang,'>',2}}},
1190                   {POS15+2,  {{read,bi,0},{erlang,'-',2}}},
1191                   {POS15+2,  {{read,bi,0},{erlang,'*',2}}},
1192                   {POS15+8,  {{read,bi,0},{erlang,'/',2}}}]
1193          end
1194    ++ [{POS14+19, {{read,bi,0},{erlang,'+',2}}},
1195        {POS14+21, {{read,bi,0},{erlang,'+',2}}},
1196        {POS13+16, {{read,bi,0},{erlang,'==',2}}},
1197        {POS14+15, {{read,bi,0},{erlang,'==',2}}},
1198        {POS13+5,  {{read,bi,0},{erlang,'>',2}}},
1199        {POS14+3,  {{read,bi,0},{erlang,'>',2}}}]
1200    ++ OKB1 ++ OK,
1201
1202    {U, OK, OKB}.
1203
1204%% Data read from the Abstract Code (cont)
1205read2(Conf) when is_list(Conf) ->
1206    %% Handles the spawn_opt versions added in R9 (OTP-4180).
1207    %% Expected augmentations: try/catch, cond.
1208    CopyDir = ?copydir,
1209    Dir = fname(CopyDir,"read"),
1210    File = fname(Dir, "read2.erl"),
1211    MFile = fname(Dir, "read2"),
1212    Beam = fname(Dir, "read2.beam"),
1213    Test = <<"-module(read2).
1214              -compile(export_all).
1215
1216              f() ->
1217                  spawn_opt({read2,f}, % POS2
1218                            [f()]),
1219                  spawn_opt(fun() -> foo end, [link]),
1220                  spawn_opt(f(),
1221                            {read2,f}, [{min_heap_size,1000}]),
1222                  spawn_opt(f(),
1223                            fun() -> f() end, [flopp]),
1224                  spawn_opt(f(),
1225                            read2, f, [], []);
1226              f() ->
1227                  %% Duplicated unresolved calls are ignored:
1228                  (f())(foo,bar),(f())(foo,bar). % POS1
1229
1230              %% Warning forms must be ignored.
1231              -warning(must_not_crash).
1232             ">>,
1233    ok = file:write_file(File, Test),
1234    {ok, read2} = compile:file(File, [debug_info,{outdir,Dir}]),
1235
1236    {ok, _} = xref:start(s),
1237    {ok, read2} = xref:add_module(s, MFile),
1238    {U0, OK0} = read2_expected(),
1239
1240    U = to_external(relation_to_family(converse(from_term(U0)))),
1241    OK = to_external(relation_to_family(converse(from_term(OK0)))),
1242    {ok, U2} = xref:q(s, "(Lin) UC"),
1243    {ok, OK2} = xref:q(s, "(Lin) (E - UC)"),
1244    true = U =:= U2,
1245    true = OK =:= OK2,
1246    ok = check_state(s),
1247    xref:stop(s),
1248
1249    ok = file:delete(File),
1250    ok = file:delete(Beam),
1251    ok.
1252
1253
1254read2_expected() ->
1255    POS1 = 16,
1256    POS2 = 5,
1257    FF = {read2,f,0},
1258    U =  [{POS1,{FF,{'$M_EXPR','$F_EXPR',2}}}],
1259    OK = [{POS2,{FF,{erlang,spawn_opt,2}}},
1260          {POS2,{FF,FF}},
1261          {POS2+1,{FF,FF}},
1262          {POS2+2,{FF,{erlang,spawn_opt,2}}},
1263          {POS2+3,{FF,{erlang,spawn_opt,3}}},
1264          {POS2+3,{FF,FF}},
1265          {POS2+3,{FF,FF}},
1266          {POS2+5,{FF,{erlang,spawn_opt,3}}},
1267          {POS2+5,{FF,FF}},
1268          {POS2+6,{FF,FF}},
1269          {POS2+7,{FF,{erlang,spawn_opt,5}}},
1270          {POS2+7,{FF,FF}},
1271          {POS2+7,{FF,FF}},
1272          {POS1,{FF,FF}}],
1273    {U, OK}.
1274
1275%% Remove modules, applications, releases
1276remove(Conf) when is_list(Conf) ->
1277    S = new(),
1278    {error, _, {no_such_module, mod}} =
1279    xref_base:remove_module(S, mod),
1280    {error, _, {no_such_application, app}} =
1281    xref_base:remove_application(S, app),
1282    {error, _, {no_such_release, rel}} =
1283    xref_base:remove_release(S, rel),
1284    ok = xref_base:delete(S),
1285    ok.
1286
1287%% Replace modules, applications, releases
1288replace(Conf) when is_list(Conf) ->
1289    CopyDir = ?copydir,
1290    Dir = fname(CopyDir,"rel2"),
1291    X = fname(Dir, "x.erl"),
1292    Y = fname(Dir, "y.erl"),
1293    A1_0 = fname(Dir, fname("lib","app1-1.0")),
1294    A1_1 = fname(Dir, fname("lib","app1-1.1")),
1295    A2 = fname(Dir, fname("lib","app2-1.1")),
1296    EB1_0 = fname(A1_0, "ebin"),
1297    EB1_1 = fname(A1_1, "ebin"),
1298    Xbeam = fname(EB1_1, "x.beam"),
1299    Ybeam = fname(EB1_1, "y.beam"),
1300
1301    {ok, x} = compile:file(X, [debug_info, {outdir,EB1_0}]),
1302    {ok, x} = compile:file(X, [debug_info, {outdir,EB1_1}]),
1303    {ok, y} = compile:file(Y, [debug_info, {outdir,EB1_1}]),
1304
1305    {ok, _} = start(s),
1306    {ok, false} = xref:set_default(s, verbose, false),
1307    {ok, true} = xref:set_default(s, warnings, false),
1308    {ok, rel2} = xref:add_release(s, Dir, []),
1309    {error, _, _} = xref:replace_application(s, app1, "no_data"),
1310    {error, _, {no_such_application, app12}} =
1311    xref:replace_application(s, app12, A1_0, []),
1312    {error, _, {invalid_filename,{foo,bar}}} =
1313    xref:replace_application(s, app1, {foo,bar}, []),
1314    {error, _, {invalid_options,[not_an_option]}} =
1315    xref:replace_application(s, foo, bar, [not_an_option]),
1316    {error, _, {invalid_options,[{builtins,not_a_value}]}} =
1317    xref:replace_application(s, foo, bar, [{builtins,not_a_value}]),
1318    {ok, app1} =
1319    xref:replace_application(s, app1, A1_0),
1320    [{_, AppInfo}] = xref:info(s, applications, app1),
1321    {value, {release, [rel2]}} = keysearch(release, 1, AppInfo),
1322
1323    {error, _, {no_such_module, xx}} =
1324    xref:replace_module(s, xx, Xbeam, []),
1325    {error, _, {invalid_options,[{builtins,true},not_an_option]}} =
1326    xref:replace_module(s, foo, bar,[{builtins,true},not_an_option]),
1327    {error, _, {invalid_options,[{builtins,not_a_value}]}} =
1328    xref:replace_module(s, foo, bar, [{builtins,not_a_value}]),
1329    {error, _, {invalid_filename,{foo,bar}}} =
1330    xref:replace_module(s, x, {foo,bar}),
1331    {ok, x} = xref:replace_module(s, x, Xbeam),
1332    [{x, ModInfo}] = xref:info(s, modules, x),
1333    {value, {application, [app1]}} =
1334    keysearch(application, 1, ModInfo),
1335
1336    {ok, x} = compile:file(X, [no_debug_info, {outdir,EB1_1}]),
1337    {error, _, {no_debug_info, _}} = xref:replace_module(s, x, Xbeam),
1338    {error, _, {module_mismatch, x,y}} =
1339    xref:replace_module(s, x, Ybeam),
1340    case os:type() of
1341        {unix, _} ->
1342            hide_file(Ybeam),
1343            {error, _, {file_error, _, _}} =
1344            xref:replace_module(s, x, Ybeam);
1345        _ ->
1346            true
1347    end,
1348    ok = xref:remove_module(s, x),
1349    {error, _, {no_debug_info, _}} = xref:add_module(s, Xbeam),
1350
1351    %% "app2" is ignored, the old application name is kept
1352    {ok, app1} = xref:replace_application(s, app1, A2),
1353
1354    xref:stop(s),
1355    ok = file:delete(fname(EB1_0, "x.beam")),
1356    ok = file:delete(Xbeam),
1357    ok = file:delete(Ybeam),
1358    ok.
1359
1360%% The update() function
1361update(Conf) when is_list(Conf) ->
1362    CopyDir = ?copydir,
1363    Dir = fname(CopyDir,"update"),
1364    Source = fname(Dir, "x.erl"),
1365    Beam = fname(Dir, "x.beam"),
1366    copy_file(fname(Dir, "x.erl.1"), Source),
1367    {ok, x} = compile:file(Source, [debug_info, {outdir,Dir}]),
1368
1369    {ok, _} = start(s),
1370    ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
1371    {ok, [x]} = xref:add_directory(s, Dir, [{builtins,true}]),
1372    {error, _, {invalid_options,[not_an_option]}} =
1373    xref:update(s, [not_an_option]),
1374    {ok, []} = xref:update(s),
1375    {ok, [{erlang,atom_to_list,1}]} = xref:q(s, "XU"),
1376
1377    [{x, ModInfo}] = xref:info(s, modules, x),
1378    case keysearch(directory, 1, ModInfo) of
1379        {value, {directory, Dir}} -> ok
1380    end,
1381
1382    timer:sleep(2000), % make sure modification time has changed
1383    copy_file(fname(Dir, "x.erl.2"), Source),
1384    {ok, x} = compile:file(Source, [debug_info, {outdir,Dir}]),
1385    {ok, [x]} = xref:update(s, []),
1386    {ok, [{erlang,list_to_atom,1}]} = xref:q(s, "XU"),
1387
1388    timer:sleep(2000),
1389    {ok, x} = compile:file(Source, [no_debug_info,{outdir,Dir}]),
1390    {error, _, {no_debug_info, _}} = xref:update(s),
1391
1392    xref:stop(s),
1393    ok = file:delete(Beam),
1394    ok = file:delete(Source),
1395    ok.
1396
1397%% OTP-4695: Deprecated functions.
1398deprecated(Conf) when is_list(Conf) ->
1399    Dir = ?copydir,
1400    File = fname(Dir, "depr.erl"),
1401    MFile_r9c = fname(Dir, "depr_r9c"),
1402    MFile = fname(Dir, "depr"),
1403    Beam = fname(Dir, "depr.beam"),
1404    %% This file has been compiled to ?datadir/depr_r9c.beam
1405    %% using the R9C compiler. From R10B and onwards the linter
1406    %% checks the 'deprecated' attribute as well.
1407    %     Test = <<"-module(depr).
1408
1409    %               -export([t/0,f/1,bar/2,f/2,g/3]).
1410
1411    %               -deprecated([{f,1},                             % DF
1412    %                            {bar,2,eventually}]).              % DF_3
1413    %               -deprecated([{f,1,next_major_release}]).        % DF_2 (again)
1414    %               -deprecated([{frutt,0,next_version}]).          % message...
1415    %               -deprecated([{f,2,next_major_release},          % DF_2
1416    %                            {g,3,next_version},                % DF_1
1417    %                            {ignored,10,100}]).                % message...
1418    %               -deprecated([{does_not_exist,1}]).              % message...
1419
1420    %               -deprecated(foo).                               % message...
1421
1422    %               t() ->
1423    %                   frutt(1),
1424    %                   g(1,2, 3),
1425    %                   ?MODULE:f(10).
1426
1427    %               f(A) ->
1428    %                   ?MODULE:f(A,A).
1429
1430    %               f(X, Y) ->
1431    %                   ?MODULE:g(X, Y, X).
1432
1433    %               g(F, G, H) ->
1434    %                   ?MODULE:bar(F, {G,H}).
1435
1436    %               bar(_, _) ->
1437    %                   true.
1438
1439    %               frutt(_) ->
1440    %                   frutt().
1441
1442    %               frutt() ->
1443    %                   true.
1444    %              ">>,
1445
1446    %    ok = file:write_file(File, Test),
1447    %    {ok, depr_r9c} = compile:file(File, [debug_info,{outdir,Dir}]),
1448
1449    {ok, _} = xref:start(s),
1450    {ok, depr_r9c} = xref:add_module(s, MFile_r9c),
1451    M9 = depr_r9c,
1452    DF_1 = usort([{{M9,f,2},{M9,g,3}}]),
1453    DF_2 = usort(DF_1++[{{M9,f,1},{M9,f,2}},{{M9,t,0},{M9,f,1}}]),
1454    DF_3 = usort(DF_2++[{{M9,g,3},{M9,bar,2}}]),
1455    DF = usort(DF_3++[{{M9,t,0},{M9,f,1}}]),
1456
1457    {ok,DF} = xref:analyze(s, deprecated_function_calls),
1458    {ok,DF_1} =
1459    xref:analyze(s, {deprecated_function_calls,next_version}),
1460    {ok,DF_2} =
1461    xref:analyze(s, {deprecated_function_calls,next_major_release}),
1462    {ok,DF_3} =
1463    xref:analyze(s, {deprecated_function_calls,eventually}),
1464
1465    D = to_external(range(from_term(DF))),
1466    D_1 = to_external(range(from_term(DF_1))),
1467    D_2 = to_external(range(from_term(DF_2))),
1468    D_3 = to_external(range(from_term(DF_3))),
1469
1470    {ok,D} = xref:analyze(s, deprecated_functions),
1471    {ok,D_1} =
1472    xref:analyze(s, {deprecated_functions,next_version}),
1473    {ok,D_2} =
1474    xref:analyze(s, {deprecated_functions,next_major_release}),
1475    {ok,D_3} =
1476    xref:analyze(s, {deprecated_functions,eventually}),
1477
1478    ok = check_state(s),
1479    xref:stop(s),
1480
1481    Test2= <<"-module(depr).
1482
1483              -export([t/0,f/1,bar/2,f/2,g/3,string/0]).
1484
1485              -deprecated([{'_','_',eventually}]).            % DF_3
1486              -deprecated([{f,'_',next_major_release}]).      % DF_2
1487              -deprecated([{g,'_',next_version}]).            % DF_1
1488              -deprecated([{bar,2}]).                         % DF
1489              -deprecated([{string,0,\"hello\"}]).            % DF
1490
1491              t() ->
1492                  g(1,2, 3),
1493                  ?MODULE:f(10).
1494
1495              f(A) ->
1496                  ?MODULE:f(A,A).
1497
1498              f(X, Y) ->
1499                  ?MODULE:g(X, Y, X).
1500
1501              g(F, G, H) ->
1502                  ?MODULE:bar(F, {G,H}).
1503
1504              string() ->
1505                  ?MODULE:string().
1506
1507              bar(_, _) ->
1508                  ?MODULE:t().
1509             ">>,
1510
1511    ok = file:write_file(File, Test2),
1512    {ok, depr} = compile:file(File, [debug_info,{outdir,Dir}]),
1513
1514    {ok, _} = xref:start(s),
1515    {ok, depr} = xref:add_module(s, MFile),
1516
1517    M = depr,
1518    DFa_1 = usort([{{M,f,2},{M,g,3}}]),
1519    DFa_2 = usort(DFa_1++[{{M,f,1},{M,f,2}},{{M,t,0},{M,f,1}}]),
1520    DFa_3 = usort(DFa_2++[{{M,bar,2},{M,t,0}},{{M,g,3},{M,bar,2}},
1521                          {{M,string,0},{M,string,0}}]),
1522    DFa = DFa_3,
1523
1524    {ok,DFa} = xref:analyze(s, deprecated_function_calls),
1525    {ok,DFa_1} =
1526    xref:analyze(s, {deprecated_function_calls,next_version}),
1527    {ok,DFa_2} =
1528    xref:analyze(s, {deprecated_function_calls,next_major_release}),
1529    {ok,DFa_3} =
1530    xref:analyze(s, {deprecated_function_calls,eventually}),
1531
1532    ok = check_state(s),
1533    xref:stop(s),
1534
1535    %% All of the module is deprecated.
1536    Test3= <<"-module(depr).
1537
1538              -export([t/0,f/1,bar/2,f/2,g/3]).
1539
1540              -deprecated([{f,'_',next_major_release}]).      % DF_2
1541              -deprecated([{g,'_',next_version}]).            % DF_1
1542              -deprecated(module).                            % DF
1543
1544              t() ->
1545                  g(1,2, 3),
1546                  ?MODULE:f(10).
1547
1548              f(A) ->
1549                  ?MODULE:f(A,A).
1550
1551              f(X, Y) ->
1552                  ?MODULE:g(X, Y, X).
1553
1554              g(F, G, H) ->
1555                  ?MODULE:bar(F, {G,H}).
1556
1557              bar(_, _) ->
1558                  ?MODULE:t().
1559             ">>,
1560
1561    ok = file:write_file(File, Test3),
1562    {ok, depr} = compile:file(File, [debug_info,{outdir,Dir}]),
1563
1564    {ok, _} = xref:start(s),
1565    {ok, depr} = xref:add_module(s, MFile),
1566
1567    DFb_1 = usort([{{M,f,2},{M,g,3}}]),
1568    DFb_2 = usort(DFb_1++[{{M,f,1},{M,f,2}},{{M,t,0},{M,f,1}}]),
1569    DFb_3 = DFb_2,
1570    DFb = usort(DFb_2++[{{M,bar,2},{M,t,0}},{{M,g,3},{M,bar,2}}]),
1571
1572    {ok,DFb} = xref:analyze(s, deprecated_function_calls),
1573    {ok,DFb_1} =
1574    xref:analyze(s, {deprecated_function_calls,next_version}),
1575    {ok,DFb_2} =
1576    xref:analyze(s, {deprecated_function_calls,next_major_release}),
1577    {ok,DFb_3} =
1578    xref:analyze(s, {deprecated_function_calls,eventually}),
1579
1580    ok = check_state(s),
1581    xref:stop(s),
1582
1583    ok = file:delete(File),
1584    ok = file:delete(Beam),
1585    ok.
1586
1587
1588%% OTP-5152: try/catch, final (?) version.
1589trycatch(Conf) when is_list(Conf) ->
1590    Dir = ?copydir,
1591    File = fname(Dir, "trycatch.erl"),
1592    MFile = fname(Dir, "trycatch"),
1593    Beam = fname(Dir, "trycatch.beam"),
1594    Test = <<"-module(trycatch).
1595
1596              -export([trycatch/0]).
1597
1598              trycatch() ->
1599                  try
1600                     foo:bar(),
1601                     bar:foo() of
1602                        1 -> foo:foo();
1603                        2 -> bar:bar()
1604                  catch
1605                     error:a -> err:e1();
1606                     error:b -> err:e2()
1607                  after
1608                     fini:shed()
1609                  end.
1610             ">>,
1611
1612    ok = file:write_file(File, Test),
1613    {ok, trycatch} = compile:file(File, [debug_info,{outdir,Dir}]),
1614
1615    {ok, _} = xref:start(s),
1616    {ok, trycatch} = xref:add_module(s, MFile),
1617    A = trycatch,
1618    {ok,[{{{A,A,0},{bar,bar,0}},[10]},
1619         {{{A,A,0},{bar,foo,0}},[8]},
1620         {{{A,A,0},{err,e1,0}},[12]},
1621         {{{A,A,0},{err,e2,0}},[13]},
1622         {{{A,A,0},{fini,shed,0}},[15]},
1623         {{{A,A,0},{foo,bar,0}},[7]},
1624         {{{A,A,0},{foo,foo,0}},[9]}]} =
1625    xref:q(s, "(Lin) (E | trycatch:trycatch/0)"),
1626
1627    ok = check_state(s),
1628    xref:stop(s),
1629
1630    ok = file:delete(File),
1631    ok = file:delete(Beam),
1632    ok.
1633
1634
1635%% OTP-5653: fun M:F/A.
1636fun_mfa(Conf) when is_list(Conf) ->
1637    Dir = ?copydir,
1638    File = fname(Dir, "fun_mfa.erl"),
1639    MFile = fname(Dir, "fun_mfa"),
1640    Beam = fname(Dir, "fun_mfa.beam"),
1641    Test = <<"-module(fun_mfa).
1642
1643              -export([t/0, t1/0, t2/0, t3/0]).
1644
1645              t() ->
1646                  F = fun ?MODULE:t/0,
1647                  (F)().
1648
1649              t1() ->
1650                  F = fun t/0,
1651                  (F)().
1652
1653              t2() ->
1654                  fun ?MODULE:t/0().
1655
1656              t3() ->
1657                  fun t3/0().
1658             ">>,
1659
1660    ok = file:write_file(File, Test),
1661    A = fun_mfa,
1662    {ok, A} = compile:file(File, [debug_info,{outdir,Dir}]),
1663    {ok, _} = xref:start(s),
1664    {ok, A} = xref:add_module(s, MFile, {warnings,false}),
1665    {ok, [{{{A,t,0},{'$M_EXPR','$F_EXPR',0}},[7]},
1666          {{{A,t,0},{A,t,0}},[6]},
1667          {{{A,t1,0},{'$M_EXPR','$F_EXPR',0}},[11]},
1668          {{{A,t1,0},{A,t,0}},[10]},
1669          {{{A,t2,0},{A,t,0}},[14]},
1670          {{{A,t3,0},{fun_mfa,t3,0}},[17]}]} =
1671    xref:q(s, "(Lin) E"),
1672
1673    ok = check_state(s),
1674    xref:stop(s),
1675
1676    ok = file:delete(File),
1677    ok = file:delete(Beam),
1678    ok.
1679
1680%% Same as the previous test case, except that we use a BEAM file
1681%% that was compiled by an R14 compiler to test backward compatibility.
1682fun_mfa_r14(Conf) when is_list(Conf) ->
1683    Dir = proplists:get_value(data_dir, Conf),
1684    MFile = fname(Dir, "fun_mfa_r14"),
1685
1686    A = fun_mfa_r14,
1687    {ok, _} = xref:start(s),
1688    {ok, A} = xref:add_module(s, MFile, {warnings,false}),
1689    {ok, [{{{A,t,0},{'$M_EXPR','$F_EXPR',0}},[7]},
1690          {{{A,t,0},{A,t,0}},[6]},
1691          {{{A,t1,0},{'$M_EXPR','$F_EXPR',0}},[11]},
1692          {{{A,t1,0},{A,t,0}},[10]},
1693          {{{A,t2,0},{A,t,0}},[14]},
1694          {{{A,t3,0},{A,t3,0}},[17]}]} =
1695    xref:q(s, "(Lin) E"),
1696
1697    ok = check_state(s),
1698    xref:stop(s),
1699
1700    ok.
1701
1702%% fun M:F/A with varibles.
1703fun_mfa_vars(Conf) when is_list(Conf) ->
1704    Dir = ?copydir,
1705    File = fname(Dir, "fun_mfa_vars.erl"),
1706    MFile = fname(Dir, "fun_mfa_vars"),
1707    Beam = fname(Dir, "fun_mfa_vars.beam"),
1708    Test = <<"-module(fun_mfa_vars).
1709
1710              -export([t/1, t1/1, t2/3]).
1711
1712              t(Mod) ->
1713                  F = fun Mod:bar/2,
1714                  (F)(a, b).
1715
1716              t1(Name) ->
1717                  F = fun ?MODULE:Name/1,
1718                  (F)(a).
1719
1720              t2(Mod, Name, Arity) ->
1721                  F = fun Mod:Name/Arity,
1722                  (F)(a).
1723
1724              t3(Arity) ->
1725                  F = fun ?MODULE:t/Arity,
1726                  (F)(1, 2, 3).
1727
1728              t4(Mod, Name) ->
1729                  F = fun Mod:Name/3,
1730                  (F)(a, b, c).
1731
1732              t5(Mod, Arity) ->
1733                  F = fun Mod:t/Arity,
1734                  (F)().
1735             ">>,
1736
1737    ok = file:write_file(File, Test),
1738    A = fun_mfa_vars,
1739    {ok, A} = compile:file(File, [report,debug_info,{outdir,Dir}]),
1740    {ok, _} = xref:start(s),
1741    {ok, A} = xref:add_module(s, MFile, {warnings,false}),
1742    {ok, [{{{A,t,1},{'$M_EXPR','$F_EXPR',2}},[7]},
1743          {{{A,t,1},{'$M_EXPR',bar,2}},[6]},
1744          {{{A,t1,1},{'$M_EXPR','$F_EXPR',1}},[11]},
1745          {{{A,t1,1},{A,'$F_EXPR',1}},[10]},
1746          {{{A,t2,3},{'$M_EXPR','$F_EXPR',-1}},[14]},
1747          {{{A,t2,3},{'$M_EXPR','$F_EXPR',1}},[15]},
1748          {{{A,t3,1},{'$M_EXPR','$F_EXPR',3}},[19]},
1749          {{{A,t3,1},{fun_mfa_vars,t,-1}},[18]},
1750          {{{A,t4,2},{'$M_EXPR','$F_EXPR',3}},[22,23]},
1751          {{{A,t5,2},{'$M_EXPR','$F_EXPR',0}},[27]},
1752          {{{A,t5,2},{'$M_EXPR',t,-1}},[26]}]} =
1753    xref:q(s, "(Lin) E"),
1754
1755    ok = check_state(s),
1756    xref:stop(s),
1757
1758    ok = file:delete(File),
1759    ok = file:delete(Beam),
1760    ok.
1761
1762%% OTP-5195: A bug fix when using qlc:q/1,2.
1763qlc(Conf) when is_list(Conf) ->
1764    Dir = ?copydir,
1765    File = fname(Dir, "qlc.erl"),
1766    MFile = fname(Dir, "qlc"),
1767    Beam = fname(Dir, "qlc.beam"),
1768    Test = <<"-module(qlc).
1769
1770              -include_lib(\"stdlib/include/qlc.hrl\").
1771
1772              -export([t/0]).
1773
1774              t() ->
1775                  dets:open_file(t, []),
1776                  dets:insert(t, [{1,a},{2,b},{3,c},{4,d}]),
1777                  MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y}
1778                                  end),
1779                  QH1 = dets:table(t, [{traverse, {select, MS}}]),
1780                  QH2 = qlc:q([{Y} || {X,Y} <- dets:table(t),
1781                                      (X > 1) or (X < 5)]),
1782                  true = qlc:info(QH1) =:= qlc:info(QH2),
1783                  dets:close(t),
1784                  ok.
1785             ">>,
1786
1787    ok = file:write_file(File, Test),
1788    A = qlc,
1789    {ok, A} = compile:file(File, [debug_info,{outdir,Dir}]),
1790    {ok, _} = xref:start(s),
1791    {ok, A} = xref:add_module(s, MFile, {warnings,false}),
1792    {ok, _} = xref:q(s, "(Lin) E"), % is can be loaded
1793
1794    ok = check_state(s),
1795    xref:stop(s),
1796
1797    ok = file:delete(File),
1798    ok = file:delete(Beam),
1799    ok.
1800
1801
1802
1803%% Simple analyses
1804analyze(Conf) when is_list(Conf) ->
1805    S0 = new(),
1806    {{error, _, {invalid_options,[not_an_option]}}, _} =
1807      xref_base:analyze(S0, undefined_function_calls, [not_an_option]),
1808    {{error, _, {invalid_query,{q}}}, _} = xref_base:q(S0,{q}),
1809    {{error, _, {unknown_analysis,foo}}, _} = xref_base:analyze(S0, foo),
1810    {{error, _, {unknown_constant,"foo:bar/-1"}}, _} =
1811      xref_base:analyze(S0, {use,{foo,bar,-1}}),
1812
1813    CopyDir = ?copydir,
1814    Dir = fname(CopyDir,"rel2"),
1815    X = fname(Dir, "x.erl"),
1816    Y = fname(Dir, "y.erl"),
1817    A1_1 = fname([Dir,"lib","app1-1.1"]),
1818    A2 = fname([Dir,"lib","app2-1.1"]),
1819    EB1_1 = fname(A1_1, "ebin"),
1820    EB2 = fname(A2, "ebin"),
1821    Xbeam = fname(EB2, "x.beam"),
1822    Ybeam = fname(EB1_1, "y.beam"),
1823
1824    {ok, x} = compile:file(X, [debug_info, {outdir,EB2}]),
1825    {ok, y} = compile:file(Y, [debug_info, {outdir,EB1_1}]),
1826
1827    {ok, rel2, S1} = xref_base:add_release(S0, Dir, [{verbose,false}]),
1828    S = set_up(S1),
1829
1830    {ok, _} = analyze(undefined_function_calls, [{{x,xx,0},{x,undef,0}}], S),
1831    {ok, _} = analyze(undefined_functions, [{x,undef,0}], S),
1832    {ok, _} = analyze(locals_not_used, [{x,l,0},{x,l1,0}], S),
1833    {ok, _} = analyze(exports_not_used, [{x,xx,0},{y,t,0}], S),
1834
1835    {ok, _} = analyze(deprecated_function_calls, [{{y,t,0},{x,t,0}}], S),
1836    {ok, _} = analyze({deprecated_function_calls,next_version}, [], S),
1837    {ok, _} = analyze({deprecated_function_calls,next_major_release}, [], S),
1838    {ok, _} = analyze({deprecated_function_calls,eventually},
1839                      [{{y,t,0},{x,t,0}}], S),
1840    {ok, _} = analyze(deprecated_functions, [{x,t,0}], S),
1841    {ok, _} = analyze({deprecated_functions,next_version}, [], S),
1842    {ok, _} = analyze({deprecated_functions,next_major_release}, [], S),
1843    {ok, _} = analyze({deprecated_functions,eventually}, [{x,t,0}], S),
1844
1845    {ok, _} = analyze({call, {x,xx,0}}, [{x,undef,0}], S),
1846    {ok, _} = analyze({call, [{x,xx,0},{x,l,0}]}, [{x,l1,0},{x,undef,0}], S),
1847    {ok, _} = analyze({use, {x,l,0}}, [{x,l1,0}], S),
1848    {ok, _} = analyze({use, [{x,l,0},{x,l1,0}]}, [{x,l,0},{x,l1,0}], S),
1849
1850    {ok, _} = analyze({module_call, x}, [x], S),
1851    {ok, _} = analyze({module_call, [x,y]}, [x], S),
1852    {ok, _} = analyze({module_use, x}, [x,y], S),
1853    {ok, _} = analyze({module_use, [x,y]}, [x,y], S),
1854
1855    {ok, _} = analyze({application_call, app1}, [app2], S),
1856    {ok, _} = analyze({application_call, [app1,app2]}, [app2], S),
1857    {ok, _} = analyze({application_use, app2}, [app1,app2], S),
1858    {ok, _} = analyze({application_use, [app1,app2]}, [app1,app2], S),
1859
1860    ok = xref_base:delete(S),
1861    ok = file:delete(Xbeam),
1862    ok = file:delete(Ybeam),
1863    ok.
1864
1865%% Use of operators
1866basic(Conf) when is_list(Conf) ->
1867    S0 = new(),
1868
1869    F1 = {m1,f1,1},
1870    F6 = {m1,f2,6}, % X
1871    F2 = {m2,f1,2},
1872    F3 = {m2,f2,3}, % X
1873    F7 = {m2,f3,7}, % X
1874    F4 = {m3,f1,4}, % X
1875    F5 = {m3,f2,5},
1876
1877    UF1 = {m1,f12,17},
1878    UF2 = {m17,f17,177},
1879
1880    E1 = {F1,F3}, % X
1881    E2 = {F6,F7}, % X
1882    E3 = {F2,F6}, % X
1883    E4 = {F1,F4}, % X
1884    E5 = {F4,F5},
1885    E6 = {F7,F4}, % X
1886    E7 = {F1,F6},
1887
1888    UE1 = {F2,UF2}, % X
1889    UE2 = {F5,UF1}, % X
1890
1891    D1 = {F1,12},
1892    D6 = {F6,3},
1893    DefAt_m1 = [D1,D6],
1894    X_m1 = [F6],
1895    % L_m1 = [F1],
1896    XC_m1 = [E1,E2,E4],
1897    LC_m1 = [E7],
1898    LCallAt_m1 = [{E7,12}],
1899    XCallAt_m1 = [{E1,13},{E2,17},{E4,7}],
1900    Info1 = #xref_mod{name = m1, app_name = [a1]},
1901    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
1902                    XC_m1, LC_m1),
1903
1904    D2 = {F2,7},
1905    D3 = {F3,9},
1906    D7 = {F7,19},
1907    DefAt_m2 = [D2,D3,D7],
1908    X_m2 = [F3,F7],
1909    % L_m2 = [F2],
1910    XC_m2 = [E3,E6,UE1],
1911    LC_m2 = [],
1912    LCallAt_m2 = [],
1913    XCallAt_m2 = [{E3,96},{E6,12},{UE1,77}],
1914    Info2 = #xref_mod{name = m2, app_name = [a2]},
1915    S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
1916                    XC_m2, LC_m2),
1917
1918    D4 = {F4,6},
1919    D5 = {F5,97},
1920    DefAt_m3 = [D4,D5],
1921    X_m3 = [F4],
1922    % L_m3 = [F5],
1923    XC_m3 = [UE2],
1924    LC_m3 = [E5],
1925    LCallAt_m3 = [{E5,19}],
1926    XCallAt_m3 = [{UE2,22}],
1927    Info3 = #xref_mod{name = m3, app_name = [a3]},
1928    S3 = add_module(S2, Info3, DefAt_m3, X_m3, LCallAt_m3, XCallAt_m3,
1929                    XC_m3, LC_m3),
1930
1931    Info4 = #xref_mod{name = m4, app_name = [a2]},
1932    S4 = add_module(S3, Info4, [], [], [], [], [], []),
1933
1934    AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
1935    S9 = add_application(S4, AppInfo1),
1936    AppInfo2 = #xref_app{name = a2, rel_name = [r1]},
1937    S10 = add_application(S9, AppInfo2),
1938    AppInfo3 = #xref_app{name = a3, rel_name = [r2]},
1939    S11 = add_application(S10, AppInfo3),
1940
1941    RelInfo1 = #xref_rel{name = r1},
1942    S12 = add_release(S11, RelInfo1),
1943    RelInfo2 = #xref_rel{name = r2},
1944    S13 = add_release(S12, RelInfo2),
1945
1946    S = set_up(S13),
1947
1948    {ok, _} = eval("[m1,m2] + m:f/1", unknown_constant, S),
1949    {ok, _} = eval("[m1, m2, m:f/1]", type_mismatch, S),
1950
1951    {ok, _} = eval("[m1, m1->m2]", type_mismatch, S),
1952    {ok, _} = eval("components:f/1", unknown_constant, S),
1953    {ok, _} = eval("'of':f/1", unknown_constant, S),
1954    {ok, _} = eval("of:f/1", parse_error, S),
1955    {ok, _} = eval("components", unknown_constant, S),
1956    {ok, _} = eval("[components, of, closure]", parse_error, S),
1957    {ok, _} = eval("[components, 'of', closure]", unknown_constant, S),
1958
1959    {ok, _} = eval("[a1->a2,m1->m2]", type_mismatch, S),
1960    {ok, _} = eval("a1->a2,m1->m2", parse_error, S),
1961
1962    {ok, _} = eval("m1->a1", type_mismatch, S),
1963    {ok, _} = eval("[{m1,f1,1}] : App", parse_error, S),
1964    {ok, _} = eval("[{m1,f1,1}] : Fun", [F1], S),
1965    {ok, _} = eval("range X", type_error, S),
1966    {ok, _} = eval("domain X", type_error, S),
1967    {ok, _} = eval("range M", type_error, S),
1968    {ok, _} = eval("domain M", type_error, S),
1969
1970    % Misc.
1971    {ok, _} = eval("not_a_prefix_operator m1", parse_error, S),
1972    {ok, _} = eval(f("(Mod) ~p", [[F1,F6,F5]]), [m1,m3], S),
1973    {ok, _} = eval("(Lin) M - (Lin) m1",
1974                   [{F2,7},{F3,9},{F7,19},{F4,6},{F5,97},{UF2,0}], S),
1975    {ok, _} = eval(f("(Lin) M * (Lin) ~p", [[F1,F6]]),
1976                   [{F1,12},{F6,3}], S),
1977
1978    {ok, _} = eval(f("X * ~p", [[F1, F2, F3, F4, F5]]), [F3, F4], S),
1979    {ok, _} = eval("X", [F6,F3,F7,F4], S),
1980    {ok, _} = eval("X * AM", [F6,F3,F7,F4], S),
1981    {ok, _} = eval("X * a2", [F3,F7], S),
1982
1983    {ok, _} = eval("L * r1", [F1,F2], S),
1984    {ok, _} = eval("U", [UF1, UF2], S),
1985    {ok, _} = eval("U * AM", [UF1], S),
1986    {ok, _} = eval("U * UM", [UF2], S),
1987    {ok, _} = eval("XU * [m1, m2]", [F6,F3,F7,UF1], S),
1988    {ok, _} = eval("LU * [m3, m4]", [F5], S),
1989    {ok, _} = eval("UU", [F1,F2], S),
1990
1991    {ok, _} = eval("XC | m1", [E1,E2,E4], S),
1992    {ok, _} = eval(f("XC | ~p", [F1]), [E1,E4], S),
1993    {ok, _} = eval(f("(XXL) (Lin) (XC | ~p)", [F1]),
1994                   [{{D1,D3},[13]},{{D1,D4},[7]}],S),
1995    {ok, _} = eval(f("XC | (~p + ~p)", [F1, F2]), [E1,E4,E3,UE1], S),
1996    {ok, _} = eval(f("(XXL) (Lin) (XC | ~p)", [F1]),
1997                   [{{D1,D3},[13]},{{D1,D4},[7]}], S),
1998    {ok, _} = eval("LC | m3", [E5], S),
1999    {ok, _} = eval(f("LC | ~p", [F1]), [E7], S),
2000    {ok, _} = eval(f("LC | (~p + ~p)", [F1, F4]), [E7, E5], S),
2001    {ok, _} = eval("E | m1", [E1,E2,E4,E7], S),
2002    {ok, _} = eval(f("E | ~p", [F1]), [E1,E7,E4], S),
2003    {ok, _} = eval(f("E | (~p + ~p)", [F1, F2]), [E1,E7,E4,E3,UE1], S),
2004
2005    {ok, _} = eval("XC || m1", [E3,UE2], S),
2006    {ok, _} = eval(f("XC || ~p", [F6]), [E3], S),
2007    {ok, _} = eval(f("XC || (~p + ~p)", [F4, UF2]), [UE1,E4,E6], S),
2008    {ok, _} = eval("LC || m3", [E5], S),
2009    {ok, _} = eval(f("LC || ~p", [F1]), [], S),
2010    {ok, _} = eval(f("LC || ~p", [F6]), [E7], S),
2011    {ok, _} = eval(f("LC || (~p + ~p)", [F5, F6]), [E7,E5], S),
2012    {ok, _} = eval("E || m1", [E3,UE2,E7], S),
2013    {ok, _} = eval(f("E || ~p", [F6]), [E3,E7], S),
2014    {ok, _} = eval(f("E || (~p + ~p)", [F3,F4]), [E1,E4,E6], S),
2015
2016    {ok, _} = eval(f("~p + ~p", [F1,F2]), [F1,F2], S),
2017    {ok, _} = eval(f("~p * ~p", [m1,[F1,F6,F2]]), [F1,F6], S),
2018    {ok, _} = eval(f("~p * ~p", [F1,F2]), [], S),
2019
2020    %% range, domain
2021    {ok, _} = eval("range (E || m1)", [F6,UF1], S),
2022    {ok, _} = eval("domain (E || m1)", [F1,F2,F5], S),
2023    {ok, _} = eval(f("E | domain ~p", [[E1, {F2,F4}]]),
2024                   [E1,E7,E4,E3,UE1], S),
2025
2026    %% components, condensation, use, call
2027    {ok, _} = eval("(Lin) components E", type_error, S),
2028    {ok, _} = eval("components (Lin) E", type_error, S),
2029    {ok, _} = eval("components V", type_error, S),
2030    {ok, _} = eval("components E + components E", type_error, S),
2031
2032    {ok, _} = eval(f("range (closure E | ~p)", [[F1,F2]]),
2033                   [F6,F3,F7,F4,F5,UF1,UF2], S),
2034    {ok, _} =
2035    eval(f("domain (closure E || ~p)", [[UF2,F7]]), [F1,F2,F6], S),
2036    {ok, _} = eval("components E", [], S),
2037    {ok, _} = eval("components (Mod) E", [[m1,m2,m3]], S),
2038    {ok, _} = eval("components closure (Mod) E", [[m1,m2,m3]], S),
2039    {ok, _} = eval("condensation (Mod) E",
2040                   [{[m1,m2,m3],[m17]}], S),
2041    {ok, _} = eval("condensation closure (Mod) E",
2042                   [{[m1,m2,m3],[m17]}], S),
2043    {ok, _} = eval("condensation closure closure closure (Mod) E",
2044                   [{[m1,m2,m3],[m17]}], S),
2045    {ok, _} = eval("weak condensation (Mod) E",
2046                   [{[m1,m2,m3],[m1,m2,m3]},{[m1,m2,m3],[m17]},{[m17],[m17]}], S),
2047    {ok, _} = eval("strict condensation (Mod) E",
2048                   [{[m1,m2,m3],[m17]}], S),
2049    {ok, _} = eval("range condensation (Mod) E",
2050                   [[m17]], S),
2051    {ok, _} = eval("domain condensation (Mod) E",
2052                   [[m1,m2,m3]], S),
2053
2054    %% |, ||, |||
2055    {ok, _} = eval("(Lin) E || V", type_error, S),
2056    {ok, _} = eval("E ||| (Lin) V", type_error, S),
2057    {ok, _} = eval("E ||| m1", [E7], S),
2058    {ok, _} = eval("closure E ||| m1", [E7,{F1,UF1},{F6,UF1}], S),
2059    {ok, _} = eval("closure E ||| [m1,m2]",
2060                   [{F1,UF1},{F2,F7},{F1,F7},{F6,UF1},{F2,UF1},{F7,UF1},E7,E1,E2,E3], S),
2061    {ok, _} = eval("AE | a1", [{a1,a1},{a1,a2},{a1,a3}], S),
2062
2063    %% path ('of')
2064    {ok, _} = eval("(Lin) {m1,m2} of E", type_error, S),
2065    {ok, _} = eval("{m1,m2} of (Lin) E", type_error, S),
2066    [m1,m2] = eval("{m1,m2} of {m1,m2}", S),
2067    {ok, _} = eval("{m1,m2} of m1", type_error, S),
2068    {ok, _} = eval("{a3,m1} of ME", type_mismatch, S),
2069    [m1,m1] = eval("{m1} of ME", S),
2070    [m1,m1] = eval("{m1} of closure closure ME", S),
2071    false = eval("{m17} of ME", S),
2072    [m2,m1,m2] = eval("{m2} : Mod of ME", S),
2073    [m1,m2,m17] = eval("{m1, m17} of ME", S),
2074    [m1,m2,m17] = eval("m1 -> m17 of ME", S),
2075    {ok, _} = eval("[m1->m17,m17->m1] of ME", type_error, S),
2076    case eval(f("~p of E", [{F1,F7,UF1}]), S) of
2077        [F1,F6,F7,F4,F5,UF1] -> ok
2078    end,
2079    [a2,a1,a2] = eval("{a2} of AE", S),
2080
2081    %% weak/strict
2082    {ok, _} = eval("weak {m1,m2}", [{m1,m1},{m1,m2},{m2,m2}], S),
2083    {ok, _} = eval("strict [{m1,m1},{m1,m2},{m2,m2}]", [{m1,m2}], S),
2084    {ok, _} = eval("range weak [{m1,m2}] : Mod", [m1,m2], S),
2085    {ok, _} = eval("domain strict [{m1,m1},{m1,m2},{m2,m2}]", [m1], S),
2086
2087    %% #, number of
2088    {ok, _} = eval("# [{r1,r2}] : Rel", 1, S),
2089    {ok, _} = eval("# [{a3,a1}] : App", 1, S),
2090    {ok, _} = eval("# AE", 7, S),
2091    {ok, _} = eval("# ME", 8, S),
2092    {ok, _} = eval("# AE + # ME", 15, S),
2093    {ok, _} = eval("# AE * # ME", 56, S),
2094    {ok, _} = eval("# AE - # ME", -1, S),
2095    {ok, _} = eval("# E", 9, S),
2096    {ok, _} = eval("# V", 9, S),
2097    {ok, _} = eval("# (Lin) E", 9, S),
2098    {ok, _} = eval("# (ELin) E", 7, S),
2099    {ok, _} = eval("# closure E", type_error, S),
2100    {ok, _} = eval("# weak {m1,m2}", 3, S),
2101    {ok, _} = eval("#strict condensation (Mod) E", 1, S),
2102    {ok, _} = eval("#components closure (Mod) E", 1, S),
2103    {ok, _} = eval("# range strict condensation (Mod) E", 1, S),
2104    ok.
2105
2106%% The xref:m() and xref:d() functions
2107md(Conf) when is_list(Conf) ->
2108    CopyDir = ?copydir,
2109    Dir = fname(CopyDir,"md"),
2110    X = fname(Dir, "x__x.erl"),
2111    Y = fname(Dir, "y__y.erl"),
2112    Xbeam = fname(Dir, "x__x.beam"),
2113    Ybeam = fname(Dir, "y__y.beam"),
2114
2115    {error, _, {invalid_filename,{foo,bar}}} = xref:m({foo,bar}),
2116    {error, _, {invalid_filename,{foo,bar}}} = xref:d({foo,bar}),
2117
2118    {ok, x__x} = compile:file(X, [debug_info, {outdir,Dir}]),
2119    {ok, y__y} = compile:file(Y, [debug_info, {outdir,Dir}]),
2120
2121    {error, _, {no_such_module, foo_bar}} = xref:m(foo_bar),
2122    OldPath = code:get_path(),
2123    true = code:set_path([Dir | OldPath]),
2124    MInfo = xref:m(x__x),
2125    [{{x__x,t,1},{y__y,t,2}}] = info_tag(MInfo, undefined),
2126    [] = info_tag(MInfo, unused),
2127    [] = info_tag(MInfo, deprecated),
2128    DInfo = xref:d(Dir),
2129    [{{x__x,t,1},{y__y,t,2}}] = info_tag(DInfo, undefined),
2130    [{y__y,l,0},{y__y,l1,0}] = info_tag(DInfo, unused),
2131    [] = info_tag(MInfo, deprecated),
2132
2133    %% Switch from 'functions' mode to 'modules' mode.
2134    {ok, x__x} = compile:file(X, [no_debug_info, {outdir,Dir}]),
2135    {ok, y__y} = compile:file(Y, [no_debug_info, {outdir,Dir}]),
2136    MInfoMod = xref:m(x__x),
2137    [{y__y,t,2}] = info_tag(MInfoMod, undefined),
2138    [] = info_tag(MInfo, deprecated),
2139    DInfoMod = xref:d(Dir),
2140    [{y__y,t,2}] = info_tag(DInfoMod, undefined),
2141    [] = info_tag(MInfo, deprecated),
2142
2143    true = code:set_path(OldPath),
2144    ok = file:delete(Xbeam),
2145    ok = file:delete(Ybeam),
2146    ok.
2147
2148%% User queries
2149q(Conf) when is_list(Conf) ->
2150    S0 = new(),
2151    {ok, _} = eval("'foo", parse_error, S0),
2152    {ok, _} = eval("TT = E, TT = V", variable_reassigned, S0),
2153    {ok, _} = eval("TT = E, TTT", unknown_variable, S0),
2154    {ok, S} = eval("TT := E", [], S0),
2155    {ok, S1} = eval("TT * TT * TT", [], S),
2156    {ok, _S2} = xref_base:forget(S1, 'TT'),
2157    ok.
2158
2159%% Setting and getting values of query variables
2160variables(Conf) when is_list(Conf) ->
2161    Sa = new(),
2162    {{error, _, {invalid_options,[not_an_option]}}, _} =
2163    xref_base:variables(Sa, [not_an_option]),
2164    {error, _, {not_user_variable,foo}} = xref_base:forget(Sa, foo),
2165    Sa1 = set_up(Sa),
2166    {error, _, {not_user_variable,foo}} = xref_base:forget(Sa1, foo),
2167    ok = xref_base:delete(Sa1),
2168
2169    S0 = new(),
2170
2171    F1 = {m1,f1,1},
2172    F2 = {m2,f1,2},
2173    Lib = {lib1,f1,1}, % undefined
2174
2175    E1 = {F1,F2},
2176    E2 = {F2,F1},
2177    E3 = {F1,Lib},
2178
2179    D1 = {F1,12},
2180    DefAt_m1 = [D1],
2181    X_m1 = [F1],
2182    % L_m1 = [],
2183    XC_m1 = [E1,E3],
2184    LC_m1 = [],
2185    LCallAt_m1 = [],
2186    XCallAt_m1 = [{E1,13},{E3,17}],
2187    Info1 = #xref_mod{name = m1, app_name = [a1]},
2188    S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
2189                    XC_m1, LC_m1),
2190
2191    D2 = {F2,7},
2192    DefAt_m2 = [D2],
2193    X_m2 = [F2],
2194    % L_m2 = [],
2195    XC_m2 = [E2],
2196    LC_m2 = [],
2197    LCallAt_m2 = [],
2198    XCallAt_m2 = [{E2,96}],
2199    Info2 = #xref_mod{name = m2, app_name = [a2]},
2200    S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
2201                    XC_m2, LC_m2),
2202
2203    S = set_up(S2),
2204
2205    eval("T1=E, T2=E*T1, T3 = T2*T2, T4=range T3, T5=T3|T4, T5",
2206         [E1,E2,E3], S),
2207    eval("((E*E)*(E*E)) | (range ((E*E)*(E*E)))",
2208         [E1,E2,E3], S),
2209    eval("T1=V*V,T2=T1*V,T3=V*V*V,T3",
2210         [F1,F2,Lib], S),
2211    eval("T1=V*V, T2=V*V, T1*T2",
2212         [F1,F2,Lib], S),
2213
2214    {ok, S100} = eval("T0 := E", [E1, E2, E3], S),
2215    {ok, S101} = eval("T1 := E  | m1", [E1, E3], S100),
2216    {ok, S102} = eval("T2 := E  | m2", [E2], S101),
2217    {{ok, [{user, ['T0', 'T1', 'T2']}]}, _} = xref_base:variables(S102),
2218    {ok, S103} = xref_base:forget(S102, 'T0'),
2219    {{ok, [{user, ['T1', 'T2']}]}, S104} =
2220    xref_base:variables(S103, [user]),
2221    {ok, S105} = xref_base:forget(S104),
2222    {{ok, [{user, []}]}, S106} = xref_base:variables(S105),
2223    {{ok, [{predefined,_}]}, S107_0} =
2224    xref_base:variables(S106, [predefined]),
2225
2226    {ok, S107_1} =
2227    eval("TT := E, TT2 := V, TT1 := TT * TT", [E1,E2,E3], S107_0),
2228    {{ok, [{user, ['TT', 'TT1', 'TT2']}]}, _} =
2229    xref_base:variables(S107_1),
2230    {ok, S107} = xref_base:forget(S107_1),
2231
2232    CopyDir = ?copydir,
2233    Dir = fname(CopyDir,"lib_test"),
2234    Beam = fname(Dir, "lib1.beam"),
2235
2236    copy_file(fname(Dir, "lib1.erl"), Beam),
2237    {ok, S108} =
2238    xref_base:set_library_path(S107, [Dir], [{verbose,false}]),
2239    {{error, _, _}, _} = xref_base:variables(S108, [{verbose,false}]),
2240    {ok, S109} = xref_base:set_library_path(S108, [], [{verbose,false}]),
2241
2242    NoOfTables = erlang:system_info(ets_count),
2243
2244    {ok, S110} = eval("Eplus := closure E, TT := Eplus",
2245                      'closure()', S109),
2246    {{ok, [{user, ['Eplus','TT']}]}, S111} = xref_base:variables(S110),
2247    {ok, S112} = xref_base:forget(S111, ['TT','Eplus']),
2248    true = NoOfTables =:= erlang:system_info(ets_count),
2249
2250    {ok, NS0} = eval("Eplus := closure E", 'closure()', S112),
2251    {{ok, [{user, ['Eplus']}]}, NS} = xref_base:variables(NS0),
2252    ok = xref_base:delete(NS),
2253    true = NoOfTables =:= erlang:system_info(ets_count),
2254
2255    ok = file:delete(Beam),
2256    ok.
2257
2258%% OTP-5071. Too many unused functions.
2259unused_locals(Conf) when is_list(Conf) ->
2260    Dir = ?copydir,
2261
2262    File1 = fname(Dir, "a.erl"),
2263    MFile1 = fname(Dir, "a"),
2264    Beam1 = fname(Dir, "a.beam"),
2265    Test1 = <<"-module(a).
2266               -export([f/1, g/2]).
2267
2268               f(X) ->
2269                   Y = b:f(X),
2270                   Z = b:g(Y),
2271                   start(b, h, [Z]).
2272
2273               g(X, Y) ->
2274                   ok.
2275
2276               start(M, F, A) ->
2277                   spawn(M, F, A).
2278             ">>,
2279    ok = file:write_file(File1, Test1),
2280    {ok, a} = compile:file(File1, [debug_info,{outdir,Dir}]),
2281
2282    File2 = fname(Dir, "b.erl"),
2283    MFile2 = fname(Dir, "b"),
2284    Beam2 = fname(Dir, "b.beam"),
2285    Test2 = <<"-module(b).
2286               -export([f/1, g/2]).
2287
2288               f(X) ->
2289                   io:write(\"~w\", [X]),
2290                   a:start(timer, sleep, [1000]).
2291
2292               g(X, Y) ->
2293                   apply(a, g, [X, Y]).
2294             ">>,
2295
2296    ok = file:write_file(File2, Test2),
2297    {ok, b} = compile:file(File2, [debug_info,{outdir,Dir}]),
2298
2299    {ok, _} = xref:start(s),
2300    {ok, a} = xref:add_module(s, MFile1),
2301    {ok, b} = xref:add_module(s, MFile2),
2302    {ok, []} = xref:analyse(s, locals_not_used),
2303    ok = check_state(s),
2304    xref:stop(s),
2305
2306    ok = file:delete(File1),
2307    ok = file:delete(Beam1),
2308    ok = file:delete(File2),
2309    ok = file:delete(Beam2),
2310    ok.
2311
2312
2313%% Format error messages
2314format_error(Conf) when is_list(Conf) ->
2315    {ok, _Pid} = start(s),
2316    ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
2317
2318    %% Parse error messages.
2319    "Invalid regular expression \"add(\"" ++ _ = fstring(xref:q(s,'"add("')),
2320    'Invalid operator foo\n' = fatom(xref:q(s,'foo E')),
2321    'Invalid wildcard variable \'_Var\' (only \'_\' is allowed)\n'
2322      = fatom(xref:q(s,"module:function/_Var")),
2323    'Missing type of regular expression ".*"\n'
2324      = fatom(xref:q(s,'".*"')),
2325    'Type does not match structure of constant: \'M\' : Fun\n'
2326      = fatom(xref:q(s,"'M' : Fun")),
2327    'Type does not match structure of constant: ".*" : Fun\n'
2328      = fatom(xref:q(s,'".*" : Fun')),
2329    'Type does not match structure of constant: [m:f/1, m1:f2/3] : App\n'
2330      = fatom(xref:q(s,"[m:f/1,m1:f2/3] : App")),
2331    'Parse error on line 1: syntax error before: \'-\'\n'
2332      = fatom(xref:q(s,"E + -")),
2333    "Parse error on line 1: unterminated atom starting with 'foo'\n"
2334      = flatten(xref:format_error(xref:q(s,"'foo"))),
2335    'Parse error at end of string: syntax error before: \n'
2336      = fatom(xref:q(s,"E +")),
2337    'Parse error on line 1: syntax error before: \'Lin\'\n'
2338      = fatom(xref:q(s,"Lin")),
2339
2340    %% Other messages
2341    'Variable \'QQ\' used before set\n' = fatom(xref:q(s,"QQ")),
2342    'Unknown constant a\n' = fatom(xref:q(s,"{a} of E")),
2343
2344    %% Testing xref_parser:t2s/1.
2345    'Variable assigned more than once: E := E + E\n' = fatom(xref:q(s,"E:=E + E")),
2346    'Variable assigned more than once: E = E + E\n' = fatom(xref:q(s,"E=E + E")),
2347    "Operator applied to argument(s) of different or invalid type(s): "
2348    "E + V * V\n" = flatten(xref:format_error(xref:q(s,"E + (V * V)"))),
2349    {error,xref_compiler,{type_error,"(V + V) * E"}} = xref:q(s,"(V + V) * E"),
2350    "Type does not match structure of constant: [m:f/3 -> g:h/17] : "
2351    "App\n" = flatten(xref:format_error(xref:q(s,"[{{m,f,3},{g,h,17}}] : App"))),
2352    'Type does not match structure of constant: [m -> f, g -> h] : Fun\n'
2353      = fatom(xref:q(s,"[{m,f},g->h] : Fun")),
2354    'Type does not match structure of constant: {m, n, o} : Fun\n' =
2355    fatom(xref:q(s,"{m,n,o} : Fun")),
2356    {error,xref_compiler,{type_error,"range (Lin) V"}} =
2357      xref:q(s,"range ((Lin) V)"),
2358    {error,xref_compiler,{type_error,"condensation range E"}} =
2359      xref:q(s,"condensation (range E)"),
2360    {error,xref_compiler,{type_error,"condensation (# E + # V)"}} =
2361      xref:q(s,"condensation (# E + # V)"),
2362    {error,xref_compiler,{type_error,"range (# E + # E)"}} =
2363      xref:q(s,"range (#E + #E)"),
2364    {error,xref_compiler,{type_error,"range (# E)"}} =
2365      xref:q(s,"range #E"), % Hm...
2366    {error,xref_compiler,{type_error,"E + # E"}} =
2367      xref:q(s,"E + #E + #E"), % Hm...
2368    {error,xref_compiler,{type_error,"V * E || V | V"}} =
2369      xref:q(s,"V * (E || V) | V"),
2370    {error,xref_compiler,{type_error,"E || (E | V)"}} =
2371      xref:q(s,"V * E || (E | V)"),
2372    {error,xref_compiler,{type_error,"E * \"m\" : Mod"}} =
2373      xref:q(s,'E * "m" : Mod'),
2374    {error,xref_compiler,{type_error,"E * (\"m\":f/_ + m:\"f\"/3)"}} =
2375      xref:q(s,'E * ("m":f/_ + m:"f"/3)'),
2376
2377    xref:stop(s),
2378    ok.
2379
2380%% OTP-7423. Xref scanner bug.
2381otp_7423(Conf) when is_list(Conf) ->
2382    {ok, _Pid} = start(s),
2383    S = "E | [compiler] : App || [{erlang,
2384                                   size,
2385                                   1}] : Fun",
2386    {error,xref_compiler,{unknown_constant,"compiler"}} = xref:q(s,S),
2387    xref:stop(s),
2388    ok.
2389
2390%% OTP-7831. Allow anonymous Xref processes.
2391otp_7831(Conf) when is_list(Conf) ->
2392    {ok, Pid1} = xref:start([]),
2393    xref:stop(Pid1),
2394    {ok, Pid2} = xref:start([{xref_mode, modules}]),
2395    xref:stop(Pid2),
2396    ok.
2397
2398%% OTP-10192. Allow filenames with character codes greater than 126.
2399otp_10192(Conf) when is_list(Conf) ->
2400    PrivDir = ?privdir,
2401    {ok, _Pid} = xref:start(s),
2402    Dir = filename:join(PrivDir, "ä"),
2403    ok = file:make_dir(Dir),
2404    {ok, []} = xref:add_directory(s, Dir),
2405    xref:stop(s),
2406    ok.
2407
2408otp_13708(Conf) when is_list(Conf) ->
2409    {ok, _} = start(s),
2410    ok = xref:set_default(s, [{verbose, true}]),
2411    {ok, []} = xref:q(s,"E"),
2412    xref:stop(s),
2413
2414    CopyDir = ?copydir,
2415    Dir = fname(CopyDir,"lib_test"),
2416    {ok, _} = start(s),
2417    ok = xref:set_library_path(s, [Dir], [{verbose, true}]),
2418    xref:stop(s).
2419
2420%% OTP-14464. Unicode atoms.
2421otp_14464(Conf) when is_list(Conf) ->
2422    Dir = ?copydir,
2423
2424    File1 = fname(Dir, "a.erl"),
2425    MFile1 = fname(Dir, "a"),
2426    Beam1 = fname(Dir, "a.beam"),
2427    Test1 = "-module(a).
2428             -export([ärlig/0, 'кlирилли́ческий атомB'/0]).
2429
2430              ärlig() ->
2431                  'кlирилли́ческий атомB'.
2432
2433              'кlирилли́ческий атомB'() ->
2434                  foo.
2435             ",
2436    ok = file:write_file(File1, unicode:characters_to_binary(Test1)),
2437    {ok, a} = compile:file(File1, [debug_info,{outdir,Dir}]),
2438
2439    {ok, _} = xref:start(s),
2440    {ok, a} = xref:add_module(s, MFile1),
2441
2442    {ok, [{arlig,0}]} = xref:q(s, 'a:"ärlig"/0'),
2443    {ok, [{a,'кlирилли́ческий атомB',0}]} =
2444        xref:q(s, 'a:"кlирилли́ческий атомB"/0'),
2445
2446    xref:stop(s),
2447    ok = file:delete(File1),
2448    ok = file:delete(Beam1).
2449
2450%% OTP-14344. -on_load() attribute.
2451otp_14344(Conf) when is_list(Conf) ->
2452    Dir = ?copydir,
2453
2454    File1 = fname(Dir, "a.erl"),
2455    MFile1 = fname(Dir, "a"),
2456    Beam1 = fname(Dir, "a.beam"),
2457    Test1 = <<"-module(a).
2458               -on_load(init/0).
2459               init() -> do_init().
2460               do_init() -> ok.
2461              ">>,
2462    ok = file:write_file(File1, Test1),
2463    {ok, a} = compile:file(File1, [debug_info,{outdir,Dir}]),
2464
2465    {ok, _} = xref:start(s),
2466    {ok, a} = xref:add_module(s, MFile1),
2467
2468    {ok, [{a,init,0}]} = xref:q(s, "OL"),
2469    {ok, []} = xref:analyze(s, locals_not_used),
2470
2471    xref:stop(s),
2472    ok = file:delete(File1),
2473    ok = file:delete(Beam1).
2474
2475%% PR-2752, ERL-1353, ERL-1476, GH-4192.
2476behaviour(Config) ->
2477    ModMode = [{xref_mode, modules}],
2478    FunMode = [],
2479
2480    Test1 = [{a, <<"-module(a).
2481                    -callback a() -> ok.
2482                    ">>}],
2483    {Undef1, UnusedExports1} = behaviour_test(Test1, Config, FunMode),
2484    [] = Undef1,
2485    [] = UnusedExports1,
2486    {Undef1m, UnusedExports1m} = behaviour_test(Test1, Config, ModMode),
2487    [] = Undef1m,
2488    [] = UnusedExports1m,
2489
2490    Test2 = [{a, <<"-module(a).
2491                    -export([behaviour_info/1]).
2492                    behaviour_info(_) ->
2493                        ok.
2494                   ">>}],
2495    {Undef2, UnusedExports2} = behaviour_test(Test2, Config, FunMode),
2496    [] = Undef2,
2497    [{a,behaviour_info,1}] = UnusedExports2,
2498    {Undef2m, UnusedExports2m} = behaviour_test(Test2, Config, ModMode),
2499    [] = Undef2m,
2500    %% Without abstract code it is not possible to determine if
2501    %% M:behaviour_info/1 is generated or not. The best we can do is
2502    %% to assume it is generated since it would otherwise always be
2503    %% returned as unused.
2504    [] = UnusedExports2m,
2505
2506    Test3 = [{a, <<"-module(a).
2507                    -export([behaviour_info/1]).
2508                    behaviour_info(_) ->
2509                        ok.
2510               ">>},
2511             {b, <<"-module(b).
2512                    -export([bar/0]).
2513                    bar() -> a:behaviour_info(callbacks).
2514                   ">>}],
2515    {Undef3, UnusedExports3} = behaviour_test(Test3, Config, FunMode),
2516    [] = Undef3,
2517    [{b,bar,0}] = UnusedExports3,
2518    {Undef3m, UnusedExports3m} = behaviour_test(Test3, Config, ModMode),
2519    [] = Undef3m,
2520    [{b,bar,0}] = UnusedExports3m,
2521    ok.
2522
2523behaviour_test(Tests, Conf, Opts) ->
2524    {ok, _} = xref:start(s, Opts),
2525    add_modules(Tests, Conf),
2526    case lists:keyfind(xref_mode, 1, Opts) of
2527        {xref_mode, modules} ->
2528            UndefinedFunctionCalls = [];
2529        _ ->
2530            {ok, UndefinedFunctionCalls} =
2531                xref:analyze(s, undefined_function_calls)
2532    end,
2533    {ok, ExportsNotUsed} = xref:analyze(s, exports_not_used),
2534    xref:stop(s),
2535    {UndefinedFunctionCalls, ExportsNotUsed}.
2536
2537add_modules([], _Conf) ->
2538    ok;
2539add_modules([{Mod, Test} |Tests], Conf) ->
2540    Dir = ?copydir,
2541    Name = atom_to_list(Mod),
2542    File = fname(Dir, Name ++ ".erl"),
2543    MFile = fname(Dir, Name),
2544    Beam = fname(Dir, Name ++ ".beam"),
2545    ok = file:write_file(File, Test),
2546    {ok, Mod} = compile:file(File, [debug_info,{outdir,Dir}]),
2547    {ok, Mod} = xref:add_module(s, MFile),
2548    check_state(s),
2549    ok = file:delete(File),
2550    ok = file:delete(Beam),
2551    add_modules(Tests, Conf).
2552
2553%%%
2554%%% Utilities
2555%%%
2556
2557copy_file(Src, Dest) ->
2558    file:copy(Src, Dest).
2559
2560fname(N) ->
2561    filename:join(N).
2562
2563fname(Dir, Basename) ->
2564    filename:join(Dir, Basename).
2565
2566new() ->
2567    {ok, S} = xref_base:new(),
2568    S.
2569
2570set_up(S) ->
2571    {ok, S1} = xref_base:set_up(S, [{verbose, false}]),
2572    S1.
2573
2574eval(Query, E, S) ->
2575    ?format("------------------------------~n", []),
2576    ?format("Evaluating ~p~n", [Query]),
2577    {Answer, NewState} = xref_base:q(S, Query, [{verbose, false}]),
2578    {Reply, Expected} =
2579    case Answer of
2580        {ok, R} when is_list(E) ->
2581            {unsetify(R), sort(E)};
2582        {ok, R} ->
2583            {unsetify(R), E};
2584        {error, _Module, Reason} ->
2585            {element(1, Reason), E}
2586    end,
2587    if
2588        Reply =:= Expected ->
2589            ?format("As expected, got ~n~p~n", [Expected]),
2590            {ok, NewState};
2591        true ->
2592            ?format("Expected ~n~p~nbut got ~n~p~n", [Expected, Reply]),
2593            not_ok
2594    end.
2595
2596analyze(Query, E, S) ->
2597    ?format("------------------------------~n", []),
2598    ?format("Evaluating ~p~n", [Query]),
2599    {{ok, L}, NewState} =
2600    xref_base:analyze(S, Query, [{verbose, false}]),
2601    case {unsetify(L), sort(E)} of
2602        {X,X} ->
2603            ?format("As was expected, got ~n~p~n", [X]),
2604            {ok, NewState};
2605        {_R,_X} ->
2606            ?format("Expected ~n~p~nbut got ~n~p~n", [_X, _R]),
2607            not_ok
2608    end.
2609
2610unsetify(S) ->
2611    case is_sofs_set(S) of
2612        true -> to_external(S);
2613        false -> S
2614    end.
2615
2616%% Note: assumes S has been set up; the new state is not returned
2617eval(Query, S) ->
2618    {{ok, Answer}, _NewState} =
2619    xref_base:q(S, Query, [{verbose, false}]),
2620    unsetify(Answer).
2621
2622add_module(S, XMod, DefAt, X, LCallAt, XCallAt, XC, LC) ->
2623    Attr = {[], [], []},
2624    Depr0 = {[], [], [], []},
2625    DBad = [],
2626    Depr = {Depr0,DBad},
2627    OL = [],
2628    Data = {DefAt, LCallAt, XCallAt, LC, XC, X, Attr, Depr, OL},
2629    Unres = [],
2630    {ok, _Module, _Bad, State} =
2631    xref_base:do_add_module(S, XMod, Unres, Data),
2632    State.
2633
2634add_application(S, XApp) ->
2635    xref_base:do_add_application(S, XApp).
2636
2637add_release(S, XRel) ->
2638    xref_base:do_add_release(S, XRel).
2639
2640remove_module(S, M) ->
2641    xref_base:do_remove_module(S, M).
2642
2643info_tag(Info, Tag) ->
2644    {value, {_Tag, Value}} = lists:keysearch(Tag, 1, Info),
2645    Value.
2646
2647make_ufile(FileName) ->
2648    ok = file:write_file(FileName, term_to_binary(foo)),
2649    hide_file(FileName).
2650
2651make_udir(Dir) ->
2652    ok = file:make_dir(Dir),
2653    hide_file(Dir).
2654
2655hide_file(FileName) ->
2656    {ok, FileInfo} = file:read_file_info(FileName),
2657    NewFileInfo = FileInfo#file_info{mode = 0},
2658    ok = file:write_file_info(FileName, NewFileInfo).
2659
2660%% Note that S has to be set up before calling this checking function.
2661check_state(S) ->
2662    Info = xref:info(S),
2663
2664    modules_mode_check(S, Info),
2665    case info(Info, mode) of
2666        modules ->
2667            ok;
2668        functions ->
2669            functions_mode_check(S, Info)
2670    end.
2671
2672%% The manual mentions some facts that should always hold.
2673%% Here they are again.
2674functions_mode_check(S, Info) ->
2675    %% F = L + X,
2676    {ok, F} = xref:q(S, "F"),
2677    {ok, F} = xref:q(S, "L + X"),
2678
2679    %% V = X + L + B + U,
2680    {ok, V} = xref:q(S, "V"),
2681    {ok, V} = xref:q(S, "X + L + B + U"),
2682
2683    %% X, L, B and U are disjoint.
2684    {ok, []} =
2685    xref:q(S, "X * L + X * B + X * U + L * B + L * U + B * U"),
2686
2687    %% V = UU + XU + LU,
2688    {ok, V} = xref:q(S, "UU + XU + LU"),
2689
2690    %% E = LC + XC
2691    {ok, E} = xref:q(S, "E"),
2692    {ok, E} = xref:q(S, "LC + XC"),
2693
2694    %% U subset of XU,
2695    {ok, []} = xref:q(S, "U - XU"),
2696
2697    %% LU = range LC
2698    {ok, []} = xref:q(S, "(LU - range LC) + (range LC - LU)"),
2699
2700    %% XU = range XC
2701    {ok, []} = xref:q(S, "(XU - range XC) + (range XC - XU)"),
2702
2703    %% LU subset F
2704    {ok, []} = xref:q(S, "LU - F"),
2705
2706    %% UU subset F
2707    {ok, []} = xref:q(S, "UU - F"),
2708
2709    %% OL subset F
2710    {ok, []} = xref:q(S, "OL - F"),
2711
2712    %% ME = (Mod) E
2713    {ok, ME} = xref:q(S, "ME"),
2714    {ok, ME} = xref:q(S, "(Mod) E"),
2715
2716    %% AE = (App) E
2717    {ok, AE} = xref:q(S, "AE"),
2718    {ok, AE} = xref:q(S, "(App) E"),
2719
2720    %% RE = (Rel) E
2721    {ok, RE} = xref:q(S, "RE"),
2722    {ok, RE} = xref:q(S, "(Rel) E"),
2723
2724    %% (Mod) V subset of M
2725    {ok, []} = xref:q(S, "(Mod) V - M"),
2726
2727    %% range UC subset of U
2728    {ok, []} = xref:q(S, "range UC - U"),
2729
2730    %% Some checks on the numbers returned by the info functions.
2731
2732    {Resolved, Unresolved} = info(Info, no_calls),
2733    AllCalls = Resolved + Unresolved,
2734    {ok, AllCalls} = xref:q(S, "# (XLin) E + # (LLin) E"),
2735
2736    {Local, Exported} = info(Info, no_functions),
2737    LX = Local+Exported,
2738    {ok, LXs} = xref:q(S, 'Extra = _:module_info/"(0|1)" + LM,
2739                                  # (F - Extra)'),
2740    true = LX =:= LXs,
2741
2742    {LocalCalls, ExternalCalls, UnresCalls} =
2743    info(Info, no_function_calls),
2744    LEU = LocalCalls + ExternalCalls + UnresCalls,
2745    {ok, LEU} = xref:q(S, "# LC + # XC"),
2746
2747    InterFunctionCalls = info(Info, no_inter_function_calls),
2748    {ok, InterFunctionCalls} = xref:q(S, "# EE"),
2749
2750    %% And some more checks on counters...
2751    check_count(S),
2752
2753    %% ... and more
2754    {ok, []} = xref:q(S, "LM - X - U - B"),
2755
2756    ok.
2757
2758modules_mode_check(S, Info) ->
2759    %% B subset of XU,
2760    {ok, []} = xref:q(S, "B - XU"),
2761
2762    %% M = AM + LM + UM
2763    {ok, M} = xref:q(S, "M"),
2764    {ok, M} = xref:q(S, "AM + LM + UM"),
2765
2766    %% DF is a subset of X U B, etc.
2767    {ok, []} = xref:q(S, "DF - X - B"),
2768    {ok, []} = xref:q(S, "DF_3 - DF"),
2769    {ok, []} = xref:q(S, "DF_2 - DF_3"),
2770    {ok, []} = xref:q(S, "DF_1 - DF_2"),
2771
2772    %% AM, LM and UM are disjoint.
2773    {ok, []} = xref:q(S, "AM * LM + AM * UM + LM * UM"),
2774
2775    %% (App) M subset of A
2776    {ok, []} = xref:q(S, "(App) M - A"),
2777
2778    AM = info(Info, no_analyzed_modules),
2779    {ok, AM} = xref:q(S, "# AM"),
2780
2781    A = info(Info, no_applications),
2782    {ok, A} = xref:q(S, "# A"),
2783
2784    NoR = info(Info, no_releases),
2785    {ok, NoR} = xref:q(S, "# R"),
2786
2787    ok.
2788
2789%% Checks the counters of some of the overall and modules info functions.
2790%% (Applications and releases are not checked.)
2791check_count(S) ->
2792    %%{ok, R} = xref:q(S, 'R'),
2793    %% {ok, A} = xref:q(S, 'A'),
2794    {ok, M} = xref:q(S, 'AM'),
2795
2796    {ok, _} = xref:q(S,
2797                     "Extra := _:module_info/\"(0|1)\" + LM"),
2798
2799    %% info/1:
2800    {ok, NoR} = xref:q(S, '# R'),
2801    {ok, NoA} = xref:q(S, '# A'),
2802    {ok, NoM} = xref:q(S, '# AM'),
2803    {ok, NoCalls} = xref:q(S, '# (XLin) E + # (LLin) E'),
2804    {ok, NoFunCalls} = xref:q(S, '# E'),
2805    {ok, NoXCalls} = xref:q(S, '# XC'),
2806    {ok, NoLCalls} = xref:q(S, '# LC'),
2807    {ok, NoLXCalls} = xref:q(S, '# (XC * LC)'),
2808    NoAllCalls = NoXCalls + NoLCalls,
2809    {ok, NoFun} = xref:q(S, '# (F - Extra)'),
2810    {ok, NoICalls} = xref:q(S, '# EE'),
2811
2812    Info = xref:info(S),
2813    NoR = info(Info, no_releases),
2814    NoA = info(Info, no_applications),
2815    NoM = info(Info, no_analyzed_modules),
2816    {NoResolved, NoUC} = info(Info, no_calls),
2817    NoCalls = NoResolved + NoUC,
2818    {NoLocal, NoExternal, NoUnres} = info(Info, no_function_calls),
2819    NoAllCalls = NoLocal + NoExternal + NoUnres,
2820    NoAllCalls = NoFunCalls + NoLXCalls,
2821    {NoLocalFuns, NoExportedFuns} = info(Info, no_functions),
2822    NoFun = NoLocalFuns + NoExportedFuns,
2823    NoICalls = info(Info, no_inter_function_calls),
2824
2825    %% per module
2826    info_module(M, S),
2827
2828    ok.
2829
2830info_module([M | Ms], S) ->
2831    {ok, NoCalls} = per_module("T = (E | ~p : Mod), # (XLin) T + # (LLin) T",
2832                               M, S),
2833    {ok, NoFunCalls} = per_module("# (E | ~p : Mod)", M, S),
2834    {ok, NoXCalls} = per_module("# (XC | ~p : Mod)", M, S),
2835    {ok, NoLCalls} = per_module("# (LC | ~p : Mod)", M, S),
2836    {ok, NoLXCalls} = per_module("# ((XC * LC) | ~p : Mod)", M, S),
2837    NoAllCalls = NoXCalls + NoLCalls,
2838    {ok, NoFun} = per_module("# (F * ~p : Mod - Extra)", M, S),
2839    {ok, NoICalls} = per_module("# (EE | ~p : Mod)", M, S),
2840
2841    [{_M,Info}] = xref:info(S, modules, M),
2842    {NoResolved, NoUC} = info(Info, no_calls),
2843    NoCalls = NoResolved + NoUC,
2844    {NoLocal, NoExternal, NoUnres} = info(Info, no_function_calls),
2845    NoAllCalls = NoLocal + NoExternal + NoUnres,
2846    NoAllCalls = NoFunCalls + NoLXCalls,
2847    {NoLocalFuns, NoExportedFuns} = info(Info, no_functions),
2848    NoFun = NoLocalFuns + NoExportedFuns,
2849    NoICalls = info(Info, no_inter_function_calls),
2850
2851    info_module(Ms, S);
2852info_module([], _S) ->
2853    ok.
2854
2855per_module(Q, M, S) ->
2856    xref:q(S, f(Q, [M])).
2857
2858info(Info, What) ->
2859    {value, {What, Value}} = lists:keysearch(What, 1, Info),
2860    Value.
2861
2862f(S, A) ->
2863    flatten(io_lib:format(S, A)).
2864
2865fatom(R) ->
2866    list_to_atom(fstring(R)).
2867
2868fstring(R) ->
2869    flatten(xref:format_error(R)).
2870
2871start(Server) ->
2872    case xref:start(Server) of
2873        {error, {already_started, _Pid}} ->
2874            xref:stop(Server),
2875            xref:start(Server);
2876        R -> R
2877    end.
2878
2879add_erts_code_path(KernelPath) ->
2880    VersionDirs =
2881    filelib:is_dir(
2882      filename:join(
2883        [code:lib_dir(),
2884         lists:flatten(
2885           ["kernel-",
2886            [X ||
2887             {kernel,_,X} <-
2888             application_controller:which_applications()]])])),
2889    case VersionDirs of
2890        true ->
2891            case code:lib_dir(erts) of
2892                String when is_list(String) ->
2893                    [KernelPath, fname(String,"ebin")];
2894                _Other1 ->
2895                    [KernelPath]
2896            end;
2897        false ->
2898            % Clearcase?
2899            PrelPath = filename:join([code:lib_dir(),"..","erts","preloaded"]),
2900            case filelib:is_dir(PrelPath) of
2901                true ->
2902                    [KernelPath, fname(PrelPath,"ebin")];
2903                false ->
2904                    [KernelPath]
2905            end
2906    end.
2907