From 91c16fc8e605ddb7f0b4f967bd7fbf62ccd6a292 Mon Sep 17 00:00:00 2001 From: mcarton Date: Sat, 6 Feb 2016 20:13:25 +0100 Subject: [PATCH] Refactor Expr comparisons --- src/copies.rs | 7 +- src/entry.rs | 5 +- src/eq_op.rs | 4 +- src/strings.rs | 7 +- src/utils/hir.rs | 239 +++++++++++++++++++++++++++++++++ src/{utils.rs => utils/mod.rs} | 227 +------------------------------ 6 files changed, 254 insertions(+), 235 deletions(-) create mode 100644 src/utils/hir.rs rename src/{utils.rs => utils/mod.rs} (68%) diff --git a/src/copies.rs b/src/copies.rs index 525c7b7a6..b1ea8f0c3 100644 --- a/src/copies.rs +++ b/src/copies.rs @@ -1,6 +1,7 @@ use rustc::lint::*; use rustc_front::hir::*; -use utils::{get_parent_expr, in_macro, is_block_equal, is_exp_equal, span_lint, span_note_and_lint}; +use utils::SpanlessEq; +use utils::{get_parent_expr, in_macro, span_lint, span_note_and_lint}; /// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is /// `Warn` by default. @@ -55,7 +56,7 @@ impl LateLintPass for CopyAndPaste { fn lint_same_then_else(cx: &LateContext, expr: &Expr) { if let ExprIf(_, ref then_block, Some(ref else_expr)) = expr.node { if let ExprBlock(ref else_block) = else_expr.node { - if is_block_equal(cx, &then_block, &else_block, false) { + if SpanlessEq::new(cx).eq_block(&then_block, &else_block) { span_lint(cx, IF_SAME_THEN_ELSE, expr.span, "this if has the same then and else blocks"); } } @@ -75,7 +76,7 @@ fn lint_same_cond(cx: &LateContext, expr: &Expr) { for (n, i) in conds.iter().enumerate() { for j in conds.iter().skip(n+1) { - if is_exp_equal(cx, i, j, true) { + if SpanlessEq::new(cx).ignore_fn().eq_expr(i, j) { span_note_and_lint(cx, IFS_SAME_COND, j.span, "this if has the same condition as a previous if", i.span, "same as this"); } } diff --git a/src/entry.rs b/src/entry.rs index d5bb086fc..c2f2e956e 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,8 +1,9 @@ use rustc::lint::*; use rustc_front::hir::*; use syntax::codemap::Span; -use utils::{get_item_name, is_exp_equal, match_type, snippet, span_lint_and_then, walk_ptrs_ty}; +use utils::SpanlessEq; use utils::{BTREEMAP_PATH, HASHMAP_PATH}; +use utils::{get_item_name, match_type, snippet, span_lint_and_then, walk_ptrs_ty}; /// **What it does:** This lint checks for uses of `contains_key` + `insert` on `HashMap` or /// `BTreeMap`. @@ -89,7 +90,7 @@ fn check_for_insert(cx: &LateContext, span: Span, map: &Expr, key: &Expr, expr: params.len() == 3, name.node.as_str() == "insert", get_item_name(cx, map) == get_item_name(cx, &*params[0]), - is_exp_equal(cx, key, ¶ms[1], false) + SpanlessEq::new(cx).eq_expr(key, ¶ms[1]) ], { let help = if sole_expr { format!("{}.entry({}).or_insert({})", diff --git a/src/eq_op.rs b/src/eq_op.rs index aecd0693f..fc1cab2cd 100644 --- a/src/eq_op.rs +++ b/src/eq_op.rs @@ -2,7 +2,7 @@ use rustc::lint::*; use rustc_front::hir::*; use rustc_front::util as ast_util; -use utils::{is_exp_equal, span_lint}; +use utils::{SpanlessEq, span_lint}; /// **What it does:** This lint checks for equal operands to comparison, logical and bitwise, /// difference and division binary operators (`==`, `>`, etc., `&&`, `||`, `&`, `|`, `^`, `-` and @@ -31,7 +31,7 @@ impl LintPass for EqOp { 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_valid_operator(op) && is_exp_equal(cx, left, right, true) { + if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) { span_lint(cx, EQ_OP, e.span, diff --git a/src/strings.rs b/src/strings.rs index b78db7f4b..40d137101 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -7,7 +7,8 @@ use rustc::lint::*; use rustc_front::hir::*; use syntax::codemap::Spanned; -use utils::{is_exp_equal, match_type, span_lint, walk_ptrs_ty, get_parent_expr}; +use utils::{match_type, span_lint, walk_ptrs_ty, get_parent_expr}; +use utils::SpanlessEq; use utils::STRING_PATH; /// **What it does:** This lint matches code of the form `x = x + y` (without `let`!). @@ -84,7 +85,7 @@ impl LateLintPass for StringAdd { if let Some(ref p) = parent { if let ExprAssign(ref target, _) = p.node { // avoid duplicate matches - if is_exp_equal(cx, target, left, false) { + if SpanlessEq::new(cx).eq_expr(target, left) { return; } } @@ -113,7 +114,7 @@ fn is_string(cx: &LateContext, e: &Expr) -> bool { fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool { match src.node { - ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => is_exp_equal(cx, target, left, false), + ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => SpanlessEq::new(cx).eq_expr(target, left), ExprBlock(ref block) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target)) } diff --git a/src/utils/hir.rs b/src/utils/hir.rs new file mode 100644 index 000000000..95356772e --- /dev/null +++ b/src/utils/hir.rs @@ -0,0 +1,239 @@ +use consts::constant; +use rustc::lint::*; +use rustc_front::hir::*; +use syntax::ptr::P; + +/// Type used to check whether two ast are the same. This is different from the operator +/// `==` on ast types as this operator would compare true equality with ID and span. +/// +/// Note that some expressions kinds are not considered but could be added. +pub struct SpanlessEq<'a, 'tcx: 'a> { + /// Context used to evaluate constant expressions. + cx: &'a LateContext<'a, 'tcx>, + /// If is true, never consider as equal expressions containing fonction calls. + ignore_fn: bool, +} + +impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> { + pub fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + SpanlessEq { cx: cx, ignore_fn: false } + } + + pub fn ignore_fn(self) -> Self { + SpanlessEq { cx: self.cx, ignore_fn: true } + } + + /// Check whether two statements are the same. + pub fn eq_stmt(&self, left: &Stmt, right: &Stmt) -> bool { + match (&left.node, &right.node) { + (&StmtDecl(ref l, _), &StmtDecl(ref r, _)) => { + if let (&DeclLocal(ref l), &DeclLocal(ref r)) = (&l.node, &r.node) { + // TODO: tys + l.ty.is_none() && r.ty.is_none() && + both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) + } + else { + false + } + } + (&StmtExpr(ref l, _), &StmtExpr(ref r, _)) => self.eq_expr(l, r), + (&StmtSemi(ref l, _), &StmtSemi(ref r, _)) => self.eq_expr(l, r), + _ => false, + } + } + + /// Check whether two blocks are the same. + pub fn eq_block(&self, left: &Block, right: &Block) -> bool { + over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) && + both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) + } + + // ok, it’s a big function, but mostly one big match with simples cases + #[allow(cyclomatic_complexity)] + pub fn eq_expr(&self, left: &Expr, right: &Expr) -> bool { + if let (Some(l), Some(r)) = (constant(self.cx, left), constant(self.cx, right)) { + if l == r { + return true; + } + } + + match (&left.node, &right.node) { + (&ExprAddrOf(ref lmut, ref le), &ExprAddrOf(ref rmut, ref re)) => { + lmut == rmut && self.eq_expr(le, re) + } + (&ExprAgain(li), &ExprAgain(ri)) => { + both(&li, &ri, |l, r| l.node.name.as_str() == r.node.name.as_str()) + } + (&ExprAssign(ref ll, ref lr), &ExprAssign(ref rl, ref rr)) => { + self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + } + (&ExprAssignOp(ref lo, ref ll, ref lr), &ExprAssignOp(ref ro, ref rl, ref rr)) => { + lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + } + (&ExprBlock(ref l), &ExprBlock(ref r)) => { + self.eq_block(l, r) + } + (&ExprBinary(lop, ref ll, ref lr), &ExprBinary(rop, ref rl, ref rr)) => { + lop.node == rop.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + } + (&ExprBreak(li), &ExprBreak(ri)) => { + both(&li, &ri, |l, r| l.node.name.as_str() == r.node.name.as_str()) + } + (&ExprBox(ref l), &ExprBox(ref r)) => { + self.eq_expr(l, r) + } + (&ExprCall(ref lfun, ref largs), &ExprCall(ref rfun, ref rargs)) => { + !self.ignore_fn && + self.eq_expr(lfun, rfun) && + self.eq_exprs(largs, rargs) + } + (&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) => { + self.eq_expr(lx, rx) && self.eq_ty(lt, rt) + } + (&ExprField(ref lfexp, ref lfident), &ExprField(ref rfexp, ref rfident)) => { + lfident.node == rfident.node && self.eq_expr(lfexp, rfexp) + } + (&ExprIndex(ref la, ref li), &ExprIndex(ref ra, ref ri)) => { + self.eq_expr(la, ra) && self.eq_expr(li, ri) + } + (&ExprIf(ref lc, ref lt, ref le), &ExprIf(ref rc, ref rt, ref re)) => { + self.eq_expr(lc, rc) && + self.eq_block(lt, rt) && + both(le, re, |l, r| self.eq_expr(l, r)) + } + (&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node, + (&ExprMatch(ref le, ref la, ref ls), &ExprMatch(ref re, ref ra, ref rs)) => { + ls == rs && + self.eq_expr(le, re) && + over(la, ra, |l, r| { + self.eq_expr(&l.body, &r.body) && + both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r)) && + over(&l.pats, &r.pats, |l, r| self.eq_pat(l, r)) + }) + } + (&ExprMethodCall(ref lname, ref ltys, ref largs), &ExprMethodCall(ref rname, ref rtys, ref rargs)) => { + // TODO: tys + !self.ignore_fn && + lname.node == rname.node && + ltys.is_empty() && + rtys.is_empty() && + self.eq_exprs(largs, rargs) + } + (&ExprRange(ref lb, ref le), &ExprRange(ref rb, ref re)) => { + both(lb, rb, |l, r| self.eq_expr(l, r)) && + both(le, re, |l, r| self.eq_expr(l, r)) + } + (&ExprRepeat(ref le, ref ll), &ExprRepeat(ref re, ref rl)) => { + self.eq_expr(le, re) && self.eq_expr(ll, rl) + } + (&ExprRet(ref l), &ExprRet(ref r)) => { + both(l, r, |l, r| self.eq_expr(l, r)) + } + (&ExprPath(ref lqself, ref lsubpath), &ExprPath(ref rqself, ref rsubpath)) => { + both(lqself, rqself, |l, r| self.eq_qself(l, r)) && self.eq_path(lsubpath, rsubpath) + } + (&ExprTup(ref ltup), &ExprTup(ref rtup)) => self.eq_exprs(ltup, rtup), + (&ExprTupField(ref le, li), &ExprTupField(ref re, ri)) => { + li.node == ri.node && self.eq_expr(le, re) + } + (&ExprUnary(lop, ref le), &ExprUnary(rop, ref re)) => { + lop == rop && self.eq_expr(le, re) + } + (&ExprVec(ref l), &ExprVec(ref r)) => self.eq_exprs(l, r), + (&ExprWhile(ref lc, ref lb, ref ll), &ExprWhile(ref rc, ref rb, ref rl)) => { + self.eq_expr(lc, rc) && + self.eq_block(lb, rb) && + both(ll, rl, |l, r| l.name.as_str() == r.name.as_str()) + } + _ => false, + } + } + + fn eq_exprs(&self, left: &[P], right: &[P]) -> bool { + over(left, right, |l, r| self.eq_expr(l, r)) + } + + /// Check whether two patterns are the same. + pub fn eq_pat(&self, left: &Pat, right: &Pat) -> bool { + match (&left.node, &right.node) { + (&PatBox(ref l), &PatBox(ref r)) => { + self.eq_pat(l, r) + } + (&PatEnum(ref lp, ref la), &PatEnum(ref rp, ref ra)) => { + self.eq_path(lp, rp) && + both(la, ra, |l, r| { + over(l, r, |l, r| self.eq_pat(l, r)) + }) + } + (&PatIdent(ref lb, ref li, ref lp), &PatIdent(ref rb, ref ri, ref rp)) => { + lb == rb && li.node.name.as_str() == ri.node.name.as_str() && + both(lp, rp, |l, r| self.eq_pat(l, r)) + } + (&PatLit(ref l), &PatLit(ref r)) => { + self.eq_expr(l, r) + } + (&PatQPath(ref ls, ref lp), &PatQPath(ref rs, ref rp)) => { + self.eq_qself(ls, rs) && self.eq_path(lp, rp) + } + (&PatTup(ref l), &PatTup(ref r)) => { + over(l, r, |l, r| self.eq_pat(l, r)) + } + (&PatRange(ref ls, ref le), &PatRange(ref rs, ref re)) => { + self.eq_expr(ls, rs) && + self.eq_expr(le, re) + } + (&PatRegion(ref le, ref lm), &PatRegion(ref re, ref rm)) => { + lm == rm && self.eq_pat(le, re) + } + (&PatVec(ref ls, ref li, ref le), &PatVec(ref rs, ref ri, ref re)) => { + over(ls, rs, |l, r| self.eq_pat(l, r)) && + over(le, re, |l, r| self.eq_pat(l, r)) && + both(li, ri, |l, r| self.eq_pat(l, r)) + } + (&PatWild, &PatWild) => true, + _ => false, + } + } + + fn eq_path(&self, 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.as_str() == r.identifier.name.as_str() && l.parameters == r.parameters) + } + + fn eq_qself(&self, left: &QSelf, right: &QSelf) -> bool { + left.ty.node == right.ty.node && left.position == right.position + } + + fn eq_ty(&self, left: &Ty, right: &Ty) -> bool { + match (&left.node, &right.node) { + (&TyVec(ref lvec), &TyVec(ref rvec)) => self.eq_ty(lvec, rvec), + (&TyPtr(ref lmut), &TyPtr(ref rmut)) => lmut.mutbl == rmut.mutbl && self.eq_ty(&*lmut.ty, &*rmut.ty), + (&TyRptr(_, ref lrmut), &TyRptr(_, ref rrmut)) => { + lrmut.mutbl == rrmut.mutbl && self.eq_ty(&*lrmut.ty, &*rrmut.ty) + } + (&TyPath(ref lq, ref lpath), &TyPath(ref rq, ref rpath)) => { + both(lq, rq, |l, r| self.eq_qself(l, r)) && self.eq_path(lpath, rpath) + } + (&TyInfer, &TyInfer) => true, + _ => false, + } + } +} + +/// Check if the two `Option`s are both `None` or some equal values as per `eq_fn`. +fn both(l: &Option, r: &Option, 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))) +} + +/// Check if two slices are equal as per `eq_fn`. +fn over(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)) +} diff --git a/src/utils.rs b/src/utils/mod.rs similarity index 68% rename from src/utils.rs rename to src/utils/mod.rs index 9adbff90e..d666ee36b 100644 --- a/src/utils.rs +++ b/src/utils/mod.rs @@ -1,4 +1,3 @@ -use consts::constant; use reexport::*; use rustc::front::map::Node; use rustc::lint::{LintContext, LateContext, Level, Lint}; @@ -16,6 +15,8 @@ use syntax::codemap::{ExpnInfo, Span, ExpnFormat}; use syntax::errors::DiagnosticBuilder; use syntax::ptr::P; +mod hir; +pub use self::hir::SpanlessEq; pub type MethodArgs = HirVec>; // module DefPaths for certain structs/enums we check for @@ -591,230 +592,6 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } -/// Check whether two statements are the same. -/// See also `is_exp_equal`. -pub fn is_stmt_equal(cx: &LateContext, left: &Stmt, right: &Stmt, ignore_fn: bool) -> bool { - match (&left.node, &right.node) { - (&StmtDecl(ref l, _), &StmtDecl(ref r, _)) => { - if let (&DeclLocal(ref l), &DeclLocal(ref r)) = (&l.node, &r.node) { - // TODO: tys - l.ty.is_none() && r.ty.is_none() && - both(&l.init, &r.init, |l, r| is_exp_equal(cx, l, r, ignore_fn)) - } - else { - false - } - } - (&StmtExpr(ref l, _), &StmtExpr(ref r, _)) => is_exp_equal(cx, l, r, ignore_fn), - (&StmtSemi(ref l, _), &StmtSemi(ref r, _)) => is_exp_equal(cx, l, r, ignore_fn), - _ => false, - } -} - -/// Check whether two blocks are the same. -/// See also `is_exp_equal`. -pub fn is_block_equal(cx: &LateContext, left: &Block, right: &Block, ignore_fn: bool) -> bool { - over(&left.stmts, &right.stmts, |l, r| is_stmt_equal(cx, l, r, ignore_fn)) && - both(&left.expr, &right.expr, |l, r| is_exp_equal(cx, l, r, ignore_fn)) -} - -/// Check whether two pattern are the same. -/// See also `is_exp_equal`. -pub fn is_pat_equal(cx: &LateContext, left: &Pat, right: &Pat, ignore_fn: bool) -> bool { - match (&left.node, &right.node) { - (&PatBox(ref l), &PatBox(ref r)) => { - is_pat_equal(cx, l, r, ignore_fn) - } - (&PatEnum(ref lp, ref la), &PatEnum(ref rp, ref ra)) => { - is_path_equal(lp, rp) && - both(la, ra, |l, r| { - over(l, r, |l, r| is_pat_equal(cx, l, r, ignore_fn)) - }) - } - (&PatIdent(ref lb, ref li, ref lp), &PatIdent(ref rb, ref ri, ref rp)) => { - lb == rb && li.node.name.as_str() == ri.node.name.as_str() && - both(lp, rp, |l, r| is_pat_equal(cx, l, r, ignore_fn)) - } - (&PatLit(ref l), &PatLit(ref r)) => { - is_exp_equal(cx, l, r, ignore_fn) - } - (&PatQPath(ref ls, ref lp), &PatQPath(ref rs, ref rp)) => { - is_qself_equal(ls, rs) && is_path_equal(lp, rp) - } - (&PatTup(ref l), &PatTup(ref r)) => { - over(l, r, |l, r| is_pat_equal(cx, l, r, ignore_fn)) - } - (&PatRange(ref ls, ref le), &PatRange(ref rs, ref re)) => { - is_exp_equal(cx, ls, rs, ignore_fn) && - is_exp_equal(cx, le, re, ignore_fn) - } - (&PatRegion(ref le, ref lm), &PatRegion(ref re, ref rm)) => { - lm == rm && is_pat_equal(cx, le, re, ignore_fn) - } - (&PatVec(ref ls, ref li, ref le), &PatVec(ref rs, ref ri, ref re)) => { - over(ls, rs, |l, r| is_pat_equal(cx, l, r, ignore_fn)) && - over(le, re, |l, r| is_pat_equal(cx, l, r, ignore_fn)) && - both(li, ri, |l, r| is_pat_equal(cx, l, r, ignore_fn)) - } - (&PatWild, &PatWild) => true, - _ => false, - } -} - -/// Check whether two expressions are the same. This is different from the operator `==` on -/// expression as this operator would compare true equality with ID and span. -/// If `ignore_fn` is true, never consider as equal fonction calls. -/// -/// Note that some expression kinds are not considered but could be added. -#[allow(cyclomatic_complexity)] // ok, it’s a big function, but mostly one big match with simples cases -pub fn is_exp_equal(cx: &LateContext, left: &Expr, right: &Expr, ignore_fn: bool) -> bool { - if let (Some(l), Some(r)) = (constant(cx, left), constant(cx, right)) { - if l == r { - return true; - } - } - - match (&left.node, &right.node) { - (&ExprAddrOf(ref lmut, ref le), &ExprAddrOf(ref rmut, ref re)) => { - lmut == rmut && is_exp_equal(cx, le, re, ignore_fn) - } - (&ExprAgain(li), &ExprAgain(ri)) => { - both(&li, &ri, |l, r| l.node.name.as_str() == r.node.name.as_str()) - } - (&ExprAssign(ref ll, ref lr), &ExprAssign(ref rl, ref rr)) => { - is_exp_equal(cx, ll, rl, ignore_fn) && is_exp_equal(cx, lr, rr, ignore_fn) - } - (&ExprAssignOp(ref lo, ref ll, ref lr), &ExprAssignOp(ref ro, ref rl, ref rr)) => { - lo.node == ro.node && is_exp_equal(cx, ll, rl, ignore_fn) && is_exp_equal(cx, lr, rr, ignore_fn) - } - (&ExprBlock(ref l), &ExprBlock(ref r)) => { - is_block_equal(cx, l, r, ignore_fn) - } - (&ExprBinary(lop, ref ll, ref lr), &ExprBinary(rop, ref rl, ref rr)) => { - lop.node == rop.node && is_exp_equal(cx, ll, rl, ignore_fn) && is_exp_equal(cx, lr, rr, ignore_fn) - } - (&ExprBreak(li), &ExprBreak(ri)) => { - both(&li, &ri, |l, r| l.node.name.as_str() == r.node.name.as_str()) - } - (&ExprBox(ref l), &ExprBox(ref r)) => { - is_exp_equal(cx, l, r, ignore_fn) - } - (&ExprCall(ref lfun, ref largs), &ExprCall(ref rfun, ref rargs)) => { - !ignore_fn && - is_exp_equal(cx, lfun, rfun, ignore_fn) && - is_exps_equal(cx, largs, rargs, ignore_fn) - } - (&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) => { - is_exp_equal(cx, lx, rx, ignore_fn) && is_cast_ty_equal(lt, rt) - } - (&ExprField(ref lfexp, ref lfident), &ExprField(ref rfexp, ref rfident)) => { - lfident.node == rfident.node && is_exp_equal(cx, lfexp, rfexp, ignore_fn) - } - (&ExprIndex(ref la, ref li), &ExprIndex(ref ra, ref ri)) => { - is_exp_equal(cx, la, ra, ignore_fn) && is_exp_equal(cx, li, ri, ignore_fn) - } - (&ExprIf(ref lc, ref lt, ref le), &ExprIf(ref rc, ref rt, ref re)) => { - is_exp_equal(cx, lc, rc, ignore_fn) && - is_block_equal(cx, lt, rt, ignore_fn) && - both(le, re, |l, r| is_exp_equal(cx, l, r, ignore_fn)) - } - (&ExprIndex(ref la, ref li), &ExprIndex(ref ra, ref ri)) => { - is_exp_equal(cx, la, ra) && is_exp_equal(cx, li, ri) - } - (&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node, - (&ExprMatch(ref le, ref la, ref ls), &ExprMatch(ref re, ref ra, ref rs)) => { - ls == rs && - is_exp_equal(cx, le, re, ignore_fn) && - over(la, ra, |l, r| { - is_exp_equal(cx, &l.body, &r.body, ignore_fn) && - both(&l.guard, &r.guard, |l, r| is_exp_equal(cx, l, r, ignore_fn)) && - over(&l.pats, &r.pats, |l, r| is_pat_equal(cx, l, r, ignore_fn)) - }) - } - (&ExprMethodCall(ref lname, ref ltys, ref largs), &ExprMethodCall(ref rname, ref rtys, ref rargs)) => { - // TODO: tys - !ignore_fn && - lname.node == rname.node && - ltys.is_empty() && - rtys.is_empty() && - is_exps_equal(cx, largs, rargs, ignore_fn) - } - (&ExprRange(ref lb, ref le), &ExprRange(ref rb, ref re)) => { - both(lb, rb, |l, r| is_exp_equal(cx, l, r, ignore_fn)) && - both(le, re, |l, r| is_exp_equal(cx, l, r, ignore_fn)) - } - (&ExprRepeat(ref le, ref ll), &ExprRepeat(ref re, ref rl)) => { - is_exp_equal(cx, le, re, ignore_fn) && is_exp_equal(cx, ll, rl, ignore_fn) - } - (&ExprRet(ref l), &ExprRet(ref r)) => { - both(l, r, |l, r| is_exp_equal(cx, l, r, ignore_fn)) - } - (&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, ignore_fn), - (&ExprTupField(ref le, li), &ExprTupField(ref re, ri)) => { - li.node == ri.node && is_exp_equal(cx, le, re, ignore_fn) - } - (&ExprUnary(lop, ref le), &ExprUnary(rop, ref re)) => { - lop == rop && is_exp_equal(cx, le, re, ignore_fn) - } - (&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r, ignore_fn), - (&ExprWhile(ref lc, ref lb, ref ll), &ExprWhile(ref rc, ref rb, ref rl)) => { - is_exp_equal(cx, lc, rc, ignore_fn) && - is_block_equal(cx, lb, rb, ignore_fn) && - both(ll, rl, |l, r| l.name.as_str() == r.name.as_str()) - } - _ => false, - } -} - -fn is_exps_equal(cx: &LateContext, left: &[P], right: &[P], ignore_fn: bool) -> bool { - over(left, right, |l, r| is_exp_equal(cx, l, r, ignore_fn)) -} - -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.as_str() == r.identifier.name.as_str() && l.parameters == r.parameters) -} - -fn is_qself_equal(left: &QSelf, right: &QSelf) -> bool { - left.ty.node == right.ty.node && left.position == right.position -} - -/// Check if two slices are equal as per `eq_fn`. -pub fn over(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)) -} - -/// Check if the two `Option`s are both `None` or some equal values as per `eq_fn`. -pub fn both(l: &Option, r: &Option, 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_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) - } - (&TyInfer, &TyInfer) => true, - _ => false, - } -} - /// Return the pre-expansion span if is this comes from an expansion of the macro `name`. pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option { loop {