Skip to content

Commit

Permalink
dialyzer: make -Wunknown a default; add -Wno_unknown flag
Browse files Browse the repository at this point in the history
Dialyzer's warnings about unknown functions and types are enabled by
default.

Prior to this PR, Dialyzer used to have disabled (by default) warnings
about unknown types and functions.

This default value has been overwritten; Dialyzer now warns about
unknown types and functions (as requested by the community in erlangGH-5695).
Thus, the following two examples are equivalent, i.e., passing the
`-Wunknown` function is enabled by default:

```bash
dialyzer moduler.erl -Wunknown -Wmissing_return
dialyzer moduler.erl -Wmissing_return
```

Dialyzer has a new flag, `-Wno_unknown`. Its purpose is to supress
warnings about unknown functions and types (erlangGH-5995).

Users who wish to suppress these warnings can invoke Dialyzer using this
flag. Example:

```bash
dialyzer module.erl -Wno_unknown
```
  • Loading branch information
kikofernandez committed Feb 8, 2023
1 parent 8b67a15 commit 94a7dfb
Show file tree
Hide file tree
Showing 20 changed files with 90 additions and 60 deletions.
1 change: 1 addition & 0 deletions lib/dialyzer/src/dialyzer.hrl
Expand Up @@ -126,6 +126,7 @@
| 'no_return'
| 'no_undefined_callbacks'
| 'no_underspecs'
| 'no_unknown'
| 'no_unused'
| 'underspecs'
| 'unknown'
Expand Down
12 changes: 8 additions & 4 deletions lib/dialyzer/src/dialyzer_cl_parse.erl
Expand Up @@ -594,6 +594,11 @@ warning_options_msg() ->
-Wno_undefined_callbacks
Suppress warnings about behaviours that have no -callback attributes for
their callbacks.
-Wno_unknown
Suppress warnings about unknown functions and types. The default is to
warn about unknown functions and types when setting the exit
status. When using Dialyzer from Erlang, warnings about unknown functions
and types are returned.
-Wunmatched_returns ***
Include warnings for function calls which ignore a structured return
value or do not match against one of many possible return value(s).
Expand All @@ -612,11 +617,10 @@ warning_options_msg() ->
Warn about overloaded functions whose specification include types that overlap.
-Wunknown ***
Let warnings about unknown functions and types affect the
exit status of the command line version. The default is to ignore
warnings about unknown functions and types when setting the exit
exit status of the command line version. The default is to
warn about unknown functions and types when setting the exit
status. When using the Dialyzer from Erlang, warnings about unknown
functions and types are returned; the default is not to return
such warnings.
functions and types are returned.
The following options are also available but their use is not recommended:
(they are mostly for Dialyzer developers and internal debugging)
Expand Down
31 changes: 17 additions & 14 deletions lib/dialyzer/src/dialyzer_options.erl
Expand Up @@ -32,20 +32,21 @@

