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