aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer')
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl6
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl14
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes16
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings5
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl95
-rw-r--r--lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl9
7 files changed, 134 insertions, 12 deletions
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 3467ab4e65..5b2c52b2d0 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -404,7 +404,8 @@ contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], RecDict,
throw({error, NewMsg})
end,
NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords),
- {NewType, []}
+ NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType),
+ {NewTypeNoVars, []}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, []} | FormAcc],
@@ -418,7 +419,8 @@ contract_from_form([{type, _L1, bounded_fun,
process_constraints(Constr, RecDict, ExpTypes, AllRecords),
Type = erl_types:t_from_form(Form, RecDict, VarDict),
NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords),
- {NewType, Constr1}
+ NewTypeNoVars = erl_types:subst_all_vars_to_any(NewType),
+ {NewTypeNoVars, Constr1}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, Constr} | FormAcc],
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 03f9684b02..b00e0465e0 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -58,10 +58,12 @@
t_fun_range/2, t_integer/0, t_integers/1,
t_is_any/1, t_is_atom/1, t_is_atom/2, t_is_any_atom/3,
t_is_boolean/2,
- t_is_integer/2, t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
+ t_is_integer/2, t_is_list/1,
+ t_is_nil/2, t_is_none/1, t_is_none_or_unit/1,
t_is_number/2, t_is_reference/2, t_is_pid/2, t_is_port/2,
t_is_unit/1,
- t_limit/2, t_list/0, t_maybe_improper_list/0, t_module/0,
+ t_limit/2, t_list/0, t_list_elements/2,
+ t_maybe_improper_list/0, t_module/0,
t_none/0, t_non_neg_integer/0, t_number/0, t_number_vals/2,
t_pid/0, t_port/0, t_product/1, t_reference/0,
t_to_string/2, t_to_tlist/1,
@@ -293,6 +295,7 @@ traverse(Tree, Map, State) ->
t_is_any(ArgType)
orelse t_is_simple(ArgType, State)
orelse is_call_to_send(Arg)
+ orelse is_lc_simple_list(Arg, ArgType, State)
of
true -> % do not warn in these cases
State1;
@@ -2713,6 +2716,13 @@ is_call_to_send(Tree) ->
andalso (Arity =:= 2)
end.
+is_lc_simple_list(Tree, TreeType, State) ->
+ Opaques = State#state.opaques,
+ Ann = cerl:get_ann(Tree),
+ lists:member(list_comprehension, Ann)
+ andalso t_is_list(TreeType)
+ andalso t_is_simple(t_list_elements(TreeType, Opaques), State).
+
filter_match_fail([Clause] = Cls) ->
Body = cerl:clause_body(Clause),
case cerl:type(Body) of
diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
index 173ff3a9f1..bfa33cd296 100644
--- a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
+++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes
@@ -8,15 +8,15 @@ contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{
contracts_with_subtypes.erl:142: The pattern 1 can never match the type string()
contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',_} | {'ok',_,string()}
contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,string()}
-contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X}
-contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X}
-contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X}
-contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X}
+contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',_}
+contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',_}
+contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',_}
+contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',_}
contracts_with_subtypes.erl:202: The pattern 1 can never match the type string()
-contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,string()}
-contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,string()}
+contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',_,string()}
+contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',_,string()}
contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return
contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed')
contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something'
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options b/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..49ac917f61
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [unmatched_returns]}]}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings
new file mode 100644
index 0000000000..2784f2119e
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/results/lc_warnings
@@ -0,0 +1,5 @@
+
+lc_warnings.erl:32: Expression produces a value of type [opaque_atom_adt:opaque_atom()], but this value is unmatched
+lc_warnings.erl:43: Expression produces a value of type [array()], but this value is unmatched
+lc_warnings.erl:65: Expression produces a value of type [lc_warnings:opaque_tuple()], but this value is unmatched
+lc_warnings.erl:7: Expression produces a value of type ['ok' | {'error',atom()}], but this value is unmatched
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl
new file mode 100644
index 0000000000..cb01a8fde3
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/lc_warnings.erl
@@ -0,0 +1,95 @@
+-module(lc_warnings).
+-compile([export_all]).
+
+close(Fs) ->
+ %% There should be a warning since we ignore a potential
+ %% {error,Error} return from file:close/1.
+ [file:close(F) || F <- Fs],
+
+ %% No warning because the type of unmatched return will be ['ok']
+ %% (which is a list of a simple type).
+ [ok = file:close(F) || F <- Fs],
+
+ %% Suppressed.
+ _ = [file:close(F) || F <- Fs],
+ ok.
+
+format(X) ->
+ %% No warning since the result of the list comprehension is
+ %% a list of simple.
+ [io:format("~p\n", [E]) || E <- X],
+
+ %% Warning explicitly suppressed.
+ _ = [io:format("~p\n", [E]) || E <- X],
+ ok.
+
+opaque1() ->
+ List = gen_atom(),
+ %% This is a list of an externally defined opaque type. Since
+ %% we are not allowed to peek inside opaque types, there should
+ %% be a warning (even though the type in this case happens to be
+ %% an atom).
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque2() ->
+ List = gen_array(),
+ %% This is an list of an externally defined opaque type. Since
+ %% we are not allowed to peek inside opaque types, there should
+ %% be a warning.
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque3() ->
+ List = gen_int(),
+
+ %% No warning, since we are allowed to look into the type and can
+ %% see that it is a simple type.
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+opaque4() ->
+ List = gen_tuple(),
+
+ %% There should be a warning, since we are allowed to look inside
+ %% the opaque type and see that it is a tuple (non-simple).
+ [E || E <- List],
+
+ %% Suppressed.
+ _ = [E || E <- List],
+ ok.
+
+gen_atom() ->
+ [opaque_atom_adt:atom(ok)].
+
+gen_array() ->
+ [array:new()].
+
+
+gen_int() ->
+ [opaque_int(42)].
+
+gen_tuple() ->
+ [opaque_tuple(x, 25)].
+
+-opaque opaque_int() :: integer().
+
+-spec opaque_int(integer()) -> opaque_int().
+
+opaque_int(Int) -> Int.
+
+-opaque opaque_tuple() :: {any(),any()}.
+
+-spec opaque_tuple(any(), any()) -> opaque_tuple().
+
+opaque_tuple(X, Y) ->
+ {X,Y}.
diff --git a/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl
new file mode 100644
index 0000000000..b5b51fe75b
--- /dev/null
+++ b/lib/dialyzer/test/unmatched_returns_SUITE_data/src/lc_warnings/opaque_atom_adt.erl
@@ -0,0 +1,9 @@
+-module(opaque_atom_adt).
+-export([atom/1]).
+
+-opaque opaque_atom() :: atom().
+
+-spec atom(atom()) -> opaque_atom().
+
+atom(Atom) ->
+ Atom.