build(Opts) ->
DefaultWarns = [?WARN_RETURN_NO_RETURN,
?WARN_NOT_CALLED,
?WARN_NON_PROPER_LIST,
?WARN_FUN_APP,
?WARN_MATCHING,
?WARN_OPAQUE,
?WARN_CALLGRAPH,
?WARN_FAILING_CALL,
?WARN_BIN_CONSTRUCTION,
?WARN_MAP_CONSTRUCTION,
?WARN_CONTRACT_RANGE,
?WARN_CONTRACT_TYPES,
?WARN_CONTRACT_SYNTAX,
?WARN_BEHAVIOUR,
?WARN_UNDEFINED_CALLBACK],
?WARN_NOT_CALLED,
?WARN_NON_PROPER_LIST,
?WARN_FUN_APP,
?WARN_MATCHING,
?WARN_OPAQUE,
?WARN_CALLGRAPH,
?WARN_FAILING_CALL,
?WARN_BIN_CONSTRUCTION,
?WARN_MAP_CONSTRUCTION,
?WARN_CONTRACT_RANGE,
?WARN_CONTRACT_TYPES,
?WARN_CONTRACT_SYNTAX,
?WARN_BEHAVIOUR,
?WARN_UNDEFINED_CALLBACK,
?WARN_UNKNOWN],
DefaultWarns1 = ordsets:from_list(DefaultWarns),
DefaultOpts = #options{},
DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns1},
Expand Down Expand Up @@ -429,6 +430,8 @@ build_warnings([Opt|Opts], Warnings) ->
ordsets:del_element(?WARN_RETURN_NO_RETURN, Warnings);
no_unused ->
ordsets:del_element(?WARN_NOT_CALLED, Warnings);
no_unknown ->
ordsets:del_element(?WARN_UNKNOWN, Warnings);
no_improper_lists ->
ordsets:del_element(?WARN_NON_PROPER_LIST, Warnings);
no_fun_app ->
Expand Down
29 changes: 18 additions & 11 deletions lib/dialyzer/test/cplt_SUITE.erl
Expand Up @@ -108,7 +108,8 @@ build_xdg_plt(Config) ->
fun() ->
?assertMatch([], dialyzer:run(
[{analysis_type, plt_build},
{apps, [erts]}])),
{apps, [erts]},
{warnings, [no_unknown]}])),
?assertMatch(
{ok,_}, file:read_file(
filename:join(
Expand Down Expand Up @@ -243,7 +244,8 @@ update_plt(Config) ->
Opts = [{check_plt, true}, {from, byte_code}],
[] = dialyzer:run([{analysis_type, plt_build},
{files, [Beam, ErlangBeam]},
{output_plt, Plt}] ++ Opts),
{output_plt, Plt},
{warnings, [no_unknown]}] ++ Opts),

Prog2 = <<"-module(plt_gc).
-export([two/0]).
Expand Down Expand Up @@ -296,7 +298,8 @@ local_fun_same_as_callback(Config) when is_list(Config) ->
Opts = [{check_plt, true}, {from, byte_code}],
[] = dialyzer:run([{analysis_type, plt_build},
{files, [Beam, ErlangBeam]},
{output_plt, Plt}] ++ Opts),
{output_plt, Plt},
{warnings, [no_unknown]}] ++ Opts),

Prog2 =
<<"-module(bad_child).
Expand Down Expand Up @@ -342,15 +345,18 @@ remove_plt(Config) ->
dialyzer:run([{analysis_type, plt_build},
{files, [Beam1, Beam2]},
{get_warnings, true},
{output_plt, Plt}] ++ Opts),
{output_plt, Plt},
{warnings, [no_unknown]}] ++ Opts),

[] = dialyzer:run([{init_plt, Plt},
{files, [Beam2]},
{analysis_type, plt_remove}]),
{analysis_type, plt_remove},
{warnings, [no_unknown]}]),

[] = dialyzer:run([{analysis_type, succ_typings},
{files, [Beam1]},
{init_plt, Plt}] ++ Opts),
{init_plt, Plt},
{warnings, [no_unknown]}] ++ Opts),
ok.

%% ERL-283, OTP-13979. As of OTP-14323 this test no longer does what
Expand Down Expand Up @@ -567,7 +573,8 @@ create(Config, PltFile) ->
_ = file:delete(PltFile),
[] = dialyzer:run([{files,Files},
{output_plt, PltFile},
{analysis_type, plt_build}]),
{analysis_type, plt_build},
{warnings, [no_unknown]}]),
BeamFile.

succ(PltFile, BeamFile2) ->
Expand Down Expand Up @@ -922,10 +929,10 @@ compile(Config, Prog, Module, CompileOpts) ->

run_dialyzer(Analysis, Files, Opts) ->
dialyzer:run([{analysis_type, Analysis},
{files, Files},
{from, byte_code} |
Opts]).

{files, Files},
{from, byte_code},
{warnings, [no_unknown]} |
Opts]).

m_src_without_warning() -> <<"
-module(m).
Expand Down
3 changes: 2 additions & 1 deletion lib/dialyzer/test/dialyzer_cl_SUITE.erl
Expand Up @@ -56,7 +56,8 @@ call_to_missing_warning_includes_callsite(Config) when is_list(Config) ->
{ok, BeamFileForPlt} = compile(Config, previously_defined, []),
[] = dialyzer:run([{analysis_type, plt_build},
{files, [BeamFileForPlt]},
{output_plt, Plt}]),
{output_plt, Plt},
{warnings, [no_unknown]}]),

