11use crate :: consts:: {
22 constant, constant_simple, Constant ,
3- Constant :: { F32 , F64 } ,
3+ Constant :: { Int , F32 , F64 } ,
44} ;
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 } ;
66use if_chain:: if_chain;
77use rustc_errors:: Applicability ;
8- use rustc_hir:: { BinOpKind , Expr , ExprKind , UnOp } ;
8+ use rustc_hir:: { BinOpKind , Expr , ExprKind , PathSegment , UnOp } ;
99use rustc_lint:: { LateContext , LateLintPass } ;
1010use rustc_middle:: ty;
1111use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -293,6 +293,121 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
293293 }
294294}
295295
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+
296411// TODO: Lint expressions of the form `x.exp() - y` where y > 1
297412// and suggest usage of `x.exp_m1() - (y - 1)` instead
298413fn check_expm1 ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
@@ -344,6 +459,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
344459 rhs,
345460 ) = & expr. kind
346461 {
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+
347470 let ( recv, arg1, arg2) = if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, lhs) {
348471 ( inner_lhs, inner_rhs, rhs)
349472 } 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<'_>) {
479602 }
480603}
481604
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+
482699impl < ' tcx > LateLintPass < ' tcx > for FloatingPointArithmetic {
483700 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
484701 if let ExprKind :: MethodCall ( ref path, _, args, _) = & expr. kind {
@@ -489,13 +706,17 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
489706 "ln" => check_ln1p ( cx, expr, args) ,
490707 "log" => check_log_base ( cx, expr, args) ,
491708 "powf" => check_powf ( cx, expr, args) ,
709+ "powi" => check_powi ( cx, expr, args) ,
710+ "sqrt" => check_hypot ( cx, expr, args) ,
492711 _ => { } ,
493712 }
494713 }
495714 } else {
496715 check_expm1 ( cx, expr) ;
497716 check_mul_add ( cx, expr) ;
498717 check_custom_abs ( cx, expr) ;
718+ check_log_division ( cx, expr) ;
719+ check_radians ( cx, expr) ;
499720 }
500721 }
501722}
0 commit comments