1
1
use crate :: consts:: {
2
- constant, Constant ,
2
+ constant, constant_simple , Constant ,
3
3
Constant :: { F32 , F64 } ,
4
4
} ;
5
- use crate :: utils:: { span_lint_and_sugg, sugg} ;
5
+ use crate :: utils:: { higher , numeric_literal , span_lint_and_sugg, sugg, SpanlessEq } ;
6
6
use if_chain:: if_chain;
7
7
use rustc:: ty;
8
8
use rustc_errors:: Applicability ;
@@ -14,7 +14,7 @@ use rustc_span::source_map::Spanned;
14
14
use rustc_ast:: ast;
15
15
use std:: f32:: consts as f32_consts;
16
16
use std:: f64:: consts as f64_consts;
17
- use sugg:: { format_numeric_literal , Sugg } ;
17
+ use sugg:: Sugg ;
18
18
19
19
declare_clippy_lint ! {
20
20
/// **What it does:** Looks for floating-point expressions that
@@ -72,6 +72,16 @@ declare_clippy_lint! {
72
72
/// let _ = a.log(E);
73
73
/// let _ = a.powf(2.0);
74
74
/// let _ = a * 2.0 + 4.0;
75
+ /// let _ = if a < 0.0 {
76
+ /// -a
77
+ /// } else {
78
+ /// a
79
+ /// };
80
+ /// let _ = if a < 0.0 {
81
+ /// a
82
+ /// } else {
83
+ /// -a
84
+ /// };
75
85
/// ```
76
86
///
77
87
/// is better expressed as
@@ -88,6 +98,8 @@ declare_clippy_lint! {
88
98
/// let _ = a.ln();
89
99
/// let _ = a.powi(2);
90
100
/// let _ = a.mul_add(2.0, 4.0);
101
+ /// let _ = a.abs();
102
+ /// let _ = -a.abs();
91
103
/// ```
92
104
pub SUBOPTIMAL_FLOPS ,
93
105
nursery,
@@ -264,7 +276,7 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
264
276
format ! (
265
277
"{}.powi({})" ,
266
278
Sugg :: hir( cx, & args[ 0 ] , ".." ) ,
267
- format_numeric_literal ( & exponent. to_string( ) , None , false )
279
+ numeric_literal :: format ( & exponent. to_string( ) , None , false )
268
280
) ,
269
281
)
270
282
} else {
@@ -359,6 +371,116 @@ fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
359
371
}
360
372
}
361
373
374
+ /// Returns true iff expr is an expression which tests whether or not
375
+ /// test is positive or an expression which tests whether or not test
376
+ /// is nonnegative.
377
+ /// Used for check-custom-abs function below
378
+ fn is_testing_positive ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , test : & Expr < ' _ > ) -> bool {
379
+ if let ExprKind :: Binary ( Spanned { node : op, .. } , left, right) = expr. kind {
380
+ match op {
381
+ BinOpKind :: Gt | BinOpKind :: Ge => is_zero ( cx, right) && are_exprs_equal ( cx, left, test) ,
382
+ BinOpKind :: Lt | BinOpKind :: Le => is_zero ( cx, left) && are_exprs_equal ( cx, right, test) ,
383
+ _ => false ,
384
+ }
385
+ } else {
386
+ false
387
+ }
388
+ }
389
+
390
+ /// See [`is_testing_positive`]
391
+ fn is_testing_negative ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , test : & Expr < ' _ > ) -> bool {
392
+ if let ExprKind :: Binary ( Spanned { node : op, .. } , left, right) = expr. kind {
393
+ match op {
394
+ BinOpKind :: Gt | BinOpKind :: Ge => is_zero ( cx, left) && are_exprs_equal ( cx, right, test) ,
395
+ BinOpKind :: Lt | BinOpKind :: Le => is_zero ( cx, right) && are_exprs_equal ( cx, left, test) ,
396
+ _ => false ,
397
+ }
398
+ } else {
399
+ false
400
+ }
401
+ }
402
+
403
+ fn are_exprs_equal ( cx : & LateContext < ' _ , ' _ > , expr1 : & Expr < ' _ > , expr2 : & Expr < ' _ > ) -> bool {
404
+ SpanlessEq :: new ( cx) . ignore_fn ( ) . eq_expr ( expr1, expr2)
405
+ }
406
+
407
+ /// Returns true iff expr is some zero literal
408
+ fn is_zero ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) -> bool {
409
+ match constant_simple ( cx, cx. tables , expr) {
410
+ Some ( Constant :: Int ( i) ) => i == 0 ,
411
+ Some ( Constant :: F32 ( f) ) => f == 0.0 ,
412
+ Some ( Constant :: F64 ( f) ) => f == 0.0 ,
413
+ _ => false ,
414
+ }
415
+ }
416
+
417
+ /// If the two expressions are negations of each other, then it returns
418
+ /// a tuple, in which the first element is true iff expr1 is the
419
+ /// positive expressions, and the second element is the positive
420
+ /// one of the two expressions
421
+ /// If the two expressions are not negations of each other, then it
422
+ /// returns None.
423
+ fn are_negated < ' a > ( cx : & LateContext < ' _ , ' _ > , expr1 : & ' a Expr < ' a > , expr2 : & ' a Expr < ' a > ) -> Option < ( bool , & ' a Expr < ' a > ) > {
424
+ if let ExprKind :: Unary ( UnOp :: UnNeg , expr1_negated) = & expr1. kind {
425
+ if are_exprs_equal ( cx, expr1_negated, expr2) {
426
+ return Some ( ( false , expr2) ) ;
427
+ }
428
+ }
429
+ if let ExprKind :: Unary ( UnOp :: UnNeg , expr2_negated) = & expr2. kind {
430
+ if are_exprs_equal ( cx, expr1, expr2_negated) {
431
+ return Some ( ( true , expr1) ) ;
432
+ }
433
+ }
434
+ None
435
+ }
436
+
437
+ fn check_custom_abs ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) {
438
+ if_chain ! {
439
+ if let Some ( ( cond, body, Some ( else_body) ) ) = higher:: if_block( & expr) ;
440
+ if let ExprKind :: Block ( block, _) = body. kind;
441
+ if block. stmts. is_empty( ) ;
442
+ if let Some ( if_body_expr) = block. expr;
443
+ if let ExprKind :: Block ( else_block, _) = else_body. kind;
444
+ if else_block. stmts. is_empty( ) ;
445
+ if let Some ( else_body_expr) = else_block. expr;
446
+ if let Some ( ( if_expr_positive, body) ) = are_negated( cx, if_body_expr, else_body_expr) ;
447
+ then {
448
+ let positive_abs_sugg = (
449
+ "manual implementation of `abs` method" ,
450
+ format!( "{}.abs()" , Sugg :: hir( cx, body, ".." ) ) ,
451
+ ) ;
452
+ let negative_abs_sugg = (
453
+ "manual implementation of negation of `abs` method" ,
454
+ format!( "-{}.abs()" , Sugg :: hir( cx, body, ".." ) ) ,
455
+ ) ;
456
+ let sugg = if is_testing_positive( cx, cond, body) {
457
+ if if_expr_positive {
458
+ positive_abs_sugg
459
+ } else {
460
+ negative_abs_sugg
461
+ }
462
+ } else if is_testing_negative( cx, cond, body) {
463
+ if if_expr_positive {
464
+ negative_abs_sugg
465
+ } else {
466
+ positive_abs_sugg
467
+ }
468
+ } else {
469
+ return ;
470
+ } ;
471
+ span_lint_and_sugg(
472
+ cx,
473
+ SUBOPTIMAL_FLOPS ,
474
+ expr. span,
475
+ sugg. 0 ,
476
+ "try" ,
477
+ sugg. 1 ,
478
+ Applicability :: MachineApplicable ,
479
+ ) ;
480
+ }
481
+ }
482
+ }
483
+
362
484
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for FloatingPointArithmetic {
363
485
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' _ > ) {
364
486
if let ExprKind :: MethodCall ( ref path, _, args) = & expr. kind {
@@ -375,6 +497,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
375
497
} else {
376
498
check_expm1 ( cx, expr) ;
377
499
check_mul_add ( cx, expr) ;
500
+ check_custom_abs ( cx, expr) ;
378
501
}
379
502
}
380
503
}
0 commit comments