Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eq_op: cut back to expressions that are guaranteed side effect free #230

Merged
merged 1 commit into from
Aug 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 26 additions & 185 deletions src/eq_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use rustc::lint::*;
use syntax::ast::*;
use syntax::ast_util as ast_util;
use syntax::ptr::P;
use syntax::codemap as code;

use consts::constant;
use utils::span_lint;
Expand Down Expand Up @@ -33,42 +32,27 @@ impl LintPass for EqOp {
}

pub fn is_exp_equal(cx: &Context, left : &Expr, right : &Expr) -> bool {
if match (&left.node, &right.node) {
(&ExprBinary(ref lop, ref ll, ref lr),
&ExprBinary(ref rop, ref rl, ref rr)) =>
lop.node == rop.node &&
is_exp_equal(cx, ll, rl) && is_exp_equal(cx, lr, rr),
(&ExprBox(ref lpl, ref lbox), &ExprBox(ref rpl, ref rbox)) =>
both(lpl, rpl, |l, r| is_exp_equal(cx, l, r)) &&
is_exp_equal(cx, lbox, rbox),
(&ExprCall(ref lcallee, ref largs),
&ExprCall(ref rcallee, ref rargs)) => is_exp_equal(cx, lcallee,
rcallee) && is_exps_equal(cx, largs, rargs),
(&ExprCast(ref lc, ref lty), &ExprCast(ref rc, ref rty)) =>
is_ty_equal(cx, lty, rty) && is_exp_equal(cx, lc, rc),
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,
(&ExprMethodCall(ref lident, ref lcty, ref lmargs),
&ExprMethodCall(ref rident, ref rcty, ref rmargs)) =>
lident.node == rident.node && is_tys_equal(cx, lcty, rcty) &&
is_exps_equal(cx, lmargs, rmargs),
(&ExprParen(ref lparen), _) => is_exp_equal(cx, lparen, right),
(_, &ExprParen(ref rparen)) => is_exp_equal(cx, left, rparen),
(&ExprPath(ref lqself, ref lsubpath),
&ExprPath(ref rqself, ref rsubpath)) =>
both(lqself, rqself, |l, r| is_qself_equal(l, r)) &&
both(lqself, rqself, is_qself_equal) &&
is_path_equal(lsubpath, rsubpath),
(&ExprTup(ref ltup), &ExprTup(ref rtup)) =>
is_exps_equal(cx, ltup, rtup),
(&ExprUnary(lunop, ref l), &ExprUnary(runop, ref r)) =>
lunop == runop && is_exp_equal(cx, l, r),
(&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r),
_ => false
} { return true; }
match (constant(cx, left), constant(cx, right)) {
(Some(l), Some(r)) => 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
}
}
Expand All @@ -90,166 +74,6 @@ fn is_qself_equal(left : &QSelf, right : &QSelf) -> bool {
left.ty.node == right.ty.node && left.position == right.position
}

fn is_ty_equal(cx: &Context, left : &Ty, right : &Ty) -> bool {
match (&left.node, &right.node) {
(&TyVec(ref lvec), &TyVec(ref rvec)) => is_ty_equal(cx, lvec, rvec),
(&TyFixedLengthVec(ref lfvty, ref lfvexp),
&TyFixedLengthVec(ref rfvty, ref rfvexp)) =>
is_ty_equal(cx, lfvty, rfvty) && is_exp_equal(cx, lfvexp, rfvexp),
(&TyPtr(ref lmut), &TyPtr(ref rmut)) => is_mut_ty_equal(cx, lmut, rmut),
(&TyRptr(ref ltime, ref lrmut), &TyRptr(ref rtime, ref rrmut)) =>
both(ltime, rtime, is_lifetime_equal) &&
is_mut_ty_equal(cx, lrmut, rrmut),
(&TyBareFn(ref lbare), &TyBareFn(ref rbare)) =>
is_bare_fn_ty_equal(cx, lbare, rbare),
(&TyTup(ref ltup), &TyTup(ref rtup)) => is_tys_equal(cx, ltup, rtup),
(&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) =>
both(lq, rq, is_qself_equal) && is_path_equal(lpath, rpath),
(&TyObjectSum(ref lsumty, ref lobounds),
&TyObjectSum(ref rsumty, ref robounds)) =>
is_ty_equal(cx, lsumty, rsumty) &&
is_param_bounds_equal(lobounds, robounds),
(&TyPolyTraitRef(ref ltbounds), &TyPolyTraitRef(ref rtbounds)) =>
is_param_bounds_equal(ltbounds, rtbounds),
(&TyParen(ref lty), &TyParen(ref rty)) => is_ty_equal(cx, lty, rty),
(&TyTypeof(ref lof), &TyTypeof(ref rof)) => is_exp_equal(cx, lof, rof),
(&TyInfer, &TyInfer) => true,
_ => false
}
}

