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
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
use rustc::lint::*;
use rustc_front::hir::*;
use rustc_front::util as ast_util;
use syntax::ptr::P;

use consts::constant;
use utils::span_lint;

declare_lint! {
    pub EQ_OP,
    Warn,
    "equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
}

#[derive(Copy,Clone)]
pub struct EqOp;

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

impl LateLintPass for EqOp {
    fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
        if let ExprBinary(ref op, ref left, ref right) = e.node {
            if is_cmp_or_bit(op) && is_exp_equal(cx, left, right) {
                span_lint(cx, EQ_OP, e.span, &format!(
                    "equal expressions as operands to {}",
                        ast_util::binop_to_string(op.node)));
            }
        }
    }
}

pub fn is_exp_equal(cx: &LateContext, left : &Expr, right : &Expr) -> bool {
    if let (Some(l), Some(r)) = (constant(cx, left), constant(cx, right)) {
        if l == r {
            return true;
        }
    }
    match (&left.node, &right.node) {
        (&ExprField(ref lfexp, ref lfident),
                &ExprField(ref rfexp, ref rfident)) =>
            lfident.node == rfident.node && is_exp_equal(cx, lfexp, rfexp),
        (&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node,
        (&ExprPath(ref lqself, ref lsubpath),
                &ExprPath(ref rqself, ref rsubpath)) =>
            both(lqself, rqself, is_qself_equal) &&
                is_path_equal(lsubpath, rsubpath),
        (&ExprTup(ref ltup), &ExprTup(ref rtup)) =>
            is_exps_equal(cx, ltup, rtup),
        (&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r),
        (&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) =>
            is_exp_equal(cx, lx, rx) && is_cast_ty_equal(lt, rt),
        _ => false
    }
}

fn is_exps_equal(cx: &LateContext, left : &[P<Expr>], right : &[P<Expr>]) -> bool {
    over(left, right, |l, r| is_exp_equal(cx, l, r))
}

fn is_path_equal(left : &Path, right : &Path) -> bool {
    // The == of idents doesn't work with different contexts,
    // we have to be explicit about hygiene
    left.global == right.global && over(&left.segments, &right.segments,
        |l, r| l.identifier.name == r.identifier.name
              && l.identifier.ctxt == r.identifier.ctxt
               && l.parameters == r.parameters)
}

fn is_qself_equal(left : &QSelf, right : &QSelf) -> bool {
    left.ty.node == right.ty.node && left.position == right.position
}

fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
        where F: FnMut(&X, &X) -> bool {
    left.len() == right.len() && left.iter().zip(right).all(|(x, y)|
        eq_fn(x, y))
}

fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn : F) -> bool
        where F: FnMut(&X, &X) -> bool {
    l.as_ref().map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false,
        |y| eq_fn(x, y)))
}

fn is_cmp_or_bit(op : &BinOp) -> bool {
    match op.node {
        BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr |
        BiBitXor | BiBitAnd | BiBitOr => true,
        _ => false
    }
}

fn is_cast_ty_equal(left: &Ty, right: &Ty) -> bool {
    match (&left.node, &right.node) {
        (&TyVec(ref lvec), &TyVec(ref rvec)) => is_cast_ty_equal(lvec, rvec),
        (&TyPtr(ref lmut), &TyPtr(ref rmut)) =>
            lmut.mutbl == rmut.mutbl &&
            is_cast_ty_equal(&*lmut.ty, &*rmut.ty),
        (&TyRptr(_, ref lrmut), &TyRptr(_, ref rrmut)) =>
            lrmut.mutbl == rrmut.mutbl &&
            is_cast_ty_equal(&*lrmut.ty, &*rrmut.ty),
        (&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) =>
            both(lq, rq, is_qself_equal) && is_path_equal(lpath, rpath),
        (&TyParen(ref lty), &TyParen(ref rty)) => is_cast_ty_equal(lty, rty),
        (&TyInfer, &TyInfer) => true,
        _ => false
    }
}