diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ad0c901ede0c..aab22f334775 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1484,7 +1484,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> { let e1 = self.lower_expr_mut(e1); let e2 = self.lower_expr_mut(e2); - let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span); + let fn_path = self.make_lang_item_qpath(hir::LangItem::RangeInclusiveNew, span, None); let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path))); hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2]) } @@ -1570,7 +1570,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); hir::ExprKind::Struct( - self.arena.alloc(hir::QPath::LangItem(lang_item, span)), + self.arena.alloc(self.make_lang_item_qpath(lang_item, span, None)), fields, hir::StructTailExpr::None, ) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 38cb12cf24bc..837acb7208d2 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -23,7 +23,9 @@ use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Spanned; -use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{ + BytePos, DUMMY_SP, DesugaringKind, ErrorGuaranteed, Ident, Span, Symbol, kw, sym, +}; use rustc_target::asm::InlineAsmRegOrRegClass; use smallvec::SmallVec; use thin_vec::ThinVec; @@ -2600,102 +2602,21 @@ impl Expr<'_> { ) => path1.res == path2.res, ( ExprKind::Struct( - QPath::LangItem(LangItem::RangeTo, _), - [val1], + &QPath::Resolved(None, &Path { res: Res::Def(_, path1_def_id), .. }), + args1, StructTailExpr::None, ), ExprKind::Struct( - QPath::LangItem(LangItem::RangeTo, _), - [val2], - StructTailExpr::None, - ), - ) - | ( - ExprKind::Struct( - QPath::LangItem(LangItem::RangeToInclusive, _), - [val1], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::RangeToInclusive, _), - [val2], - StructTailExpr::None, - ), - ) - | ( - ExprKind::Struct( - QPath::LangItem(LangItem::RangeToInclusiveCopy, _), - [val1], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::RangeToInclusiveCopy, _), - [val2], - StructTailExpr::None, - ), - ) - | ( - ExprKind::Struct( - QPath::LangItem(LangItem::RangeFrom, _), - [val1], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::RangeFrom, _), - [val2], - StructTailExpr::None, - ), - ) - | ( - ExprKind::Struct( - QPath::LangItem(LangItem::RangeFromCopy, _), - [val1], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::RangeFromCopy, _), - [val2], - StructTailExpr::None, - ), - ) => val1.expr.equivalent_for_indexing(val2.expr), - ( - ExprKind::Struct( - QPath::LangItem(LangItem::Range, _), - [val1, val3], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::Range, _), - [val2, val4], - StructTailExpr::None, - ), - ) - | ( - ExprKind::Struct( - QPath::LangItem(LangItem::RangeCopy, _), - [val1, val3], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::RangeCopy, _), - [val2, val4], - StructTailExpr::None, - ), - ) - | ( - ExprKind::Struct( - QPath::LangItem(LangItem::RangeInclusiveCopy, _), - [val1, val3], - StructTailExpr::None, - ), - ExprKind::Struct( - QPath::LangItem(LangItem::RangeInclusiveCopy, _), - [val2, val4], + &QPath::Resolved(None, &Path { res: Res::Def(_, path2_def_id), .. }), + args2, StructTailExpr::None, ), ) => { - val1.expr.equivalent_for_indexing(val2.expr) - && val3.expr.equivalent_for_indexing(val4.expr) + path2_def_id == path1_def_id + && is_range_literal(self) + && is_range_literal(other) + && std::iter::zip(args1, args2) + .all(|(a, b)| a.expr.equivalent_for_indexing(b.expr)) } _ => false, } @@ -2713,30 +2634,29 @@ impl Expr<'_> { /// Checks if the specified expression is a built-in range literal. /// (See: `LoweringContext::lower_expr()`). pub fn is_range_literal(expr: &Expr<'_>) -> bool { - match expr.kind { - // All built-in range literals but `..=` and `..` desugar to `Struct`s. - ExprKind::Struct(ref qpath, _, _) => matches!( - **qpath, - QPath::LangItem( - LangItem::Range - | LangItem::RangeTo - | LangItem::RangeFrom - | LangItem::RangeFull - | LangItem::RangeToInclusive - | LangItem::RangeCopy - | LangItem::RangeFromCopy - | LangItem::RangeInclusiveCopy - | LangItem::RangeToInclusiveCopy, - .. - ) - ), - - // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. - ExprKind::Call(ref func, _) => { - matches!(func.kind, ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..))) - } - - _ => false, + if let ExprKind::Struct(QPath::Resolved(None, path), _, StructTailExpr::None) = expr.kind + && let [.., segment] = path.segments + && let sym::RangeFrom + | sym::RangeFull + | sym::Range + | sym::RangeToInclusive + | sym::RangeTo + | sym::RangeFromCopy + | sym::RangeCopy + | sym::RangeInclusiveCopy + | sym::RangeToInclusiveCopy = segment.ident.name + && expr.span.is_desugaring(DesugaringKind::RangeExpr) + { + true + } else if let ExprKind::Call(func, _) = &expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let [.., segment] = path.segments + && let sym::range_inclusive_new = segment.ident.name + && expr.span.is_desugaring(DesugaringKind::RangeExpr) + { + true + } else { + false } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index aff663ba8f59..38216336cce8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -19,7 +19,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, HirId, QPath, find_attr}; +use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal}; use rustc_hir_analysis::NoVariantNamed; use rustc_hir_analysis::hir_ty_lowering::{FeedConstTy, HirTyLowerer as _}; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk, RegionVariableOrigin}; @@ -2506,9 +2506,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args: GenericArgsRef<'tcx>, mut err: Diag<'_>, ) { - // I don't use 'is_range_literal' because only double-sided, half-open ranges count. - if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) = - last_expr_field.expr.kind + if is_range_literal(last_expr_field.expr) + && let ExprKind::Struct(&qpath, [range_start, range_end], _) = last_expr_field.expr.kind + && self.tcx.qpath_is_lang_item(qpath, LangItem::Range) && let variant_field = variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident) && let range_def_id = self.tcx.lang_items().range_struct() diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 2404ec4c706d..4a3237365d6f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1032,8 +1032,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // inherent impl, we need to record the // `T` for posterity (see `UserSelfTy` for // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw; - user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty }); + // Generated desugaring code may have a path without a self. + user_self_ty = self_ty.map(|self_ty| UserSelfTy { + impl_def_id: container_id, + self_ty: self_ty.raw, + }); } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 4d1c7be39197..88a38060091d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -8,7 +8,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, lis use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath, is_range_literal}; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; use rustc_index::IndexVec; @@ -2052,8 +2052,9 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { fn detect_dotdot(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'tcx>) { if let ty::Adt(adt, _) = ty.kind() && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) - && let hir::ExprKind::Struct(hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], _) = - expr.kind + && is_range_literal(expr) + && let hir::ExprKind::Struct(&path, [], _) = expr.kind + && self.tcx().qpath_is_lang_item(path, hir::LangItem::RangeFull) { // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax // from default field values, which is not supported on tuples. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d097f8ab8485..28bd3e7e8d5b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -11,7 +11,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind, - WherePredicateKind, expr_needs_parens, + WherePredicateKind, expr_needs_parens, is_range_literal, }; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::suggest_impl_trait; @@ -1664,7 +1664,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } match expr.kind { - ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { + ExprKind::Struct(&qpath, [start, end], _) + if is_range_literal(expr) + && self.tcx.qpath_is_lang_item(qpath, LangItem::Range) => + { err.span_suggestion_verbose( start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()), "remove the unnecessary `.` operator for a floating point literal", @@ -1673,24 +1676,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); true } - ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + ExprKind::Struct(&qpath, [arg], _) + if is_range_literal(expr) + && let Some(qpath @ (LangItem::RangeFrom | LangItem::RangeTo)) = + self.tcx.qpath_lang_item(qpath) => + { let range_span = expr.span.parent_callsite().unwrap(); - err.span_suggestion_verbose( - range_span.with_lo(start.expr.span.hi()), - "remove the unnecessary `.` operator for a floating point literal", - '.', - Applicability::MaybeIncorrect, - ); - true - } - ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { - let range_span = expr.span.parent_callsite().unwrap(); - err.span_suggestion_verbose( - range_span.until(end.expr.span), - "remove the unnecessary `.` operator and add an integer part for a floating point literal", - "0.", - Applicability::MaybeIncorrect, - ); + match qpath { + LangItem::RangeFrom => { + err.span_suggestion_verbose( + range_span.with_lo(arg.expr.span.hi()), + "remove the unnecessary `.` operator for a floating point literal", + '.', + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_suggestion_verbose( + range_span.until(arg.expr.span), + "remove the unnecessary `.` operator and add an integer part for a floating point literal", + "0.", + Applicability::MaybeIncorrect, + ); + } + } true } ExprKind::Lit(Spanned { @@ -3497,11 +3506,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !hir::is_range_literal(expr) { return; } - let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) = - expr.kind - else { + let hir::ExprKind::Struct(&qpath, [start, end], _) = expr.kind else { return; }; + if !self.tcx.qpath_is_lang_item(qpath, LangItem::Range) { + return; + } if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) { // Ignore `Foo { field: a..Default::default() }` return; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 8f2e06dd7127..11ed1657d879 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -19,7 +19,9 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir, ExprKind, HirId, Node, PathSegment, QPath, find_attr}; +use rustc_hir::{ + self as hir, ExprKind, HirId, Node, PathSegment, QPath, find_attr, is_range_literal, +}; use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin}; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; @@ -2412,22 +2414,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let SelfSource::MethodCall(expr) = source { for (_, parent) in tcx.hir_parent_iter(expr.hir_id).take(5) { if let Node::Expr(parent_expr) = parent { + if !is_range_literal(parent_expr) { + continue; + } let lang_item = match parent_expr.kind { - ExprKind::Struct(qpath, _, _) => match *qpath { - QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), - QPath::LangItem(LangItem::RangeCopy, ..) => Some(LangItem::RangeCopy), - QPath::LangItem(LangItem::RangeInclusiveCopy, ..) => { - Some(LangItem::RangeInclusiveCopy) - } - QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), - QPath::LangItem(LangItem::RangeToInclusive, ..) => { - Some(LangItem::RangeToInclusive) - } + ExprKind::Struct(qpath, _, _) => match tcx.qpath_lang_item(*qpath) { + Some( + lang_item @ (LangItem::Range + | LangItem::RangeCopy + | LangItem::RangeInclusiveCopy + | LangItem::RangeTo + | LangItem::RangeToInclusive), + ) => Some(lang_item), _ => None, }, ExprKind::Call(func, _) => match func.kind { // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. - ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + ExprKind::Path(qpath) + if tcx.qpath_is_lang_item(qpath, LangItem::RangeInclusiveNew) => + { Some(LangItem::RangeInclusiveStruct) } _ => None, diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 99a393b4d53a..a2fcdb4a54b4 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); let allowed_in_tests = self.allow_indexing_slicing_in_tests && is_in_test(cx.tcx, expr.hir_id); - if let Some(range) = higher::Range::hir(index) { + if let Some(range) = higher::Range::hir(cx, index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { let size: u128 = if let Some(size) = s.try_to_target_usize(cx.tcx) { diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index f193f065e68d..8f6de9fc60bd 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -178,7 +178,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { Finite } }, - ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(), + ExprKind::Struct(..) => higher::Range::hir(cx, expr).is_some_and(|r| r.end.is_none()).into(), _ => Finite, } } diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 7acf2a546222..4aae602ea051 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -131,7 +131,7 @@ fn check_index_usage<'tcx>( fn index_consumed_at<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { for (_, node) in cx.tcx.hir_parent_iter(expr.hir_id) { match node { - Node::Expr(expr) if higher::Range::hir(expr).is_some() => {}, + Node::Expr(expr) if higher::Range::hir(cx, expr).is_some() => {}, Node::ExprField(_) => {}, Node::Expr(expr) => return Some(expr), _ => break, diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index 7e48fe28011a..e5c37377e857 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( end: Some(end), limits, span: _, - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, _, _) = pat.kind { diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index 94cddb4a1506..a2ff60a3d8ac 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( end: Some(end), limits: RangeLimits::HalfOpen, span: _, - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), .. diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs index daeda227f670..6ab7bb628eca 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs @@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { start: Some(start), end: Some(end), .. - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) && let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)) && (mut_id_start.is_some() || mut_id_end.is_some()) { diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 7d890621d47c..c974d9cca7d2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( ref end, limits, span, - }) = higher::Range::hir(arg) + }) = higher::Range::hir(cx, arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index d66771a8b5be..edbf6e63e659 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -90,7 +90,7 @@ pub(super) fn check<'tcx>( arg_snip = format!("({arg_snip})").into(); } - if clippy_utils::higher::Range::hir(arg_expression).is_some() { + if clippy_utils::higher::Range::hir(cx, arg_expression).is_some() { let range_expr = snippet(cx, arg_expression.span, "?").to_string(); let sugg = snippet(cx, arg_expression.span, ".."); diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 456b48808090..a0f946d22fa6 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { end: Some(end), limits: RangeLimits::Closed, span: _, - }) = higher::Range::hir(receiver) + }) = higher::Range::hir(cx, receiver) && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index 63f6d89f2ad7..86f0eff9cd86 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -207,7 +207,7 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ExprKind::Index(arr, range, _) => match arr.kind { ExprKind::Array([]) => is_range_literal(range), ExprKind::Array(_) => { - let Some(range) = clippy_utils::higher::Range::hir(range) else { + let Some(range) = clippy_utils::higher::Range::hir(cx, range) else { return false; }; range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0)) diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index f5d15310879a..b668f391d67a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -237,7 +237,7 @@ fn find_stripping<'tcx>( if is_ref_str(self.cx, ex) && let unref = peel_ref(ex) && let ExprKind::Index(indexed, index, _) = &unref.kind - && let Some(higher::Range { start, end, .. }) = higher::Range::hir(index) + && let Some(higher::Range { start, end, .. }) = higher::Range::hir(self.cx, index) && let ExprKind::Path(path) = &indexed.kind && self.cx.qpath_res(path, ex.hir_id) == self.target { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs index f37b15371c25..8c8c8c1dd855 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal end: None, limits: ast::RangeLimits::HalfOpen, span: _, - }) = higher::Range::hir(index_expr) + }) = higher::Range::hir(cx, index_expr) && let hir::ExprKind::Lit(start_lit) = &start_expr.kind && let ast::LitKind::Int(start_idx, _) = start_lit.node { diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 38ca15cb1334..f60387fe86f7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -66,7 +66,7 @@ pub(super) fn check( method_name_span: Span, ) { let mut applicability = Applicability::MaybeIncorrect; - if let Some(range) = higher::Range::hir(receiver) + if let Some(range) = higher::Range::hir(cx, receiver) && let ExprKind::Closure(Closure { body, .. }) = arg.kind && let body_hir = cx.tcx.hir_body(*body) && let Body { diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index de4207c2abf4..7ece83ba7ca3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -11,7 +11,7 @@ use super::RANGE_ZIP_WITH_LEN; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) // range expression in `.zip()` call: `0..x.len()` - && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg) + && let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(cx, zip_arg) && is_integer_const(cx, start, 0) // `.len()` call && let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index cf0c85990b15..35d06780bcb8 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -220,14 +220,14 @@ impl<'hir> IndexEntry<'hir> { /// /// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`, /// for `..=5` this returns `Some(5)` -fn upper_index_expr(expr: &Expr<'_>) -> Option { +fn upper_index_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(Pu128(index), _) = lit.node { Some(index as usize) } else if let Some(higher::Range { end: Some(end), limits, .. - }) = higher::Range::hir(expr) + }) = higher::Range::hir(cx, expr) && let ExprKind::Lit(lit) = &end.kind && let LitKind::Int(Pu128(index @ 1..), _) = lit.node { @@ -244,7 +244,7 @@ fn upper_index_expr(expr: &Expr<'_>) -> Option { fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap>>) { if let ExprKind::Index(slice, index_lit, _) = expr.kind && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice() - && let Some(index) = upper_index_expr(index_lit) + && let Some(index) = upper_index_expr(cx, index_lit) { let hash = hash_expr(cx, slice); diff --git a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs index 021a11593f3a..f270ba7277cb 100644 --- a/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs @@ -74,7 +74,7 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) { + if let Some(higher::Range { start, end, .. }) = higher::Range::hir(cx, expr) { if let Some(start) = start { check_for_parens(cx, start, true); } diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 5c1a906599d3..ca0d41fca524 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -503,7 +503,7 @@ fn check_range_switch<'tcx>( msg: &'static str, operator: &str, ) { - if let Some(range) = higher::Range::hir(expr) + if let Some(range) = higher::Range::hir(cx, expr) && let higher::Range { start, end: Some(end), @@ -571,7 +571,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { end: Some(end), limits, span, - }) = higher::Range::hir(expr) + }) = higher::Range::hir(cx, expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() && let ecx = ConstEvalCtxt::new(cx) diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index dda2f8cc1d00..960edc6b0280 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; +use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use std::fmt::{self, Display, Formatter}; @@ -86,12 +86,12 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind + let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else { return; }; - if matches!(lang_item, LangItem::Range) + if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range) && let ty = cx.typeck_results().expr_ty(start.expr) && let Some(snippet) = span.get_source_text(cx) // `is_from_proc_macro` will skip any `vec![]`. Let's not! diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 47306949a699..1d0efa46a14c 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -6,7 +6,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -266,7 +266,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { && expressions[0].1.is_empty() // Check for slicer - && let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind + && let ExprKind::Struct(&qpath, _, _) = right.kind + && cx.tcx.qpath_is_lang_item(qpath, LangItem::Range) { let mut applicability = Applicability::MachineApplicable; let string_expression = &expressions[0].0; diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 9d895c9aa649..3904818cc9b6 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -215,14 +215,12 @@ pub struct Range<'a> { impl<'a> Range<'a> { /// Higher a `hir` range to something similar to `ast::ExprKind::Range`. #[expect(clippy::similar_names)] - pub fn hir(expr: &'a Expr<'_>) -> Option> { + pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option> { let span = expr.range_span()?; match expr.kind { ExprKind::Call(path, [arg1, arg2]) - if matches!( - path.kind, - ExprKind::Path(QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..)) - ) => + if let ExprKind::Path(qpath) = path.kind + && cx.tcx.qpath_is_lang_item(qpath, hir::LangItem::RangeInclusiveNew) => { Some(Range { start: Some(arg1), @@ -231,14 +229,14 @@ impl<'a> Range<'a> { span, }) }, - ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) { - (QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range { + ExprKind::Struct(&qpath, fields, StructTailExpr::None) => match (cx.tcx.qpath_lang_item(qpath)?, fields) { + (hir::LangItem::RangeFull, []) => Some(Range { start: None, end: None, limits: ast::RangeLimits::HalfOpen, span, }), - (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => { + (hir::LangItem::RangeFrom, [field]) if field.ident.name == sym::start => { Some(Range { start: Some(field.expr), end: None, @@ -246,7 +244,7 @@ impl<'a> Range<'a> { span, }) }, - (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => { + (hir::LangItem::Range, [field1, field2]) => { let (start, end) = match (field1.ident.name, field2.ident.name) { (sym::start, sym::end) => (field1.expr, field2.expr), (sym::end, sym::start) => (field2.expr, field1.expr), @@ -259,7 +257,7 @@ impl<'a> Range<'a> { span, }) }, - (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => { + (hir::LangItem::RangeToInclusive, [field]) if field.ident.name == sym::end => { Some(Range { start: None, end: Some(field.expr), @@ -267,7 +265,7 @@ impl<'a> Range<'a> { span, }) }, - (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range { + (hir::LangItem::RangeTo, [field]) if field.ident.name == sym::end => Some(Range { start: None, end: Some(field.expr), limits: ast::RangeLimits::HalfOpen, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6ee991eae137..75247d8fa751 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1282,7 +1282,7 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { /// If the given `Expr` is not some kind of range, the function returns `false`. pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - if let Some(Range { start, end, limits, .. }) = Range::hir(expr) { + if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) { let start_is_none_or_min = start.is_none_or(|start| { if let rustc_ty::Adt(_, subst) = ty.kind() && let bnd_ty = subst.type_at(0) diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 581c2b02839d..5945a0c7b558 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -59,7 +59,7 @@ impl<'a> Sugg<'a> { pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { let ctxt = expr.span.ctxt(); let get_snippet = |span| snippet_with_context(cx, span, ctxt, "", &mut Applicability::Unspecified).0; - snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet)) + snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(cx, expr, get_snippet)) } /// Convenience function around `hir_opt` for suggestions with a default @@ -115,7 +115,7 @@ impl<'a> Sugg<'a> { Box::new(Self::hir_with_context(cx, inner, ctxt, default, applicability)), ) } else { - Self::hir_from_snippet(expr, |span| { + Self::hir_from_snippet(cx, expr, |span| { snippet_with_context(cx, span, ctxt, default, applicability).0 }) } @@ -127,8 +127,8 @@ impl<'a> Sugg<'a> { /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. - fn hir_from_snippet(expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { - if let Some(range) = higher::Range::hir(expr) { + fn hir_from_snippet(cx: &LateContext<'_>, expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>) -> Self { + if let Some(range) = higher::Range::hir(cx, expr) { let op = AssocOp::Range(range.limits); let start = range.start.map_or("".into(), |expr| get_snippet(expr.span)); let end = range.end.map_or("".into(), |expr| get_snippet(expr.span)); @@ -166,7 +166,7 @@ impl<'a> Sugg<'a> { | ExprKind::Use(..) | ExprKind::Err(_) | ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(get_snippet(expr.span)), - ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet), + ExprKind::DropTemps(inner) => Self::hir_from_snippet(cx, inner, get_snippet), ExprKind::Assign(lhs, rhs, _) => { Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span)) }, diff --git a/src/tools/clippy/tests/ui/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout index 79794cec9269..6a3159939dd1 100644 --- a/src/tools/clippy/tests/ui/author/loop.stdout +++ b/src/tools/clippy/tests/ui/author/loop.stdout @@ -2,7 +2,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind && name.as_str() == "y" && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind @@ -23,7 +24,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) && let PatKind::Wild = pat.kind && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind @@ -43,7 +45,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr) && let PatKind::Wild = pat.kind && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout index 80717900b525..ba3b7e244204 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout @@ -2,7 +2,8 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let PatKind::Binding(BindingMode::NONE, _, name, None) = pat.kind && name.as_str() == "i" && let ExprKind::Struct(qpath, fields, None) = arg.kind - && matches!(qpath, QPath::LangItem(LangItem::Range, _)) + && let Some(def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && paths::CORE_OPS_RANGE_RANGE.matches(cx, def_id) // Add the path to `clippy_utils::paths` if needed && fields.len() == 2 && fields[0].ident.as_str() == "start" && let ExprKind::Lit(ref lit) = fields[0].expr.kind diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index 00c052d3f798..e8b174189130 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -29,11 +29,9 @@ impl Foo<'_> { fn d<'a>(&self, x: &'a u32) { } // FIXME: impl Traits printed as just `/*impl Trait*/`, ugh - fn iter1<'a>(&self) - -> /*impl Trait*/ { #[lang = "Range"] { start: 0, end: 1 } } + fn iter1<'a>(&self) -> /*impl Trait*/ { Range { start: 0, end: 1 } } - fn iter2(&self) - -> /*impl Trait*/ { #[lang = "Range"] { start: 0, end: 1 } } + fn iter2(&self) -> /*impl Trait*/ { Range { start: 0, end: 1 } } } fn a(x: Foo<'_>) { } diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index b5fa442dd247..9d4b027c21e8 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -273,14 +273,14 @@ mod expressions { /// ExprKind::Range fn expr_range() { let (lo, hi); - #[lang = "RangeFull"] { }; - #[lang = "RangeTo"] { end: hi }; - #[lang = "RangeFrom"] { start: lo }; - #[lang = "Range"] { start: lo, end: hi }; - #[lang = "Range"] { start: lo, end: hi }; - #[lang = "RangeToInclusive"] { end: hi }; - #[lang = "range_inclusive_new"](lo, hi); - #[lang = "range_inclusive_new"](-2, -1); + RangeFull { }; + RangeTo { end: hi }; + RangeFrom { start: lo }; + Range { start: lo, end: hi }; + Range { start: lo, end: hi }; + RangeToInclusive { end: hi }; + range_inclusive_new(lo, hi); + range_inclusive_new(-2, -1); } /// ExprKind::Underscore