aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorStavros Aronis <[email protected]>2011-10-28 16:54:29 +0200
committerStavros Aronis <[email protected]>2011-11-18 15:06:39 +0100
commitd101155c5dc115a51725b52e500c9a981845f2da (patch)
treec433d52c42da87a03a1aba58bc9b23c1a51bd19f /lib
parent7fde343786329e7465a53c1d2f36e9436343e387 (diff)
downloadotp-d101155c5dc115a51725b52e500c9a981845f2da.tar.gz
otp-d101155c5dc115a51725b52e500c9a981845f2da.tar.bz2
otp-d101155c5dc115a51725b52e500c9a981845f2da.zip
Behaviour callback discrepancy detection for Dialyzer
Diffstat (limited to 'lib')
-rw-r--r--lib/dialyzer/src/dialyzer.erl16
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl48
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl291
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl16
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl24
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/gen_event_incorrect_return2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_incorrect_args8
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_missing_callbacks3
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour9
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour_old4
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/results/undefined_callbacks2
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/gen_event_incorrect_return.erl33
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_incorrect_args.erl11
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_missing_callbacks.erl23
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_behaviour.erl13
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct.erl32
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct_2.erl38
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl25
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_behaviour.erl6
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_callback.erl15
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_args_callback.erl13
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_return_callback.erl15
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/missing_callback.erl10
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/supervisor_incorrect_return.erl17
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_beh_callback.erl13
-rw-r--r--lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_behaviour.erl10
29 files changed, 449 insertions, 253 deletions
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 5014a4244c..acbe574e2b 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -443,22 +443,18 @@ message_to_string({race_condition, [M, F, Args, Reason]}) ->
io_lib:format("The call ~w:~w~s ~s\n", [M, F, Args, Reason]);
%%----- Warnings for behaviour errors --------------------
message_to_string({callback_type_mismatch, [B, F, A, O]}) ->
- io_lib:format("The inferred return type of the ~w/~w callback includes the"
- " type ~s which is not a valid return for the ~w behaviour\n",
+ io_lib:format("The inferred return type for ~w/~w is ~s which is not valid"
+ " return for the callback of the ~w behaviour\n",
[F, A, erl_types:t_to_string(O), B]);
message_to_string({callback_arg_type_mismatch, [B, F, A, N, O]}) ->
- io_lib:format("The inferred type of the ~s argument of ~w/~w callback"
- " includes the type ~s which is not valid for the ~w behaviour"
+ io_lib:format("The inferred type for the ~s argument of ~w/~w is ~s which is"
+ " not valid for the callback of the ~w behaviour"
"\n", [ordinal(N), F, A, erl_types:t_to_string(O), B]);
message_to_string({callback_missing, [B, F, A]}) ->
io_lib:format("Undefined callback function ~w/~w (behaviour '~w')\n",
[F, A, B]);
-message_to_string({invalid_spec, [B, F, A, R]}) ->
- io_lib:format("The spec for the ~w:~w/~w callback is not correct: ~s\n",
- [B, F, A, R]);
-message_to_string({spec_missing, [B, F, A]}) ->
- io_lib:format("Type info about ~w:~w/~w callback is not available\n",
- [B, F, A]).
+message_to_string({callback_info_missing, [B]}) ->
+ io_lib:format("Callback info about the ~w behaviour is not available\n", [B]).
%%-----------------------------------------------------------------------------
%% Auxiliary functions below
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 1f462c216b..62153fa176 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -43,8 +43,7 @@
parent :: pid(),
plt :: dialyzer_plt:plt(),
start_from = byte_code :: start_from(),
- use_contracts = true :: boolean(),
- behaviours = {false,[]} :: {boolean(),[atom()]}
+ use_contracts = true :: boolean()
}).
-record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}).
@@ -57,9 +56,7 @@
start(Parent, LegalWarnings, Analysis) ->
RacesOn = ordsets:is_element(?WARN_RACE_CONDITION, LegalWarnings),
- BehavOn = ordsets:is_element(?WARN_BEHAVIOUR, LegalWarnings),
- Analysis0 = Analysis#analysis{race_detection = RacesOn,
- behaviours_chk = BehavOn},
+ Analysis0 = Analysis#analysis{race_detection = RacesOn},
Analysis1 = expand_files(Analysis0),
Analysis2 = run_analysis(Analysis1),
State = #server_state{parent = Parent, legal_warnings = LegalWarnings},
@@ -125,8 +122,7 @@ analysis_start(Parent, Analysis) ->
plt = Plt,
parent = Parent,
start_from = Analysis#analysis.start_from,
- use_contracts = Analysis#analysis.use_contracts,
- behaviours = {Analysis#analysis.behaviours_chk, []}
+ use_contracts = Analysis#analysis.use_contracts
},
Files = ordsets:from_list(Analysis#analysis.files),
{Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State),
@@ -180,8 +176,8 @@ analysis_start(Parent, Analysis) ->
send_analysis_done(Parent, Plt4, State3#analysis_state.doc_plt).
analyze_callgraph(Callgraph, State) ->
- Plt = State#analysis_state.plt,
Codeserver = State#analysis_state.codeserver,
+ Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver),
Parent = State#analysis_state.parent,
case State#analysis_state.analysis_type of
plt_build ->
@@ -192,13 +188,11 @@ analyze_callgraph(Callgraph, State) ->
State#analysis_state{plt = NewPlt};
succ_typings ->
NoWarn = State#analysis_state.no_warn_unused,
- {BehavioursChk, _Known} = State#analysis_state.behaviours,
DocPlt = State#analysis_state.doc_plt,
Callgraph1 = dialyzer_callgraph:finalize(Callgraph),
{Warnings, NewPlt, NewDocPlt} =
dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt,
- Codeserver, NoWarn, Parent,
- BehavioursChk),
+ Codeserver, NoWarn, Parent),
dialyzer_callgraph:delete(Callgraph1),
send_warnings(State#analysis_state.parent, Warnings),
State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}
@@ -213,8 +207,7 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
include_dirs = Dirs,
parent = Parent,
use_contracts = UseContracts,
- start_from = StartFrom,
- behaviours = {BehChk, _}
+ start_from = StartFrom
} = State) ->
send_log(Parent, "Reading files and computing callgraph... "),
{T1, _} = statistics(runtime),
@@ -263,37 +256,26 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
{T2, _} = statistics(runtime),
Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]),
send_log(Parent, Msg1),
- {KnownBehaviours, UnknownBehaviours} =
- dialyzer_behaviours:get_behaviours(Modules, NewCServer),
- if UnknownBehaviours =:= [] -> ok;
- true -> send_unknown_behaviours(Parent, UnknownBehaviours)
- end,
- State1 = State#analysis_state{behaviours = {BehChk, KnownBehaviours}},
- NewCallgraph2 = cleanup_callgraph(State1, NewCServer, NewCallgraph1, Modules),
+ NewCallgraph2 = cleanup_callgraph(State, NewCServer, NewCallgraph1, Modules),
{T3, _} = statistics(runtime),
Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]),
send_log(Parent, Msg2),
{NewCallgraph2, sets:from_list(NoWarn), NewCServer}.
cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
- codeserver = CodeServer,
- behaviours = {BehChk, KnownBehaviours}
+ codeserver = CodeServer
},
CServer, Callgraph, Modules) ->
ModuleDeps = dialyzer_callgraph:module_deps(Callgraph),
send_mod_deps(Parent, ModuleDeps),
{Callgraph1, ExtCalls} = dialyzer_callgraph:remove_external(Callgraph),
- if BehChk ->
- RelevantAPICalls =
- dialyzer_behaviours:get_behaviour_apis(KnownBehaviours),
- BehaviourAPICalls = [Call || {_From, To} = Call <- ExtCalls,
- lists:member(To, RelevantAPICalls)],
- Callgraph2 =
- dialyzer_callgraph:put_behaviour_api_calls(BehaviourAPICalls,
- Callgraph1);
- true ->
- Callgraph2 = Callgraph1
- end,
+ RelevantAPICalls =
+ dialyzer_behaviours:get_behaviour_apis([gen_server]),
+ BehaviourAPICalls = [Call || {_From, To} = Call <- ExtCalls,
+ lists:member(To, RelevantAPICalls)],
+ Callgraph2 =
+ dialyzer_callgraph:put_behaviour_api_calls(BehaviourAPICalls,
+ Callgraph1),
ExtCalls1 = [Call || Call = {_From, To} <- ExtCalls,
not dialyzer_plt:contains_mfa(InitPlt, To)],
{BadCalls1, RealExtCalls} =
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 47ce9ba6eb..b478af01df 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -30,7 +30,7 @@
-module(dialyzer_behaviours).
--export([check_callbacks/4, get_behaviours/2, get_behaviour_apis/1,
+-export([check_callbacks/4, get_behaviour_apis/1,
translate_behaviour_api_call/5, translatable_behaviours/1,
translate_callgraph/3]).
@@ -51,12 +51,6 @@
%%--------------------------------------------------------------------
--spec get_behaviours([module()], dialyzer_codeserver:codeserver()) ->
- {[behaviour()], [behaviour()]}.
-
-get_behaviours(Modules, Codeserver) ->
- get_behaviours(Modules, Codeserver, [], []).
-
-spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}],
dialyzer_plt:plt(),
dialyzer_codeserver:codeserver()) -> [dial_warning()].
@@ -69,12 +63,115 @@ check_callbacks(Module, Attrs, Plt, Codeserver) ->
MFA = {Module,module_info,0},
{_Var,Code} = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
File = get_file(cerl:get_ann(Code)),
- State = #state{plt = Plt, codeserver = Codeserver, filename = File,
- behlines = BehLines},
+ State = #state{plt = Plt, filename = File, behlines = BehLines,
+ codeserver = Codeserver},
Warnings = get_warnings(Module, Behaviours, State),
[add_tag_file_line(Module, W, State) || W <- Warnings]
end.
+%%--------------------------------------------------------------------
+
+get_behaviours(Attrs) ->
+ BehaviourListsAndLine = [{cerl:concrete(L2), hd(cerl:get_ann(L2))} ||
+ {L1, L2} <- Attrs, cerl:is_literal(L1),
+ cerl:is_literal(L2), cerl:concrete(L1) =:= 'behaviour'],
+ Behaviours = lists:append([Behs || {Behs,_} <- BehaviourListsAndLine]),
+ BehLines = [{B,L} || {L1,L} <- BehaviourListsAndLine, B <- L1],
+ {Behaviours, BehLines}.
+
+get_warnings(Module, Behaviours, State) ->
+ get_warnings(Module, Behaviours, State, []).
+
+get_warnings(_, [], _, Acc) ->
+ Acc;
+get_warnings(Module, [Behaviour|Rest], State, Acc) ->
+ NewAcc = check_behaviour(Module, Behaviour, State, Acc),
+ get_warnings(Module, Rest, State, NewAcc).
+
+check_behaviour(Module, Behaviour, #state{plt = Plt}, Acc) ->
+ case dialyzer_plt:lookup_callbacks(Plt, Behaviour) of
+ [] -> [{callback_info_missing, [Behaviour]}|Acc];
+ Callbacks -> check_all_callbacks(Module, Behaviour, Callbacks, Plt, Acc)
+ end.
+
+check_all_callbacks(_Module, _Behaviour, [], _Plt, Acc) ->
+ Acc;
+check_all_callbacks(Module, Behaviour, [Cb|Rest], Plt, Acc) ->
+ {{Behaviour, Function, Arity},
+ {{_BehFile, _BehLine}, Callback}} = Cb,
+ CbMFA = {Module, Function, Arity},
+ CbReturnType = dialyzer_contracts:get_contract_return(Callback),
+ CbArgTypes = dialyzer_contracts:get_contract_args(Callback),
+ Acc0 = Acc,
+ Acc1 =
+ case dialyzer_plt:lookup(Plt, CbMFA) of
+ 'none' -> [{callback_missing, [Behaviour, Function, Arity]}|Acc0];
+ {'value', RetArgTypes} ->
+ Acc00 = Acc0,
+ {ReturnType, ArgTypes} = RetArgTypes,
+ Acc01 =
+ case erl_types:t_is_none(erl_types:t_inf(ReturnType, CbReturnType)) of
+ false -> Acc00;
+ true ->
+ [{callback_type_mismatch,[Behaviour, Function,
+ Arity, ReturnType]}|Acc00]
+ end,
+ Acc02 =
+ case erl_types:any_none(
+ erl_types:t_inf_lists(ArgTypes, CbArgTypes)) of
+ false -> Acc01;
+ true ->
+ find_mismatching_args(ArgTypes, CbArgTypes, Behaviour,
+ Function, Arity, 1, Acc01)
+ end,
+ Acc02
+ end,
+ Acc2 =
+ case dialyzer_plt:lookup_contract(Plt, CbMFA) of
+ 'none' -> Acc1;
+ {value, _Contract} ->
+ %% TODO: Check spec for discrepancies
+ Acc1
+ end,
+ NewAcc = Acc2,
+ check_all_callbacks(Module, Behaviour, Rest, Plt, NewAcc).
+
+find_mismatching_args([], [], _Behaviour, _Function, _Arity, _N, Acc) ->
+ Acc;
+find_mismatching_args([Type|Rest], [CbType|CbRest], Behaviour,
+ Function, Arity, N, Acc) ->
+ case erl_types:t_is_none(erl_types:t_inf(Type, CbType)) of
+ false ->
+ find_mismatching_args(Rest, CbRest, Behaviour, Function, Arity, N+1, Acc);
+ true ->
+ NewAcc =
+ [{callback_arg_type_mismatch,
+ [Behaviour, Function, Arity, N, Type]}|Acc],
+ find_mismatching_args(Rest, CbRest, Behaviour, Function, Arity, N+1, NewAcc)
+ end.
+
+add_tag_file_line(_Module, {Tag, [B|_R]} = Warn, State)
+ when Tag =:= callback_missing;
+ Tag =:= callback_info_missing ->
+ {B, Line} = lists:keyfind(B, 1, State#state.behlines),
+ {?WARN_BEHAVIOUR, {State#state.filename, Line}, Warn};
+add_tag_file_line(Module, {_Tag, [_B, Fun, Arity|_R]} = Warn, State) ->
+ {_A, FunCode} =
+ dialyzer_codeserver:lookup_mfa_code({Module, Fun, Arity},
+ State#state.codeserver),
+ Anns = cerl:get_ann(FunCode),
+ FileLine = {get_file(Anns), get_line(Anns)},
+ {?WARN_BEHAVIOUR, FileLine, Warn}.
+
+get_line([Line|_]) when is_integer(Line) -> Line;
+get_line([_|Tail]) -> get_line(Tail);
+get_line([]) -> -1.
+
+get_file([{file, File}|_]) -> File;
+get_file([_|Tail]) -> get_file(Tail).
+
+%%-----------------------------------------------------------------------------
+
-spec translatable_behaviours(cerl:c_module()) -> behaviour_api_dict().
translatable_behaviours(Tree) ->
@@ -133,182 +230,6 @@ translate_callgraph([{Behaviour,_}|Behaviours], Module, Callgraph) ->
translate_callgraph([], _Module, Callgraph) ->
Callgraph.
-%%--------------------------------------------------------------------
-
-get_behaviours(Attrs) ->
- BehaviourListsAndLine = [{cerl:concrete(L2), hd(cerl:get_ann(L2))} ||
- {L1, L2} <- Attrs, cerl:is_literal(L1),
- cerl:is_literal(L2), cerl:concrete(L1) =:= 'behaviour'],
- Behaviours = lists:append([Behs || {Behs,_} <- BehaviourListsAndLine]),
- BehLines = [{B,L} || {L1,L} <- BehaviourListsAndLine, B <- L1],
- {Behaviours, BehLines}.
-
-get_warnings(Module, Behaviours, State) ->
- get_warnings(Module, Behaviours, State, []).
-
-get_warnings(_, [], _, Acc) ->
- Acc;
-get_warnings(Module, [Behaviour|Rest], State, Acc) ->
- Warnings = check_behaviour(Module, Behaviour, State),
- get_warnings(Module, Rest, State, Warnings ++ Acc).
-
-check_behaviour(Module, Behaviour, State) ->
- try
- Callbacks = Behaviour:behaviour_info(callbacks),
- Fun = fun({_,_,_}) -> true;
- (_) -> false
- end,
- case lists:any(Fun, Callbacks) of
- true -> check_all_callbacks(Module, Behaviour, Callbacks, State);
- false -> []
- end
- catch
- _:_ -> []
- end.
-
-check_all_callbacks(Module, Behaviour, Callbacks, State) ->
- check_all_callbacks(Module, Behaviour, Callbacks, State, []).
-
-check_all_callbacks(_Module, _Behaviour, [], _State, Acc) ->
- Acc;
-check_all_callbacks(Module, Behaviour, [{Fun, Arity, Spec}|Rest],
- #state{codeserver = CServer} = State, Acc) ->
- Records = dialyzer_codeserver:get_records(CServer),
- ExpTypes = dialyzer_codeserver:get_exported_types(CServer),
- case parse_spec(Spec, ExpTypes, Records) of
- {ok, Fun, Type} ->
- RetType = erl_types:t_fun_range(Type),
- ArgTypes = erl_types:t_fun_args(Type),
- Warns = check_callback(Module, Behaviour, Fun, Arity, RetType,
- ArgTypes, State#state.plt);
- Else ->
- Warns = [{invalid_spec, [Behaviour, Fun, Arity, reason_spec_error(Else)]}]
- end,
- check_all_callbacks(Module, Behaviour, Rest, State, Warns ++ Acc);
-check_all_callbacks(Module, Behaviour, [{Fun, Arity}|Rest], State, Acc) ->
- Warns = {spec_missing, [Behaviour, Fun, Arity]},
- check_all_callbacks(Module, Behaviour, Rest, State, [Warns|Acc]).
-
-parse_spec(String, ExpTypes, Records) ->
- case erl_scan:string(String) of
- {ok, Tokens, _} ->
- case erl_parse:parse(Tokens) of
- {ok, Form} ->
- case Form of
- {attribute, _, 'spec', {{Fun, _}, [TypeForm|_Constraint]}} ->
- MaybeRemoteType = erl_types:t_from_form(TypeForm),
- try
- Type = erl_types:t_solve_remote(MaybeRemoteType, ExpTypes,
- Records),
- {ok, Fun, Type}
- catch
- throw:{error,Msg} -> {spec_remote_error, Msg}
- end;
- _Other -> not_a_spec
- end;
- {error, {Line, _, Msg}} -> {spec_parser_error, Line, Msg}
- end;
- _Other ->
- lexer_error
- end.
-
-reason_spec_error({spec_remote_error, Msg}) ->
- io_lib:format("Remote type solver error: ~s. Make sure the behaviour source is included in the analysis or the plt",[Msg]);
-reason_spec_error(not_a_spec) ->
- "This is not a spec";
-reason_spec_error({spec_parser_error, Line, Msg}) ->
- io_lib:format("~s line of the spec: ~s", [ordinal(Line),Msg]);
-reason_spec_error(lexer_error) ->
- "Lexical error".
-
-ordinal(1) -> "1st";
-ordinal(2) -> "2nd";
-ordinal(3) -> "3rd";
-ordinal(N) when is_integer(N) -> io_lib:format("~wth",[N]).
-
-check_callback(Module, Behaviour, Fun, Arity, XRetType, XArgTypes, Plt) ->
- LookupType = dialyzer_plt:lookup(Plt, {Module, Fun, Arity}),
- case LookupType of
- {value, {Type,Args}} ->
- Warn1 = case unifiable(Type, XRetType) of
- [] -> [];
- Offenders ->
- [{callback_type_mismatch,
- [Behaviour, Fun, Arity, erl_types:t_sup(Offenders)]}]
- end,
- ZipArgs = lists:zip3(lists:seq(1, Arity), Args, XArgTypes),
- Warn2 = [{callback_arg_type_mismatch,
- [Behaviour, Fun, Arity, N,
- erl_types:t_sup(Offenders)]} ||
- {Offenders, N} <- [check_callback_1(V) || V <- ZipArgs],
- Offenders =/= []],
- Warn1 ++ Warn2;
- _ -> [{callback_missing, [Behaviour, Fun, Arity]}]
- end.
-
-check_callback_1({N, T1, T2}) ->
- {unifiable(T1, T2), N}.
-
-unifiable(Type1, Type2) ->
- List1 = erl_types:t_elements(Type1),
- List2 = erl_types:t_elements(Type2),
- [T || T <- List1,
- lists:all(fun(T1) ->
- erl_types:t_is_none(erl_types:t_inf(T, T1, opaque))
- end, List2)].
-
-add_tag_file_line(_Module, {Tag, [B|_R]} = Warn, State)
- when Tag =:= spec_missing;
- Tag =:= invalid_spec;
- Tag =:= callback_missing ->
- {B, Line} = lists:keyfind(B, 1, State#state.behlines),
- {?WARN_BEHAVIOUR, {State#state.filename, Line}, Warn};
-add_tag_file_line(Module, {_Tag, [_B, Fun, Arity|_R]} = Warn, State) ->
- {_A, FunCode} =
- dialyzer_codeserver:lookup_mfa_code({Module, Fun, Arity},
- State#state.codeserver),
- Anns = cerl:get_ann(FunCode),
- FileLine = {get_file(Anns), get_line(Anns)},
- {?WARN_BEHAVIOUR, FileLine, Warn}.
-
-get_line([Line|_]) when is_integer(Line) -> Line;
-get_line([_|Tail]) -> get_line(Tail);
-get_line([]) -> -1.
-
-get_file([{file, File}|_]) -> File;
-get_file([_|Tail]) -> get_file(Tail).
-
-%%-----------------------------------------------------------------------------
-
-get_behaviours([], _Codeserver, KnownAcc, UnknownAcc) ->
- {KnownAcc, UnknownAcc};
-get_behaviours([M|Rest], Codeserver, KnownAcc, UnknownAcc) ->
- Tree = dialyzer_codeserver:lookup_mod_code(M, Codeserver),
- Attrs = cerl:module_attrs(Tree),
- {Behaviours, _BehLines} = get_behaviours(Attrs),
- {Known, Unknown} = call_behaviours(Behaviours),
- get_behaviours(Rest, Codeserver, Known ++ KnownAcc, Unknown ++ UnknownAcc).
-
-call_behaviours(Behaviours) ->
- call_behaviours(Behaviours, [], []).
-call_behaviours([], KnownAcc, UnknownAcc) ->
- {lists:reverse(KnownAcc), lists:reverse(UnknownAcc)};
-call_behaviours([Behaviour|Rest], KnownAcc, UnknownAcc) ->
- try
- Callbacks = Behaviour:behaviour_info(callbacks),
- Fun = fun({_,_,_}) -> true;
- (_) -> false
- end,
- case lists:any(Fun, Callbacks) of
- false -> call_behaviours(Rest, KnownAcc, [Behaviour | UnknownAcc]);
- true -> call_behaviours(Rest, [Behaviour | KnownAcc], UnknownAcc)
- end
- catch
- _:_ -> call_behaviours(Rest, KnownAcc, [Behaviour | UnknownAcc])
- end.
-
-%------------------------------------------------------------------------------
-
get_behaviour_apis([], Acc) ->
Acc;
get_behaviour_apis([Behaviour | Rest], Acc) ->
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 8d61216b7a..e638e2fff2 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -754,15 +754,13 @@ print_unknown_behaviours(#cl_state{output = Output,
true -> io:nl(Output); %% Need to do a newline first
false -> ok
end,
- case Format of
- formatted ->
- io:put_chars(Output, "Unknown behaviours (behaviour_info(callbacks)"
- " does not return any specs):\n"),
- do_print_unknown_behaviours(Output, Behaviours, " ");
- raw ->
- io:put_chars(Output, "%% Unknown behaviours:\n"),
- do_print_unknown_behaviours(Output, Behaviours, "%% ")
- end
+ {Prompt, Prefix} =
+ case Format of
+ formatted -> {"Unknown behaviours:\n"," "};
+ raw -> {"%% Unknown behaviours:\n","%% "}
+ end,
+ io:put_chars(Output, Prompt),
+ do_print_unknown_behaviours(Output, Behaviours, Prefix)
end.
do_print_unknown_behaviours(Output, [B|T], Before) ->
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index e7d60f4cb0..c34a9f0b7d 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -253,7 +253,7 @@ store_temp_contracts(Mod, SpecDict, CallbackDict,
false -> CS1#codeserver{temp_callbacks = dict:store(Mod, CallbackDict, Cb)}
end.
--spec get_temp_contracts(codeserver()) -> dict().
+-spec get_temp_contracts(codeserver()) -> {dict(), dict()}.
get_temp_contracts(#codeserver{temp_contracts = TempContDict,
temp_callbacks = TempCallDict}) ->
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index 45444ec6a3..4d86bb34a7 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -29,7 +29,7 @@
-export([analyze_callgraph/3,
analyze_callgraph/4,
- get_warnings/7]).
+ get_warnings/6]).
%% These are only intended as debug functions.
-export([doit/1,
@@ -78,7 +78,7 @@ analyze_callgraph(Callgraph, Plt, Codeserver, Parent) ->
State = #st{callgraph = Callgraph, plt = Plt,
codeserver = Codeserver, parent = Parent},
NewState = get_refined_success_typings(State),
- dialyzer_plt:insert_callbacks(NewState#st.plt, Codeserver).
+ NewState#st.plt.
%%--------------------------------------------------------------------
@@ -106,21 +106,19 @@ get_refined_success_typings(State) ->
-type doc_plt() :: 'undefined' | dialyzer_plt:plt().
-spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(),
doc_plt(), dialyzer_codeserver:codeserver(), set(),
- pid(), boolean()) ->
+ pid()) ->
{[dial_warning()], dialyzer_plt:plt(), doc_plt()}.
-get_warnings(Callgraph, Plt, DocPlt, Codeserver,
- NoWarnUnused, Parent, BehavioursChk) ->
+get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) ->
InitState = #st{callgraph = Callgraph, codeserver = Codeserver,
no_warn_unused = NoWarnUnused, parent = Parent, plt = Plt},
NewState = get_refined_success_typings(InitState),
Mods = dialyzer_callgraph:modules(NewState#st.callgraph),
CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver,
NewState#st.plt),
- get_warnings_from_modules(Mods, NewState, DocPlt, BehavioursChk, CWarns).
+ get_warnings_from_modules(Mods, NewState, DocPlt, CWarns).
-get_warnings_from_modules([M|Ms], State, DocPlt,
- BehavioursChk, Acc) when is_atom(M) ->
+get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) ->
send_log(State#st.parent, io_lib:format("Getting warnings for ~w\n", [M])),
#st{callgraph = Callgraph, codeserver = Codeserver,
no_warn_unused = NoWarnUnused, plt = Plt} = State,
@@ -135,19 +133,15 @@ get_warnings_from_modules([M|Ms], State, DocPlt,
dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused),
{NewAcc, Warnings2} = postprocess_dataflow_warns(RawWarnings2, State, Acc),
Attrs = cerl:module_attrs(ModCode),
- Warnings3 = if BehavioursChk ->
- dialyzer_behaviours:check_callbacks(M, Attrs,
- Plt, Codeserver);
- true -> []
- end,
+ Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver),
NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt),
NewCallgraph =
dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables,
NamedTables),
State1 = st__renew_state_calls(NewCallgraph, State),
- get_warnings_from_modules(Ms, State1, NewDocPlt, BehavioursChk,
+ get_warnings_from_modules(Ms, State1, NewDocPlt,
[Warnings1, Warnings2, Warnings3|NewAcc]);
-get_warnings_from_modules([], #st{plt = Plt}, DocPlt, _, Acc) ->
+get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) ->
{lists:flatten(Acc), Plt, DocPlt}.
postprocess_dataflow_warns(RawWarnings, State, WarnAcc) ->
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..6df50943d8
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [behaviours]}]}.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/gen_event_incorrect_return b/lib/dialyzer/test/behaviour_SUITE_data/results/gen_event_incorrect_return
new file mode 100644
index 0000000000..41c6dae55f
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/gen_event_incorrect_return
@@ -0,0 +1,2 @@
+
+gen_event_incorrect_return.erl:16: The inferred return type for init/1 is 'error' which is not valid return for the callback of the gen_event behaviour
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_incorrect_args b/lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_incorrect_args
new file mode 100644
index 0000000000..333216bc5c
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_incorrect_args
@@ -0,0 +1,8 @@
+
+gen_server_incorrect_args.erl:3: Undefined callback function code_change/3 (behaviour 'gen_server')
+gen_server_incorrect_args.erl:3: Undefined callback function handle_cast/2 (behaviour 'gen_server')
+gen_server_incorrect_args.erl:3: Undefined callback function handle_info/2 (behaviour 'gen_server')
+gen_server_incorrect_args.erl:3: Undefined callback function init/1 (behaviour 'gen_server')
+gen_server_incorrect_args.erl:3: Undefined callback function terminate/2 (behaviour 'gen_server')
+gen_server_incorrect_args.erl:7: The inferred return type for handle_call/3 is {'no'} | {'ok'} which is not valid return for the callback of the gen_server behaviour
+gen_server_incorrect_args.erl:7: The inferred type for the 2nd argument of handle_call/3 is 'boo' | 'foo' which is not valid for the callback of the gen_server behaviour
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_missing_callbacks b/lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_missing_callbacks
new file mode 100644
index 0000000000..5e0ed5fd27
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/gen_server_missing_callbacks
@@ -0,0 +1,3 @@
+
+gen_server_missing_callbacks.erl:3: Undefined callback function handle_cast/2 (behaviour 'gen_server')
+gen_server_missing_callbacks.erl:3: Undefined callback function handle_info/2 (behaviour 'gen_server')
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour b/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour
new file mode 100644
index 0000000000..7420da2476
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour
@@ -0,0 +1,9 @@
+
+sample_callback_wrong.erl:15: The inferred return type for sample_callback_2/0 is 42 which is not valid return for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:16: The inferred return type for sample_callback_3/0 is 'fair' which is not valid return for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:17: The inferred return type for sample_callback_4/1 is 'fail' which is not valid return for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:19: The inferred return type for sample_callback_5/1 is string() which is not valid return for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:19: The inferred type for the 1st argument of sample_callback_5/1 is atom() which is not valid for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:21: The inferred return type for sample_callback_6/3 is {'okk',number()} which is not valid return for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:21: The inferred type for the 3rd argument of sample_callback_6/3 is atom() which is not valid for the callback of the sample_behaviour behaviour
+sample_callback_wrong.erl:3: Undefined callback function sample_callback_1/0 (behaviour 'sample_behaviour')
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour_old b/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour_old
new file mode 100644
index 0000000000..948ea49ab1
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/sample_behaviour_old
@@ -0,0 +1,4 @@
+
+incorrect_args_callback.erl:12: The inferred type for the 2nd argument of bar/2 is 'yes' which is not valid for the callback of the correct_behaviour behaviour
+incorrect_return_callback.erl:9: The inferred return type for foo/0 is 'error' which is not valid return for the callback of the correct_behaviour behaviour
+missing_callback.erl:5: Undefined callback function foo/0 (behaviour 'correct_behaviour')
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
new file mode 100644
index 0000000000..c96eb733a2
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return
@@ -0,0 +1,2 @@
+
+supervisor_incorrect_return.erl:14: The inferred return type for init/1 is {'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}} which is not valid return for the callback of the supervisor behaviour
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/undefined_callbacks b/lib/dialyzer/test/behaviour_SUITE_data/results/undefined_callbacks
new file mode 100644
index 0000000000..7147ffc750
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/results/undefined_callbacks
@@ -0,0 +1,2 @@
+
+undefined_beh_callback.erl:5: Callback info about the undefined_behaviour behaviour is not available
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/gen_event_incorrect_return.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/gen_event_incorrect_return.erl
new file mode 100644
index 0000000000..f5ccf7f8c4
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/gen_event_incorrect_return.erl
@@ -0,0 +1,33 @@
+-module(gen_event_incorrect_return).
+
+-behaviour(gen_event).
+
+-export([start_link/0, add_handler/0]).
+
+-export([init/1, handle_event/2, handle_call/2,
+ handle_info/2, terminate/2, code_change/3]).
+
+start_link() ->
+ gen_event:start_link({local, myserver}).
+
+add_handler() ->
+ gen_event:add_handler(myserver, ?MODULE, []).
+
+init([]) ->
+ error.
+
+handle_event(_Event, State) ->
+ {ok, State}.
+
+handle_call(_Request, State) ->
+ Reply = ok,
+ {ok, Reply, State}.
+
+handle_info(_Info, State) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_incorrect_args.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_incorrect_args.erl
new file mode 100644
index 0000000000..df04dff80d
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_incorrect_args.erl
@@ -0,0 +1,11 @@
+-module(gen_server_incorrect_args).
+
+-behaviour(gen_server).
+
+-export([handle_call/3]).
+
+handle_call(_Request, From, _State) ->
+ case From of
+ 'boo' -> {'ok'};
+ 'foo' -> {'no'}
+ end.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_missing_callbacks.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_missing_callbacks.erl
new file mode 100644
index 0000000000..760466fdac
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/gen_server_missing_callbacks.erl
@@ -0,0 +1,23 @@
+-module(gen_server_missing_callbacks).
+
+-behaviour(gen_server).
+
+-export([start_link/0]).
+
+-export([init/1, handle_call/3, terminate/2, code_change/3]).
+
+start_link() ->
+ gen_server:start_link({local, myserver}, ?MODULE, [], []).
+
+init([]) ->
+ ignore.
+
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_behaviour.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_behaviour.erl
new file mode 100644
index 0000000000..116980986b
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_behaviour.erl
@@ -0,0 +1,13 @@
+-module(sample_behaviour).
+
+-type custom() :: 1..42.
+
+-callback sample_callback_1() -> term().
+-callback sample_callback_2() -> atom().
+-callback sample_callback_3() -> {'ok', custom()} | 'fail'.
+
+-callback sample_callback_4(term()) -> 'ok'.
+-callback sample_callback_5(custom()) -> 'ok' | 'fail'.
+
+-callback sample_callback_6(custom(), custom(), string()) ->
+ {'ok', custom()} | 'fail'.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct.erl
new file mode 100644
index 0000000000..ab0378e6f0
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct.erl
@@ -0,0 +1,32 @@
+-module(sample_callback_correct).
+
+-behaviour(sample_behaviour).
+
+-export([
+ sample_callback_1/0,
+ sample_callback_2/0,
+ sample_callback_3/0,
+ sample_callback_4/1,
+ sample_callback_5/1,
+ sample_callback_6/3
+ ]).
+
+sample_callback_1() -> 42. % This is a valid return.
+sample_callback_2() -> foo. % This is a valid return.
+sample_callback_3() -> {ok, 17}. % This is a valid return.
+sample_callback_4(Input) ->
+ put(mine, Input+1), % This is valid handling of the input
+ ok. % This is a valid return.
+sample_callback_5(Input) ->
+ case Input - 1 < 22 of % This is valid handling of the input
+ true -> ok; % This is a valid return.
+ false -> fail % This is a valid return.
+ end.
+sample_callback_6(OldNr, NewNr, Reason) ->
+ Diff = NewNr - OldNr, % This is valid handling of the input
+ Msg = string:join(["Reason: ", Reason], ","), % This is valid handling of the input
+ case Diff > 0 of
+ true -> put(mine, {NewNr, Msg}),
+ {ok, NewNr}; % This is a valid return.
+ false -> fail % This is a valid return.
+ end.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct_2.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct_2.erl
new file mode 100644
index 0000000000..148cea6843
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_correct_2.erl
@@ -0,0 +1,38 @@
+-module(sample_callback_correct_2).
+
+-behaviour(sample_behaviour).
+
+-export([
+ sample_callback_1/0,
+ sample_callback_2/0,
+ sample_callback_3/0,
+ sample_callback_4/1,
+ sample_callback_5/1,
+ sample_callback_6/3,
+ common_infrastructure/1
+ ]).
+
+sample_callback_1() -> 42. % This is a valid return.
+sample_callback_2() -> foo. % This is a valid return.
+sample_callback_3() -> {ok, 17}. % This is a valid return.
+sample_callback_4(Input) ->
+ case Input of
+ 1 -> common_infrastructure(Input); % This is 'correct' input for
+ _ -> ok % common_infrastructure.
+ end.
+sample_callback_5(Input) ->
+ case get(Input) of % This is valid handling of a more generic input
+ true -> ok; % This is a valid return.
+ false -> fail % This is a valid return.
+ end.
+sample_callback_6(OldNr, NewNr, Reason) ->
+ Diff = NewNr - OldNr, % This is valid handling of the input
+ Msg = string:join(["Reason: ", Reason], ","), % This is valid handling of the input
+ case Diff > 0 of
+ true -> put(mine, {NewNr, Msg}),
+ {ok, NewNr}; % This is a valid return.
+ false -> fail % This is a valid return.
+ end.
+
+common_infrastructure( 1) -> 'ok';
+common_infrastructure(42) -> 'fail'.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl
new file mode 100644
index 0000000000..02a063fab7
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour/sample_callback_wrong.erl
@@ -0,0 +1,25 @@
+-module(sample_callback_wrong).
+
+-behaviour(sample_behaviour).
+
+-export([
+% sample_callback_1/0,
+ sample_callback_2/0,
+ sample_callback_3/0,
+ sample_callback_4/1,
+ sample_callback_5/1,
+ sample_callback_6/3
+ ]).
+
+% sample_callback_1() -> 41. % We can't really break this contract so: missing!
+sample_callback_2() -> 42. % This is not an atom().
+sample_callback_3() -> fair. % This is probably a typo.
+sample_callback_4(_) -> % We cannot break the input.
+ fail. % We can definitely return a wrong value however. :)
+sample_callback_5(Input) -> % Input is treated as an atom, result is a list.
+ atom_to_list(Input). % Both violate the contract.
+sample_callback_6(OldNr, NewNr, Reason) ->
+ Diff = NewNr - OldNr, % This is valid handling of the input
+ %% Reason should have been treated as a string.
+ Msg = string:join(["Reason: ", atom_to_list(Reason)], ","),
+ {okk, NewNr}. %% This, too, is a typo.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_behaviour.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_behaviour.erl
new file mode 100644
index 0000000000..90ce590997
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_behaviour.erl
@@ -0,0 +1,6 @@
+%%% This is a behaviour with info about its calllbacks.
+
+-module(correct_behaviour).
+
+-callback foo() -> yes | no.
+-callback bar({atom(),_},[_]) -> term().
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_callback.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_callback.erl
new file mode 100644
index 0000000000..8f254520ab
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/correct_callback.erl
@@ -0,0 +1,15 @@
+%%% This is a correct callback module for the correct_behaviour.
+
+-module(correct_callback).
+
+-behaviour(correct_behaviour).
+
+-export([foo/0, bar/2]).
+
+foo() ->
+ yes.
+
+bar({'query', 'boo'}, _Any) ->
+ no;
+bar({'reply', [_R]}, [1,2,3]) ->
+ yes.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_args_callback.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_args_callback.erl
new file mode 100644
index 0000000000..68fc60d418
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_args_callback.erl
@@ -0,0 +1,13 @@
+%%% This is a correct callback module for the correct_behaviour.
+
+-module(incorrect_args_callback).
+
+-behaviour(correct_behaviour).
+
+-export([foo/0, bar/2]).
+
+foo() ->
+ yes.
+
+bar({'reply', _Any}, yes) -> %% Should be a tuple and a list.
+ yes.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_return_callback.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_return_callback.erl
new file mode 100644
index 0000000000..9ff920cdd0
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/incorrect_return_callback.erl
@@ -0,0 +1,15 @@
+%%% This is a correct callback module for the correct_behaviour.
+
+-module(incorrect_return_callback).
+
+-behaviour(correct_behaviour).
+
+-export([foo/0, bar/2]).
+
+foo() ->
+ error. %% Should be 'yes' or 'no'.
+
+bar({'query', 'boo'}, _Any) ->
+ no;
+bar({'reply', [_R]}, [1,2,3]) ->
+ yes.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/missing_callback.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/missing_callback.erl
new file mode 100644
index 0000000000..e6c5306839
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/sample_behaviour_old/missing_callback.erl
@@ -0,0 +1,10 @@
+%%% This is a correct callback module for the correct_behaviour.
+
+-module(missing_callback).
+
+-behaviour(correct_behaviour).
+
+-export([bar/2]).
+
+bar({'reply', _Any}, []) ->
+ yes.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/supervisor_incorrect_return.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/supervisor_incorrect_return.erl
new file mode 100644
index 0000000000..616a9073ae
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/supervisor_incorrect_return.erl
@@ -0,0 +1,17 @@
+-module(supervisor_incorrect_return).
+
+-behaviour(supervisor).
+
+-export([start_link/0]).
+
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+init([]) ->
+ AChild = {'AName',{'AModule',start_link,[]},
+ permanent,2000,worker,['AModule']},
+ {ok,{{one_against_one,0,1}, [AChild]}}.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_beh_callback.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_beh_callback.erl
new file mode 100644
index 0000000000..8223225b4b
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_beh_callback.erl
@@ -0,0 +1,13 @@
+%%% This is a correct callback module for the correct_behaviour.
+
+-module(undefined_beh_callback).
+
+-behaviour(undefined_behaviour).
+
+-export([foo/0, bar/2]).
+
+foo() ->
+ yes.
+
+bar({'reply', _Any}, yes) ->
+ yes.
diff --git a/lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_behaviour.erl b/lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_behaviour.erl
new file mode 100644
index 0000000000..fb3d4c5e03
--- /dev/null
+++ b/lib/dialyzer/test/behaviour_SUITE_data/src/undefined_callbacks/undefined_behaviour.erl
@@ -0,0 +1,10 @@
+%%% This is a behaviour with undefined info about its calllbacks.
+
+-module(undefined_behaviour).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [{foo, 0}, {bar, 2}];
+behaviour_info(_Other) ->
+ undefined.