]> git.lizzy.rs Git - rust.git/blob - src/eq_op.rs
Remove * dep
[rust.git] / src / eq_op.rs
1 use rustc::lint::*;
2 use rustc_front::hir::*;
3 use rustc_front::util as ast_util;
4 use syntax::ptr::P;
5
6 use consts::constant;
7 use utils::span_lint;
8
9 /// **What it does:** This lint checks for equal operands to comparisons and bitwise binary operators (`&`, `|` and `^`). It is `Warn` by default.
10 ///
11 /// **Why is this bad?** This is usually just a typo.
12 ///
13 /// **Known problems:** False negatives: We had some false positives regarding calls (notably [racer](https://github.com/phildawes/racer) had one instance of `x.pop() && x.pop()`), so we removed matching any function or method calls. We may introduce a whitelist of known pure functions in the future.
14 ///
15 /// **Example:** `x + 1 == x + 1`
16 declare_lint! {
17     pub EQ_OP,
18     Warn,
19     "equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
20 }
21
22 #[derive(Copy,Clone)]
23 pub struct EqOp;
24
25 impl LintPass for EqOp {
26     fn get_lints(&self) -> LintArray {
27         lint_array!(EQ_OP)
28     }
29 }
30
31 impl LateLintPass for EqOp {
32     fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
33         if let ExprBinary(ref op, ref left, ref right) = e.node {
34             if is_cmp_or_bit(op) && is_exp_equal(cx, left, right) {
35                 span_lint(cx, EQ_OP, e.span, &format!(
36                     "equal expressions as operands to {}",
37                         ast_util::binop_to_string(op.node)));
38             }
39         }
40     }
41 }
42
43 pub fn is_exp_equal(cx: &LateContext, left : &Expr, right : &Expr) -> bool {
44     if let (Some(l), Some(r)) = (constant(cx, left), constant(cx, right)) {
45         if l == r {
46             return true;
47         }
48     }
49     match (&left.node, &right.node) {
50         (&ExprField(ref lfexp, ref lfident),
51                 &ExprField(ref rfexp, ref rfident)) =>
52             lfident.node == rfident.node && is_exp_equal(cx, lfexp, rfexp),
53         (&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node,
54         (&ExprPath(ref lqself, ref lsubpath),
55                 &ExprPath(ref rqself, ref rsubpath)) =>
56             both(lqself, rqself, is_qself_equal) &&
57                 is_path_equal(lsubpath, rsubpath),
58         (&ExprTup(ref ltup), &ExprTup(ref rtup)) =>
59             is_exps_equal(cx, ltup, rtup),
60         (&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r),
61         (&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) =>
62             is_exp_equal(cx, lx, rx) && is_cast_ty_equal(lt, rt),
63         _ => false
64     }
65 }
66
67 fn is_exps_equal(cx: &LateContext, left : &[P<Expr>], right : &[P<Expr>]) -> bool {
68     over(left, right, |l, r| is_exp_equal(cx, l, r))
69 }
70
71 fn is_path_equal(left : &Path, right : &Path) -> bool {
72     // The == of idents doesn't work with different contexts,
73     // we have to be explicit about hygiene
74     left.global == right.global && over(&left.segments, &right.segments,
75         |l, r| l.identifier.name == r.identifier.name
76                && l.parameters == r.parameters)
77 }
78
79 fn is_qself_equal(left : &QSelf, right : &QSelf) -> bool {
80     left.ty.node == right.ty.node && left.position == right.position
81 }
82
83 fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
84         where F: FnMut(&X, &X) -> bool {
85     left.len() == right.len() && left.iter().zip(right).all(|(x, y)|
86         eq_fn(x, y))
87 }
88
89 fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn : F) -> bool
90         where F: FnMut(&X, &X) -> bool {
91     l.as_ref().map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false,
92         |y| eq_fn(x, y)))
93 }
94
95 fn is_cmp_or_bit(op : &BinOp) -> bool {
96     match op.node {
97         BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr |
98         BiBitXor | BiBitAnd | BiBitOr => true,
99         _ => false
100     }
101 }
102
103 fn is_cast_ty_equal(left: &Ty, right: &Ty) -> bool {
104     match (&left.node, &right.node) {
105         (&TyVec(ref lvec), &TyVec(ref rvec)) => is_cast_ty_equal(lvec, rvec),
106         (&TyPtr(ref lmut), &TyPtr(ref rmut)) =>
107             lmut.mutbl == rmut.mutbl &&
108             is_cast_ty_equal(&*lmut.ty, &*rmut.ty),
109         (&TyRptr(_, ref lrmut), &TyRptr(_, ref rrmut)) =>
110             lrmut.mutbl == rrmut.mutbl &&
111             is_cast_ty_equal(&*lrmut.ty, &*rrmut.ty),
112         (&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) =>
113             both(lq, rq, is_qself_equal) && is_path_equal(lpath, rpath),
114         (&TyInfer, &TyInfer) => true,
115         _ => false
116     }
117 }