]>
Commit | Line | Data |
---|---|---|
17df50a5 | 1 | use clippy_utils::consts::{constant_simple, Constant}; |
cdc7bbd5 | 2 | use clippy_utils::diagnostics::span_lint_and_help; |
f20569fa XL |
3 | use if_chain::if_chain; |
4 | use rustc_hir::{BinOpKind, Expr, ExprKind}; | |
5 | use rustc_lint::{LateContext, LateLintPass}; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | ||
8 | declare_clippy_lint! { | |
94222f64 XL |
9 | /// ### What it does |
10 | /// Checks for `0.0 / 0.0`. | |
f20569fa | 11 | /// |
94222f64 XL |
12 | /// ### Why is this bad? |
13 | /// It's less readable than `f32::NAN` or `f64::NAN`. | |
f20569fa | 14 | /// |
94222f64 | 15 | /// ### Example |
f20569fa XL |
16 | /// ```rust |
17 | /// // Bad | |
18 | /// let nan = 0.0f32 / 0.0; | |
19 | /// | |
20 | /// // Good | |
21 | /// let nan = f32::NAN; | |
22 | /// ``` | |
a2a8927a | 23 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
24 | pub ZERO_DIVIDED_BY_ZERO, |
25 | complexity, | |
26 | "usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`" | |
27 | } | |
28 | ||
29 | declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]); | |
30 | ||
31 | impl<'tcx> LateLintPass<'tcx> for ZeroDiv { | |
32 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
33 | // check for instances of 0.0/0.0 | |
34 | if_chain! { | |
cdc7bbd5 | 35 | if let ExprKind::Binary(ref op, left, right) = expr.kind; |
c295e0f8 | 36 | if op.node == BinOpKind::Div; |
f20569fa XL |
37 | // TODO - constant_simple does not fold many operations involving floats. |
38 | // That's probably fine for this lint - it's pretty unlikely that someone would | |
39 | // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too. | |
40 | if let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left); | |
41 | if let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right); | |
42 | if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value; | |
43 | if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value; | |
44 | then { | |
45 | // since we're about to suggest a use of f32::NAN or f64::NAN, | |
46 | // match the precision of the literals that are given. | |
47 | let float_type = match (lhs_value, rhs_value) { | |
48 | (Constant::F64(_), _) | |
49 | | (_, Constant::F64(_)) => "f64", | |
50 | _ => "f32" | |
51 | }; | |
52 | span_lint_and_help( | |
53 | cx, | |
54 | ZERO_DIVIDED_BY_ZERO, | |
55 | expr.span, | |
56 | "constant division of `0.0` with `0.0` will always result in NaN", | |
57 | None, | |
58 | &format!( | |
59 | "consider using `{}::NAN` if you would like a constant representing NaN", | |
60 | float_type, | |
61 | ), | |
62 | ); | |
63 | } | |
64 | } | |
65 | } | |
66 | } |