1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use consts::{constant_simple, Constant};
use rustc::lint::*;
use rustc::hir::*;
use utils::span_help_and_lint;

/// **What it does:** Checks for `0.0 / 0.0`.
///
/// **Why is this bad?** It's less readable than `std::f32::NAN` or
/// `std::f64::NAN`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// 0.0f32 / 0.0
/// ```
declare_clippy_lint! {
    pub ZERO_DIVIDED_BY_ZERO,
    complexity,
    "usage of `0.0 / 0.0` to obtain NaN instead of std::f32::NaN or std::f64::NaN"
}

pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(ZERO_DIVIDED_BY_ZERO)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        // check for instances of 0.0/0.0
        if_chain! {
            if let ExprBinary(ref op, ref left, ref right) = expr.node;
            if let BinOp_::BiDiv = op.node;
            // TODO - constant_simple does not fold many operations involving floats.
            // That's probably fine for this lint - it's pretty unlikely that someone would
            // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
            if let Some(lhs_value) = constant_simple(cx, left);
            if let Some(rhs_value) = constant_simple(cx, right);
            if Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value;
            if Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value;
            then {
                // since we're about to suggest a use of std::f32::NaN or std::f64::NaN,
                // match the precision of the literals that are given.
                let float_type = match (lhs_value, rhs_value) {
                    (Constant::F64(_), _)
                    | (_, Constant::F64(_)) => "f64",
                    _ => "f32"
                };
                span_help_and_lint(
                    cx,
                    ZERO_DIVIDED_BY_ZERO,
                    expr.span,
                    "constant division of 0.0 with 0.0 will always result in NaN",
                    &format!(
                        "Consider using `std::{}::NAN` if you would like a constant representing NaN",
                        float_type,
                    ),
                );
            }
        }
    }
}