fn is_param_bound_equal(left : &TyParamBound, right : &TyParamBound)
-> bool {
match(left, right) {
(&TraitTyParamBound(ref lpoly, ref lmod),
&TraitTyParamBound(ref rpoly, ref rmod)) =>
lmod == rmod && is_poly_traitref_equal(lpoly, rpoly),
(&RegionTyParamBound(ref ltime), &RegionTyParamBound(ref rtime)) =>
is_lifetime_equal(ltime, rtime),
_ => false
}
}

fn is_poly_traitref_equal(left : &PolyTraitRef, right : &PolyTraitRef)
-> bool {
is_lifetimedefs_equal(&left.bound_lifetimes, &right.bound_lifetimes)
&& is_path_equal(&left.trait_ref.path, &right.trait_ref.path)
}

fn is_param_bounds_equal(left : &TyParamBounds, right : &TyParamBounds)
-> bool {
over(left, right, is_param_bound_equal)
}

fn is_mut_ty_equal(cx: &Context, left : &MutTy, right : &MutTy) -> bool {
left.mutbl == right.mutbl && is_ty_equal(cx, &left.ty, &right.ty)
}

fn is_bare_fn_ty_equal(cx: &Context, left : &BareFnTy, right : &BareFnTy) -> bool {
left.unsafety == right.unsafety && left.abi == right.abi &&
is_lifetimedefs_equal(&left.lifetimes, &right.lifetimes) &&
is_fndecl_equal(cx, &left.decl, &right.decl)
}

fn is_fndecl_equal(cx: &Context, left : &P<FnDecl>, right : &P<FnDecl>) -> bool {
left.variadic == right.variadic &&
is_args_equal(cx, &left.inputs, &right.inputs) &&
is_fnret_ty_equal(cx, &left.output, &right.output)
}

fn is_fnret_ty_equal(cx: &Context, left : &FunctionRetTy,
right : &FunctionRetTy) -> bool {
match (left, right) {
(&NoReturn(_), &NoReturn(_)) |
(&DefaultReturn(_), &DefaultReturn(_)) => true,
(&Return(ref lty), &Return(ref rty)) => is_ty_equal(cx, lty, rty),
_ => false
}
}

fn is_arg_equal(cx: &Context, l: &Arg, r : &Arg) -> bool {
is_ty_equal(cx, &l.ty, &r.ty) && is_pat_equal(cx, &l.pat, &r.pat)
}

fn is_args_equal(cx: &Context, left : &[Arg], right : &[Arg]) -> bool {
over(left, right, |l, r| is_arg_equal(cx, l, r))
}

