@@ -2,10 +2,10 @@ use crate::consts::{
22 constant, constant_simple, Constant ,
33 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} ;
@@ -298,6 +298,19 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
298298fn check_powi ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
299299 // Check argument
300300 if let Some ( ( value, _) ) = constant ( cx, cx. tables , & args[ 1 ] ) {
301+ // TODO: need more specific check. this is too wide. remember also to include tests
302+ if let Some ( parent) = get_parent_expr ( cx, expr) {
303+ if let Some ( grandparent) = get_parent_expr ( cx, parent) {
304+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args) = grandparent. kind {
305+ if method_name. as_str ( ) == "sqrt" {
306+ if let Some ( _) = detect_hypot ( cx, args) {
307+ return
308+ }
309+ }
310+ }
311+ }
312+ }
313+
301314 let ( lint, help, suggestion) = match value {
302315 Int ( 2 ) => (
303316 IMPRECISE_FLOPS ,
@@ -319,6 +332,50 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
319332 }
320333}
321334
335+ fn detect_hypot ( cx : & LateContext < ' _ , ' _ > , args : & [ Expr < ' _ > ] ) -> Option < String > {
336+ if let ExprKind :: Binary ( Spanned { node : BinOpKind :: Add , .. } , ref add_lhs, ref add_rhs) = args[ 0 ] . kind {
337+ // check if expression of the form x * x + y * y
338+ if_chain ! {
339+ if let ExprKind :: Binary ( Spanned { node: BinOpKind :: Mul , .. } , ref lmul_lhs, ref lmul_rhs) = add_lhs. kind;
340+ if let ExprKind :: Binary ( Spanned { node: BinOpKind :: Mul , .. } , ref rmul_lhs, ref rmul_rhs) = add_rhs. kind;
341+ if are_exprs_equal( cx, lmul_lhs, lmul_rhs) ;
342+ if are_exprs_equal( cx, rmul_lhs, rmul_rhs) ;
343+ then {
344+ return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & lmul_lhs, ".." ) , Sugg :: hir( cx, & rmul_lhs, ".." ) ) ) ;
345+ }
346+ }
347+
348+ // check if expression of the form x.powi(2) + y.powi(2)
349+ if_chain ! {
350+ if let ExprKind :: MethodCall ( PathSegment { ident: lmethod_name, .. } , ref _lspan, ref largs) = add_lhs. kind;
351+ if let ExprKind :: MethodCall ( PathSegment { ident: rmethod_name, .. } , ref _rspan, ref rargs) = add_rhs. kind;
352+ if lmethod_name. as_str( ) == "powi" && rmethod_name. as_str( ) == "powi" ;
353+ if let Some ( ( lvalue, _) ) = constant( cx, cx. tables, & largs[ 1 ] ) ;
354+ if let Some ( ( rvalue, _) ) = constant( cx, cx. tables, & rargs[ 1 ] ) ;
355+ if Int ( 2 ) == lvalue && Int ( 2 ) == rvalue;
356+ then {
357+ return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & largs[ 0 ] , ".." ) , Sugg :: hir( cx, & rargs[ 0 ] , ".." ) ) ) ;
358+ }
359+ }
360+ }
361+
362+ None
363+ }
364+
365+ fn check_hypot ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
366+ if let Some ( message) = detect_hypot ( cx, args) {
367+ span_lint_and_sugg (
368+ cx,
369+ IMPRECISE_FLOPS ,
370+ expr. span ,
371+ "hypotenuse can be computed more accurately" ,
372+ "consider using" ,
373+ message,
374+ Applicability :: MachineApplicable ,
375+ ) ;
376+ }
377+ }
378+
322379// TODO: Lint expressions of the form `x.exp() - y` where y > 1
323380// and suggest usage of `x.exp_m1() - (y - 1)` instead
324381fn check_expm1 ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) {
@@ -370,6 +427,16 @@ fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
370427 rhs,
371428 ) = & expr. kind
372429 {
430+ if let Some ( parent) = get_parent_expr ( cx, expr) {
431+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args) = parent. kind {
432+ if method_name. as_str ( ) == "sqrt" {
433+ if let Some ( _) = detect_hypot ( cx, args) {
434+ return ;
435+ }
436+ }
437+ }
438+ }
439+
373440 let ( recv, arg1, arg2) = if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, lhs) {
374441 ( inner_lhs, inner_rhs, rhs)
375442 } else if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, rhs) {
@@ -516,6 +583,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
516583 "log" => check_log_base ( cx, expr, args) ,
517584 "powf" => check_powf ( cx, expr, args) ,
518585 "powi" => check_powi ( cx, expr, args) ,
586+ "sqrt" => check_hypot ( cx, expr, args) ,
519587 _ => { } ,
520588 }
521589 }
0 commit comments