Skip to content

Commit

Permalink
Support disabling opportunistic warnings
Browse files Browse the repository at this point in the history
It has never been possible to disable the opportunistic warnings
generated by the code generation and optimization passes.

It is high time to allow the user to disable those warnings,
especially since using funs to suppress warnings may no longer
work since commit 72675ba that introduced inlining of funs
that were immediately used.
  • Loading branch information
bjorng committed Mar 12, 2021
1 parent 4761479 commit b217b21
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 25 deletions.
58 changes: 36 additions & 22 deletions lib/compiler/doc/src/compile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -772,29 +772,43 @@ module.beam: module.erl \
</item>
</taglist>

<p>Another class of warnings is generated by the compiler
during optimization and code generation. They warn about
patterns that will never match (such as <c>a=b</c>), guards
that always evaluate to false, and expressions that
always fail (such as <c>atom+42</c>).</p>
<p>Those warnings cannot be disabled (except by
disabling all warnings).</p>
<p>Other kinds of warnings are <em>opportunistic
warnings</em>. They are generated when the compiler happens to
notice potential issues during optimization and code
generation.</p>

<note>
<p>The compiler does not warn for expressions that it
does not attempt to optimize. For example, the compiler will
emit a warning for <c>1/0</c> but not for <c>X/0</c>,
because <c>1/0</c> is a constant expression that the compiler
will attempt to evaluate.</p>

<p>The absence of warnings does not mean that there are no
remaining errors in the code.</p>
</note>

<p>Opportunistic warnings can be disabled using the following
options:</p>

<taglist>
<tag><c>nowarn_opportunistic</c></tag>
<item><p>Disable all opportunistic warnings.</p></item>

<tag><c>nowarn_failed</c></tag>
<item><p>Disable warnings for expressions that will always
fail (such as <c>atom+42</c>).</p></item>

<tag><c>nowarn_ignored</c></tag>
<item><p>Disable warnings for expressions whose
values are ignored.</p></item>

<tag><c>nowarn_nomatch</c></tag>
<item><p>Disable warnings for patterns that will never match
(such as <c>a=b</c>) and for guards that always evaluate to
<c>false</c>.</p></item>
</taglist>

<note>
<p>The compiler does not warn for expressions that it
does not attempt to optimize. For example, the compiler tries
to evaluate <c>1/0</c>, detects that it will cause an
exception, and emits a warning. However,
the compiler is silent about the similar expression,
<c>X/0</c>, because of the variable in it. Thus, the compiler does
not even try to evaluate and therefore it emits no warnings.</p>
</note>

<warning>
<p>The absence of warnings does not mean that
there are no remaining errors in the code.</p>
</warning>

<note>
<p>All options, except the include path
(<c>{i,Dir}</c>), can also be given in the file with attribute
Expand Down
38 changes: 37 additions & 1 deletion lib/compiler/src/compile.erl
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ run_eprof({_,Fun}, Code, _, St) ->
Fun(Code, St).

comp_ret_ok(Code, #compile{warnings=Warn0,module=Mod,options=Opts}=St) ->
Warn1 = filter_warnings(Warn0, Opts),
case werror(St) of
true ->
case member(report_warnings, Opts) of
Expand All @@ -525,7 +526,7 @@ comp_ret_ok(Code, #compile{warnings=Warn0,module=Mod,options=Opts}=St) ->
end,
comp_ret_err(St);
false ->
Warn = messages_per_file(Warn0),
Warn = messages_per_file(Warn1),
report_warnings(St#compile{warnings = Warn}),
Ret1 = case member(binary, Opts) andalso
not member(no_code_generation, Opts) of
Expand Down Expand Up @@ -1720,6 +1721,41 @@ report_warnings(#compile{options=Opts,warnings=Ws0}) ->
false -> ok
end.

%%%
%%% Filter warnings.
%%%

filter_warnings(Ws, Opts) ->
Ignore = ignore_tags(Opts, sets:new([{version,2}])),
filter_warnings_1(Ws, Ignore).

filter_warnings_1([{Source,Ws0}|T], Ignore) ->
Ws = [W || W <- Ws0, not ignore_warning(W, Ignore)],
[{Source,Ws}|filter_warnings_1(T, Ignore)];
filter_warnings_1([], _Ignore) -> [].

ignore_warning({_Location,Pass,{Category,_}}, Ignore) ->
IgnoreMod = case Pass of
v3_core -> true;
sys_core_fold -> true;
v3_kernel -> true;
_ -> false
end,
IgnoreMod andalso sets:is_element(Category, Ignore);
ignore_warning(_, _) -> false.

ignore_tags([nowarn_opportunistic|_], _Ignore) ->
sets:from_list([failed,ignored,nomatch], [{version,2}]);
ignore_tags([nowarn_failed|Opts], Ignore) ->
ignore_tags(Opts, sets:add_element(failed, Ignore));
ignore_tags([nowarn_ignored|Opts], Ignore) ->
ignore_tags(Opts, sets:add_element(ignored, Ignore));
ignore_tags([nowarn_nomatch|Opts], Ignore) ->
ignore_tags(Opts, sets:add_element(nomatch, Ignore));
ignore_tags([_|Opts], Ignore) ->
ignore_tags(Opts, Ignore);
ignore_tags([], Ignore) -> Ignore.

%% erlfile(Dir, Base) -> ErlFile
%% outfile(Base, Extension, Options) -> OutputFile
%% objfile(Base, Target, Options) -> ObjFile
Expand Down
77 changes: 75 additions & 2 deletions lib/compiler/test/warnings_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
redundant_boolean_clauses/1,
underscore/1,no_warnings/1,
bit_syntax/1,inlining/1,tuple_calls/1,
recv_opt_info/1]).
recv_opt_info/1,opportunistic_warnings/1]).

