]> git.lizzy.rs Git - rust.git/blob - src/eq_op.rs
Merge pull request #160 from Manishearth/dogfood
[rust.git] / src / eq_op.rs
1 #![allow(redundant_closure)] // FIXME (#116)
2 use rustc::lint::*;
3 use syntax::ast::*;
4 use syntax::ast_util as ast_util;
5 use syntax::ptr::P;
6 use syntax::codemap as code;
7 use utils::span_lint;
8
9 declare_lint! {
10     pub EQ_OP,
11     Warn,
12     "equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
13 }
14
15 #[derive(Copy,Clone)]
16 pub struct EqOp;
17
18 impl LintPass for EqOp {
19     fn get_lints(&self) -> LintArray {
20         lint_array!(EQ_OP)
21     }
22
23     fn check_expr(&mut self, cx: &Context, e: &Expr) {
24         if let ExprBinary(ref op, ref left, ref right) = e.node {
25             if is_cmp_or_bit(op) && is_exp_equal(left, right) {
26                 span_lint(cx, EQ_OP, e.span, &format!(
27                     "equal expressions as operands to {}",
28                         ast_util::binop_to_string(op.node)));
29             }
30         }
31     }
32 }
33
34 pub fn is_exp_equal(left : &Expr, right : &Expr) -> bool {
35     match (&left.node, &right.node) {
36         (&ExprBinary(ref lop, ref ll, ref lr),
37                 &ExprBinary(ref rop, ref rl, ref rr)) =>
38             lop.node == rop.node &&
39             is_exp_equal(ll, rl) && is_exp_equal(lr, rr),
40         (&ExprBox(ref lpl, ref lbox), &ExprBox(ref rpl, ref rbox)) =>
41             both(lpl, rpl, |l, r| is_exp_equal(l, r)) &&
42                 is_exp_equal(lbox, rbox),
43         (&ExprCall(ref lcallee, ref largs),
44          &ExprCall(ref rcallee, ref rargs)) => is_exp_equal(lcallee,
45             rcallee) && is_exps_equal(largs, rargs),
46         (&ExprCast(ref lc, ref lty), &ExprCast(ref rc, ref rty)) =>
47             is_ty_equal(lty, rty) && is_exp_equal(lc, rc),
48         (&ExprField(ref lfexp, ref lfident),
49                 &ExprField(ref rfexp, ref rfident)) =>
50             lfident.node == rfident.node && is_exp_equal(lfexp, rfexp),
51         (&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node,
52         (&ExprMethodCall(ref lident, ref lcty, ref lmargs),
53                 &ExprMethodCall(ref rident, ref rcty, ref rmargs)) =>
54             lident.node == rident.node && is_tys_equal(lcty, rcty) &&
55                 is_exps_equal(lmargs, rmargs),
56         (&ExprParen(ref lparen), _) => is_exp_equal(lparen, right),
57         (_, &ExprParen(ref rparen)) => is_exp_equal(left, rparen),
58         (&ExprPath(ref lqself, ref lsubpath),
59                 &ExprPath(ref rqself, ref rsubpath)) =>
60             both(lqself, rqself, |l, r| is_qself_equal(l, r)) &&
61                 is_path_equal(lsubpath, rsubpath),
62         (&ExprTup(ref ltup), &ExprTup(ref rtup)) =>
63             is_exps_equal(ltup, rtup),
64         (&ExprUnary(lunop, ref l), &ExprUnary(runop, ref r)) =>
65             lunop == runop && is_exp_equal(l, r),
66         (&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(l, r),
67         _ => false
68     }
69 }
70
71 fn is_exps_equal(left : &[P<Expr>], right : &[P<Expr>]) -> bool {
72     over(left, right, |l, r| is_exp_equal(l, r))
73 }
74
75 fn is_path_equal(left : &Path, right : &Path) -> bool {
76     // The == of idents doesn't work with different contexts,
77     // we have to be explicit about hygiene
78     left.global == right.global && over(&left.segments, &right.segments,
79         |l, r| l.identifier.name == r.identifier.name
80               && l.identifier.ctxt == r.identifier.ctxt
81                && l.parameters == r.parameters)
82 }
83
84 fn is_qself_equal(left : &QSelf, right : &QSelf) -> bool {
85     left.ty.node == right.ty.node && left.position == right.position
86 }
87
88 fn is_ty_equal(left : &Ty, right : &Ty) -> bool {
89     match (&left.node, &right.node) {
90     (&TyVec(ref lvec), &TyVec(ref rvec)) => is_ty_equal(lvec, rvec),
91     (&TyFixedLengthVec(ref lfvty, ref lfvexp),
92             &TyFixedLengthVec(ref rfvty, ref rfvexp)) =>
93         is_ty_equal(lfvty, rfvty) && is_exp_equal(lfvexp, rfvexp),
94     (&TyPtr(ref lmut), &TyPtr(ref rmut)) => is_mut_ty_equal(lmut, rmut),
95     (&TyRptr(ref ltime, ref lrmut), &TyRptr(ref rtime, ref rrmut)) =>
96         both(ltime, rtime, is_lifetime_equal) &&
97         is_mut_ty_equal(lrmut, rrmut),
98     (&TyBareFn(ref lbare), &TyBareFn(ref rbare)) =>
99         is_bare_fn_ty_equal(lbare, rbare),
100     (&TyTup(ref ltup), &TyTup(ref rtup)) => is_tys_equal(ltup, rtup),
101     (&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) =>
102         both(lq, rq, is_qself_equal) && is_path_equal(lpath, rpath),
103     (&TyObjectSum(ref lsumty, ref lobounds),
104             &TyObjectSum(ref rsumty, ref robounds)) =>
105         is_ty_equal(lsumty, rsumty) &&
106         is_param_bounds_equal(lobounds, robounds),
107     (&TyPolyTraitRef(ref ltbounds), &TyPolyTraitRef(ref rtbounds)) =>
108         is_param_bounds_equal(ltbounds, rtbounds),
109     (&TyParen(ref lty), &TyParen(ref rty)) => is_ty_equal(lty, rty),
110     (&TyTypeof(ref lof), &TyTypeof(ref rof)) => is_exp_equal(lof, rof),
111     (&TyInfer, &TyInfer) => true,
112     _ => false
113     }
114 }
115
116 fn is_param_bound_equal(left : &TyParamBound, right : &TyParamBound)
117         -> bool {
118     match(left, right) {
119     (&TraitTyParamBound(ref lpoly, ref lmod),
120             &TraitTyParamBound(ref rpoly, ref rmod)) =>
121         lmod == rmod && is_poly_traitref_equal(lpoly, rpoly),
122     (&RegionTyParamBound(ref ltime), &RegionTyParamBound(ref rtime)) =>
123         is_lifetime_equal(ltime, rtime),
124     _ => false
125     }
126 }
127
128 fn is_poly_traitref_equal(left : &PolyTraitRef, right : &PolyTraitRef)
129         -> bool {
130     is_lifetimedefs_equal(&left.bound_lifetimes, &right.bound_lifetimes)
131         && is_path_equal(&left.trait_ref.path, &right.trait_ref.path)
132 }
133
134 fn is_param_bounds_equal(left : &TyParamBounds, right : &TyParamBounds)
135         -> bool {
136     over(left, right, is_param_bound_equal)
137 }
138
139 fn is_mut_ty_equal(left : &MutTy, right : &MutTy) -> bool {
140     left.mutbl == right.mutbl && is_ty_equal(&left.ty, &right.ty)
141 }
142
143 fn is_bare_fn_ty_equal(left : &BareFnTy, right : &BareFnTy) -> bool {
144     left.unsafety == right.unsafety && left.abi == right.abi &&
145         is_lifetimedefs_equal(&left.lifetimes, &right.lifetimes) &&
146             is_fndecl_equal(&left.decl, &right.decl)
147 }
148
149 fn is_fndecl_equal(left : &P<FnDecl>, right : &P<FnDecl>) -> bool {
150     left.variadic == right.variadic &&
151         is_args_equal(&left.inputs, &right.inputs) &&
152         is_fnret_ty_equal(&left.output, &right.output)
153 }
154
155 fn is_fnret_ty_equal(left : &FunctionRetTy, right : &FunctionRetTy)
156         -> bool {
157     match (left, right) {
158     (&NoReturn(_), &NoReturn(_)) |
159     (&DefaultReturn(_), &DefaultReturn(_)) => true,
160     (&Return(ref lty), &Return(ref rty)) => is_ty_equal(lty, rty),
161     _ => false
162     }
163 }
164
165 fn is_arg_equal(l: &Arg, r : &Arg) -> bool {
166     is_ty_equal(&l.ty, &r.ty) && is_pat_equal(&l.pat, &r.pat)
167 }
168
169 fn is_args_equal(left : &[Arg], right : &[Arg]) -> bool {
170     over(left, right, is_arg_equal)
171 }
172
173 fn is_pat_equal(left : &Pat, right : &Pat) -> bool {
174     match(&left.node, &right.node) {
175     (&PatWild(lwild), &PatWild(rwild)) => lwild == rwild,
176     (&PatIdent(ref lmode, ref lident, Option::None),
177             &PatIdent(ref rmode, ref rident, Option::None)) =>
178         lmode == rmode && is_ident_equal(&lident.node, &rident.node),
179     (&PatIdent(ref lmode, ref lident, Option::Some(ref lpat)),
180             &PatIdent(ref rmode, ref rident, Option::Some(ref rpat))) =>
181         lmode == rmode && is_ident_equal(&lident.node, &rident.node) &&
182             is_pat_equal(lpat, rpat),
183     (&PatEnum(ref lpath, ref lenum), &PatEnum(ref rpath, ref renum)) =>
184         is_path_equal(lpath, rpath) && both(lenum, renum, |l, r|
185             is_pats_equal(l, r)),
186     (&PatStruct(ref lpath, ref lfieldpat, lbool),
187             &PatStruct(ref rpath, ref rfieldpat, rbool)) =>
188         lbool == rbool && is_path_equal(lpath, rpath) &&
189             is_spanned_fieldpats_equal(lfieldpat, rfieldpat),
190     (&PatTup(ref ltup), &PatTup(ref rtup)) => is_pats_equal(ltup, rtup),
191     (&PatBox(ref lboxed), &PatBox(ref rboxed)) =>
192         is_pat_equal(lboxed, rboxed),
193     (&PatRegion(ref lpat, ref lmut), &PatRegion(ref rpat, ref rmut)) =>
194         is_pat_equal(lpat, rpat) && lmut == rmut,
195     (&PatLit(ref llit), &PatLit(ref rlit)) => is_exp_equal(llit, rlit),
196     (&PatRange(ref lfrom, ref lto), &PatRange(ref rfrom, ref rto)) =>
197         is_exp_equal(lfrom, rfrom) && is_exp_equal(lto, rto),
198     (&PatVec(ref lfirst, Option::None, ref llast),
199             &PatVec(ref rfirst, Option::None, ref rlast)) =>
200         is_pats_equal(lfirst, rfirst) && is_pats_equal(llast, rlast),
201     (&PatVec(ref lfirst, Option::Some(ref lpat), ref llast),
202             &PatVec(ref rfirst, Option::Some(ref rpat), ref rlast)) =>
203         is_pats_equal(lfirst, rfirst) && is_pat_equal(lpat, rpat) &&
204             is_pats_equal(llast, rlast),
205     // I don't match macros for now, the code is slow enough as is ;-)
206     _ => false
207     }
208 }
209
210 fn is_spanned_fieldpats_equal(left : &[code::Spanned<FieldPat>],
211         right : &[code::Spanned<FieldPat>]) -> bool {
212     over(left, right, |l, r| is_fieldpat_equal(&l.node, &r.node))
213 }
214
215 fn is_fieldpat_equal(left : &FieldPat, right : &FieldPat) -> bool {
216     left.is_shorthand == right.is_shorthand &&
217         is_ident_equal(&left.ident, &right.ident) &&
218         is_pat_equal(&left.pat, &right.pat)
219 }
220
221 fn is_ident_equal(left : &Ident, right : &Ident) -> bool {
222     &left.name == &right.name && left.ctxt == right.ctxt
223 }
224
225 fn is_pats_equal(left : &[P<Pat>], right : &[P<Pat>]) -> bool {
226     over(left, right, |l, r| is_pat_equal(l, r))
227 }
228
229 fn is_lifetimedef_equal(left : &LifetimeDef, right : &LifetimeDef)
230         -> bool {
231     is_lifetime_equal(&left.lifetime, &right.lifetime) &&
232         over(&left.bounds, &right.bounds, is_lifetime_equal)
233 }
234
235 fn is_lifetimedefs_equal(left : &[LifetimeDef], right : &[LifetimeDef])
236         -> bool {
237     over(left, right, is_lifetimedef_equal)
238 }
239
240 fn is_lifetime_equal(left : &Lifetime, right : &Lifetime) -> bool {
241     left.name == right.name
242 }
243
244 fn is_tys_equal(left : &[P<Ty>], right : &[P<Ty>]) -> bool {
245     over(left, right, |l, r| is_ty_equal(l, r))
246 }
247
248 fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
249         where F: FnMut(&X, &X) -> bool {
250     left.len() == right.len() && left.iter().zip(right).all(|(x, y)|
251         eq_fn(x, y))
252 }
253
254 fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn : F) -> bool
255         where F: FnMut(&X, &X) -> bool {
256     l.as_ref().map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false,
257         |y| eq_fn(x, y)))
258 }
259
260 fn is_cmp_or_bit(op : &BinOp) -> bool {
261     match op.node {
262         BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr |
263         BiBitXor | BiBitAnd | BiBitOr => true,
264         _ => false
265     }
266 }