@@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg;
1818use crate :: utils:: {
1919 get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
2020 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 ,
2222} ;
2323
2424declare_clippy_lint ! {
@@ -139,23 +139,26 @@ declare_clippy_lint! {
139139}
140140
141141declare_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.
143144 ///
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.
148150 ///
149151 /// **Known problems:** None.
150152 ///
151153 /// **Example:**
152154 /// ```rust
153155 /// # let x = 1;
154156 /// let a = x % 1;
157+ /// let a = x % -1;
155158 /// ```
156159 pub MODULO_ONE ,
157160 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"
159162}
160163
161164declare_clippy_lint ! {
@@ -378,60 +381,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
378381 return ;
379382 } ,
380383 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 ;
435386 } ,
436387 _ => { } ,
437388 }
@@ -744,3 +695,74 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
744695 }
745696 }
746697}
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