init_per_testcase(_Case, Config) ->
Config.
Expand All @@ -66,7 +66,7 @@ groups() ->
maps_bin_opt_info,
redundant_boolean_clauses,
underscore,no_warnings,bit_syntax,inlining,
tuple_calls,recv_opt_info]}].
tuple_calls,recv_opt_info,opportunistic_warnings]}].

init_per_suite(Config) ->
test_lib:recompile(?MODULE),
Expand Down Expand Up @@ -1087,6 +1087,79 @@ recv_opt_info(Config) when is_list(Config) ->
%% For coverage: don't give the recv_opt_info option.
[] = (catch run_test(Config, Code, [])),

ok.

%% OTP-17260: Test that opportunistic warnings can be disabled.
opportunistic_warnings(Config) ->
Source = <<"m(_) -> ok;
m(_) -> error.
a() -> <<0.5>>.
b() -> Bin = <<1,2,3,7:4>>, <<Bin/binary>>.
c() -> Size = bad_size, <<1:Size>>.
i() -> {a,b,c}, ok.
">>,

%% Don't disable any warnings.
Ts1 = [{nothing_disabled,
Source,
[],
{warnings,[{{2,17},sys_core_fold,{nomatch,{shadow,1,{m,1}}}},
{{4,24},v3_core,{failed,bad_binary}},
{{5,45},sys_core_fold,{failed,{embedded_unit,8,28}}},
{{6,43},v3_kernel,{failed,bad_segment_size}},
{{8,24},sys_core_fold,{ignored,useless_building}}
]}}],
[] = run(Config, Ts1),

%% Disable all opportunistic warnings.
Ts2 = [{all_disabled,
Source,
[nowarn_opportunistic],
[]}],
[] = run(Config, Ts2),

%% Disable warnings for patterns that don't match.
Ts3 = [{nomatch_disabled,
Source,
[nowarn_nomatch],
{warnings,[{{4,24},v3_core,{failed,bad_binary}},
{{5,45},sys_core_fold,{failed,{embedded_unit,8,28}}},
{{6,43},v3_kernel,{failed,bad_segment_size}},
{{8,24},sys_core_fold,{ignored,useless_building}}
]}}],
[] = run(Config, Ts3),

%% Disable warnings for failures.
Ts4 = [{failures_disabled,
Source,
[nowarn_failed],
{warnings,[{{2,17},sys_core_fold,{nomatch,{shadow,1,{m,1}}}},
{{8,24},sys_core_fold,{ignored,useless_building}}
]}}],
[] = run(Config, Ts4),

%% Disable warnings for useless building.
Ts5 = [{disabled_useless_building,
Source,
[nowarn_ignored],
{warnings,[{{2,17},sys_core_fold,{nomatch,{shadow,1,{m,1}}}},
{{4,24},v3_core,{failed,bad_binary}},
{{5,45},sys_core_fold,{failed,{embedded_unit,8,28}}},
{{6,43},v3_kernel,{failed,bad_segment_size}}
]}}],
[] = run(Config, Ts5),

%% Disable warnings for useless building and failures.
Ts6 = [{disabled_combination,
Source,
[nowarn_ignored,nowarn_failed],
{warnings,[{{2,17},sys_core_fold,{nomatch,{shadow,1,{m,1}}}}
]}}],
[] = run(Config, Ts6),


ok.

%%%
Expand Down

0 comments on commit b217b21

Please sign in to comment.