@@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg;
18
18
use crate :: utils:: {
19
19
get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
20
20
last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
21
- span_lint_and_then, span_lint_hir_and_then, SpanlessEq ,
21
+ span_lint_and_then, span_lint_hir_and_then, unsext , SpanlessEq ,
22
22
} ;
23
23
24
24
declare_clippy_lint ! {
@@ -139,23 +139,26 @@ declare_clippy_lint! {
139
139
}
140
140
141
141
declare_clippy_lint ! {
142
- /// **What it does:** Checks for getting the remainder of a division by one.
142
+ /// **What it does:** Checks for getting the remainder of a division by one or minus
143
+ /// one.
143
144
///
144
- /// **Why is this bad?** The result can only ever be zero. No one will write
145
- /// such code deliberately, unless trying to win an Underhanded Rust
146
- /// Contest. Even for that contest, it's probably a bad idea. Use something more
147
- /// underhanded.
145
+ /// **Why is this bad?** The result for a divisor of one can only ever be zero; for
146
+ /// minus one it can cause panic/overflow (if the left operand is the minimal value of
147
+ /// the respective integer type) or results in zero. No one will write such code
148
+ /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
149
+ /// contest, it's probably a bad idea. Use something more underhanded.
148
150
///
149
151
/// **Known problems:** None.
150
152
///
151
153
/// **Example:**
152
154
/// ```rust
153
155
/// # let x = 1;
154
156
/// let a = x % 1;
157
+ /// let a = x % -1;
155
158
/// ```
156
159
pub MODULO_ONE ,
157
160
correctness,
158
- "taking a number modulo 1, which always returns 0"
161
+ "taking a number modulo +/- 1, which can either panic/overflow or always returns 0"
159
162
}
160
163
161
164
declare_clippy_lint ! {
@@ -378,60 +381,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
378
381
return ;
379
382
} ,
380
383
ExprKind :: Binary ( ref cmp, ref left, ref right) => {
381
- let op = cmp. node ;
382
- if op. is_comparison ( ) {
383
- check_nan ( cx, left, expr) ;
384
- check_nan ( cx, right, expr) ;
385
- check_to_owned ( cx, left, right, true ) ;
386
- check_to_owned ( cx, right, left, false ) ;
387
- }
388
- if ( op == BinOpKind :: Eq || op == BinOpKind :: Ne ) && ( is_float ( cx, left) || is_float ( cx, right) ) {
389
- if is_allowed ( cx, left) || is_allowed ( cx, right) {
390
- return ;
391
- }
392
-
393
- // Allow comparing the results of signum()
394
- if is_signum ( cx, left) && is_signum ( cx, right) {
395
- return ;
396
- }
397
-
398
- if let Some ( name) = get_item_name ( cx, expr) {
399
- let name = name. as_str ( ) ;
400
- if name == "eq"
401
- || name == "ne"
402
- || name == "is_nan"
403
- || name. starts_with ( "eq_" )
404
- || name. ends_with ( "_eq" )
405
- {
406
- return ;
407
- }
408
- }
409
- let is_comparing_arrays = is_array ( cx, left) || is_array ( cx, right) ;
410
- let ( lint, msg) = get_lint_and_message (
411
- is_named_constant ( cx, left) || is_named_constant ( cx, right) ,
412
- is_comparing_arrays,
413
- ) ;
414
- span_lint_and_then ( cx, lint, expr. span , msg, |diag| {
415
- let lhs = Sugg :: hir ( cx, left, ".." ) ;
416
- let rhs = Sugg :: hir ( cx, right, ".." ) ;
417
-
418
- if !is_comparing_arrays {
419
- diag. span_suggestion (
420
- expr. span ,
421
- "consider comparing them within some margin of error" ,
422
- format ! (
423
- "({}).abs() {} error_margin" ,
424
- lhs - rhs,
425
- if op == BinOpKind :: Eq { '<' } else { '>' }
426
- ) ,
427
- Applicability :: HasPlaceholders , // snippet
428
- ) ;
429
- }
430
- diag. note ( "`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`" ) ;
431
- } ) ;
432
- } else if op == BinOpKind :: Rem && is_integer_const ( cx, right, 1 ) {
433
- span_lint ( cx, MODULO_ONE , expr. span , "any number modulo 1 will be 0" ) ;
434
- }
384
+ check_binary ( cx, expr, cmp, left, right) ;
385
+ return ;
435
386
} ,
436
387
_ => { } ,
437
388
}
@@ -744,3 +695,74 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
744
695
}
745
696
}
746
697
}
698
+
699
+ fn check_binary (
700
+ cx : & LateContext < ' a > ,
701
+ expr : & Expr < ' _ > ,
702
+ cmp : & rustc_span:: source_map:: Spanned < rustc_hir:: BinOpKind > ,
703
+ left : & ' a Expr < ' _ > ,
704
+ right : & ' a Expr < ' _ > ,
705
+ ) {
706
+ let op = cmp. node ;
707
+ if op. is_comparison ( ) {
708
+ check_nan ( cx, left, expr) ;
709
+ check_nan ( cx, right, expr) ;
710
+ check_to_owned ( cx, left, right, true ) ;
711
+ check_to_owned ( cx, right, left, false ) ;
712
+ }
713
+ if ( op == BinOpKind :: Eq || op == BinOpKind :: Ne ) && ( is_float ( cx, left) || is_float ( cx, right) ) {
714
+ if is_allowed ( cx, left) || is_allowed ( cx, right) {
715
+ return ;
716
+ }
717
+
718
+ // Allow comparing the results of signum()
719
+ if is_signum ( cx, left) && is_signum ( cx, right) {
720
+ return ;
721
+ }
722
+
723
+ if let Some ( name) = get_item_name ( cx, expr) {
724
+ let name = name. as_str ( ) ;
725
+ if name == "eq" || name == "ne" || name == "is_nan" || name. starts_with ( "eq_" ) || name. ends_with ( "_eq" ) {
726
+ return ;
727
+ }
728
+ }
729
+ let is_comparing_arrays = is_array ( cx, left) || is_array ( cx, right) ;
730
+ let ( lint, msg) = get_lint_and_message (
731
+ is_named_constant ( cx, left) || is_named_constant ( cx, right) ,
732
+ is_comparing_arrays,
733
+ ) ;
734
+ span_lint_and_then ( cx, lint, expr. span , msg, |diag| {
735
+ let lhs = Sugg :: hir ( cx, left, ".." ) ;
736
+ let rhs = Sugg :: hir ( cx, right, ".." ) ;
737
+
738
+ if !is_comparing_arrays {
739
+ diag. span_suggestion (
740
+ expr. span ,
741
+ "consider comparing them within some margin of error" ,
742
+ format ! (
743
+ "({}).abs() {} error_margin" ,
744
+ lhs - rhs,
745
+ if op == BinOpKind :: Eq { '<' } else { '>' }
746
+ ) ,
747
+ Applicability :: HasPlaceholders , // snippet
748
+ ) ;
749
+ }
750
+ diag. note ( "`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`" ) ;
751
+ } ) ;
752
+ } else if op == BinOpKind :: Rem {
753
+ if is_integer_const ( cx, right, 1 ) {
754
+ span_lint ( cx, MODULO_ONE , expr. span , "any number modulo 1 will be 0" ) ;
755
+ }
756
+
757
+ if let ty:: Int ( ity) = cx. typeck_results ( ) . expr_ty ( right) . kind ( ) {
758
+ if is_integer_const ( cx, right, unsext ( cx. tcx , -1 , * ity) ) {
759
+ span_lint (
760
+ cx,
761
+ MODULO_ONE ,
762
+ expr. span ,
763
+ "any number modulo -1 will panic/overflow or result in 0" ,
764
+ ) ;
765
+ }
766
+ } ;
767
+ }
768
+ }
0 commit comments