103
108
{[dial_warning()], #dialyzer_plt{}, doc_plt()}.
105
110
get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) ->
106
InitState = #state{callgraph=Callgraph, plt=Plt, no_warn_unused=NoWarnUnused,
107
codeserver=Codeserver, parent=Parent},
111
InitState = #st{callgraph = Callgraph, codeserver = Codeserver,
112
no_warn_unused = NoWarnUnused, parent = Parent, plt = Plt},
108
113
NewState = get_refined_success_typings(InitState),
109
Mods = dialyzer_callgraph:modules(NewState#state.callgraph),
114
Mods = dialyzer_callgraph:modules(NewState#st.callgraph),
110
115
CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver,
112
117
get_warnings_from_modules(Mods, NewState, DocPlt, CWarns).
114
get_warnings_from_modules([M|Left], State, DocPlt, Acc) when is_atom(M) ->
115
send_log(State#state.parent, io_lib:format("Getting warnings for ~w\n", [M])),
118
codeserver=Codeserver,
119
no_warn_unused=NoWarnUnused} = State,
119
get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) ->
120
send_log(State#st.parent, io_lib:format("Getting warnings for ~w\n", [M])),
121
#st{callgraph = Callgraph, codeserver = Codeserver,
122
no_warn_unused = NoWarnUnused, plt = Plt} = State,
120
123
{ok, Tree} = dialyzer_codeserver:lookup(M, Codeserver),
121
124
Records = dialyzer_codeserver:lookup_records(M, Codeserver),
122
125
Contracts = dialyzer_codeserver:lookup_contracts(M, Codeserver),
124
127
%% Check if there are contracts for functions that do not exist
126
129
dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph),
127
{Warnings2, FunTypes} = dialyzer_dataflow:get_warnings(Tree, Plt, Callgraph,
128
Records, NoWarnUnused),
130
{Warnings2, FunTypes, InterModuleCalls, ModLocalCalls} =
131
dialyzer_dataflow:get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused),
129
132
NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt),
130
get_warnings_from_modules(Left, State, NewDocPlt, [Warnings1,Warnings2|Acc]);
131
get_warnings_from_modules([], #state{plt=Plt}, DocPlt, Acc) ->
134
callgraph__renew_module_calls(InterModuleCalls, ModLocalCalls, Callgraph),
135
State1 = st__renew_state_calls(NewCallgraph, State),
136
get_warnings_from_modules(Ms, State1, NewDocPlt, [Warnings1,Warnings2|Acc]);
137
get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) ->
132
138
{lists:flatten(Acc), Plt, DocPlt}.
134
140
refine_succ_typings(ModulePostorder, State) ->
135
141
?debug("Module postorder: ~p\n", [ModulePostorder]),
136
142
refine_succ_typings(ModulePostorder, State, []).
138
refine_succ_typings([SCC|Left], State, Fixpoint) ->
144
refine_succ_typings([SCC|SCCs], State, Fixpoint) ->
139
145
Msg = io_lib:format("Dataflow of one SCC: ~w\n", [SCC]),
140
send_log(State#state.parent, Msg),
146
send_log(State#st.parent, Msg),
141
147
?debug("~s\n", [Msg]),
142
148
{NewState, FixpointFromScc} =
145
151
[_|_] -> refine_one_scc(SCC, State)
147
153
NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc),
148
refine_succ_typings(Left, NewState, NewFixpoint);
154
refine_succ_typings(SCCs, NewState, NewFixpoint);
149
155
refine_succ_typings([], State, Fixpoint) ->
150
156
case Fixpoint =:= [] of
151
157
true -> {fixpoint, State};
152
158
false -> {not_fixpoint, Fixpoint, State}
155
-spec refine_one_module(atom(), #state{}) -> {#state{}, ordset(non_neg_integer())}. % labels
161
-spec refine_one_module(module(), #st{}) -> {#st{}, [label()]}. % ordset
157
refine_one_module(M, State) ->
158
{ok, Tree} = dialyzer_codeserver:lookup(M, State#state.codeserver),
163
refine_one_module(M, State) ->
164
#st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State,
165
{ok, Tree} = dialyzer_codeserver:lookup(M, CodeServer),
159
166
AllFuns = collect_fun_info([Tree]),
160
167
FunTypes = get_fun_types_from_plt(AllFuns, State),
161
Records = dialyzer_codeserver:lookup_records(M, State#state.codeserver),
162
NewFunTypes = dialyzer_dataflow:get_fun_types(Tree, State#state.plt,
163
State#state.callgraph, Records),
168
Records = dialyzer_codeserver:lookup_records(M, CodeServer),
169
{NewFunTypes, InterModCalls, ModLocalCalls} =
170
dialyzer_dataflow:get_fun_types(Tree, PLT, Callgraph, Records),
172
callgraph__renew_module_calls(InterModCalls, ModLocalCalls, Callgraph),
164
173
case reached_fixpoint(FunTypes, NewFunTypes) of
175
State1 = st__renew_state_calls(NewCallgraph, State),
166
177
{false, NotFixpoint} ->
167
178
?debug("Not fixpoint\n", []),
168
179
NewState = insert_into_plt(dict:from_list(NotFixpoint), State),
169
{NewState, ordsets:from_list([FunLbl || {FunLbl, _Type} <- NotFixpoint])}
180
NewState1 = st__renew_state_calls(NewCallgraph, NewState),
181
{NewState1, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])}
184
callgraph__renew_module_calls(InterModuleCalls, ModuleLocalCalls, Callgraph) ->
185
Callgraph#dialyzer_callgraph{inter_module_calls = InterModuleCalls,
186
module_local_calls = ModuleLocalCalls}.
188
st__renew_state_calls(Callgraph, State) ->
189
State#st{callgraph = Callgraph}.
172
191
refine_one_scc(SCC, State) ->
173
192
refine_one_scc(SCC, State, []).
254
265
find_succ_typings(State) ->
255
266
find_succ_typings(State, []).
257
find_succ_typings(State, NotFixpoint) ->
258
case dialyzer_callgraph:take_scc(State#state.callgraph) of
268
find_succ_typings(#st{callgraph = Callgraph, parent = Parent} = State,
270
case dialyzer_callgraph:take_scc(Callgraph) of
259
271
{ok, SCC, NewCallgraph} ->
260
Msg = io_lib:format("Typesig analysis for scc: ~w\n", [format_scc(SCC)]),
261
?debug("~s\n", [Msg]),
262
send_log(State#state.parent, Msg),
263
{NewState, NewNotFixpoint1} =
264
analyze_scc(SCC, State#state{callgraph=NewCallgraph}),
272
Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]),
274
send_log(Parent, Msg),
275
{NewState, NewNotFixpoint1} =
276
analyze_scc(SCC, State#st{callgraph = NewCallgraph}),
265
277
NewNotFixpoint2 = ordsets:union(NewNotFixpoint1, NotFixpoint),
266
278
find_succ_typings(NewState, NewNotFixpoint2);
268
?debug("Done\n\n", []),
280
?debug("==================== Typesig done ====================\n\n", []),
269
281
case NotFixpoint =:= [] of
270
282
true -> {fixpoint, State};
271
283
false -> {not_fixpoint, NotFixpoint, State}
275
analyze_scc(SCC, State = #state{codeserver=Codeserver}) ->
287
analyze_scc(SCC, #st{codeserver = Codeserver} = State) ->
277
289
dialyzer_codeserver:lookup(MFA, Codeserver),
278
290
dialyzer_codeserver:lookup_records(M, Codeserver)}
279
|| MFA = {M,_,_} <- SCC],
291
|| {M, _, _} = MFA <- SCC],
280
292
false = lists:any(fun({_, X, _}) -> X =:= error end, SCC1),
281
293
SCC2 = [{MFA, Def, Rec} || {MFA, {ok, Def}, Rec} <- SCC1],
282
294
Contracts1 = [{MFA, dialyzer_codeserver:lookup_contract(MFA, Codeserver)}
283
|| MFA = {_, _, _} <- SCC],
295
|| {_, _, _} = MFA <- SCC],
284
296
Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1],
285
297
Contracts3 = orddict:from_list(Contracts2),
286
298
{SuccTypes, PltContracts, NotFixpoint} =
287
299
find_succ_types_for_scc(SCC2, Contracts3, State),
288
300
State1 = insert_into_plt(SuccTypes, State),
289
ContrPlt = dialyzer_plt:insert_contract_list(State1#state.plt, PltContracts),
290
{State1#state{plt=ContrPlt}, NotFixpoint}.
301
ContrPlt = dialyzer_plt:insert_contract_list(State1#st.plt, PltContracts),
302
{State1#st{plt = ContrPlt}, NotFixpoint}.
292
304
find_succ_types_for_scc(SCC, Contracts,
293
State = #state{codeserver=Codeserver,
296
%% Assume that the Plt contains the current propagated types.
305
#st{codeserver = Codeserver,
306
callgraph = Callgraph, plt = Plt} = State) ->
307
%% Assume that the PLT contains the current propagated types
297
308
AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC]),
298
309
PropTypes = get_fun_types_from_plt(AllFuns, State),
299
310
MFAs = [MFA || {MFA, {_Var, _Fun}, _Rec} <- SCC],
300
311
NextLabel = dialyzer_codeserver:next_core_label(Codeserver),
301
Plt1 = dialyzer_plt:delete_contract_list(State#state.plt, MFAs),
312
Plt1 = dialyzer_plt:delete_contract_list(Plt, MFAs),
302
313
FunTypes = dialyzer_typesig:analyze_scc_get_all_fun_types(SCC, NextLabel,
305
316
AllFunSet = sets:from_list([X || {X, _} <- AllFuns]),
306
FilteredFunTypes = dict:filter(fun(X, _) ->
307
sets:is_element(X, AllFunSet)
317
FilteredFunTypes = dict:filter(fun(X, _) ->
318
sets:is_element(X, AllFunSet)
309
320
%% Check contracts
310
321
PltContracts = dialyzer_contracts:check_contracts(Contracts, Callgraph,
311
322
FilteredFunTypes),
312
323
ContractFixpoint =
313
not lists:any(fun({MFA, _C}) ->
314
%% Check the non-deleted plt
315
case dialyzer_plt:lookup_contract(Plt, MFA) of
324
lists:all(fun({MFA, _C}) ->
325
%% Check the non-deleted PLT
326
case dialyzer_plt:lookup_contract(Plt, MFA) of
320
331
case (ContractFixpoint andalso
321
332
reached_fixpoint_strict(PropTypes, FilteredFunTypes)) of
323
334
{FilteredFunTypes, PltContracts, []};
325
336
?debug("Not fixpoint for: ~w\n", [AllFuns]),
326
{FilteredFunTypes, PltContracts,
337
{FilteredFunTypes, PltContracts,
327
338
ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns])}
339
350
collect_fun_info(Trees) ->
340
351
collect_fun_info(Trees, []).
342
collect_fun_info([Tree|Left], List) ->
343
FoldFun = fun(SubTree, Acc) ->
344
case cerl:is_c_fun(SubTree) of
345
true -> [{cerl_trees:get_label(SubTree),
346
cerl:fun_arity(SubTree)}|Acc];
350
collect_fun_info(Left, cerl_trees:fold(FoldFun, List, Tree));
353
collect_fun_info([Tree|Trees], List) ->
354
Fun = fun(SubTree, Acc) ->
355
case cerl:is_c_fun(SubTree) of
357
[{cerl_trees:get_label(SubTree), cerl:fun_arity(SubTree)}|Acc];
361
collect_fun_info(Trees, cerl_trees:fold(Fun, List, Tree));
351
362
collect_fun_info([], List) ->
354
lookup_fun_type(Label, Arity, #state{callgraph=Callgraph, plt=Plt}) ->
365
lookup_fun_type(Label, Arity, #st{callgraph = Callgraph, plt = Plt}) ->
355
366
ID = lookup_name(Label, Callgraph),
356
367
case dialyzer_plt:lookup(Plt, ID) of
357
368
none -> erl_types:t_fun(Arity, erl_types:t_any());
425
438
%% contract typing info in dictionary format
426
439
{ok, Contracts} = dialyzer_utils:get_spec_info(AbstrCode, Records),
427
440
Sigs0 = get_top_level_signatures(Code, Records, Contracts),
429
if is_atom(Module) ->
430
list_to_atom(filename:basename(atom_to_list(Module)));
432
list_to_atom(filename:basename(Module))
441
M = if is_atom(Module) ->
442
list_to_atom(filename:basename(atom_to_list(Module)));
444
list_to_atom(filename:basename(Module))
434
446
Sigs1 = [{{M, F, A}, Type} || {{F, A}, Type} <- Sigs0],
435
447
Sigs = ordsets:from_list(Sigs1),
448
io:format("==================== Final result ====================\n\n", []),
437
449
pp_signatures(Sigs, Records),
440
452
-spec get_top_level_signatures(core_records(), dict(), dict()) ->
441
ordset({{atom(),byte()},erl_type()}).
453
[{{atom(), arity()}, erl_type()}].
443
455
get_top_level_signatures(Code, Records, Contracts) ->
444
456
Tree = cerl:from_records(Code),
473
485
pp_signatures(Left, Records);
474
486
pp_signatures([{{_, module_info, 1}, _}|Left], Records) ->
475
487
pp_signatures(Left, Records);
476
pp_signatures([{{M, F, A}, Type}|Left], Records) ->
488
pp_signatures([{{M, F, _A}, Type}|Left], Records) ->
478
490
case cerl:is_literal(Type) of
479
491
true -> io_lib:format("~w", [cerl:concrete(Type)]);
480
false -> "fun" ++ String = erl_types:t_to_string(Type, Records),
492
false -> "fun(" ++ String = erl_types:t_to_string(Type, Records),
493
string:strip(String, right, $))
483
io:format("~w:~w/~w :: ~s\n", [M, F, A, TypeString]),
495
io:format("~w:~w~s\n", [M, F, TypeString]),
484
496
pp_signatures(Left, Records);
485
497
pp_signatures([], _Records) ->