Fix ICE in match_same_arms (#16685)

The wrong `TypeckResults` was used in the fallback equality function
passed by the `match_same_arms` and `filter_map` lints. Previously,
those fallback functions had no way of using the proper `TypeckResults`.
Those (one per expression being compared) are now passed to the
registered fallback function.

changelog: [`match_same_arms`]: fix internal compiler error

Fixes rust-lang/rust-clippy#16678
Fixes rust-lang/rust-clippy#16686

<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_SUMMARY_START -->

### Summary Notes

- [Beta
nomination](https://github.com/rust-lang/rust-clippy/pull/16685#issuecomment-4017155418)
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-07 22:48:24 +00:00
committed by Josh Stone
parent 6ae56934ab
commit 0c9d5f0d25
5 changed files with 59 additions and 9 deletions
@@ -12,7 +12,7 @@
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExpr, PatExprKind, PatKind, RangeEnd};
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
use rustc_middle::ty::{self, TypeckResults};
use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol};
use super::MATCH_SAME_ARMS;
@@ -61,7 +61,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| {
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
let eq_fallback = |a_typeck_results: &TypeckResults<'tcx>,
a: &Expr<'_>,
b_typeck_results: &TypeckResults<'tcx>,
b: &Expr<'_>| {
if let Some(a_id) = a.res_local_id()
&& let Some(b_id) = b.res_local_id()
&& let entry = match local_map.entry(a_id) {
@@ -71,7 +74,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
}
// the names technically don't have to match; this makes the lint more conservative
&& cx.tcx.hir_name(a_id) == cx.tcx.hir_name(b_id)
&& cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b)
&& a_typeck_results.expr_ty(a) == b_typeck_results.expr_ty(b)
&& pat_contains_local(lhs.pat, a_id)
&& pat_contains_local(rhs.pat, b_id)
{
@@ -9,6 +9,7 @@
use rustc_hir::def::Res;
use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty::TypeckResults;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::Span;
use rustc_span::symbol::{Ident, Symbol};
@@ -136,7 +137,9 @@ pub fn check_map_call(
// .map(|y| y[.acceptable_method()].unwrap())
&& let simple_equal = (receiver.res_local_id() == Some(filter_param_id)
&& map_arg_peeled.res_local_id() == Some(map_param_id))
&& let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| {
&& let eq_fallback =
(|a_typeck_results: &TypeckResults<'tcx>, a: &Expr<'_>,
b_typeck_results: &TypeckResults<'tcx>, b: &Expr<'_>| {
// in `filter(|x| ..)`, replace `*x` with `x`
let a_path = if !is_filter_param_ref
&& let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind
@@ -144,7 +147,7 @@ pub fn check_map_call(
// let the filter closure arg and the map closure arg be equal
a_path.res_local_id() == Some(filter_param_id)
&& b.res_local_id() == Some(map_param_id)
&& cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
&& a_typeck_results.expr_ty_adjusted(a) == b_typeck_results.expr_ty_adjusted(b)
})
&& (simple_equal
|| SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled))
+16 -4
View File
@@ -26,7 +26,8 @@
/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
/// other conditions would make them equal.
type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
type SpanlessEqCallback<'a, 'tcx> =
dyn FnMut(&TypeckResults<'tcx>, &Expr<'_>, &TypeckResults<'tcx>, &Expr<'_>) -> bool + 'a;
/// Determines how paths are hashed and compared for equality.
#[derive(Copy, Clone, Debug, Default)]
@@ -59,7 +60,7 @@ pub struct SpanlessEq<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
allow_side_effects: bool,
expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
expr_fallback: Option<Box<SpanlessEqCallback<'a, 'tcx>>>,
path_check: PathCheck,
}
@@ -94,7 +95,10 @@ pub fn paths_by_resolution(self) -> Self {
}
#[must_use]
pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
pub fn expr_fallback(
self,
expr_fallback: impl FnMut(&TypeckResults<'tcx>, &Expr<'_>, &TypeckResults<'tcx>, &Expr<'_>) -> bool + 'a,
) -> Self {
Self {
expr_fallback: Some(Box::new(expr_fallback)),
..self
@@ -639,7 +643,15 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
) => false,
};
(is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
|| self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right))
|| self
.inner
.maybe_typeck_results
.is_some_and(|(left_typeck_results, right_typeck_results)| {
self.inner
.expr_fallback
.as_mut()
.is_some_and(|f| f(left_typeck_results, left, right_typeck_results, right))
})
}
fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
@@ -140,3 +140,19 @@ fn main() {
_ => false,
};
}
fn issue16678() {
// ICE in Rust 1.94.0
match true {
true => {
fn wrapper(_arg: ()) {
_arg;
}
},
false => {
fn wrapper(_arg: ()) {
_arg;
}
},
}
}
@@ -149,3 +149,19 @@ fn main() {
_ => false,
};
}
fn issue16678() {
// ICE in Rust 1.94.0
match true {
true => {
fn wrapper(_arg: ()) {
_arg;
}
},
false => {
fn wrapper(_arg: ()) {
_arg;
}
},
}
}