{ok, Beam} = compile(Config, call_to_missing_example, []),
Opts =
Expand Down
7 changes: 6 additions & 1 deletion lib/dialyzer/test/dialyzer_common.erl
Expand Up @@ -107,9 +107,14 @@ obtain_plt(PltFilename) ->

build_plt(PltFilename) ->
io:format("Building plt from scratch:"),
%% note: suppress unknown type and fn errors. this is simply because
%% build_plt builds the plt using defaults, and one would need to add other
%% apps to silence missing unknown dependencies such as dependencies to
%% crypto and compiler amont others.
DefaultWarnings = {warnings, [no_unknown]},
try dialyzer:run([{analysis_type, plt_build},
{apps, ?required_modules},
{output_plt, PltFilename}]) of
{output_plt, PltFilename}, DefaultWarnings]) of
[] ->
io:format("Successfully created plt!"),
ok
Expand Down
13 changes: 7 additions & 6 deletions lib/dialyzer/test/incremental_SUITE.erl
Expand Up @@ -202,7 +202,7 @@ add_and_remove_test(Config) ->
ToPath = fun(Modules) -> [PrivDir ++ atom_to_list(Module) ++ ".beam" || Module <- Modules] end,
Plt = filename:join(PrivDir, "add_and_remove.iplt"),
compile_all(DataDir, PrivDir),
Opts = [{init_plt, [Plt]}, {report_mode, verbose}],
Opts = [{init_plt, [Plt]}, {report_mode, verbose}, {warnings, [no_unknown]}],
Run = fun(Mods) -> run_dialyzer_for_modules_analyzed(incremental, [erlang_module() | ToPath(Mods)], Opts) end,
AllSubSets = [Mods || Mods <- all_subsets(all_mods())],
%Pairs = [{I, N} || I <- AllSubSets, N <- AllSubSets], % All pairs takes ~ 10 mins to run
Expand Down Expand Up @@ -584,7 +584,7 @@ report_legal_warnings_added(Config) ->
ToPath = fun(Modules) -> [PrivDir ++ atom_to_list(Module) ++ ".beam" || Module <- Modules] end,
Plt = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME) ++ ".iplt"),
compile_all(DataDir, PrivDir),
Opts = [{init_plt, [Plt]}, {report_mode, verbose}],
Opts = [{init_plt, [Plt]}, {report_mode, verbose}, {warnings, [no_unknown]}],
_WarningsBeforeTouchingFile = run_dialyzer(incremental, [erlang_module() | ToPath(all_mods())], Opts),
Opts1 = [{init_plt, [Plt]}, {report_mode, verbose}, {warnings, [unknown]}, {metrics_file, MetricsFile}],
{_WarningsAfterTouchingFile, Stdout} = run_dialyzer_capture(incremental, [erlang_module() | ToPath(all_mods())], Opts1),
Expand Down Expand Up @@ -615,7 +615,7 @@ report_legal_warnings_removed(Config) ->
compile_all(DataDir, PrivDir),
Opts = [{init_plt, [Plt]}, {report_mode, verbose}, {warnings, [unknown]}],
_ = run_dialyzer(incremental, [erlang_module() | ToPath(all_mods())], Opts),
Opts1 = [{init_plt, [Plt]}, {report_mode, verbose}, {metrics_file, MetricsFile}],
Opts1 = [{init_plt, [Plt]}, {report_mode, verbose}, {metrics_file, MetricsFile}, {warnings, [no_unknown]}],
{_, Stdout} = run_dialyzer_capture(incremental, [erlang_module() | ToPath(all_mods())], Opts1),

ExpectedLine =
Expand Down Expand Up @@ -713,9 +713,10 @@ all_mods() -> [m1,m2,m3,m4,m5,m6].

run_dialyzer(Analysis, Files, Opts) ->
dialyzer:run([{analysis_type, Analysis},
{files, Files},
{from, byte_code}|
Opts]).
{files, Files},
{from, byte_code},
{warnings, [no_unknown]}|
Opts]).

