1
1
use crate :: consts:: {
2
2
constant, constant_simple, Constant ,
3
- Constant :: { F32 , F64 } ,
3
+ Constant :: { Int , F32 , F64 } ,
4
4
} ;
5
- use crate :: utils:: { higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq } ;
5
+ use crate :: utils:: { get_parent_expr , higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq } ;
6
6
use if_chain:: if_chain;
7
7
use rustc_errors:: Applicability ;
8
- use rustc_hir:: { BinOpKind , Expr , ExprKind , UnOp } ;
8
+ use rustc_hir:: { BinOpKind , Expr , ExprKind , PathSegment , UnOp } ;
9
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
10
use rustc_middle:: ty;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -293,6 +293,121 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
293
293
}
294
294
}
295
295
296
+ fn check_powi ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
297
+ if let Some ( ( value, _) ) = constant ( cx, cx. tables ( ) , & args[ 1 ] ) {
298
+ if value == Int ( 2 ) {
299
+ if let Some ( parent) = get_parent_expr ( cx, expr) {
300
+ if let Some ( grandparent) = get_parent_expr ( cx, parent) {
301
+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args, _) = grandparent. kind {
302
+ if method_name. as_str ( ) == "sqrt" && detect_hypot ( cx, args) . is_some ( ) {
303
+ return ;
304
+ }
305
+ }
306
+ }
307
+
308
+ if let ExprKind :: Binary (
309
+ Spanned {
310
+ node : BinOpKind :: Add , ..
311
+ } ,
312
+ ref lhs,
313
+ ref rhs,
314
+ ) = parent. kind
315
+ {
316
+ let other_addend = if lhs. hir_id == expr. hir_id { rhs } else { lhs } ;
317
+
318
+ span_lint_and_sugg (
319
+ cx,
320
+ SUBOPTIMAL_FLOPS ,
321
+ parent. span ,
322
+ "square can be computed more efficiently" ,
323
+ "consider using" ,
324
+ format ! (
325
+ "{}.mul_add({}, {})" ,
326
+ Sugg :: hir( cx, & args[ 0 ] , ".." ) ,
327
+ Sugg :: hir( cx, & args[ 0 ] , ".." ) ,
328
+ Sugg :: hir( cx, & other_addend, ".." ) ,
329
+ ) ,
330
+ Applicability :: MachineApplicable ,
331
+ ) ;
332
+
333
+ return ;
334
+ }
335
+ }
336
+
337
+ span_lint_and_sugg (
338
+ cx,
339
+ SUBOPTIMAL_FLOPS ,
340
+ expr. span ,
341
+ "square can be computed more efficiently" ,
342
+ "consider using" ,
343
+ format ! ( "{} * {}" , Sugg :: hir( cx, & args[ 0 ] , ".." ) , Sugg :: hir( cx, & args[ 0 ] , ".." ) ) ,
344
+ Applicability :: MachineApplicable ,
345
+ ) ;
346
+ }
347
+ }
348
+ }
349
+
350
+ fn detect_hypot ( cx : & LateContext < ' _ > , args : & [ Expr < ' _ > ] ) -> Option < String > {
351
+ if let ExprKind :: Binary (
352
+ Spanned {
353
+ node : BinOpKind :: Add , ..
354
+ } ,
355
+ ref add_lhs,
356
+ ref add_rhs,
357
+ ) = args[ 0 ] . kind
358
+ {
359
+ // check if expression of the form x * x + y * y
360
+ if_chain ! {
361
+ if let ExprKind :: Binary ( Spanned { node: BinOpKind :: Mul , .. } , ref lmul_lhs, ref lmul_rhs) = add_lhs. kind;
362
+ if let ExprKind :: Binary ( Spanned { node: BinOpKind :: Mul , .. } , ref rmul_lhs, ref rmul_rhs) = add_rhs. kind;
363
+ if are_exprs_equal( cx, lmul_lhs, lmul_rhs) ;
364
+ if are_exprs_equal( cx, rmul_lhs, rmul_rhs) ;
365
+ then {
366
+ return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & lmul_lhs, ".." ) , Sugg :: hir( cx, & rmul_lhs, ".." ) ) ) ;
367
+ }
368
+ }
369
+
370
+ // check if expression of the form x.powi(2) + y.powi(2)
371
+ if_chain ! {
372
+ if let ExprKind :: MethodCall (
373
+ PathSegment { ident: lmethod_name, .. } ,
374
+ ref _lspan,
375
+ ref largs,
376
+ _
377
+ ) = add_lhs. kind;
378
+ if let ExprKind :: MethodCall (
379
+ PathSegment { ident: rmethod_name, .. } ,
380
+ ref _rspan,
381
+ ref rargs,
382
+ _
383
+ ) = add_rhs. kind;
384
+ if lmethod_name. as_str( ) == "powi" && rmethod_name. as_str( ) == "powi" ;
385
+ if let Some ( ( lvalue, _) ) = constant( cx, cx. tables( ) , & largs[ 1 ] ) ;
386
+ if let Some ( ( rvalue, _) ) = constant( cx, cx. tables( ) , & rargs[ 1 ] ) ;
387
+ if Int ( 2 ) == lvalue && Int ( 2 ) == rvalue;
388
+ then {
389
+ return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & largs[ 0 ] , ".." ) , Sugg :: hir( cx, & rargs[ 0 ] , ".." ) ) ) ;
390
+ }
391
+ }
392
+ }
393
+
394
+ None
395
+ }
396
+
397
+ fn check_hypot ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
398
+ if let Some ( message) = detect_hypot ( cx, args) {
399
+ span_lint_and_sugg (
400
+ cx,
401
+ IMPRECISE_FLOPS ,
402
+ expr. span ,
403
+ "hypotenuse can be computed more accurately" ,
404
+ "consider using" ,
405
+ message,
406
+ Applicability :: MachineApplicable ,
407
+ ) ;
408
+ }
409
+ }
410
+
296
411
// TODO: Lint expressions of the form `x.exp() - y` where y > 1
297
412
// and suggest usage of `x.exp_m1() - (y - 1)` instead
298
413
fn check_expm1 ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
@@ -344,6 +459,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
344
459
rhs,
345
460
) = & expr. kind
346
461
{
462
+ if let Some ( parent) = get_parent_expr ( cx, expr) {
463
+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args, _) = parent. kind {
464
+ if method_name. as_str ( ) == "sqrt" && detect_hypot ( cx, args) . is_some ( ) {
465
+ return ;
466
+ }
467
+ }
468
+ }
469
+
347
470
let ( recv, arg1, arg2) = if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, lhs) {
348
471
( inner_lhs, inner_rhs, rhs)
349
472
} else if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, rhs) {
@@ -479,6 +602,100 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
479
602
}
480
603
}
481
604
605
+ fn are_same_base_logs ( cx : & LateContext < ' _ > , expr_a : & Expr < ' _ > , expr_b : & Expr < ' _ > ) -> bool {
606
+ if_chain ! {
607
+ if let ExprKind :: MethodCall ( PathSegment { ident: method_name_a, .. } , _, ref args_a, _) = expr_a. kind;
608
+ if let ExprKind :: MethodCall ( PathSegment { ident: method_name_b, .. } , _, ref args_b, _) = expr_b. kind;
609
+ then {
610
+ return method_name_a. as_str( ) == method_name_b. as_str( ) &&
611
+ args_a. len( ) == args_b. len( ) &&
612
+ (
613
+ [ "ln" , "log2" , "log10" ] . contains( &&* method_name_a. as_str( ) ) ||
614
+ method_name_a. as_str( ) == "log" && args_a. len( ) == 2 && are_exprs_equal( cx, & args_a[ 1 ] , & args_b[ 1 ] )
615
+ ) ;
616
+ }
617
+ }
618
+
619
+ false
620
+ }
621
+
622
+ fn check_log_division ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
623
+ // check if expression of the form x.logN() / y.logN()
624
+ if_chain ! {
625
+ if let ExprKind :: Binary (
626
+ Spanned {
627
+ node: BinOpKind :: Div , ..
628
+ } ,
629
+ lhs,
630
+ rhs,
631
+ ) = & expr. kind;
632
+ if are_same_base_logs( cx, lhs, rhs) ;
633
+ if let ExprKind :: MethodCall ( _, _, ref largs, _) = lhs. kind;
634
+ if let ExprKind :: MethodCall ( _, _, ref rargs, _) = rhs. kind;
635
+ then {
636
+ span_lint_and_sugg(
637
+ cx,
638
+ SUBOPTIMAL_FLOPS ,
639
+ expr. span,
640
+ "log base can be expressed more clearly" ,
641
+ "consider using" ,
642
+ format!( "{}.log({})" , Sugg :: hir( cx, & largs[ 0 ] , ".." ) , Sugg :: hir( cx, & rargs[ 0 ] , ".." ) , ) ,
643
+ Applicability :: MachineApplicable ,
644
+ ) ;
645
+ }
646
+ }
647
+ }
648
+
649
+ fn check_radians ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
650
+ if_chain ! {
651
+ if let ExprKind :: Binary (
652
+ Spanned {
653
+ node: BinOpKind :: Div , ..
654
+ } ,
655
+ div_lhs,
656
+ div_rhs,
657
+ ) = & expr. kind;
658
+ if let ExprKind :: Binary (
659
+ Spanned {
660
+ node: BinOpKind :: Mul , ..
661
+ } ,
662
+ mul_lhs,
663
+ mul_rhs,
664
+ ) = & div_lhs. kind;
665
+ if let Some ( ( rvalue, _) ) = constant( cx, cx. tables( ) , div_rhs) ;
666
+ if let Some ( ( lvalue, _) ) = constant( cx, cx. tables( ) , mul_rhs) ;
667
+ then {
668
+ // TODO: also check for constant values near PI/180 or 180/PI
669
+ if ( F32 ( f32_consts:: PI ) == rvalue || F64 ( f64_consts:: PI ) == rvalue) &&
670
+ ( F32 ( 180_f32 ) == lvalue || F64 ( 180_f64 ) == lvalue)
671
+ {
672
+ span_lint_and_sugg(
673
+ cx,
674
+ SUBOPTIMAL_FLOPS ,
675
+ expr. span,
676
+ "conversion to degrees can be done more accurately" ,
677
+ "consider using" ,
678
+ format!( "{}.to_degrees()" , Sugg :: hir( cx, & mul_lhs, ".." ) ) ,
679
+ Applicability :: MachineApplicable ,
680
+ ) ;
681
+ } else if
682
+ ( F32 ( 180_f32 ) == rvalue || F64 ( 180_f64 ) == rvalue) &&
683
+ ( F32 ( f32_consts:: PI ) == lvalue || F64 ( f64_consts:: PI ) == lvalue)
684
+ {
685
+ span_lint_and_sugg(
686
+ cx,
687
+ SUBOPTIMAL_FLOPS ,
688
+ expr. span,
689
+ "conversion to radians can be done more accurately" ,
690
+ "consider using" ,
691
+ format!( "{}.to_radians()" , Sugg :: hir( cx, & mul_lhs, ".." ) ) ,
692
+ Applicability :: MachineApplicable ,
693
+ ) ;
694
+ }
695
+ }
696
+ }
697
+ }
698
+
482
699
impl < ' tcx > LateLintPass < ' tcx > for FloatingPointArithmetic {
483
700
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
484
701
if let ExprKind :: MethodCall ( ref path, _, args, _) = & expr. kind {
@@ -489,13 +706,17 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
489
706
"ln" => check_ln1p ( cx, expr, args) ,
490
707
"log" => check_log_base ( cx, expr, args) ,
491
708
"powf" => check_powf ( cx, expr, args) ,
709
+ "powi" => check_powi ( cx, expr, args) ,
710
+ "sqrt" => check_hypot ( cx, expr, args) ,
492
711
_ => { } ,
493
712
}
494
713
}
495
714
} else {
496
715
check_expm1 ( cx, expr) ;
497
716
check_mul_add ( cx, expr) ;
498
717
check_custom_abs ( cx, expr) ;
718
+ check_log_division ( cx, expr) ;
719
+ check_radians ( cx, expr) ;
499
720
}
500
721
}
501
722
}
0 commit comments