fn is_pat_equal(cx: &Context, left : &Pat, right : &Pat) -> bool {
match(&left.node, &right.node) {
(&PatWild(lwild), &PatWild(rwild)) => lwild == rwild,
(&PatIdent(ref lmode, ref lident, Option::None),
&PatIdent(ref rmode, ref rident, Option::None)) =>
lmode == rmode && is_ident_equal(&lident.node, &rident.node),
(&PatIdent(ref lmode, ref lident, Option::Some(ref lpat)),
&PatIdent(ref rmode, ref rident, Option::Some(ref rpat))) =>
lmode == rmode && is_ident_equal(&lident.node, &rident.node) &&
is_pat_equal(cx, lpat, rpat),
(&PatEnum(ref lpath, ref lenum), &PatEnum(ref rpath, ref renum)) =>
is_path_equal(lpath, rpath) && both(lenum, renum, |l, r|
is_pats_equal(cx, l, r)),
(&PatStruct(ref lpath, ref lfieldpat, lbool),
&PatStruct(ref rpath, ref rfieldpat, rbool)) =>
lbool == rbool && is_path_equal(lpath, rpath) &&
is_spanned_fieldpats_equal(cx, lfieldpat, rfieldpat),
(&PatTup(ref ltup), &PatTup(ref rtup)) => is_pats_equal(cx, ltup, rtup),
(&PatBox(ref lboxed), &PatBox(ref rboxed)) =>
is_pat_equal(cx, lboxed, rboxed),
(&PatRegion(ref lpat, ref lmut), &PatRegion(ref rpat, ref rmut)) =>
is_pat_equal(cx, lpat, rpat) && lmut == rmut,
(&PatLit(ref llit), &PatLit(ref rlit)) => is_exp_equal(cx, llit, rlit),
(&PatRange(ref lfrom, ref lto), &PatRange(ref rfrom, ref rto)) =>
is_exp_equal(cx, lfrom, rfrom) && is_exp_equal(cx, lto, rto),
(&PatVec(ref lfirst, Option::None, ref llast),
&PatVec(ref rfirst, Option::None, ref rlast)) =>
is_pats_equal(cx, lfirst, rfirst) && is_pats_equal(cx, llast, rlast),
(&PatVec(ref lfirst, Option::Some(ref lpat), ref llast),
&PatVec(ref rfirst, Option::Some(ref rpat), ref rlast)) =>
is_pats_equal(cx, lfirst, rfirst) && is_pat_equal(cx, lpat, rpat) &&
is_pats_equal(cx, llast, rlast),
// I don't match macros for now, the code is slow enough as is ;-)
_ => false
}
}

fn is_spanned_fieldpats_equal(cx: &Context, left : &[code::Spanned<FieldPat>],
right : &[code::Spanned<FieldPat>]) -> bool {
over(left, right, |l, r| is_fieldpat_equal(cx, &l.node, &r.node))
}

fn is_fieldpat_equal(cx: &Context, left : &FieldPat, right : &FieldPat) -> bool {
left.is_shorthand == right.is_shorthand &&
is_ident_equal(&left.ident, &right.ident) &&
is_pat_equal(cx, &left.pat, &right.pat)
}

fn is_ident_equal(left : &Ident, right : &Ident) -> bool {
&left.name == &right.name && left.ctxt == right.ctxt
}

fn is_pats_equal(cx: &Context, left : &[P<Pat>], right : &[P<Pat>]) -> bool {
over(left, right, |l, r| is_pat_equal(cx, l, r))
}

fn is_lifetimedef_equal(left : &LifetimeDef, right : &LifetimeDef)
-> bool {
is_lifetime_equal(&left.lifetime, &right.lifetime) &&
over(&left.bounds, &right.bounds, is_lifetime_equal)
}

fn is_lifetimedefs_equal(left : &[LifetimeDef], right : &[LifetimeDef])
-> bool {
over(left, right, is_lifetimedef_equal)
}

fn is_lifetime_equal(left : &Lifetime, right : &Lifetime) -> bool {
left.name == right.name
}

fn is_tys_equal(cx: &Context, left : &[P<Ty>], right : &[P<Ty>]) -> bool {
over(left, right, |l, r| is_ty_equal(cx, l, r))
}

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)|
Expand All @@ -269,3 +93,20 @@ fn is_cmp_or_bit(op : &BinOp) -> bool {
_ => 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
}
}
6 changes: 0 additions & 6 deletions tests/compile-fail/eq_op.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
#![feature(plugin)]
#![plugin(clippy)]

fn id<X>(x: X) -> X {
x
}

#[deny(eq_op)]
#[allow(identity_op)]
fn main() {
Expand All @@ -19,7 +15,6 @@ fn main() {
// casts, methods, parentheses
(1 as u64) & (1 as u64); //~ERROR equal expressions
1 ^ ((((((1)))))); //~ERROR equal expressions
id((1)) | id(1); //~ERROR equal expressions

// unary and binary operators
(-(2) < -(2)); //~ERROR equal expressions
Expand All @@ -32,7 +27,6 @@ fn main() {
// various other things
([1] != [1]); //~ERROR equal expressions
((1, 2) != (1, 2)); //~ERROR equal expressions
[1].len() == [1].len(); //~ERROR equal expressions
vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros

// const folding
Expand Down