]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/double_comparison.rs
Change Hash{Map, Set} to FxHash{Map, Set}
[rust.git] / clippy_lints / src / double_comparison.rs
1 //! Lint on unnecessary double comparisons. Some examples:
2
3 use rustc::hir::*;
4 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5 use rustc::{declare_tool_lint, lint_array};
6 use syntax::source_map::Span;
7
8 use crate::utils::{snippet, span_lint_and_sugg, SpanlessEq};
9
10 /// **What it does:** Checks for double comparions that could be simpified to a single expression.
11 ///
12 ///
13 /// **Why is this bad?** Readability.
14 ///
15 /// **Known problems:** None.
16 ///
17 /// **Example:**
18 /// ```rust
19 /// x == y || x < y
20 /// ```
21 ///
22 /// Could be written as:
23 ///
24 /// ```rust
25 /// x <= y
26 /// ```
27 declare_clippy_lint! {
28     pub DOUBLE_COMPARISONS,
29     complexity,
30     "unnecessary double comparisons that can be simplified"
31 }
32
33 pub struct DoubleComparisonPass;
34
35 impl LintPass for DoubleComparisonPass {
36     fn get_lints(&self) -> LintArray {
37         lint_array!(DOUBLE_COMPARISONS)
38     }
39 }
40
41 impl<'a, 'tcx> DoubleComparisonPass {
42     fn check_binop(
43         &self,
44         cx: &LateContext<'a, 'tcx>,
45         op: BinOpKind,
46         lhs: &'tcx Expr,
47         rhs: &'tcx Expr,
48         span: Span,
49     ) {
50         let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (lhs.node.clone(), rhs.node.clone()) {
51             (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
52                 (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
53             }
54             _ => return,
55         };
56         let mut spanless_eq = SpanlessEq::new(cx).ignore_fn();
57         if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) {
58             return;
59         }
60         macro_rules! lint_double_comparison {
61             ($op:tt) => {{
62                 let lhs_str = snippet(cx, llhs.span, "");
63                 let rhs_str = snippet(cx, lrhs.span, "");
64                 let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
65                 span_lint_and_sugg(cx, DOUBLE_COMPARISONS, span,
66                                    "This binary expression can be simplified",
67                                    "try", sugg);
68             }}
69         }
70         match (op, lkind, rkind) {
71             (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => lint_double_comparison!(<=),
72             (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => lint_double_comparison!(>=),
73             (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => lint_double_comparison!(!=),
74             (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => lint_double_comparison!(==),
75             _ => (),
76         };
77     }
78 }
79
80 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DoubleComparisonPass {
81     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
82         if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.node {
83             self.check_binop(cx, kind.node, lhs, rhs, expr.span);
84         }
85     }
86 }