From 96cf13613c53b641cf7e8a45f83fb71809d6b336 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 8 Sep 2017 13:16:16 +0200 Subject: dialyzer: Improve check of unknown types The implementation of OTP-14218 (commit 6d3b38a) has a weakness: only a very limited part of the type form is checked. This is now fixed: types not used by specs are checked equally well as types used by specs. The new function erl_types:t_from_form_check_remote() checks usage of remote types. It does not expand used local types, and has (almost) no limits on depth and size. --- lib/dialyzer/src/dialyzer_contracts.erl | 5 +++- lib/dialyzer/src/dialyzer_utils.erl | 22 +++++++-------- .../results/unused_unknown_type | 2 +- .../src/unused_unknown_type.erl | 32 +++++++++++++++++++++- 4 files changed, 47 insertions(+), 14 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index b554ebc2cc..e72c1aecfc 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -555,6 +555,9 @@ from_form_with_check(Form, ExpTypes, MFA, RecordTable, VarTable, Cache) -> Site = {spec, MFA}, C1 = erl_types:t_check_record_fields(Form, ExpTypes, Site, RecordTable, VarTable, Cache), + %% The check costs some time, and with the assumption that contracts + %% are not very deep, it does not add anything. + %% erl_types:t_from_form_check_remote(Form, ExpTypes, MFA, RecordTable), erl_types:t_from_form(Form, ExpTypes, Site, RecordTable, VarTable, C1). constraints_to_dict(Constrs, MFA, RecDict, ExpTypes, RecordTable, @@ -840,7 +843,7 @@ is_remote_types_related(Contract, CSig, Sig, MFA, RecDict) -> t_from_forms_without_remote([{FType, []}], MFA, RecDict) -> Site = {spec, MFA}, - {Type1, _} = erl_types:t_from_form_without_remote(FType, Site, RecDict), + Type1 = erl_types:t_from_form_without_remote(FType, Site, RecDict), {ok, erl_types:subst_all_vars_to_any(Type1)}; t_from_forms_without_remote([{_FType, _Constrs}], _MFA, _RecDict) -> %% 'When' constraints diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 511a6d66bf..9b8fbc67eb 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -326,9 +326,12 @@ process_record_remote_types(CServer) -> {record, Name} -> FieldFun = fun({Arity, Fields}, C4) -> - Site = {record, {Module, Name, Arity}}, + MRA = {Module, Name, Arity}, + Site = {record, MRA}, {Fields1, C7} = lists:mapfoldl(fun({FieldName, Field, _}, C5) -> + check_remote(Field, ExpTypes, + MRA, RecordTable), {FieldT, C6} = erl_types:t_from_form (Field, ExpTypes, Site, @@ -342,18 +345,12 @@ process_record_remote_types(CServer) -> {FieldsList, C3} = lists:mapfoldl(FieldFun, C2, orddict:to_list(Fields)), {{Key, {FileLine, orddict:from_list(FieldsList)}}, C3}; - {type, Name, NArgs} -> + {_TypeOrOpaque, Name, NArgs} -> %% Make sure warnings about unknown types are output %% also for types unused by specs. - Site = {type, {Module, Name, NArgs}}, - L = erl_anno:new(0), - Args = lists:duplicate(NArgs, {var, L, '_'}), - UserType = {user_type, L, Name, Args}, - {_NewType, C3} = - erl_types:t_from_form(UserType, ExpTypes, Site, - RecordTable, VarTable, C2), - {{Key, Value}, C3}; - {opaque, _Name, _NArgs} -> + MTA = {Module, Name, NArgs}, + {{_Module, _FileLine, Form, _ArgNames}, _Type} = Value, + check_remote(Form, ExpTypes, MTA, RecordTable), {{Key, Value}, C2} end end, @@ -454,6 +451,9 @@ msg_with_position(Fun, FileLine) -> throw({error, NewMsg}) end. +check_remote(Form, ExpTypes, What, RecordTable) -> + erl_types:t_from_form_check_remote(Form, ExpTypes, What, RecordTable). + -spec merge_types(codeserver(), dialyzer_plt:plt()) -> codeserver(). merge_types(CServer, Plt) -> diff --git a/lib/dialyzer/test/options2_SUITE_data/results/unused_unknown_type b/lib/dialyzer/test/options2_SUITE_data/results/unused_unknown_type index 110d896c76..74d2ac33ad 100644 --- a/lib/dialyzer/test/options2_SUITE_data/results/unused_unknown_type +++ b/lib/dialyzer/test/options2_SUITE_data/results/unused_unknown_type @@ -1,2 +1,2 @@ -:0: Unknown type unknown:type1/0:0: Unknown type unknown:type2/0:0: Unknown type unknown:type3/0 \ No newline at end of file +:0: Unknown type foo:bar/0:0: Unknown type ofoo:obar/0:0: Unknown type owww:y/0:0: Unknown type rfoo:rbar/0:0: Unknown type unknown:type1/0:0: Unknown type unknown:type2/0:0: Unknown type unknown:type3/0:0: Unknown type xxx:y/0:0: Unknown type yyy:x/0:0: Unknown type zzz:arg/1:0: Unknown type zzz:x/0 \ No newline at end of file diff --git a/lib/dialyzer/test/options2_SUITE_data/src/unused_unknown_type.erl b/lib/dialyzer/test/options2_SUITE_data/src/unused_unknown_type.erl index 90df7d528a..e6f9d2392c 100644 --- a/lib/dialyzer/test/options2_SUITE_data/src/unused_unknown_type.erl +++ b/lib/dialyzer/test/options2_SUITE_data/src/unused_unknown_type.erl @@ -1,10 +1,40 @@ -module(unused_unknown_type). +-export([t/0]). + -export_type([unused/0]). +-export_type([wide/0, deep/0]). +-export_type([owide/0, odeep/0]). +-export_type([arg/0, rargs1/0, rargs2/0]). + -type unused() :: unknown:type1(). --record(unused_rec, {a :: unknown:type2()}). +-record(unused_rec, + {a :: unknown:type2(), + b :: {{{{{{{{{{{{{{{{{{{{rfoo:rbar()}}}}}}}}}}}}}}}}}}}}}). -record(rec, {a}). -type unused_rec() :: #rec{a :: unknown:type3()}. + +-type wide() :: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,xxx:y()}. +-type owide() :: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,owww:y()}. + +%% Deeper than the hardcoded limit in erl_types.erl of 16. +-type deep() :: {{{{{{{{{{{{{{{{{{{{foo:bar()}}}}}}}}}}}}}}}}}}}}. +-type odeep() :: {{{{{{{{{{{{{{{{{{{{ofoo:obar()}}}}}}}}}}}}}}}}}}}}. + +-type arg1(A) :: [A]. +-type arg() :: arg1({a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,yyy:x()}). + +%% No warning about www:x/0 because parameters are currently not +%% handled if the parameterized type cannot be found. +-type rargs1() :: zzz:arg({a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,www:x()}). + +-type rargs2() :: dict:dict({a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,zzz:x()}, + any()). + +%% No warning. The check is commented out as it takes too long. +-spec t() -> 'a' | {{{{{{{{{{{{{{{{{{{{sfoo:sbar()}}}}}}}}}}}}}}}}}}}}. +t() -> + a. -- cgit v1.2.3