Skip to content

Commit

Permalink
Merge pull request #7521 from jhogberg/john/stdlib/lint-match-float-z…
Browse files Browse the repository at this point in the history
…ero/OTP-18696

erl_lint: Warn on matching float 0.0
  • Loading branch information
jhogberg authored Aug 11, 2023
2 parents 9c299d2 + 5e193bd commit 061bec4
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 3 deletions.
33 changes: 32 additions & 1 deletion lib/stdlib/src/erl_lint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ format_error({illegal_guard_local_call, {F,A}}) ->
io_lib:format("call to local/imported function ~tw/~w is illegal in guard",
[F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
format_error(match_float_zero) ->
"matching on the float 0.0 will no longer also match -0.0 in OTP 27. If "
"you specifically intend to match 0.0 alone, write +0.0 instead.";
%% --- maps ---
format_error(illegal_map_construction) ->
"only association operators '=>' are allowed in map construction";
Expand Down Expand Up @@ -672,6 +675,9 @@ start(File, Opts) ->
true, Opts)},
{singleton_typevar,
bool_option(warn_singleton_typevar, nowarn_singleton_typevar,
true, Opts)},
{match_float_zero,
bool_option(warn_match_float_zero, nowarn_match_float_zero,
true, Opts)}
],
Enabled1 = [Category || {Category,true} <- Enabled0],
Expand Down Expand Up @@ -1704,7 +1710,12 @@ pattern({var,Anno,V}, _Vt, Old, St) ->
pat_var(V, Anno, Old, [], St);
pattern({char,_Anno,_C}, _Vt, _Old, St) -> {[],[],St};
pattern({integer,_Anno,_I}, _Vt, _Old, St) -> {[],[],St};
pattern({float,_Anno,_F}, _Vt, _Old, St) -> {[],[],St};
pattern({float,Anno,F}, _Vt, _Old, St0) ->
St = case F == 0 andalso is_warn_enabled(match_float_zero, St0) of
true -> add_warning(Anno, match_float_zero, St0);
false -> St0
end,
{[], [], St};
pattern({atom,Anno,A}, _Vt, _Old, St) ->
{[],[],keyword_warning(Anno, A, St)};
pattern({string,_Anno,_S}, _Vt, _Old, St) -> {[],[],St};
Expand Down Expand Up @@ -2149,6 +2160,9 @@ gexpr({op,_,'andalso',L,R}, Vt, St) ->
gexpr_list([L,R], Vt, St);
gexpr({op,_,'orelse',L,R}, Vt, St) ->
gexpr_list([L,R], Vt, St);
gexpr({op,_Anno,EqOp,L,R}, Vt, St0) when EqOp =:= '=:='; EqOp =:= '=/=' ->
St1 = expr_check_match_zero(R, expr_check_match_zero(L, St0)),
gexpr_list([L,R], Vt, St1);
gexpr({op,Anno,Op,L,R}, Vt, St0) ->
{Avt,St1} = gexpr_list([L,R], Vt, St0),
case is_gexpr_op(Op, 2) of
Expand Down Expand Up @@ -2565,6 +2579,9 @@ expr({op,Anno,Op,L,R}, Vt, St0) when Op =:= 'orelse'; Op =:= 'andalso' ->
{Evt2,St2} = expr(R, Vt1, St1),
Evt3 = vtupdate(vtunsafe({Op,Anno}, Evt2, Vt1), Evt2),
{vtmerge(Evt1, Evt3),St2};
expr({op,_Anno,EqOp,L,R}, Vt, St0) when EqOp =:= '=:='; EqOp =:= '=/=' ->
St = expr_check_match_zero(R, expr_check_match_zero(L, St0)),
expr_list([L,R], Vt, St); %They see the same variables
expr({op,_Anno,_Op,L,R}, Vt, St) ->
expr_list([L,R], Vt, St); %They see the same variables
%% The following are not allowed to occur anywhere!
Expand All @@ -2573,6 +2590,20 @@ expr({remote,_Anno,M,_F}, _Vt, St) ->
expr({ssa_check_when,_Anno,_WantedResult,_Args,_Tag,_Exprs}, _Vt, St) ->
{[], St}.

%% Checks whether 0.0 occurs naked in the LHS or RHS of an equality check. Note
%% that we do not warn when it's being used as arguments for expressions in
%% in general: `A =:= abs(0.0)` is fine.
expr_check_match_zero({float,Anno,F}, St) ->
case F == 0 andalso is_warn_enabled(match_float_zero, St) of
true -> add_warning(Anno, match_float_zero, St);
false -> St
end;
expr_check_match_zero({cons,_Anno,H,T}, St) ->
expr_check_match_zero(H, expr_check_match_zero(T, St));
expr_check_match_zero({tuple,_Anno,Es}, St) ->
foldl(fun expr_check_match_zero/2, St, Es);
expr_check_match_zero(_Expr, St) ->
St.

%% expr_list(Expressions, Variables, State) ->
%% {UsedVarTable,State}
Expand Down
35 changes: 33 additions & 2 deletions lib/stdlib/test/erl_lint_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@
unused_type2/1,
eep49/1,
redefined_builtin_type/1,
tilde_k/1]).
tilde_k/1,
match_float_zero/1]).

suite() ->
[{ct_hooks,[ts_install_cth]},
Expand Down Expand Up @@ -113,7 +114,8 @@ all() ->
eep49,
redefined_builtin_type,
tilde_k,
singleton_type_var_errors].
singleton_type_var_errors,
match_float_zero].

groups() ->
[{unused_vars_warn, [],
Expand Down Expand Up @@ -5183,6 +5185,35 @@ tilde_k(Config) ->

ok.

match_float_zero(Config) ->
Ts = [{float_zero_1,
<<"t(+0.0) -> ok.\n"
"k(-0.0) -> ok.\n">>,
[],
[]},
{float_zero_2,
<<"t(0.0) -> ok.\n"
"k({0.0}) -> ok.\n">>,
[],
{warnings,[{{1,23},erl_lint,match_float_zero},
{{2,4},erl_lint,match_float_zero}]}},
{float_zero_3,
<<"t(A) when A =:= 0.0 -> ok;\n" %% Should warn.
"t(A) when A =:= {0.0} -> ok.\n" %% Should warn.
"k(A) -> A =:= 0.0.\n" %% Should warn.
"q(A) -> A =:= {0.0}.\n" %% Should warn.
"z(A) when A =:= +0.0 -> ok;\n" %% Should not warn.
"z(A) when A =:= {+0.0} -> ok.\n">>, %% Should not warn.
[],
{warnings,[{{1,37},erl_lint,match_float_zero},
{{2,18},erl_lint,match_float_zero},
{{3,15},erl_lint,match_float_zero},
{{4,16},erl_lint,match_float_zero}]}}
],
[] = run(Config, Ts),

ok.

%%%
%%% Common utilities.
%%%
Expand Down

0 comments on commit 061bec4

Please sign in to comment.