diff options
Diffstat (limited to 'lib/dialyzer/src/dialyzer_contracts.erl')
-rw-r--r-- | lib/dialyzer/src/dialyzer_contracts.erl | 62 |
1 files changed, 43 insertions, 19 deletions
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 067e7956f3..9623a6d6ff 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -185,7 +185,7 @@ process_contract_remote_types_module(ModuleName, CodeServer) -> RecordTable = dialyzer_codeserver:get_records_table(CodeServer), ExpTypes = dialyzer_codeserver:get_exported_types_table(CodeServer), ContractFun = - fun({MFA, {File, TmpContract, Xtra}}, C0) -> + fun({{_,_,_} = MFA, {File, TmpContract, Xtra}}, C0) -> #tmp_contract{contract_funs = CFuns, forms = Forms} = TmpContract, {NewCs, C2} = lists:mapfoldl(fun(CFun, C1) -> CFun(ExpTypes, RecordTable, C1) @@ -269,6 +269,7 @@ check_contracts(Contracts, Callgraph, FunTypes, ModOpaques) -> 'ok' | {'error', 'invalid_contract' + | {'invalid_contract', {InvalidArgIdxs :: [pos_integer()], IsReturnTypeInvalid :: boolean()}} | {'opaque_mismatch', erl_types:erl_type()} | {'overlapping_contract', [module() | atom() | byte()]} | string()} @@ -299,12 +300,11 @@ check_contract(#contract{contracts = Contracts}, SuccType, Opaques) -> ok -> InfList = [{Contract, erl_types:t_inf(Contract, SuccType, Opaques)} || Contract <- Contracts2], - case check_contract_inf_list(InfList, SuccType, Opaques) of - {error, _} = Invalid -> Invalid; + case check_contract_inf_list(InfList, SuccType, Opaques) of + {error, _} = Invalid -> Invalid; ok -> case check_extraneous(Contracts2, SuccType, Opaques) of - {error, invalid_contract} = Err -> - Err; + {error, {invalid_contract, _}} = Err -> Err; {error, {extra_range, _, _}} = Err -> MissingError = check_missing(Contracts2, SuccType, Opaques), {range_warnings, [Err | MissingError]}; @@ -320,6 +320,25 @@ check_contract(#contract{contracts = Contracts}, SuccType, Opaques) -> throw:{error, _} = Error -> Error end. +locate_invalid_elems(InfList) -> + case InfList of + [{Contract, Inf}] -> + ArgComparisons = lists:zip(erl_types:t_fun_args(Contract), + erl_types:t_fun_args(Inf)), + ProblematicArgs = + [erl_types:t_is_none(Succ) andalso (not erl_types:t_is_none(Cont)) + || {Cont,Succ} <- ArgComparisons], + ProblematicRange = + erl_types:t_is_none(erl_types:t_fun_range(Inf)) + andalso (not erl_types:t_is_none(erl_types:t_fun_range(Contract))), + ProblematicArgIdxs = [Idx || + {Idx, IsProblematic} <- + lists:enumerate(ProblematicArgs), IsProblematic], + {error, {invalid_contract, {ProblematicArgIdxs, ProblematicRange}}}; + _ -> + {error, invalid_contract} + end. + check_domains([_]) -> ok; check_domains([Dom|Doms]) -> Fun = fun(D) -> @@ -330,16 +349,19 @@ check_domains([Dom|Doms]) -> false -> error end. + %% Allow a contract if one of the overloaded contracts is possible. %% We used to be more strict, e.g., all overloaded contracts had to be %% possible. check_contract_inf_list(List, SuccType, Opaques) -> case check_contract_inf_list(List, SuccType, Opaques, []) of ok -> ok; - {error, []} -> {error, invalid_contract}; + {error, []} -> + locate_invalid_elems(List); {error, [{SigRange, ContrRange}|_]} -> case erl_types:t_find_opaque_mismatch(SigRange, ContrRange, Opaques) of - error -> {error, invalid_contract}; + error -> + locate_invalid_elems(List); {ok, _T1, T2} -> {error, {opaque_mismatch, T2}} end end. @@ -383,13 +405,12 @@ check_extraneous_1(Contract, SuccType, Opaques) -> case [CR || CR <- CRngs, erl_types:t_is_none(erl_types:t_inf(CR, STRng, Opaques))] of [] -> - case bad_extraneous_list(CRng, STRng) - orelse bad_extraneous_map(CRng, STRng) - of - true -> {error, invalid_contract}; - false -> ok + case bad_extraneous_list(CRng, STRng) orelse bad_extraneous_map(CRng, STRng) of + true -> {error, {invalid_contract, {[],true}}}; + false -> ok end; - CRs -> {error, {extra_range, erl_types:t_sup(CRs), STRng}} + CRs -> + {error, {extra_range, erl_types:t_sup(CRs), STRng}} end. bad_extraneous_list(CRng, STRng) -> @@ -819,7 +840,9 @@ get_invalid_contract_warnings_funs([{MFA, {FileLocation, Contract, _Xtra}}|Left] NewAcc = case check_contract(Contract, Sig, Opaques) of {error, invalid_contract} -> - [invalid_contract_warning(MFA, WarningInfo, Sig, RecDict)|Acc]; + [invalid_contract_warning(MFA, WarningInfo, none, Contract, Sig, RecDict)|Acc]; + {error, {invalid_contract, {_ProblematicArgIdxs, _IsRangeProblematic} = ProblemDetails}} -> + [invalid_contract_warning(MFA, WarningInfo, ProblemDetails, Contract, Sig, RecDict)|Acc]; {error, {opaque_mismatch, T2}} -> W = contract_opaque_warning(MFA, WarningInfo, T2, Sig, RecDict), [W|Acc]; @@ -864,7 +887,7 @@ get_invalid_contract_warnings_funs([{MFA, {FileLocation, Contract, _Xtra}}|Left] BifSig = erl_types:t_fun(BifArgs, BifRet), case check_contract(Contract, BifSig, Opaques) of {error, _} -> - [invalid_contract_warning(MFA, WarningInfo, BifSig, RecDict) + [invalid_contract_warning(MFA, WarningInfo, none, Contract, BifSig, RecDict) |Acc]; {range_warnings, _} -> picky_contract_check(CSig, BifSig, MFA, WarningInfo, @@ -883,9 +906,10 @@ get_invalid_contract_warnings_funs([{MFA, {FileLocation, Contract, _Xtra}}|Left] get_invalid_contract_warnings_funs([], _Plt, _RecDict, _Opaques, Acc) -> Acc. -invalid_contract_warning({M, F, A}, WarningInfo, SuccType, RecDict) -> - SuccTypeStr = dialyzer_utils:format_sig(SuccType, RecDict), - {?WARN_CONTRACT_TYPES, WarningInfo, {invalid_contract, [M, F, A, SuccTypeStr]}}. +invalid_contract_warning({M, F, A}, WarningInfo, ProblemDetails, Contract, SuccType, RecDict) -> + SuccTypeStr = lists:flatten(dialyzer_utils:format_sig(SuccType, RecDict)), + ContractTypeStr = contract_to_string(Contract), + {?WARN_CONTRACT_TYPES, WarningInfo, {invalid_contract, [M, F, A, ProblemDetails, ContractTypeStr, SuccTypeStr]}}. contract_opaque_warning({M, F, A}, WarningInfo, OpType, SuccType, RecDict) -> OpaqueStr = erl_types:t_to_string(OpType), @@ -894,7 +918,7 @@ contract_opaque_warning({M, F, A}, WarningInfo, OpType, SuccType, RecDict) -> {contract_with_opaque, [M, F, A, OpaqueStr, SuccTypeStr]}}. overlapping_contract_warning({M, F, A}, WarningInfo) -> - {?WARN_CONTRACT_TYPES, WarningInfo, {overlapping_contract, [M, F, A]}}. + {?WARN_OVERLAPPING_CONTRACT, WarningInfo, {overlapping_contract, [M, F, A]}}. extra_range_warning({M, F, A}, WarningInfo, ExtraRanges, STRange) -> ERangesStr = erl_types:t_to_string(ExtraRanges), |