diff options
author | Stavros Aronis <[email protected]> | 2012-12-07 18:53:18 +0100 |
---|---|---|
committer | Stavros Aronis <[email protected]> | 2012-12-20 14:09:33 +0100 |
commit | 0e37e993c477dfab0e7218b8246bf94b7a9b97a1 (patch) | |
tree | fd5cc0c5a532fac57d367f9cbd0f66830f921c83 /lib/dialyzer/src/dialyzer_contracts.erl | |
parent | af68f5a22f995284b07ece178c42e630ecf94d54 (diff) | |
download | otp-0e37e993c477dfab0e7218b8246bf94b7a9b97a1.tar.gz otp-0e37e993c477dfab0e7218b8246bf94b7a9b97a1.tar.bz2 otp-0e37e993c477dfab0e7218b8246bf94b7a9b97a1.zip |
Report spec discrepancy on mismatching lists
This patch enables Dialyzer to detect cases where a function has a specification
for a return type containing some list with elements different than the ones
Dialyzer's own algorithm can infer e.g. a function specified to return [atom()]
when actually [[atom()]] is returned.
Previously Dialyzer remained silent, under normal operation, seeing that these
two types have the empty list as a common element, so they were not 'completely
irrelevant'.
Diffstat (limited to 'lib/dialyzer/src/dialyzer_contracts.erl')
-rw-r--r-- | lib/dialyzer/src/dialyzer_contracts.erl | 27 |
1 files changed, 24 insertions, 3 deletions
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 0b932d5a1f..157c951f77 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -254,14 +254,35 @@ check_extraneous([C|Cs], SuccType) -> end. check_extraneous_1(Contract, SuccType) -> - CRngs = erl_types:t_elements(erl_types:t_fun_range(Contract)), + CRng = erl_types:t_fun_range(Contract), + CRngs = erl_types:t_elements(CRng), STRng = erl_types:t_fun_range(SuccType), ?debug("CR = ~p\nSR = ~p\n", [CRngs, STRng]), - case [CR || CR <- CRngs, erl_types:t_is_none(erl_types:t_inf(CR, STRng, opaque))] of - [] -> ok; + case [CR || CR <- CRngs, + erl_types:t_is_none(erl_types:t_inf(CR, STRng, opaque))] of + [] -> + CRngList = list_part(CRng), + STRngList = list_part(STRng), + case is_not_nil_list(CRngList) andalso is_not_nil_list(STRngList) of + false -> ok; + true -> + CRngElements = erl_types:t_list_elements(CRngList), + STRngElements = erl_types:t_list_elements(STRngList), + Inf = erl_types:t_inf(CRngElements, STRngElements, opaque), + case erl_types:t_is_none(Inf) of + true -> {error, invalid_contract}; + false -> ok + end + end; CRs -> {error, {extra_range, erl_types:t_sup(CRs), STRng}} end. +list_part(Type) -> + erl_types:t_inf(erl_types:t_list(), Type, opaque). + +is_not_nil_list(Type) -> + erl_types:t_is_list(Type) andalso not erl_types:t_is_nil(Type). + %% This is the heart of the "range function" -spec process_contracts([contract_pair()], [erl_types:erl_type()]) -> erl_types:erl_type(). |