11use clippy_utils:: {
2- consts:: constant, diagnostics:: span_lint_and_sugg , is_from_proc_macro, path_to_local, source:: snippet_opt,
2+ consts:: constant, diagnostics:: span_lint_and_then , is_from_proc_macro, path_to_local, source:: snippet_opt,
33} ;
44use rustc_errors:: Applicability ;
55use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
6- use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
6+ use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext } ;
77use rustc_middle:: lint:: in_external_macro;
88use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
99
@@ -53,12 +53,37 @@ declare_clippy_lint! {
5353}
5454declare_lint_pass ! ( ManualFloatMethods => [ MANUAL_IS_INFINITE , MANUAL_IS_FINITE ] ) ;
5555
56+ #[ derive( Clone , Copy ) ]
57+ enum Variant {
58+ ManualIsInfinite ,
59+ ManualIsFinite ,
60+ }
61+
62+ impl Variant {
63+ pub fn lint ( self ) -> & ' static Lint {
64+ match self {
65+ Self :: ManualIsInfinite => MANUAL_IS_INFINITE ,
66+ Self :: ManualIsFinite => MANUAL_IS_FINITE ,
67+ }
68+ }
69+
70+ pub fn msg ( self ) -> & ' static str {
71+ match self {
72+ Self :: ManualIsInfinite => "manually checking if a float is infinite" ,
73+ Self :: ManualIsFinite => "manually checking if a float is finite" ,
74+ }
75+ }
76+ }
77+
5678impl < ' tcx > LateLintPass < ' tcx > for ManualFloatMethods {
5779 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
5880 if !in_external_macro ( cx. sess ( ) , expr. span )
81+ && ( !cx. param_env . is_const ( ) || cx. tcx . features ( ) . active ( sym ! ( const_float_classify) ) )
5982 && let ExprKind :: Binary ( kind, lhs, rhs) = expr. kind
6083 && let ExprKind :: Binary ( lhs_kind, lhs_lhs, lhs_rhs) = lhs. kind
6184 && let ExprKind :: Binary ( rhs_kind, rhs_lhs, rhs_rhs) = rhs. kind
85+ // Checking all possible scenarios using a function would be a hopeless task, as we have
86+ // 16 possible alignments of constants/operands. For now, let's use `partition`.
6287 && let ( operands, consts) = [ lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
6388 . into_iter ( )
6489 . partition :: < Vec < & Expr < ' _ > > , _ > ( |i| path_to_local ( i) . is_some ( ) )
@@ -74,24 +99,51 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
7499 && let Some ( local_snippet) = snippet_opt ( cx, first. span )
75100 && !is_from_proc_macro ( cx, expr)
76101 {
77- let ( msg, lint, sugg_fn) = match ( kind. node , lhs_kind. node , rhs_kind. node ) {
78- ( BinOpKind :: Or , BinOpKind :: Eq , BinOpKind :: Eq ) => {
79- ( "manually checking if a float is infinite" , MANUAL_IS_INFINITE , "is_infinite" )
80- } ,
81- ( BinOpKind :: And , BinOpKind :: Ne , BinOpKind :: Ne ) => {
82- ( "manually checking if a float is finite" , MANUAL_IS_FINITE , "is_finite" )
83- } ,
102+ let variant = match ( kind. node , lhs_kind. node , rhs_kind. node ) {
103+ ( BinOpKind :: Or , BinOpKind :: Eq , BinOpKind :: Eq ) => Variant :: ManualIsInfinite ,
104+ ( BinOpKind :: And , BinOpKind :: Ne , BinOpKind :: Ne ) => Variant :: ManualIsFinite ,
84105 _ => return ,
85106 } ;
86107
87- span_lint_and_sugg (
108+ span_lint_and_then (
88109 cx,
89- lint,
110+ variant . lint ( ) ,
90111 expr. span ,
91- msg,
92- "try" ,
93- format ! ( "{local_snippet}.{sugg_fn}()" ) ,
94- Applicability :: MachineApplicable ,
112+ variant. msg ( ) ,
113+ |diag| {
114+ match variant {
115+ Variant :: ManualIsInfinite => {
116+ diag. span_suggestion (
117+ expr. span ,
118+ "use the dedicated method instead" ,
119+ format ! ( "{local_snippet}.is_infinite()" ) ,
120+ Applicability :: MachineApplicable ,
121+ ) ;
122+ } ,
123+ Variant :: ManualIsFinite => {
124+ // TODO: There's probably some better way to do this, i.e., create
125+ // multiple suggestions with notes between each of them
126+ diag. span_suggestion_verbose (
127+ expr. span ,
128+ "use the dedicated method instead" ,
129+ format ! ( "{local_snippet}.is_finite()" ) ,
130+ Applicability :: MaybeIncorrect ,
131+ ) ;
132+ diag. span_suggestion_verbose (
133+ expr. span ,
134+ "this will alter how it handles NaN; if that is a problem, use instead" ,
135+ format ! ( "{local_snippet}.is_finite() || {local_snippet}.is_nan()" ) ,
136+ Applicability :: MaybeIncorrect ,
137+ ) ;
138+ diag. span_suggestion_verbose (
139+ expr. span ,
140+ "or, for conciseness" ,
141+ format ! ( "!{local_snippet}.is_infinite()" ) ,
142+ Applicability :: MaybeIncorrect ,
143+ ) ;
144+ }
145+ }
146+ }
95147 ) ;
96148 }
97149 }
0 commit comments