run_dialyzer_for_modules_analyzed(Analysis, Files, Opts) ->
dialyzer:run_report_modules_analyzed([{analysis_type, Analysis},
Expand Down
2 changes: 1 addition & 1 deletion lib/dialyzer/test/indent2_SUITE_data/dialyzer_options
@@ -1 +1 @@
{dialyzer_options, [{warnings, [no_unused, no_return, specdiffs]}]}.
{dialyzer_options, [{warnings, [no_unused, no_return, no_unknown, specdiffs]}]}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/indent_SUITE_data/dialyzer_options
@@ -1 +1 @@
{dialyzer_options, [{warnings, [no_unused, no_return, overlapping_contract]}]}.
{dialyzer_options, [{warnings, [no_unused, no_return, no_unknown, overlapping_contract]}]}.
28 changes: 18 additions & 10 deletions lib/dialyzer/test/iplt_SUITE.erl
Expand Up @@ -115,7 +115,8 @@ build_xdg_plt(Config) ->
fun() ->
?assertMatch([], dialyzer:run(
[{analysis_type, incremental},
{apps, [erts]}])),
{apps, [erts]},
{warnings, [no_unknown]}])),
?assertMatch(
{ok,_}, file:read_file(
filename:join(
Expand Down Expand Up @@ -178,7 +179,7 @@ local_fun_same_as_callback(Config) when is_list(Config) ->
EBeam
end,
Plt = filename:join(PrivDir, "plt_bad_behaviour.iplt"),
Opts = [{from, byte_code}],
Opts = [{from, byte_code}, {warnings, [no_unknown]}],
[] = dialyzer:run([{analysis_type, incremental},
{files, [Beam, ErlangBeam]},
{output_plt, Plt}] ++ Opts),
Expand Down Expand Up @@ -587,7 +588,9 @@ check_plt_deps(Config, TestName, DependerSrc, ExpectedTypeDepsInPltUnsorted) ->
PltFile = filename:join(PrivDir, atom_to_list(TestName) ++ ".iplt"),
{ok, DepsBeamFile} = compile(Config, type_deps, []),
{ok, DependerBeamFile} = compile(Config, DependerSrc, depender, []),
[] = run_dialyzer(incremental, [DependerBeamFile, DepsBeamFile], [{init_plt, PltFile}, {output_plt, PltFile}]),
[] = run_dialyzer(incremental,
[DependerBeamFile, DepsBeamFile],
[{init_plt, PltFile}, {output_plt, PltFile}, {warnings, [no_unknown]}]),
{_ResPlt, #iplt_info{mod_deps = DepsByModule}} = dialyzer_iplt:plt_and_info_from_file(PltFile),

ActualTypeDepsInPlt =
Expand Down Expand Up @@ -623,9 +626,10 @@ compile(Config, Prog, Module, CompileOpts) ->

run_dialyzer(Analysis, Files, Opts) ->
dialyzer:run([{analysis_type, Analysis},
{files, Files},
{from, byte_code} |
Opts]).
{files, Files},
{from, byte_code},
{warnings, [no_unknown]} |
Opts]).


m_src_without_warning() -> <<"
Expand Down Expand Up @@ -721,7 +725,8 @@ reading_from_one_plt_and_writing_to_another_does_not_mutate_the_input_plt(Config
?assertMatch([], dialyzer:run([{analysis_type, incremental},
{files, [Beam1]},
{init_plt, Plt1},
{from, byte_code}])),
{from, byte_code},
{warnings, [no_unknown]}])),

% Now PLT v1 should exist
Plt1ContentsBefore = dialyzer_plt:get_all_contracts(dialyzer_iplt:from_file(Plt1)),
Expand All @@ -746,7 +751,8 @@ reading_from_one_plt_and_writing_to_another_does_not_mutate_the_input_plt(Config
{files, [Beam2]},
{init_plt, Plt1},
{output_plt, Plt2},
{from, byte_code}])),
{from, byte_code},
{warnings, [no_unknown]}])),

% Now PLT v1 should be the same, but PLT v2 should have the changes in it
Plt1ContentsAfter = dialyzer_plt:get_all_contracts(dialyzer_iplt:from_file(Plt1)),
Expand Down Expand Up @@ -775,7 +781,8 @@ reading_from_and_writing_to_one_plt_mutates_it(Config) when is_list(Config) ->
?assertMatch([], dialyzer:run([{analysis_type, incremental},
{files, [Beam1]},
{init_plt, Plt},
{from, byte_code}])),
{from, byte_code},
{warnings, [no_unknown]}])),

