Fix ICE in swap_binop() (#16659)

Make `swap_binop()` a method of `HirEqInterExpr`, and use the proper
typeck to check the type of an expression.

changelog: none (fix of a regression, should be backported)

Fixes rust-lang/rust-clippy#16505

<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_SUMMARY_START -->

### Summary Notes

-
[Beta-nomination](https://github.com/rust-lang/rust-clippy/pull/16659#issuecomment-3993931812)
by [samueltardieu](https://github.com/samueltardieu)

*Managed by `@rustbot`—see
[help](https://forge.rust-lang.org/triagebot/note.html) for details*

<!-- TRIAGEBOT_SUMMARY_END -->
<!-- TRIAGEBOT_END -->
This commit is contained in:
Jason Newcomb
2026-03-04 21:01:08 +00:00
committed by GitHub
2 changed files with 52 additions and 34 deletions
+35 -34
View File
@@ -505,7 +505,7 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
(ExprKind::Block(l, _), ExprKind::Block(r, _)) => self.eq_block(l, r),
(ExprKind::Binary(l_op, ll, lr), ExprKind::Binary(r_op, rl, rr)) => {
l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|| swap_binop(self.inner.cx, l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
|| self.swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
})
},
@@ -921,6 +921,40 @@ fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
self.right_ctxt = right;
true
}
fn swap_binop<'a>(
&self,
binop: BinOpKind,
lhs: &'a Expr<'a>,
rhs: &'a Expr<'a>,
) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
match binop {
// `==` and `!=`, are commutative
BinOpKind::Eq | BinOpKind::Ne => Some((binop, rhs, lhs)),
// Comparisons can be reversed
BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
// Non-commutative operators
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Rem | BinOpKind::Sub | BinOpKind::Div => None,
// We know that those operators are commutative for primitive types,
// and we don't assume anything for other types
BinOpKind::Mul
| BinOpKind::Add
| BinOpKind::And
| BinOpKind::Or
| BinOpKind::BitAnd
| BinOpKind::BitXor
| BinOpKind::BitOr => self.inner.maybe_typeck_results.and_then(|(typeck_lhs, _)| {
typeck_lhs
.expr_ty_adjusted(lhs)
.peel_refs()
.is_primitive()
.then_some((binop, rhs, lhs))
}),
}
}
}
/// Some simple reductions like `{ return }` => `return`
@@ -966,39 +1000,6 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
}
}
fn swap_binop<'a>(
cx: &LateContext<'_>,
binop: BinOpKind,
lhs: &'a Expr<'a>,
rhs: &'a Expr<'a>,
) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
match binop {
// `==` and `!=`, are commutative
BinOpKind::Eq | BinOpKind::Ne => Some((binop, rhs, lhs)),
// Comparisons can be reversed
BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
// Non-commutative operators
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Rem | BinOpKind::Sub | BinOpKind::Div => None,
// We know that those operators are commutative for primitive types,
// and we don't assume anything for other types
BinOpKind::Mul
| BinOpKind::Add
| BinOpKind::And
| BinOpKind::Or
| BinOpKind::BitAnd
| BinOpKind::BitXor
| BinOpKind::BitOr => cx
.typeck_results()
.expr_ty_adjusted(lhs)
.peel_refs()
.is_primitive()
.then_some((binop, rhs, lhs)),
}
}
/// Checks if the two `Option`s are both `None` or some equal values as per
/// `eq_fn`.
pub fn both<X>(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+17
View File
@@ -305,3 +305,20 @@ fn issue16416_prim(x: bool, a: u32, b: u32) {
//~v if_same_then_else
_ = if x { a >= b } else { b <= a };
}
mod issue16505 {
macro_rules! foo {
(< $hi:literal : $lo:literal > | $N:tt bits) => {{
const NEW_N_: usize = $hi - $lo + 1;
NEW_N_
}};
}
fn bar(x: bool) {
_ = if x {
foo!(<2:0> | 3 bits) == foo!(<3:1> | 3 bits)
} else {
foo!(<3:1> | 3 bits) == foo!(<2:0> | 3 bits)
};
}
}