@@ -2,10 +2,10 @@ use crate::consts::{
2
2
constant, constant_simple, Constant ,
3
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} ;
@@ -298,6 +298,19 @@ fn check_powf(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
298
298
fn check_powi ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
299
299
// Check argument
300
300
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 detect_hypot ( cx, args) . is_some ( ) {
307
+ return ;
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+
301
314
let ( lint, help, suggestion) = match value {
302
315
Int ( 2 ) => (
303
316
IMPRECISE_FLOPS ,
@@ -319,6 +332,57 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
319
332
}
320
333
}
321
334
335
+ fn detect_hypot ( cx : & LateContext < ' _ , ' _ > , args : & [ Expr < ' _ > ] ) -> Option < String > {
336
+ if let ExprKind :: Binary (
337
+ Spanned {
338
+ node : BinOpKind :: Add , ..
339
+ } ,
340
+ ref add_lhs,
341
+ ref add_rhs,
342
+ ) = args[ 0 ] . kind
343
+ {
344
+ // check if expression of the form x * x + y * y
345
+ if_chain ! {
346
+ if let ExprKind :: Binary ( Spanned { node: BinOpKind :: Mul , .. } , ref lmul_lhs, ref lmul_rhs) = add_lhs. kind;
347
+ if let ExprKind :: Binary ( Spanned { node: BinOpKind :: Mul , .. } , ref rmul_lhs, ref rmul_rhs) = add_rhs. kind;
348
+ if are_exprs_equal( cx, lmul_lhs, lmul_rhs) ;
349
+ if are_exprs_equal( cx, rmul_lhs, rmul_rhs) ;
350
+ then {
351
+ return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & lmul_lhs, ".." ) , Sugg :: hir( cx, & rmul_lhs, ".." ) ) ) ;
352
+ }
353
+ }
354
+
355
+ // check if expression of the form x.powi(2) + y.powi(2)
356
+ if_chain ! {
357
+ if let ExprKind :: MethodCall ( PathSegment { ident: lmethod_name, .. } , ref _lspan, ref largs) = add_lhs. kind;
358
+ if let ExprKind :: MethodCall ( PathSegment { ident: rmethod_name, .. } , ref _rspan, ref rargs) = add_rhs. kind;
359
+ if lmethod_name. as_str( ) == "powi" && rmethod_name. as_str( ) == "powi" ;
360
+ if let Some ( ( lvalue, _) ) = constant( cx, cx. tables, & largs[ 1 ] ) ;
361
+ if let Some ( ( rvalue, _) ) = constant( cx, cx. tables, & rargs[ 1 ] ) ;
362
+ if Int ( 2 ) == lvalue && Int ( 2 ) == rvalue;
363
+ then {
364
+ return Some ( format!( "{}.hypot({})" , Sugg :: hir( cx, & largs[ 0 ] , ".." ) , Sugg :: hir( cx, & rargs[ 0 ] , ".." ) ) ) ;
365
+ }
366
+ }
367
+ }
368
+
369
+ None
370
+ }
371
+
372
+ fn check_hypot ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
373
+ if let Some ( message) = detect_hypot ( cx, args) {
374
+ span_lint_and_sugg (
375
+ cx,
376
+ IMPRECISE_FLOPS ,
377
+ expr. span ,
378
+ "hypotenuse can be computed more accurately" ,
379
+ "consider using" ,
380
+ message,
381
+ Applicability :: MachineApplicable ,
382
+ ) ;
383
+ }
384
+ }
385
+
322
386
// TODO: Lint expressions of the form `x.exp() - y` where y > 1
323
387
// and suggest usage of `x.exp_m1() - (y - 1)` instead
324
388
fn check_expm1 ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) {
@@ -370,6 +434,16 @@ fn check_mul_add(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
370
434
rhs,
371
435
) = & expr. kind
372
436
{
437
+ if let Some ( parent) = get_parent_expr ( cx, expr) {
438
+ if let ExprKind :: MethodCall ( PathSegment { ident : method_name, .. } , _, args) = parent. kind {
439
+ if method_name. as_str ( ) == "sqrt" {
440
+ if detect_hypot ( cx, args) . is_some ( ) {
441
+ return ;
442
+ }
443
+ }
444
+ }
445
+ }
446
+
373
447
let ( recv, arg1, arg2) = if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, lhs) {
374
448
( inner_lhs, inner_rhs, rhs)
375
449
} else if let Some ( ( inner_lhs, inner_rhs) ) = is_float_mul_expr ( cx, rhs) {
@@ -516,6 +590,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatingPointArithmetic {
516
590
"log" => check_log_base ( cx, expr, args) ,
517
591
"powf" => check_powf ( cx, expr, args) ,
518
592
"powi" => check_powi ( cx, expr, args) ,
593
+ "sqrt" => check_hypot ( cx, expr, args) ,
519
594
_ => { } ,
520
595
}
521
596
}
0 commit comments