]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
Auto merge of #85344 - cbeuw:remap-across-cwd, r=michaelwoerister
[rust.git] / src / tools / clippy / clippy_lints / src / floating_point_arithmetic.rs
1 use clippy_utils::consts::{
2     constant, constant_simple, Constant,
3     Constant::{Int, F32, F64},
4 };
5 use clippy_utils::diagnostics::span_lint_and_sugg;
6 use clippy_utils::higher;
7 use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
8 use if_chain::if_chain;
9 use rustc_errors::Applicability;
10 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
11 use rustc_lint::{LateContext, LateLintPass};
12 use rustc_middle::ty;
13 use rustc_session::{declare_lint_pass, declare_tool_lint};
14 use rustc_span::source_map::Spanned;
15
16 use rustc_ast::ast;
17 use std::f32::consts as f32_consts;
18 use std::f64::consts as f64_consts;
19 use sugg::Sugg;
20
21 declare_clippy_lint! {
22     /// ### What it does
23     /// Looks for floating-point expressions that
24     /// can be expressed using built-in methods to improve accuracy
25     /// at the cost of performance.
26     ///
27     /// ### Why is this bad?
28     /// Negatively impacts accuracy.
29     ///
30     /// ### Example
31     /// ```rust
32     /// let a = 3f32;
33     /// let _ = a.powf(1.0 / 3.0);
34     /// let _ = (1.0 + a).ln();
35     /// let _ = a.exp() - 1.0;
36     /// ```
37     ///
38     /// is better expressed as
39     ///
40     /// ```rust
41     /// let a = 3f32;
42     /// let _ = a.cbrt();
43     /// let _ = a.ln_1p();
44     /// let _ = a.exp_m1();
45     /// ```
46     pub IMPRECISE_FLOPS,
47     nursery,
48     "usage of imprecise floating point operations"
49 }
50
51 declare_clippy_lint! {
52     /// ### What it does
53     /// Looks for floating-point expressions that
54     /// can be expressed using built-in methods to improve both
55     /// accuracy and performance.
56     ///
57     /// ### Why is this bad?
58     /// Negatively impacts accuracy and performance.
59     ///
60     /// ### Example
61     /// ```rust
62     /// use std::f32::consts::E;
63     ///
64     /// let a = 3f32;
65     /// let _ = (2f32).powf(a);
66     /// let _ = E.powf(a);
67     /// let _ = a.powf(1.0 / 2.0);
68     /// let _ = a.log(2.0);
69     /// let _ = a.log(10.0);
70     /// let _ = a.log(E);
71     /// let _ = a.powf(2.0);
72     /// let _ = a * 2.0 + 4.0;
73     /// let _ = if a < 0.0 {
74     ///     -a
75     /// } else {
76     ///     a
77     /// };
78     /// let _ = if a < 0.0 {
79     ///     a
80     /// } else {
81     ///     -a
82     /// };
83     /// ```
84     ///
85     /// is better expressed as
86     ///
87     /// ```rust
88     /// use std::f32::consts::E;
89     ///
90     /// let a = 3f32;
91     /// let _ = a.exp2();
92     /// let _ = a.exp();
93     /// let _ = a.sqrt();
94     /// let _ = a.log2();
95     /// let _ = a.log10();
96     /// let _ = a.ln();
97     /// let _ = a.powi(2);
98     /// let _ = a.mul_add(2.0, 4.0);
99     /// let _ = a.abs();
100     /// let _ = -a.abs();
101     /// ```
102     pub SUBOPTIMAL_FLOPS,
103     nursery,
104     "usage of sub-optimal floating point operations"
105 }
106
107 declare_lint_pass!(FloatingPointArithmetic => [
108     IMPRECISE_FLOPS,
109     SUBOPTIMAL_FLOPS
110 ]);
111
112 // Returns the specialized log method for a given base if base is constant
113 // and is one of 2, 10 and e
114 fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
115     if let Some((value, _)) = constant(cx, cx.typeck_results(), base) {
116         if F32(2.0) == value || F64(2.0) == value {
117             return Some("log2");
118         } else if F32(10.0) == value || F64(10.0) == value {
119             return Some("log10");
120         } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
121             return Some("ln");
122         }
123     }
124
125     None
126 }
127
128 // Adds type suffixes and parenthesis to method receivers if necessary
129 fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
130     let mut suggestion = Sugg::hir(cx, expr, "..");
131
132     if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
133         expr = inner_expr;
134     }
135
136     if_chain! {
137         // if the expression is a float literal and it is unsuffixed then
138         // add a suffix so the suggestion is valid and unambiguous
139         if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
140         if let ExprKind::Lit(lit) = &expr.kind;
141         if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
142         then {
143             let op = format!(
144                 "{}{}{}",
145                 suggestion,
146                 // Check for float literals without numbers following the decimal
147                 // separator such as `2.` and adds a trailing zero
148                 if sym.as_str().ends_with('.') {
149                     "0"
150                 } else {
151                     ""
152                 },
153                 float_ty.name_str()
154             ).into();
155
156             suggestion = match suggestion {
157                 Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
158                 _ => Sugg::NonParen(op)
159             };
160         }
161     }
162
163     suggestion.maybe_par()
164 }
165
166 fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
167     if let Some(method) = get_specialized_log_method(cx, &args[1]) {
168         span_lint_and_sugg(
169             cx,
170             SUBOPTIMAL_FLOPS,
171             expr.span,
172             "logarithm for bases 2, 10 and e can be computed more accurately",
173             "consider using",
174             format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
175             Applicability::MachineApplicable,
176         );
177     }
178 }
179
180 // TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
181 // suggest usage of `(x + (y - 1)).ln_1p()` instead
182 fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
183     if let ExprKind::Binary(
184         Spanned {
185             node: BinOpKind::Add, ..
186         },
187         lhs,
188         rhs,
189     ) = &args[0].kind
190     {
191         let recv = match (
192             constant(cx, cx.typeck_results(), lhs),
193             constant(cx, cx.typeck_results(), rhs),
194         ) {
195             (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
196             (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
197             _ => return,
198         };
199
200         span_lint_and_sugg(
201             cx,
202             IMPRECISE_FLOPS,
203             expr.span,
204             "ln(1 + x) can be computed more accurately",
205             "consider using",
206             format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
207             Applicability::MachineApplicable,
208         );
209     }
210 }
211
212 // Returns an integer if the float constant is a whole number and it can be
213 // converted to an integer without loss of precision. For now we only check
214 // ranges [-16777215, 16777216) for type f32 as whole number floats outside
215 // this range are lossy and ambiguous.
216 #[allow(clippy::cast_possible_truncation)]
217 fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
218     match value {
219         F32(num) if num.fract() == 0.0 => {
220             if (-16_777_215.0..16_777_216.0).contains(num) {
221                 Some(num.round() as i32)
222             } else {
223                 None
224             }
225         },
226         F64(num) if num.fract() == 0.0 => {
227             if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
228                 Some(num.round() as i32)
229             } else {
230                 None
231             }
232         },
233         _ => None,
234     }
235 }
236
237 fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
238     // Check receiver
239     if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
240         let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
241             "exp"
242         } else if F32(2.0) == value || F64(2.0) == value {
243             "exp2"
244         } else {
245             return;
246         };
247
248         span_lint_and_sugg(
249             cx,
250             SUBOPTIMAL_FLOPS,
251             expr.span,
252             "exponent for bases 2 and e can be computed more accurately",
253             "consider using",
254             format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
255             Applicability::MachineApplicable,
256         );
257     }
258
259     // Check argument
260     if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
261         let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
262             (
263                 SUBOPTIMAL_FLOPS,
264                 "square-root of a number can be computed more efficiently and accurately",
265                 format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
266             )
267         } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
268             (
269                 IMPRECISE_FLOPS,
270                 "cube-root of a number can be computed more accurately",
271                 format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
272             )
273         } else if let Some(exponent) = get_integer_from_float_constant(&value) {
274             (
275                 SUBOPTIMAL_FLOPS,
276                 "exponentiation with integer powers can be computed more efficiently",
277                 format!(
278                     "{}.powi({})",
279                     Sugg::hir(cx, &args[0], ".."),
280                     numeric_literal::format(&exponent.to_string(), None, false)
281                 ),
282             )
283         } else {
284             return;
285         };
286
287         span_lint_and_sugg(
288             cx,
289             lint,
290             expr.span,
291             help,
292             "consider using",
293             suggestion,
294             Applicability::MachineApplicable,
295         );
296     }
297 }
298
299 fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
300     if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
301         if value == Int(2) {
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" && detect_hypot(cx, args).is_some() {
306                             return;
307                         }
308                     }
309                 }
310
311                 if let ExprKind::Binary(
312                     Spanned {
313                         node: BinOpKind::Add, ..
314                     },
315                     lhs,
316                     rhs,
317                 ) = parent.kind
318                 {
319                     let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
320
321                     span_lint_and_sugg(
322                         cx,
323                         SUBOPTIMAL_FLOPS,
324                         parent.span,
325                         "multiply and add expressions can be calculated more efficiently and accurately",
326                         "consider using",
327                         format!(
328                             "{}.mul_add({}, {})",
329                             Sugg::hir(cx, &args[0], ".."),
330                             Sugg::hir(cx, &args[0], ".."),
331                             Sugg::hir(cx, other_addend, ".."),
332                         ),
333                         Applicability::MachineApplicable,
334                     );
335
336                     return;
337                 }
338             }
339         }
340     }
341 }
342
343 fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
344     if let ExprKind::Binary(
345         Spanned {
346             node: BinOpKind::Add, ..
347         },
348         add_lhs,
349         add_rhs,
350     ) = args[0].kind
351     {
352         // check if expression of the form x * x + y * y
353         if_chain! {
354             if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind;
355             if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind;
356             if eq_expr_value(cx, lmul_lhs, lmul_rhs);
357             if eq_expr_value(cx, rmul_lhs, rmul_rhs);
358             then {
359                 return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, "..")));
360             }
361         }
362
363         // check if expression of the form x.powi(2) + y.powi(2)
364         if_chain! {
365             if let ExprKind::MethodCall(
366                 PathSegment { ident: lmethod_name, .. },
367                 ref _lspan,
368                 largs,
369                 _
370             ) = add_lhs.kind;
371             if let ExprKind::MethodCall(
372                 PathSegment { ident: rmethod_name, .. },
373                 ref _rspan,
374                 rargs,
375                 _
376             ) = add_rhs.kind;
377             if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
378             if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), &largs[1]);
379             if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), &rargs[1]);
380             if Int(2) == lvalue && Int(2) == rvalue;
381             then {
382                 return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], "..")));
383             }
384         }
385     }
386
387     None
388 }
389
390 fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
391     if let Some(message) = detect_hypot(cx, args) {
392         span_lint_and_sugg(
393             cx,
394             IMPRECISE_FLOPS,
395             expr.span,
396             "hypotenuse can be computed more accurately",
397             "consider using",
398             message,
399             Applicability::MachineApplicable,
400         );
401     }
402 }
403
404 // TODO: Lint expressions of the form `x.exp() - y` where y > 1
405 // and suggest usage of `x.exp_m1() - (y - 1)` instead
406 fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
407     if_chain! {
408         if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
409         if cx.typeck_results().expr_ty(lhs).is_floating_point();
410         if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
411         if F32(1.0) == value || F64(1.0) == value;
412         if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind;
413         if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point();
414         if path.ident.name.as_str() == "exp";
415         then {
416             span_lint_and_sugg(
417                 cx,
418                 IMPRECISE_FLOPS,
419                 expr.span,
420                 "(e.pow(x) - 1) can be computed more accurately",
421                 "consider using",
422                 format!(
423                     "{}.exp_m1()",
424                     Sugg::hir(cx, &method_args[0], "..")
425                 ),
426                 Applicability::MachineApplicable,
427             );
428         }
429     }
430 }
431
432 fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
433     if_chain! {
434         if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind;
435         if cx.typeck_results().expr_ty(lhs).is_floating_point();
436         if cx.typeck_results().expr_ty(rhs).is_floating_point();
437         then {
438             return Some((lhs, rhs));
439         }
440     }
441
442     None
443 }
444
445 // TODO: Fix rust-lang/rust-clippy#4735
446 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
447     if let ExprKind::Binary(
448         Spanned {
449             node: BinOpKind::Add, ..
450         },
451         lhs,
452         rhs,
453     ) = &expr.kind
454     {
455         if let Some(parent) = get_parent_expr(cx, expr) {
456             if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind {
457                 if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
458                     return;
459                 }
460             }
461         }
462
463         let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
464             (inner_lhs, inner_rhs, rhs)
465         } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
466             (inner_lhs, inner_rhs, lhs)
467         } else {
468             return;
469         };
470
471         span_lint_and_sugg(
472             cx,
473             SUBOPTIMAL_FLOPS,
474             expr.span,
475             "multiply and add expressions can be calculated more efficiently and accurately",
476             "consider using",
477             format!(
478                 "{}.mul_add({}, {})",
479                 prepare_receiver_sugg(cx, recv),
480                 Sugg::hir(cx, arg1, ".."),
481                 Sugg::hir(cx, arg2, ".."),
482             ),
483             Applicability::MachineApplicable,
484         );
485     }
486 }
487
488 /// Returns true iff expr is an expression which tests whether or not
489 /// test is positive or an expression which tests whether or not test
490 /// is nonnegative.
491 /// Used for check-custom-abs function below
492 fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
493     if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
494         match op {
495             BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
496             BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
497             _ => false,
498         }
499     } else {
500         false
501     }
502 }
503
504 /// See [`is_testing_positive`]
505 fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
506     if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
507         match op {
508             BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
509             BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
510             _ => false,
511         }
512     } else {
513         false
514     }
515 }
516
517 /// Returns true iff expr is some zero literal
518 fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
519     match constant_simple(cx, cx.typeck_results(), expr) {
520         Some(Constant::Int(i)) => i == 0,
521         Some(Constant::F32(f)) => f == 0.0,
522         Some(Constant::F64(f)) => f == 0.0,
523         _ => false,
524     }
525 }
526
527 /// If the two expressions are negations of each other, then it returns
528 /// a tuple, in which the first element is true iff expr1 is the
529 /// positive expressions, and the second element is the positive
530 /// one of the two expressions
531 /// If the two expressions are not negations of each other, then it
532 /// returns None.
533 fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
534     if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
535         if eq_expr_value(cx, expr1_negated, expr2) {
536             return Some((false, expr2));
537         }
538     }
539     if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
540         if eq_expr_value(cx, expr1, expr2_negated) {
541             return Some((true, expr1));
542         }
543     }
544     None
545 }
546
547 fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
548     if_chain! {
549         if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
550         if let ExprKind::Block(block, _) = then.kind;
551         if block.stmts.is_empty();
552         if let Some(if_body_expr) = block.expr;
553         if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind);
554         if else_block.stmts.is_empty();
555         if let Some(else_body_expr) = else_block.expr;
556         if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
557         then {
558             let positive_abs_sugg = (
559                 "manual implementation of `abs` method",
560                 format!("{}.abs()", Sugg::hir(cx, body, "..")),
561             );
562             let negative_abs_sugg = (
563                 "manual implementation of negation of `abs` method",
564                 format!("-{}.abs()", Sugg::hir(cx, body, "..")),
565             );
566             let sugg = if is_testing_positive(cx, cond, body) {
567                 if if_expr_positive {
568                     positive_abs_sugg
569                 } else {
570                     negative_abs_sugg
571                 }
572             } else if is_testing_negative(cx, cond, body) {
573                 if if_expr_positive {
574                     negative_abs_sugg
575                 } else {
576                     positive_abs_sugg
577                 }
578             } else {
579                 return;
580             };
581             span_lint_and_sugg(
582                 cx,
583                 SUBOPTIMAL_FLOPS,
584                 expr.span,
585                 sugg.0,
586                 "try",
587                 sugg.1,
588                 Applicability::MachineApplicable,
589             );
590         }
591     }
592 }
593
594 fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
595     if_chain! {
596         if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind;
597         if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind;
598         then {
599             return method_name_a.as_str() == method_name_b.as_str() &&
600                 args_a.len() == args_b.len() &&
601                 (
602                     ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
603                     method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
604                 );
605         }
606     }
607
608     false
609 }
610
611 fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
612     // check if expression of the form x.logN() / y.logN()
613     if_chain! {
614         if let ExprKind::Binary(
615             Spanned {
616                 node: BinOpKind::Div, ..
617             },
618             lhs,
619             rhs,
620         ) = &expr.kind;
621         if are_same_base_logs(cx, lhs, rhs);
622         if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind;
623         if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind;
624         then {
625             span_lint_and_sugg(
626                 cx,
627                 SUBOPTIMAL_FLOPS,
628                 expr.span,
629                 "log base can be expressed more clearly",
630                 "consider using",
631                 format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),),
632                 Applicability::MachineApplicable,
633             );
634         }
635     }
636 }
637
638 fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
639     if_chain! {
640         if let ExprKind::Binary(
641             Spanned {
642                 node: BinOpKind::Div, ..
643             },
644             div_lhs,
645             div_rhs,
646         ) = &expr.kind;
647         if let ExprKind::Binary(
648             Spanned {
649                 node: BinOpKind::Mul, ..
650             },
651             mul_lhs,
652             mul_rhs,
653         ) = &div_lhs.kind;
654         if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
655         if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
656         then {
657             // TODO: also check for constant values near PI/180 or 180/PI
658             if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
659                (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
660             {
661                 span_lint_and_sugg(
662                     cx,
663                     SUBOPTIMAL_FLOPS,
664                     expr.span,
665                     "conversion to degrees can be done more accurately",
666                     "consider using",
667                     format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")),
668                     Applicability::MachineApplicable,
669                 );
670             } else if
671                 (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
672                 (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
673             {
674                 span_lint_and_sugg(
675                     cx,
676                     SUBOPTIMAL_FLOPS,
677                     expr.span,
678                     "conversion to radians can be done more accurately",
679                     "consider using",
680                     format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")),
681                     Applicability::MachineApplicable,
682                 );
683             }
684         }
685     }
686 }
687
688 impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
689     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
690         if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
691             let recv_ty = cx.typeck_results().expr_ty(&args[0]);
692
693             if recv_ty.is_floating_point() {
694                 match &*path.ident.name.as_str() {
695                     "ln" => check_ln1p(cx, expr, args),
696                     "log" => check_log_base(cx, expr, args),
697                     "powf" => check_powf(cx, expr, args),
698                     "powi" => check_powi(cx, expr, args),
699                     "sqrt" => check_hypot(cx, expr, args),
700                     _ => {},
701                 }
702             }
703         } else {
704             check_expm1(cx, expr);
705             check_mul_add(cx, expr);
706             check_custom_abs(cx, expr);
707             check_log_division(cx, expr);
708             check_radians(cx, expr);
709         }
710     }
711 }