const folding for eq_op

This commit is contained in:
llogiq 2015-08-21 12:19:07 +02:00
parent c91857c62d
commit 4dcbad1b08
2 changed files with 73 additions and 68 deletions

View File

@ -4,6 +4,7 @@ use syntax::ast_util as ast_util;
use syntax::ptr::P; use syntax::ptr::P;
use syntax::codemap as code; use syntax::codemap as code;
use consts::constant;
use utils::span_lint; use utils::span_lint;
declare_lint! { declare_lint! {
@ -22,7 +23,7 @@ impl LintPass for EqOp {
fn check_expr(&mut self, cx: &Context, e: &Expr) { fn check_expr(&mut self, cx: &Context, e: &Expr) {
if let ExprBinary(ref op, ref left, ref right) = e.node { if let ExprBinary(ref op, ref left, ref right) = e.node {
if is_cmp_or_bit(op) && is_exp_equal(left, right) { if is_cmp_or_bit(op) && is_exp_equal(cx, left, right) {
span_lint(cx, EQ_OP, e.span, &format!( span_lint(cx, EQ_OP, e.span, &format!(
"equal expressions as operands to {}", "equal expressions as operands to {}",
ast_util::binop_to_string(op.node))); ast_util::binop_to_string(op.node)));
@ -31,45 +32,48 @@ impl LintPass for EqOp {
} }
} }
pub fn is_exp_equal(left : &Expr, right : &Expr) -> bool { pub fn is_exp_equal(cx: &Context, left : &Expr, right : &Expr) -> bool {
match (&left.node, &right.node) { match (&left.node, &right.node) {
(&ExprBinary(ref lop, ref ll, ref lr), (&ExprBinary(ref lop, ref ll, ref lr),
&ExprBinary(ref rop, ref rl, ref rr)) => &ExprBinary(ref rop, ref rl, ref rr)) =>
lop.node == rop.node && lop.node == rop.node &&
is_exp_equal(ll, rl) && is_exp_equal(lr, rr), is_exp_equal(cx, ll, rl) && is_exp_equal(cx, lr, rr),
(&ExprBox(ref lpl, ref lbox), &ExprBox(ref rpl, ref rbox)) => (&ExprBox(ref lpl, ref lbox), &ExprBox(ref rpl, ref rbox)) =>
both(lpl, rpl, |l, r| is_exp_equal(l, r)) && both(lpl, rpl, |l, r| is_exp_equal(cx, l, r)) &&
is_exp_equal(lbox, rbox), is_exp_equal(cx, lbox, rbox),
(&ExprCall(ref lcallee, ref largs), (&ExprCall(ref lcallee, ref largs),
&ExprCall(ref rcallee, ref rargs)) => is_exp_equal(lcallee, &ExprCall(ref rcallee, ref rargs)) => is_exp_equal(cx, lcallee,
rcallee) && is_exps_equal(largs, rargs), rcallee) && is_exps_equal(cx, largs, rargs),
(&ExprCast(ref lc, ref lty), &ExprCast(ref rc, ref rty)) => (&ExprCast(ref lc, ref lty), &ExprCast(ref rc, ref rty)) =>
is_ty_equal(lty, rty) && is_exp_equal(lc, rc), is_ty_equal(cx, lty, rty) && is_exp_equal(cx, lc, rc),
(&ExprField(ref lfexp, ref lfident), (&ExprField(ref lfexp, ref lfident),
&ExprField(ref rfexp, ref rfident)) => &ExprField(ref rfexp, ref rfident)) =>
lfident.node == rfident.node && is_exp_equal(lfexp, rfexp), lfident.node == rfident.node && is_exp_equal(cx, lfexp, rfexp),
(&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node, (&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node,
(&ExprMethodCall(ref lident, ref lcty, ref lmargs), (&ExprMethodCall(ref lident, ref lcty, ref lmargs),
&ExprMethodCall(ref rident, ref rcty, ref rmargs)) => &ExprMethodCall(ref rident, ref rcty, ref rmargs)) =>
lident.node == rident.node && is_tys_equal(lcty, rcty) && lident.node == rident.node && is_tys_equal(cx, lcty, rcty) &&
is_exps_equal(lmargs, rmargs), is_exps_equal(cx, lmargs, rmargs),
(&ExprParen(ref lparen), _) => is_exp_equal(lparen, right), (&ExprParen(ref lparen), _) => is_exp_equal(cx, lparen, right),
(_, &ExprParen(ref rparen)) => is_exp_equal(left, rparen), (_, &ExprParen(ref rparen)) => is_exp_equal(cx, left, rparen),
(&ExprPath(ref lqself, ref lsubpath), (&ExprPath(ref lqself, ref lsubpath),
&ExprPath(ref rqself, ref rsubpath)) => &ExprPath(ref rqself, ref rsubpath)) =>
both(lqself, rqself, |l, r| is_qself_equal(l, r)) && both(lqself, rqself, |l, r| is_qself_equal(l, r)) &&
is_path_equal(lsubpath, rsubpath), is_path_equal(lsubpath, rsubpath),
(&ExprTup(ref ltup), &ExprTup(ref rtup)) => (&ExprTup(ref ltup), &ExprTup(ref rtup)) =>
is_exps_equal(ltup, rtup), is_exps_equal(cx, ltup, rtup),
(&ExprUnary(lunop, ref l), &ExprUnary(runop, ref r)) => (&ExprUnary(lunop, ref l), &ExprUnary(runop, ref r)) =>
lunop == runop && is_exp_equal(l, r), lunop == runop && is_exp_equal(cx, l, r),
(&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(l, r), (&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r),
_ => false
} || match (constant(cx, left), constant(cx, right)) {
(Some(l), Some(r)) => l == r,
_ => false _ => false
} }
} }
fn is_exps_equal(left : &[P<Expr>], right : &[P<Expr>]) -> bool { fn is_exps_equal(cx: &Context, left : &[P<Expr>], right : &[P<Expr>]) -> bool {
over(left, right, |l, r| is_exp_equal(l, r)) over(left, right, |l, r| is_exp_equal(cx, l, r))
} }
fn is_path_equal(left : &Path, right : &Path) -> bool { fn is_path_equal(left : &Path, right : &Path) -> bool {
@ -85,29 +89,29 @@ fn is_qself_equal(left : &QSelf, right : &QSelf) -> bool {
left.ty.node == right.ty.node && left.position == right.position left.ty.node == right.ty.node && left.position == right.position
} }
fn is_ty_equal(left : &Ty, right : &Ty) -> bool { fn is_ty_equal(cx: &Context, left : &Ty, right : &Ty) -> bool {
match (&left.node, &right.node) { match (&left.node, &right.node) {
(&TyVec(ref lvec), &TyVec(ref rvec)) => is_ty_equal(lvec, rvec), (&TyVec(ref lvec), &TyVec(ref rvec)) => is_ty_equal(cx, lvec, rvec),
(&TyFixedLengthVec(ref lfvty, ref lfvexp), (&TyFixedLengthVec(ref lfvty, ref lfvexp),
&TyFixedLengthVec(ref rfvty, ref rfvexp)) => &TyFixedLengthVec(ref rfvty, ref rfvexp)) =>
is_ty_equal(lfvty, rfvty) && is_exp_equal(lfvexp, rfvexp), is_ty_equal(cx, lfvty, rfvty) && is_exp_equal(cx, lfvexp, rfvexp),
(&TyPtr(ref lmut), &TyPtr(ref rmut)) => is_mut_ty_equal(lmut, rmut), (&TyPtr(ref lmut), &TyPtr(ref rmut)) => is_mut_ty_equal(cx, lmut, rmut),
(&TyRptr(ref ltime, ref lrmut), &TyRptr(ref rtime, ref rrmut)) => (&TyRptr(ref ltime, ref lrmut), &TyRptr(ref rtime, ref rrmut)) =>
both(ltime, rtime, is_lifetime_equal) && both(ltime, rtime, is_lifetime_equal) &&
is_mut_ty_equal(lrmut, rrmut), is_mut_ty_equal(cx, lrmut, rrmut),
(&TyBareFn(ref lbare), &TyBareFn(ref rbare)) => (&TyBareFn(ref lbare), &TyBareFn(ref rbare)) =>
is_bare_fn_ty_equal(lbare, rbare), is_bare_fn_ty_equal(cx, lbare, rbare),
(&TyTup(ref ltup), &TyTup(ref rtup)) => is_tys_equal(ltup, rtup), (&TyTup(ref ltup), &TyTup(ref rtup)) => is_tys_equal(cx, ltup, rtup),
(&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) => (&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) =>
both(lq, rq, is_qself_equal) && is_path_equal(lpath, rpath), both(lq, rq, is_qself_equal) && is_path_equal(lpath, rpath),
(&TyObjectSum(ref lsumty, ref lobounds), (&TyObjectSum(ref lsumty, ref lobounds),
&TyObjectSum(ref rsumty, ref robounds)) => &TyObjectSum(ref rsumty, ref robounds)) =>
is_ty_equal(lsumty, rsumty) && is_ty_equal(cx, lsumty, rsumty) &&
is_param_bounds_equal(lobounds, robounds), is_param_bounds_equal(lobounds, robounds),
(&TyPolyTraitRef(ref ltbounds), &TyPolyTraitRef(ref rtbounds)) => (&TyPolyTraitRef(ref ltbounds), &TyPolyTraitRef(ref rtbounds)) =>
is_param_bounds_equal(ltbounds, rtbounds), is_param_bounds_equal(ltbounds, rtbounds),
(&TyParen(ref lty), &TyParen(ref rty)) => is_ty_equal(lty, rty), (&TyParen(ref lty), &TyParen(ref rty)) => is_ty_equal(cx, lty, rty),
(&TyTypeof(ref lof), &TyTypeof(ref rof)) => is_exp_equal(lof, rof), (&TyTypeof(ref lof), &TyTypeof(ref rof)) => is_exp_equal(cx, lof, rof),
(&TyInfer, &TyInfer) => true, (&TyInfer, &TyInfer) => true,
_ => false _ => false
} }
@ -136,41 +140,41 @@ fn is_param_bounds_equal(left : &TyParamBounds, right : &TyParamBounds)
over(left, right, is_param_bound_equal) over(left, right, is_param_bound_equal)
} }
fn is_mut_ty_equal(left : &MutTy, right : &MutTy) -> bool { fn is_mut_ty_equal(cx: &Context, left : &MutTy, right : &MutTy) -> bool {
left.mutbl == right.mutbl && is_ty_equal(&left.ty, &right.ty) left.mutbl == right.mutbl && is_ty_equal(cx, &left.ty, &right.ty)
} }
fn is_bare_fn_ty_equal(left : &BareFnTy, right : &BareFnTy) -> bool { fn is_bare_fn_ty_equal(cx: &Context, left : &BareFnTy, right : &BareFnTy) -> bool {
left.unsafety == right.unsafety && left.abi == right.abi && left.unsafety == right.unsafety && left.abi == right.abi &&
is_lifetimedefs_equal(&left.lifetimes, &right.lifetimes) && is_lifetimedefs_equal(&left.lifetimes, &right.lifetimes) &&
is_fndecl_equal(&left.decl, &right.decl) is_fndecl_equal(cx, &left.decl, &right.decl)
} }
fn is_fndecl_equal(left : &P<FnDecl>, right : &P<FnDecl>) -> bool { fn is_fndecl_equal(cx: &Context, left : &P<FnDecl>, right : &P<FnDecl>) -> bool {
left.variadic == right.variadic && left.variadic == right.variadic &&
is_args_equal(&left.inputs, &right.inputs) && is_args_equal(cx, &left.inputs, &right.inputs) &&
is_fnret_ty_equal(&left.output, &right.output) is_fnret_ty_equal(cx, &left.output, &right.output)
} }
fn is_fnret_ty_equal(left : &FunctionRetTy, right : &FunctionRetTy) fn is_fnret_ty_equal(cx: &Context, left : &FunctionRetTy,
-> bool { right : &FunctionRetTy) -> bool {
match (left, right) { match (left, right) {
(&NoReturn(_), &NoReturn(_)) | (&NoReturn(_), &NoReturn(_)) |
(&DefaultReturn(_), &DefaultReturn(_)) => true, (&DefaultReturn(_), &DefaultReturn(_)) => true,
(&Return(ref lty), &Return(ref rty)) => is_ty_equal(lty, rty), (&Return(ref lty), &Return(ref rty)) => is_ty_equal(cx, lty, rty),
_ => false _ => false
} }
} }
fn is_arg_equal(l: &Arg, r : &Arg) -> bool { fn is_arg_equal(cx: &Context, l: &Arg, r : &Arg) -> bool {
is_ty_equal(&l.ty, &r.ty) && is_pat_equal(&l.pat, &r.pat) is_ty_equal(cx, &l.ty, &r.ty) && is_pat_equal(cx, &l.pat, &r.pat)
} }
fn is_args_equal(left : &[Arg], right : &[Arg]) -> bool { fn is_args_equal(cx: &Context, left : &[Arg], right : &[Arg]) -> bool {
over(left, right, is_arg_equal) over(left, right, |l, r| is_arg_equal(cx, l, r))
} }
fn is_pat_equal(left : &Pat, right : &Pat) -> bool { fn is_pat_equal(cx: &Context, left : &Pat, right : &Pat) -> bool {
match(&left.node, &right.node) { match(&left.node, &right.node) {
(&PatWild(lwild), &PatWild(rwild)) => lwild == rwild, (&PatWild(lwild), &PatWild(rwild)) => lwild == rwild,
(&PatIdent(ref lmode, ref lident, Option::None), (&PatIdent(ref lmode, ref lident, Option::None),
@ -179,51 +183,51 @@ fn is_pat_equal(left : &Pat, right : &Pat) -> bool {
(&PatIdent(ref lmode, ref lident, Option::Some(ref lpat)), (&PatIdent(ref lmode, ref lident, Option::Some(ref lpat)),
&PatIdent(ref rmode, ref rident, Option::Some(ref rpat))) => &PatIdent(ref rmode, ref rident, Option::Some(ref rpat))) =>
lmode == rmode && is_ident_equal(&lident.node, &rident.node) && lmode == rmode && is_ident_equal(&lident.node, &rident.node) &&
is_pat_equal(lpat, rpat), is_pat_equal(cx, lpat, rpat),
(&PatEnum(ref lpath, ref lenum), &PatEnum(ref rpath, ref renum)) => (&PatEnum(ref lpath, ref lenum), &PatEnum(ref rpath, ref renum)) =>
is_path_equal(lpath, rpath) && both(lenum, renum, |l, r| is_path_equal(lpath, rpath) && both(lenum, renum, |l, r|
is_pats_equal(l, r)), is_pats_equal(cx, l, r)),
(&PatStruct(ref lpath, ref lfieldpat, lbool), (&PatStruct(ref lpath, ref lfieldpat, lbool),
&PatStruct(ref rpath, ref rfieldpat, rbool)) => &PatStruct(ref rpath, ref rfieldpat, rbool)) =>
lbool == rbool && is_path_equal(lpath, rpath) && lbool == rbool && is_path_equal(lpath, rpath) &&
is_spanned_fieldpats_equal(lfieldpat, rfieldpat), is_spanned_fieldpats_equal(cx, lfieldpat, rfieldpat),
(&PatTup(ref ltup), &PatTup(ref rtup)) => is_pats_equal(ltup, rtup), (&PatTup(ref ltup), &PatTup(ref rtup)) => is_pats_equal(cx, ltup, rtup),
(&PatBox(ref lboxed), &PatBox(ref rboxed)) => (&PatBox(ref lboxed), &PatBox(ref rboxed)) =>
is_pat_equal(lboxed, rboxed), is_pat_equal(cx, lboxed, rboxed),
(&PatRegion(ref lpat, ref lmut), &PatRegion(ref rpat, ref rmut)) => (&PatRegion(ref lpat, ref lmut), &PatRegion(ref rpat, ref rmut)) =>
is_pat_equal(lpat, rpat) && lmut == rmut, is_pat_equal(cx, lpat, rpat) && lmut == rmut,
(&PatLit(ref llit), &PatLit(ref rlit)) => is_exp_equal(llit, rlit), (&PatLit(ref llit), &PatLit(ref rlit)) => is_exp_equal(cx, llit, rlit),
(&PatRange(ref lfrom, ref lto), &PatRange(ref rfrom, ref rto)) => (&PatRange(ref lfrom, ref lto), &PatRange(ref rfrom, ref rto)) =>
is_exp_equal(lfrom, rfrom) && is_exp_equal(lto, rto), is_exp_equal(cx, lfrom, rfrom) && is_exp_equal(cx, lto, rto),
(&PatVec(ref lfirst, Option::None, ref llast), (&PatVec(ref lfirst, Option::None, ref llast),
&PatVec(ref rfirst, Option::None, ref rlast)) => &PatVec(ref rfirst, Option::None, ref rlast)) =>
is_pats_equal(lfirst, rfirst) && is_pats_equal(llast, rlast), is_pats_equal(cx, lfirst, rfirst) && is_pats_equal(cx, llast, rlast),
(&PatVec(ref lfirst, Option::Some(ref lpat), ref llast), (&PatVec(ref lfirst, Option::Some(ref lpat), ref llast),
&PatVec(ref rfirst, Option::Some(ref rpat), ref rlast)) => &PatVec(ref rfirst, Option::Some(ref rpat), ref rlast)) =>
is_pats_equal(lfirst, rfirst) && is_pat_equal(lpat, rpat) && is_pats_equal(cx, lfirst, rfirst) && is_pat_equal(cx, lpat, rpat) &&
is_pats_equal(llast, rlast), is_pats_equal(cx, llast, rlast),
// I don't match macros for now, the code is slow enough as is ;-) // I don't match macros for now, the code is slow enough as is ;-)
_ => false _ => false
} }
} }
fn is_spanned_fieldpats_equal(left : &[code::Spanned<FieldPat>], fn is_spanned_fieldpats_equal(cx: &Context, left : &[code::Spanned<FieldPat>],
right : &[code::Spanned<FieldPat>]) -> bool { right : &[code::Spanned<FieldPat>]) -> bool {
over(left, right, |l, r| is_fieldpat_equal(&l.node, &r.node)) over(left, right, |l, r| is_fieldpat_equal(cx, &l.node, &r.node))
} }
fn is_fieldpat_equal(left : &FieldPat, right : &FieldPat) -> bool { fn is_fieldpat_equal(cx: &Context, left : &FieldPat, right : &FieldPat) -> bool {
left.is_shorthand == right.is_shorthand && left.is_shorthand == right.is_shorthand &&
is_ident_equal(&left.ident, &right.ident) && is_ident_equal(&left.ident, &right.ident) &&
is_pat_equal(&left.pat, &right.pat) is_pat_equal(cx, &left.pat, &right.pat)
} }
fn is_ident_equal(left : &Ident, right : &Ident) -> bool { fn is_ident_equal(left : &Ident, right : &Ident) -> bool {
&left.name == &right.name && left.ctxt == right.ctxt &left.name == &right.name && left.ctxt == right.ctxt
} }
fn is_pats_equal(left : &[P<Pat>], right : &[P<Pat>]) -> bool { fn is_pats_equal(cx: &Context, left : &[P<Pat>], right : &[P<Pat>]) -> bool {
over(left, right, |l, r| is_pat_equal(l, r)) over(left, right, |l, r| is_pat_equal(cx, l, r))
} }
fn is_lifetimedef_equal(left : &LifetimeDef, right : &LifetimeDef) fn is_lifetimedef_equal(left : &LifetimeDef, right : &LifetimeDef)
@ -241,8 +245,8 @@ fn is_lifetime_equal(left : &Lifetime, right : &Lifetime) -> bool {
left.name == right.name left.name == right.name
} }
fn is_tys_equal(left : &[P<Ty>], right : &[P<Ty>]) -> bool { fn is_tys_equal(cx: &Context, left : &[P<Ty>], right : &[P<Ty>]) -> bool {
over(left, right, |l, r| is_ty_equal(l, r)) over(left, right, |l, r| is_ty_equal(cx, l, r))
} }
fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool

View File

@ -41,7 +41,7 @@ impl LintPass for StringAdd {
if let Some(ref p) = parent { if let Some(ref p) = parent {
if let &ExprAssign(ref target, _) = &p.node { if let &ExprAssign(ref target, _) = &p.node {
// avoid duplicate matches // avoid duplicate matches
if is_exp_equal(target, left) { return; } if is_exp_equal(cx, target, left) { return; }
} }
} }
} }
@ -51,7 +51,7 @@ impl LintPass for StringAdd {
Consider using `String::push_str()` instead") Consider using `String::push_str()` instead")
} }
} else if let &ExprAssign(ref target, ref src) = &e.node { } else if let &ExprAssign(ref target, ref src) = &e.node {
if is_string(cx, target) && is_add(src, target) { if is_string(cx, target) && is_add(cx, src, target) {
span_lint(cx, STRING_ADD_ASSIGN, e.span, span_lint(cx, STRING_ADD_ASSIGN, e.span,
"you assigned the result of adding something to this string. \ "you assigned the result of adding something to this string. \
Consider using `String::push_str()` instead") Consider using `String::push_str()` instead")
@ -67,13 +67,14 @@ fn is_string(cx: &Context, e: &Expr) -> bool {
} else { false } } else { false }
} }
fn is_add(src: &Expr, target: &Expr) -> bool { fn is_add(cx: &Context, src: &Expr, target: &Expr) -> bool {
match &src.node { match &src.node {
&ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
is_exp_equal(target, left), is_exp_equal(cx, target, left),
&ExprBlock(ref block) => block.stmts.is_empty() && &ExprBlock(ref block) => block.stmts.is_empty() &&
block.expr.as_ref().map_or(false, |expr| is_add(&*expr, target)), block.expr.as_ref().map_or(false,
&ExprParen(ref expr) => is_add(&*expr, target), |expr| is_add(cx, &*expr, target)),
&ExprParen(ref expr) => is_add(cx, &*expr, target),
_ => false _ => false
} }
} }