diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 5b0de80e67fd7..a8312a04f36f8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -12,7 +12,7 @@ use rustc_hir::def_id::DefId; 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 = 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) { diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 7b10c37de42df..d2e593fc17df8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -9,6 +9,7 @@ use rustc_hir as hir; 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 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // .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 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // 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)) diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 74db7ae03ba0b..994ad446593af 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4761,6 +4761,8 @@ pub struct Methods { allowed_dotfiles: FxHashSet<&'static str>, format_args: FormatArgsStorage, allow_unwrap_types: Vec, + unwrap_allowed_ids: FxHashSet, + unwrap_allowed_aliases: Vec, } impl Methods { @@ -4778,6 +4780,8 @@ impl Methods { allowed_dotfiles, format_args, allow_unwrap_types: conf.allow_unwrap_types.clone(), + unwrap_allowed_ids: FxHashSet::default(), + unwrap_allowed_aliases: Vec::new(), } } } @@ -4953,6 +4957,19 @@ pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<' } impl<'tcx> LateLintPass<'tcx> for Methods { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for s in &self.allow_unwrap_types { + let def_ids = clippy_utils::paths::lookup_path_str(cx.tcx, clippy_utils::paths::PathNS::Type, s); + for def_id in def_ids { + if cx.tcx.def_kind(def_id) == rustc_hir::def::DefKind::TyAlias { + self.unwrap_allowed_aliases.push(def_id); + } else { + self.unwrap_allowed_ids.insert(def_id); + } + } + } + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; @@ -4976,7 +4993,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self.allow_expect_in_tests, self.allow_unwrap_in_consts, self.allow_expect_in_consts, - &self.allow_unwrap_types, + &self.unwrap_allowed_ids, + &self.unwrap_allowed_aliases, ); }, ExprKind::MethodCall(..) => { @@ -5730,7 +5748,8 @@ impl Methods { false, self.allow_expect_in_consts, self.allow_expect_in_tests, - &self.allow_unwrap_types, + &self.unwrap_allowed_ids, + &self.unwrap_allowed_aliases, unwrap_expect_used::Variant::Expect, ); expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); @@ -5743,7 +5762,8 @@ impl Methods { true, self.allow_expect_in_consts, self.allow_expect_in_tests, - &self.allow_unwrap_types, + &self.unwrap_allowed_ids, + &self.unwrap_allowed_aliases, unwrap_expect_used::Variant::Expect, ); }, @@ -5764,7 +5784,8 @@ impl Methods { false, self.allow_unwrap_in_consts, self.allow_unwrap_in_tests, - &self.allow_unwrap_types, + &self.unwrap_allowed_ids, + &self.unwrap_allowed_aliases, unwrap_expect_used::Variant::Unwrap, ); }, @@ -5776,7 +5797,8 @@ impl Methods { true, self.allow_unwrap_in_consts, self.allow_unwrap_in_tests, - &self.allow_unwrap_types, + &self.unwrap_allowed_ids, + &self.unwrap_allowed_aliases, unwrap_expect_used::Variant::Unwrap, ); }, diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs index 6d8777cbc23f8..c07c932176462 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs @@ -44,7 +44,8 @@ pub(super) fn check( is_err: bool, allow_unwrap_in_consts: bool, allow_unwrap_in_tests: bool, - allow_unwrap_types: &[String], + unwrap_allowed_ids: &rustc_data_structures::fx::FxHashSet, + unwrap_allowed_aliases: &[rustc_hir::def_id::DefId], variant: Variant, ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); @@ -66,51 +67,39 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; - let ty_name = ty.to_string(); - if allow_unwrap_types - .iter() - .any(|allowed_type| ty_name.starts_with(allowed_type) || ty_name == *allowed_type) + if let ty::Adt(adt, _) = ty.kind() + && unwrap_allowed_ids.contains(&adt.did()) { return; } - for s in allow_unwrap_types { - let def_ids = clippy_utils::paths::lookup_path_str(cx.tcx, clippy_utils::paths::PathNS::Type, s); - for def_id in def_ids { - if let ty::Adt(adt, _) = ty.kind() - && adt.did() == def_id - { - return; - } - if cx.tcx.def_kind(def_id) == DefKind::TyAlias { - let alias_ty = cx.tcx.type_of(def_id).instantiate_identity(); - if let (ty::Adt(adt, substs), ty::Adt(alias_adt, alias_substs)) = (ty.kind(), alias_ty.kind()) - && adt.did() == alias_adt.did() - { - let mut all_match = true; - for (arg, alias_arg) in substs.iter().zip(alias_substs.iter()) { - if let (Some(arg_ty), Some(alias_arg_ty)) = (arg.as_type(), alias_arg.as_type()) { - if matches!(alias_arg_ty.kind(), ty::Param(_)) { - continue; - } - if let (ty::Adt(arg_adt, _), ty::Adt(alias_arg_adt, _)) = - (arg_ty.peel_refs().kind(), alias_arg_ty.peel_refs().kind()) - { - if arg_adt.did() != alias_arg_adt.did() { - all_match = false; - break; - } - } else if arg_ty != alias_arg_ty { - all_match = false; - break; - } - } + for &def_id in unwrap_allowed_aliases { + let alias_ty = cx.tcx.type_of(def_id).instantiate_identity(); + if let (ty::Adt(adt, substs), ty::Adt(alias_adt, alias_substs)) = (ty.kind(), alias_ty.kind()) + && adt.did() == alias_adt.did() + { + let mut all_match = true; + for (arg, alias_arg) in substs.iter().zip(alias_substs.iter()) { + if let (Some(arg_ty), Some(alias_arg_ty)) = (arg.as_type(), alias_arg.as_type()) { + if matches!(alias_arg_ty.kind(), ty::Param(_)) { + continue; } - if all_match { - return; + if let (ty::Adt(arg_adt, _), ty::Adt(alias_arg_adt, _)) = + (arg_ty.peel_refs().kind(), alias_arg_ty.peel_refs().kind()) + { + if arg_adt.did() != alias_arg_adt.did() { + all_match = false; + break; + } + } else if arg_ty != alias_arg_ty { + all_match = false; + break; } } } + if all_match { + return; + } } } @@ -149,7 +138,8 @@ pub(super) fn check_call( allow_unwrap_in_tests: bool, allow_expect_in_consts: bool, allow_expect_in_tests: bool, - allow_unwrap_types: &[String], + unwrap_allowed_ids: &rustc_data_structures::fx::FxHashSet, + unwrap_allowed_aliases: &[rustc_hir::def_id::DefId], ) { let Some(recv) = args.first() else { return; @@ -167,7 +157,8 @@ pub(super) fn check_call( false, allow_unwrap_in_consts, allow_unwrap_in_tests, - allow_unwrap_types, + unwrap_allowed_ids, + unwrap_allowed_aliases, Variant::Unwrap, ); }, @@ -179,7 +170,8 @@ pub(super) fn check_call( false, allow_expect_in_consts, allow_expect_in_tests, - allow_unwrap_types, + unwrap_allowed_ids, + unwrap_allowed_aliases, Variant::Expect, ); }, @@ -191,7 +183,8 @@ pub(super) fn check_call( true, allow_unwrap_in_consts, allow_unwrap_in_tests, - allow_unwrap_types, + unwrap_allowed_ids, + unwrap_allowed_aliases, Variant::Unwrap, ); }, @@ -203,7 +196,8 @@ pub(super) fn check_call( true, allow_expect_in_consts, allow_expect_in_tests, - allow_unwrap_types, + unwrap_allowed_ids, + unwrap_allowed_aliases, Variant::Expect, ); }, diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 3ff40f2fb722b..1018b1eb37b0d 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -26,7 +26,8 @@ use std::slice; /// 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>>, + expr_fallback: Option>>, path_check: PathCheck, } @@ -94,7 +95,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } #[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 @@ -505,7 +509,7 @@ impl HirEqInterExpr<'_, '_, '_> { (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) }) }, @@ -639,7 +643,15 @@ impl HirEqInterExpr<'_, '_, '_> { ) => 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 { @@ -921,6 +933,40 @@ impl HirEqInterExpr<'_, '_, '_> { 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 +1012,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(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs index 6e0e2c5ea7202..6982a9d143e4c 100644 --- a/src/tools/clippy/tests/ui/if_same_then_else.rs +++ b/src/tools/clippy/tests/ui/if_same_then_else.rs @@ -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) + }; + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms.fixed b/src/tools/clippy/tests/ui/match_same_arms.fixed index 31684a5759fe9..8b16fd3193f5f 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms.fixed @@ -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; + } + }, + } +} diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs index 39bee01bac22b..3b2d585c579d3 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.rs +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -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; + } + }, + } +}