1
1
use 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,
3
3
} ;
4
4
use rustc_errors:: Applicability ;
5
5
use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
6
- use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
6
+ use rustc_lint:: { LateContext , LateLintPass , Lint , LintContext } ;
7
7
use rustc_middle:: lint:: in_external_macro;
8
8
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9
9
@@ -53,12 +53,37 @@ declare_clippy_lint! {
53
53
}
54
54
declare_lint_pass ! ( ManualFloatMethods => [ MANUAL_IS_INFINITE , MANUAL_IS_FINITE ] ) ;
55
55
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
+
56
78
impl < ' tcx > LateLintPass < ' tcx > for ManualFloatMethods {
57
79
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
58
80
if !in_external_macro ( cx. sess ( ) , expr. span )
81
+ && ( !cx. param_env . is_const ( ) || cx. tcx . features ( ) . active ( sym ! ( const_float_classify) ) )
59
82
&& let ExprKind :: Binary ( kind, lhs, rhs) = expr. kind
60
83
&& let ExprKind :: Binary ( lhs_kind, lhs_lhs, lhs_rhs) = lhs. kind
61
84
&& 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`.
62
87
&& let ( operands, consts) = [ lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
63
88
. into_iter ( )
64
89
. partition :: < Vec < & Expr < ' _ > > , _ > ( |i| path_to_local ( i) . is_some ( ) )
@@ -74,24 +99,51 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
74
99
&& let Some ( local_snippet) = snippet_opt ( cx, first. span )
75
100
&& !is_from_proc_macro ( cx, expr)
76
101
{
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 ,
84
105
_ => return ,
85
106
} ;
86
107
87
- span_lint_and_sugg (
108
+ span_lint_and_then (
88
109
cx,
89
- lint,
110
+ variant . lint ( ) ,
90
111
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
+ }
95
147
) ;
96
148
}
97
149
}
0 commit comments