% Now PLT should exist after running incremental mode
PltContentsBefore = dialyzer_plt:get_all_contracts(dialyzer_iplt:from_file(Plt)),
Expand All @@ -799,7 +806,8 @@ reading_from_and_writing_to_one_plt_mutates_it(Config) when is_list(Config) ->
{files, [Beam2]},
{init_plt, Plt},
{output_plt, Plt},
{from, byte_code}])),
{from, byte_code},
{warnings, [no_unknown]}])),

% Now PLT should have been mutated to contain the new version of module 'foo'
PltContentsAfter = dialyzer_plt:get_all_contracts(dialyzer_iplt:from_file(Plt)),
Expand Down
2 changes: 1 addition & 1 deletion lib/dialyzer/test/line_SUITE_data/dialyzer_options
@@ -1 +1 @@
{dialyzer_options, [{error_location, line}, {warnings, [no_return]}]}.
{dialyzer_options, [{error_location, line}, {warnings, [no_return, no_unknown]}]}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/map_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
{dialyzer_options, [{indent_opt, false}]}.
{dialyzer_options, [{indent_opt, false}, {warnings, [no_unknown]}]}.
{time_limit, 30}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/opaque_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
{dialyzer_options, [{indent_opt, false}, {warnings, [no_unused, no_return]}]}.
{dialyzer_options, [{indent_opt, false}, {warnings, [no_unused, no_return, no_unknown]}]}.
{time_limit, 40}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/options1_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
{dialyzer_options, [{indent_opt, false}, {include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists]}]}.
{dialyzer_options, [{indent_opt, false}, {include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists, no_unknown]}]}.
{time_limit, 30}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/options2_SUITE_data/dialyzer_options
@@ -1 +1 @@
{dialyzer_options, [{indent_opt, false}, {defines, [{'vsn', 4}]}, {warnings, [unknown, no_return]}]}.
{dialyzer_options, [{indent_opt, false}, {defines, [{'vsn', 4}]}, {warnings, [no_return]}]}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/r9c_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
{dialyzer_options, [{indent_opt, false}, {defines, [{vsn, 42}]}]}.
{dialyzer_options, [{indent_opt, false}, {defines, [{vsn, 42}]}, {warnings, [no_unknown]}]}.
{time_limit, 20}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/small_SUITE_data/dialyzer_options
@@ -1 +1 @@
{dialyzer_options, [{indent_opt, false}, {error_location, column}, {warnings, [overlapping_contract]}]}.
{dialyzer_options, [{indent_opt, false}, {error_location, column}, {warnings, [no_unknown, overlapping_contract]}]}.
2 changes: 1 addition & 1 deletion lib/dialyzer/test/underspecs_SUITE_data/dialyzer_options
@@ -1 +1 @@
{dialyzer_options, [{indent_opt, false}, {warnings, [underspecs]}]}.
{dialyzer_options, [{indent_opt, false}, {warnings, [no_unknown, underspecs]}]}.
4 changes: 2 additions & 2 deletions lib/dialyzer/test/user_SUITE_data/dialyzer_options
@@ -1,2 +1,2 @@
{dialyzer_options, [{indent_opt, false}]}.
{time_limit, 3}.
{dialyzer_options, [{indent_opt, false}, {warnings, [no_unknown]}]}.
{time_limit, 3}.
2 changes: 1 addition & 1 deletion lib/stdlib/src/erl_lint.erl
Expand Up @@ -3414,7 +3414,7 @@ is_function_dialyzer_option(Option) ->
is_module_dialyzer_option(Option) ->
lists:member(Option,
[no_return,no_unused,no_improper_lists,no_fun_app,
no_match,no_opaque,no_fail_call,no_contracts,
no_match,no_opaque,no_fail_call,no_contracts,no_unknown,
no_behaviours,no_undefined_callbacks,unmatched_returns,
error_handling,race_conditions,no_missing_calls,
specdiffs,overspecs,underspecs,unknown,
Expand Down

0 comments on commit 94a7dfb

Please sign in to comment.