aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer
diff options
context:
space:
mode:
authorHans Bolinder <[email protected]>2017-09-08 13:16:16 +0200
committerHans Bolinder <[email protected]>2017-09-15 08:17:49 +0200
commit96cf13613c53b641cf7e8a45f83fb71809d6b336 (patch)
tree264f4c6176e8de70cc8bcb4f4355f8bca47b4254 /lib/dialyzer
parentbd36da0f509ee4e357fdf51b60f3dfcecc824eb8 (diff)
downloadotp-96cf13613c53b641cf7e8a45f83fb71809d6b336.tar.gz
otp-96cf13613c53b641cf7e8a45f83fb71809d6b336.tar.bz2
otp-96cf13613c53b641cf7e8a45f83fb71809d6b336.zip
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.
Diffstat (limited to 'lib/dialyzer')
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl22
-rw-r--r--lib/dialyzer/test/options2_SUITE_data/results/unused_unknown_type2
-rw-r--r--lib/dialyzer/test/options2_SUITE_data/src/unused_unknown_type.erl32
4 files changed, 47 insertions, 14 deletions
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.