Refactor Expr comparisons

This commit is contained in:
mcarton 2016-02-06 20:13:25 +01:00
parent e1c7914c2e
commit 91c16fc8e6
6 changed files with 254 additions and 235 deletions

View File

@ -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");
}
}

View File

@ -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, &params[1], false)
SpanlessEq::new(cx).eq_expr(key, &params[1])
], {
let help = if sole_expr {
format!("{}.entry({}).or_insert({})",

View File

@ -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,

View File

@ -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))
}

239
src/utils/hir.rs Normal file
View File

@ -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, its 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<Expr>], right: &[P<Expr>]) -> 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<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)))
}
/// Check if two slices are equal as per `eq_fn`.
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))
}

View File

@ -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<P<Expr>>;
// module DefPaths for certain structs/enums we check for
@ -591,230 +592,6 @@ fn parse_attrs<F: FnMut(u64)>(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, its 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<Expr>], right: &[P<Expr>], 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<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))
}
/// Check if the two `Option`s are both `None` or some equal values as per `eq_fn`.
pub 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_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<Span> {
loop {