aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/src
diff options
context:
space:
mode:
authorStavros Aronis <[email protected]>2011-11-11 16:35:28 +0100
committerStavros Aronis <[email protected]>2011-11-18 15:06:46 +0100
commit8682e6b3db55f7f021e529b2134f223b4cc70ace (patch)
treed2d45e62bd65229a2bd6f9dffa067357c16255a8 /lib/dialyzer/src
parentfc075ff9064eccd3ea2da0c067ec9ef01aaeb183 (diff)
downloadotp-8682e6b3db55f7f021e529b2134f223b4cc70ace.tar.gz
otp-8682e6b3db55f7f021e529b2134f223b4cc70ace.tar.bz2
otp-8682e6b3db55f7f021e529b2134f223b4cc70ace.zip
Detection of callback-spec discrepancies
Diffstat (limited to 'lib/dialyzer/src')
-rw-r--r--lib/dialyzer/src/dialyzer.erl12
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl58
2 files changed, 48 insertions, 22 deletions
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index acbe574e2b..487a12b252 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -442,14 +442,18 @@ message_to_string({opaque_type_test, [Fun, Opaque]}) ->
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]}) ->
+message_to_string({callback_type_mismatch, [B, F, A, T]}) ->
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]}) ->
+ [F, A, T, B]);
+message_to_string({callback_arg_type_mismatch, [B, F, A, N, T]}) ->
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]);
+ "\n", [ordinal(N), F, A, T, B]);
+message_to_string({callback_spec_type_mismatch, [B, F, A, ST, CT]}) ->
+ io_lib:format("The return type ~s in the specification of ~w/~w is not a"
+ " subtype of ~s, which is the expected return type for the"
+ " callback of ~w behaviour.\n", [ST, F, A, CT, B]);
message_to_string({callback_missing, [B, F, A]}) ->
io_lib:format("Undefined callback function ~w/~w (behaviour '~w')\n",
[F, A, B]);
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 82963783d8..a30707c7c3 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -88,20 +88,26 @@ 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) ->
+check_behaviour(Module, Behaviour, #state{plt = Plt} = State, Acc) ->
case dialyzer_plt:lookup_callbacks(Plt, Behaviour) of
[] -> [{callback_info_missing, [Behaviour]}|Acc];
- Callbacks -> check_all_callbacks(Module, Behaviour, Callbacks, Plt, Acc)
+ Callbacks -> check_all_callbacks(Module, Behaviour, Callbacks, State, Acc)
end.
-check_all_callbacks(_Module, _Behaviour, [], _Plt, Acc) ->
+check_all_callbacks(_Module, _Behaviour, [], _State, Acc) ->
Acc;
-check_all_callbacks(Module, Behaviour, [Cb|Rest], Plt, Acc) ->
+check_all_callbacks(Module, Behaviour, [Cb|Rest],
+ #state{plt = Plt, codeserver = Codeserver} = State, 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),
+ Records =
+ case dict:find(Module, dialyzer_codeserver:get_records(Codeserver)) of
+ {ok, V} -> V;
+ error -> dict:new()
+ end,
Acc0 = Acc,
Acc1 =
case dialyzer_plt:lookup(Plt, CbMFA) of
@@ -117,8 +123,9 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest], Plt, Acc) ->
erl_types:t_inf(ReturnType, CbReturnType)) of
false -> Acc00;
true ->
- [{callback_type_mismatch,[Behaviour, Function,
- Arity, ReturnType]}|Acc00]
+ [{callback_type_mismatch,
+ [Behaviour, Function, Arity,
+ erl_types:t_to_string(ReturnType, Records)]}|Acc00]
end
end,
Acc02 =
@@ -127,32 +134,44 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest], Plt, Acc) ->
false -> Acc01;
true ->
find_mismatching_args(ArgTypes, CbArgTypes, Behaviour,
- Function, Arity, 1, Acc01)
+ Function, Arity, Records, 1, Acc01)
end,
Acc02
end,
Acc2 =
- case dialyzer_plt:lookup_contract(Plt, CbMFA) of
- 'none' -> Acc1;
- {value, _Contract} ->
- %% TODO: Check spec for discrepancies
- Acc1
+ case dialyzer_codeserver:lookup_mfa_contract(CbMFA, Codeserver) of
+ 'error' -> Acc1;
+ {ok, {{File, Line}, Contract}} ->
+ Acc10 = Acc1,
+ SpecReturnType = dialyzer_contracts:get_contract_return(Contract),
+ SpecArgTypes = dialyzer_contracts:get_contract_args(Contract),
+ Acc11 =
+ case erl_types:t_is_subtype(SpecReturnType, CbReturnType) of
+ true -> Acc10;
+ false -> [{callback_spec_type_mismatch,
+ [File, Line, Behaviour, Function, Arity,
+ erl_types:t_to_string(SpecReturnType, Records),
+ erl_types:t_to_string(CbReturnType, Records)]}|Acc10]
+ end,
+ Acc11
end,
NewAcc = Acc2,
- check_all_callbacks(Module, Behaviour, Rest, Plt, NewAcc).
+ check_all_callbacks(Module, Behaviour, Rest, State, NewAcc).
-find_mismatching_args([], [], _Behaviour, _Function, _Arity, _N, Acc) ->
+find_mismatching_args([], [], _Beh, _Function, _Arity, _Records, _N, Acc) ->
Acc;
find_mismatching_args([Type|Rest], [CbType|CbRest], Behaviour,
- Function, Arity, N, Acc) ->
+ Function, Arity, Records, 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);
+ find_mismatching_args(Rest, CbRest, Behaviour, Function,
+ Arity, Records, 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)
+ [Behaviour, Function, Arity, N, erl_types:t_to_string(Type, Records)]}|Acc],
+ find_mismatching_args(Rest, CbRest, Behaviour, Function,
+ Arity, Records, N+1, NewAcc)
end.
add_tag_file_line(_Module, {Tag, [B|_R]} = Warn, State)
@@ -160,6 +179,9 @@ add_tag_file_line(_Module, {Tag, [B|_R]} = Warn, State)
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, [File, Line|R]}, State)
+ when Tag =:= callback_spec_type_mismatch ->
+ {?WARN_BEHAVIOUR, {File, Line}, {Tag, R}};
add_tag_file_line(Module, {_Tag, [_B, Fun, Arity|_R]} = Warn, State) ->
{_A, FunCode} =
dialyzer_codeserver:lookup_mfa_code({Module, Fun, Arity},