diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4a9b9f544b53d..561887b01b137 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -62,13 +62,14 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { ensure_sufficient_stack(|| { + let mut span = self.lower_span(e.span); match &e.kind { // Parenthesis expression does not have a HirId and is handled specially. ExprKind::Paren(ex) => { let mut ex = self.lower_expr_mut(ex); // Include parens in span, but only if it is a super-span. if e.span.contains(ex.span) { - ex.span = self.lower_span(e.span); + ex.span = self.lower_span(e.span.with_ctxt(ex.span.ctxt())); } // Merge attributes into the inner expression. if !e.attrs.is_empty() { @@ -287,7 +288,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_span(*brackets_span), ), ExprKind::Range(e1, e2, lims) => { - self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims) + span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None); + self.lower_expr_range(span, e1.as_deref(), e2.as_deref(), *lims) } ExprKind::Underscore => { let guar = self.dcx().emit_err(UnderscoreExprLhsAssign { span: e.span }); @@ -379,7 +381,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), }; - hir::Expr { hir_id: expr_hir_id, kind, span: self.lower_span(e.span) } + hir::Expr { hir_id: expr_hir_id, kind, span } }) } @@ -1475,7 +1477,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, self.lower_span(span)); + let fn_path = hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, span); let fn_expr = self.arena.alloc(self.expr(span, hir::ExprKind::Path(fn_path))); hir::ExprKind::Call(fn_expr, arena_vec![self; e1, e2]) } @@ -1552,14 +1554,16 @@ impl<'hir> LoweringContext<'_, 'hir> { ) })) .map(|(s, e)| { + let span = self.lower_span(e.span); + let span = self.mark_span_with_reason(DesugaringKind::RangeExpr, span, None); let expr = self.lower_expr(e); - let ident = Ident::new(s, self.lower_span(e.span)); - self.expr_field(ident, expr, e.span) + let ident = Ident::new(s, span); + self.expr_field(ident, expr, span) }), ); hir::ExprKind::Struct( - self.arena.alloc(hir::QPath::LangItem(lang_item, self.lower_span(span))), + self.arena.alloc(hir::QPath::LangItem(lang_item, span)), fields, hir::StructTailExpr::None, ) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 394747f81581e..007c0e7e7e191 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2457,6 +2457,12 @@ impl Expr<'_> { } } + /// If this is a desugared range expression, + /// returns the span of the range without desugaring context. + pub fn range_span(&self) -> Option { + is_range_literal(self).then(|| self.span.parent_callsite().unwrap()) + } + /// Check if expression is an integer literal that can be used /// where `usize` is expected. pub fn is_size_lit(&self) -> bool { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2605aa18b91ee..0b1c725ba4082 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2509,11 +2509,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .sess .source_map() - .span_extend_while_whitespace(range_start.span) + .span_extend_while_whitespace(range_start.expr.span) .shrink_to_hi() - .to(range_end.span); + .to(range_end.expr.span); - err.subdiagnostic(TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr }); + err.subdiagnostic(TypeMismatchFruTypo { + expr_span: range_start.expr.span, + fru_span, + expr, + }); // Suppress any range expr type mismatches self.dcx().try_steal_replace_and_emit_err( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 709a8b74d0b7a..30f57975e45fd 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1666,7 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match expr.kind { ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { err.span_suggestion_verbose( - start.span.shrink_to_hi().with_hi(end.span.lo()), + start.expr.span.shrink_to_hi().with_hi(end.expr.span.lo()), "remove the unnecessary `.` operator for a floating point literal", '.', Applicability::MaybeIncorrect, @@ -1674,8 +1674,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { + let range_span = expr.span.parent_callsite().unwrap(); err.span_suggestion_verbose( - expr.span.with_lo(start.span.hi()), + range_span.with_lo(start.expr.span.hi()), "remove the unnecessary `.` operator for a floating point literal", '.', Applicability::MaybeIncorrect, @@ -1683,8 +1684,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true } ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { + let range_span = expr.span.parent_callsite().unwrap(); err.span_suggestion_verbose( - expr.span.until(end.span), + range_span.until(end.expr.span), "remove the unnecessary `.` operator and add an integer part for a floating point literal", "0.", Applicability::MaybeIncorrect, @@ -2693,7 +2695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bool, /* suggest `&` or `&mut` type annotation */ )> { let sess = self.sess(); - let sp = expr.span; + let sp = expr.range_span().unwrap_or(expr.span); let sm = sess.source_map(); // If the span is from an external macro, there's no suggestion we can make. diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 1febbff4bbf85..d49f599407c25 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -1,4 +1,4 @@ -use hir::{ExprKind, Node, is_range_literal}; +use hir::{ExprKind, Node}; use rustc_abi::{Integer, Size}; use rustc_hir::{HirId, attrs}; use rustc_middle::ty::Ty; @@ -44,7 +44,7 @@ fn lint_overflowing_range_endpoint<'tcx>( let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false; }; - if !is_range_literal(struct_expr) { + let Some(range_span) = struct_expr.range_span() else { return false; }; let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { @@ -71,7 +71,7 @@ fn lint_overflowing_range_endpoint<'tcx>( return false; }; UseInclusiveRange::WithoutParen { - sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), + sugg: range_span.shrink_to_lo().to(lit_span.shrink_to_hi()), start, literal: lit_val - 1, suffix, @@ -87,7 +87,7 @@ fn lint_overflowing_range_endpoint<'tcx>( cx.emit_span_lint( OVERFLOWING_LITERALS, - struct_expr.span, + range_span, RangeEndpointOutOfRange { ty, sub: sub_sugg }, ); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 633cbc3a4ae2a..51da538de94d4 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1247,6 +1247,7 @@ pub enum DesugaringKind { /// rewriting it. source: bool, }, + RangeExpr, } impl DesugaringKind { @@ -1268,6 +1269,7 @@ impl DesugaringKind { DesugaringKind::FormatLiteral { source: false } => { "expression that expanded into a format string literal" } + DesugaringKind::RangeExpr => "range expression", } } @@ -1287,6 +1289,7 @@ impl DesugaringKind { DesugaringKind::Contract => value == "Contract", DesugaringKind::PatTyRange => value == "PatTyRange", DesugaringKind::FormatLiteral { .. } => value == "FormatLiteral", + DesugaringKind::RangeExpr => value == "RangeExpr", } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 136a61598d50e..4ecc48d6cc40f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -20,7 +20,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, - expr_needs_parens, is_range_literal, + expr_needs_parens, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::middle::privacy::Level; @@ -668,12 +668,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } let derefs = "*".repeat(steps); - let needs_parens = steps > 0 - && match expr.kind { - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - _ if is_range_literal(expr) => true, - _ => false, - }; + let needs_parens = steps > 0 && expr_needs_parens(expr); let mut suggestion = if needs_parens { vec![ ( 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 a2da43c2ca245..7e48fe28011a8 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -28,6 +28,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits, + span: _, }) = higher::Range::hir(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 15c656cc7bc76..94cddb4a15069 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 @@ -31,6 +31,7 @@ pub(super) fn check<'tcx>( start: Some(start), end: Some(end), limits: RangeLimits::HalfOpen, + span: _, }) = higher::Range::hir(arg) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), 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 11edb929d70b2..7d890621d47c0 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 @@ -30,6 +30,7 @@ pub(super) fn check<'tcx>( start: Some(start), ref end, limits, + span, }) = higher::Range::hir(arg) // the var must be a single name && let PatKind::Binding(_, canonical_id, ident, _) = pat.kind @@ -149,7 +150,7 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - arg.span, + span, format!("the loop variable `{}` is used to index `{indexed}`", ident.name), |diag| { diag.multipart_suggestion( @@ -157,7 +158,7 @@ pub(super) fn check<'tcx>( vec![ (pat.span, format!("({}, )", ident.name)), ( - arg.span, + span, format!("{indexed}.{method}().enumerate(){method_1}{method_2}"), ), ], @@ -175,12 +176,12 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - arg.span, + span, format!("the loop variable `{}` is only used to index `{indexed}`", ident.name), |diag| { diag.multipart_suggestion( "consider using an iterator", - vec![(pat.span, "".to_string()), (arg.span, repl)], + vec![(pat.span, "".to_string()), (span, repl)], Applicability::HasPlaceholders, ); }, 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 8c6abbeac4489..456b488080907 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 @@ -110,6 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { start: Some(start), end: Some(end), limits: RangeLimits::Closed, + span: _, }) = higher::Range::hir(receiver) && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { 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 01f35ff02d44a..f37b15371c259 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 @@ -30,6 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen, + span: _, }) = higher::Range::hir(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 a2a522a60687d..38ca15cb1334c 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 @@ -63,7 +63,7 @@ pub(super) fn check( receiver: &Expr<'_>, arg: &Expr<'_>, msrv: Msrv, - method_call_span: Span, + method_name_span: Span, ) { let mut applicability = Applicability::MaybeIncorrect; if let Some(range) = higher::Range::hir(receiver) @@ -105,7 +105,7 @@ pub(super) fn check( // collate all our parts here and then remove those that are empty. let mut parts = vec![ ( - receiver.span.to(method_call_span), + ex.span.with_hi(method_name_span.hi()), format!("{exec_context}::iter::{method_to_use_name}"), ), new_span, diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index c9066be51c44b..48fa5f7bdc6ef 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4854,8 +4854,8 @@ impl_lint_pass!(Methods => [ /// come from expansion. pub fn method_call<'tcx>(recv: &'tcx Expr<'tcx>) -> Option<(Symbol, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> { if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind - && !args.iter().any(|e| e.span.from_expansion()) - && !receiver.span.from_expansion() + && !args.iter().any(|e| e.range_span().unwrap_or(e.span).from_expansion()) + && !receiver.range_span().unwrap_or(receiver.span).from_expansion() { Some((path.ident.name, receiver, args, path.ident.span, call_span)) } else { diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 701923cf6efc4..fdb8e1b475c1d 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -122,7 +122,7 @@ impl NoEffect { return true; } - if expr.span.from_expansion() { + if expr.range_span().unwrap_or(expr.span).from_expansion() { return false; } let expr = peel_blocks(expr); @@ -273,7 +273,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let StmtKind::Semi(expr) = stmt.kind && !stmt.span.in_external_macro(cx.sess().source_map()) && let ctxt = stmt.span.ctxt() - && expr.span.ctxt() == ctxt + && expr.range_span().unwrap_or(expr.span).ctxt() == ctxt && let Some(reduced) = reduce_expression(cx, expr) && reduced.iter().all(|e| e.span.ctxt() == ctxt) { @@ -330,7 +330,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option>> { - if expr.span.from_expansion() { + if expr.range_span().unwrap_or(expr.span).from_expansion() { return None; } match expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index e4c91b7efd2bd..ac2cc11d30206 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -501,17 +501,18 @@ fn check_range_switch<'tcx>( msg: &'static str, operator: &str, ) { - if expr.span.can_be_used_for_suggestions() - && let Some(higher::Range { + if let Some(range) = higher::Range::hir(expr) + && let higher::Range { start, end: Some(end), limits, - }) = higher::Range::hir(expr) + span, + } = range + && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(cx, end) && can_switch_ranges(cx, expr, kind, cx.typeck_results().expr_ty(y)) { - let span = expr.span; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -567,6 +568,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { start: Some(start), end: Some(end), limits, + span, }) = higher::Range::hir(expr) && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() @@ -582,7 +584,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { span_lint( cx, REVERSED_EMPTY_RANGES, - expr.span, + span, "this range is reversed and using it to index a slice will panic at run-time", ); } @@ -591,7 +593,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { span_lint_and_then( cx, REVERSED_EMPTY_RANGES, - expr.span, + span, "this range is empty so it will yield no values", |diag| { if ordering != Ordering::Equal { @@ -603,7 +605,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { }; diag.span_suggestion( - expr.span, + span, "consider using the following if you are attempting to iterate over this \ range in reverse", format!("({end_snippet}{dots}{start_snippet}).rev()"), diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs index 51596859e4c7b..51397accefeae 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs @@ -6,6 +6,7 @@ use clippy_utils::{get_parent_expr, is_mutable}; use rustc_hir::{Expr, ExprField, ExprKind, Path, QPath, StructTailExpr, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::{DesugaringKind, Ident}; declare_clippy_lint! { /// ### What it does @@ -52,7 +53,8 @@ impl LateLintPass<'_> for UnnecessaryStruct { return; }; - if expr.span.from_expansion() { + let expr_span = expr.range_span().unwrap_or(expr.span); + if expr_span.from_expansion() { // Prevent lint from hitting inside macro code return; } @@ -80,7 +82,7 @@ impl LateLintPass<'_> for UnnecessaryStruct { span_lint_and_sugg( cx, UNNECESSARY_STRUCT_INITIALIZATION, - expr.span, + expr_span, "unnecessary struct building", "replace with", snippet(cx, sugg, "..").into_owned(), @@ -130,7 +132,7 @@ fn same_path_in_all_fields<'tcx>( // expression type matches && ty == cx.typeck_results().expr_ty(src_expr) // field name matches - && f.ident == ident + && ident_without_range_desugaring(f.ident) == ident // assigned from a path expression && let ExprKind::Path(QPath::Resolved(None, src_path)) = src_expr.kind { @@ -197,3 +199,14 @@ fn path_matches_base(path: &Path<'_>, base: &Expr<'_>) -> bool { }; path.res == base_path.res } + +fn ident_without_range_desugaring(ident: Ident) -> Ident { + if ident.span.desugaring_kind() == Some(DesugaringKind::RangeExpr) { + Ident { + span: ident.span.parent_callsite().unwrap(), + ..ident + } + } else { + ident + } +} diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 3383e66fe3688..9d895c9aa649a 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -209,12 +209,14 @@ pub struct Range<'a> { pub end: Option<&'a Expr<'a>>, /// Whether the interval is open or closed. pub limits: ast::RangeLimits, + pub span: Span } 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> { + let span = expr.range_span()?; match expr.kind { ExprKind::Call(path, [arg1, arg2]) if matches!( @@ -226,6 +228,7 @@ impl<'a> Range<'a> { start: Some(arg1), end: Some(arg2), limits: ast::RangeLimits::Closed, + span, }) }, ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) { @@ -233,12 +236,14 @@ impl<'a> Range<'a> { start: None, end: None, limits: ast::RangeLimits::HalfOpen, + span, }), (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => { Some(Range { start: Some(field.expr), end: None, limits: ast::RangeLimits::HalfOpen, + span, }) }, (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => { @@ -251,6 +256,7 @@ impl<'a> Range<'a> { start: Some(start), end: Some(end), limits: ast::RangeLimits::HalfOpen, + span, }) }, (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => { @@ -258,12 +264,14 @@ impl<'a> Range<'a> { start: None, end: Some(field.expr), limits: ast::RangeLimits::Closed, + span, }) }, (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range { start: None, end: Some(field.expr), limits: ast::RangeLimits::HalfOpen, + span, }), _ => None, }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c8943523e1798..6ee991eae137f 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(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/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 83d7f6bc565d7..954a71f6c320e 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -13,8 +13,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, Span, SpanData, - SyntaxContext, hygiene, + BytePos, DesugaringKind, DUMMY_SP, FileNameDisplayPreference, Pos, RelativeBytePos, SourceFile, SourceFileAndLine, + Span, SpanData, SyntaxContext, hygiene, }; use std::borrow::Cow; use std::fmt; @@ -670,6 +670,14 @@ fn snippet_with_context_sess<'a>( default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { + // If it is just range desugaring, use the desugaring span since it may include parenthesis. + if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { + return ( + snippet_with_applicability_sess(sess, span, default, applicability), + false, + ) + } + let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( || { // The span is from a macro argument, and the outer context is the macro using the argument diff --git a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr index a419d935bd62f..60abe50efa10a 100644 --- a/src/tools/clippy/tests/ui/range_plus_minus_one.stderr +++ b/src/tools/clippy/tests/ui/range_plus_minus_one.stderr @@ -32,22 +32,22 @@ LL | for _ in 1..ONE + ONE {} | ^^^^^^^^^^^^ help: use: `1..=ONE` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:70:5 + --> tests/ui/range_plus_minus_one.rs:70:6 | LL | (1..10 + 1).for_each(|_| {}); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:75:5 + --> tests/ui/range_plus_minus_one.rs:75:6 | LL | (1..10 + 1).into_iter().for_each(|_| {}); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:80:17 + --> tests/ui/range_plus_minus_one.rs:80:18 | LL | let _ = (1..10 + 1).start_bound(); - | ^^^^^^^^^^^ help: use: `(1..=10)` + | ^^^^^^^^^ help: use: `1..=10` error: an inclusive range would be more readable --> tests/ui/range_plus_minus_one.rs:86:16 @@ -80,10 +80,10 @@ LL | a[0..2 + 1][0] = 1; | ^^^^^^^^ help: use: `0..=2` error: an exclusive range would be more readable - --> tests/ui/range_plus_minus_one.rs:180:5 + --> tests/ui/range_plus_minus_one.rs:180:6 | LL | (1..=n - 1).sum() - | ^^^^^^^^^^^ help: use: `(1..n)` + | ^^^^^^^^^ help: use: `1..n` | = note: `-D clippy::range-minus-one` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]` diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed index ba5059bbaa37e..6c866aceed4f1 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.fixed @@ -6,9 +6,9 @@ const ANSWER: i32 = 42; fn main() { // These should be linted: - (21..=42).rev().for_each(|x| println!("{}", x)); + ((21..=42).rev()).for_each(|x| println!("{}", x)); //~^ reversed_empty_ranges - let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + let _ = ((21..ANSWER).rev()).filter(|x| x % 2 == 0).take(10).collect::>(); //~^ reversed_empty_ranges for _ in (-42..=-21).rev() {} diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr index 3fadc4c169f13..f3d3ac298cec8 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,27 +1,27 @@ error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_fixable.rs:9:5 + --> tests/ui/reversed_empty_ranges_fixable.rs:9:6 | LL | (42..=21).for_each(|x| println!("{}", x)); - | ^^^^^^^^^ + | ^^^^^^^ | = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::reversed_empty_ranges)]` help: consider using the following if you are attempting to iterate over this range in reverse | LL - (42..=21).for_each(|x| println!("{}", x)); -LL + (21..=42).rev().for_each(|x| println!("{}", x)); +LL + ((21..=42).rev()).for_each(|x| println!("{}", x)); | error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_fixable.rs:11:13 + --> tests/ui/reversed_empty_ranges_fixable.rs:11:14 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); - | ^^^^^^^^^^^^ + | ^^^^^^^^^^ | help: consider using the following if you are attempting to iterate over this range in reverse | LL - let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); -LL + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); +LL + let _ = ((21..ANSWER).rev()).filter(|x| x % 2 == 0).take(10).collect::>(); | error: this range is empty so it will yield no values diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed index 55080da8a1377..9fb46c9533cdd 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -34,7 +34,7 @@ fn main() { println!("{}", i); } - for i in (0..10).rev().map(|x| x * 2) { + for i in ((0..10).rev()).map(|x| x * 2) { //~^ reversed_empty_ranges println!("{}", i); } diff --git a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr index eadd9d3675e1c..775d109296a1b 100644 --- a/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr +++ b/src/tools/clippy/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -37,15 +37,15 @@ LL + for i in (0..MAX_LEN).rev() { | error: this range is empty so it will yield no values - --> tests/ui/reversed_empty_ranges_loops_fixable.rs:37:14 + --> tests/ui/reversed_empty_ranges_loops_fixable.rs:37:15 | LL | for i in (10..0).map(|x| x * 2) { - | ^^^^^^^ + | ^^^^^ | help: consider using the following if you are attempting to iterate over this range in reverse | LL - for i in (10..0).map(|x| x * 2) { -LL + for i in (0..10).rev().map(|x| x * 2) { +LL + for i in ((0..10).rev()).map(|x| x * 2) { | error: this range is empty so it will yield no values diff --git a/tests/ui/iterators/collect-into-array.rs b/tests/ui/iterators/collect-into-array.rs index 99d0d9bd73553..b0d6d48b6b441 100644 --- a/tests/ui/iterators/collect-into-array.rs +++ b/tests/ui/iterators/collect-into-array.rs @@ -1,6 +1,7 @@ fn main() { let whatever: [u32; 10] = (0..10).collect(); //~^ ERROR an array of type `[u32; 10]` cannot be built directly from an iterator + //~| NOTE required by a bound introduced by this call //~| NOTE try collecting into a `Vec<{integer}>`, then using `.try_into()` //~| NOTE required by a bound in `collect` } diff --git a/tests/ui/iterators/collect-into-array.stderr b/tests/ui/iterators/collect-into-array.stderr index 38e5de803ccf0..6b6e8552ed8dc 100644 --- a/tests/ui/iterators/collect-into-array.stderr +++ b/tests/ui/iterators/collect-into-array.stderr @@ -1,8 +1,10 @@ error[E0277]: an array of type `[u32; 10]` cannot be built directly from an iterator - --> $DIR/collect-into-array.rs:2:39 + --> $DIR/collect-into-array.rs:2:32 | LL | let whatever: [u32; 10] = (0..10).collect(); - | ^^^^^^^ try collecting into a `Vec<{integer}>`, then using `.try_into()` + | ^^^^^ ------- required by a bound introduced by this call + | | + | try collecting into a `Vec<{integer}>`, then using `.try_into()` | = help: the trait `FromIterator<{integer}>` is not implemented for `[u32; 10]` note: required by a bound in `collect` diff --git a/tests/ui/iterators/collect-into-slice.rs b/tests/ui/iterators/collect-into-slice.rs index 120e56a6549e4..c90cfa4e90d3a 100644 --- a/tests/ui/iterators/collect-into-slice.rs +++ b/tests/ui/iterators/collect-into-slice.rs @@ -5,6 +5,7 @@ fn process_slice(data: &[i32]) { fn main() { let some_generated_vec = (0..10).collect(); //~^ ERROR the size for values of type `[i32]` cannot be known at compilation time + //~| NOTE required by a bound introduced by this call //~| ERROR the size for values of type `[i32]` cannot be known at compilation time //~| ERROR a slice of type `[i32]` cannot be built since `[i32]` has no definite size //~| NOTE try explicitly collecting into a `Vec<{integer}>` @@ -17,6 +18,7 @@ fn main() { let some_generated_vec = (0..10).collect(); //~^ ERROR a slice of type `&[i32]` cannot be built since we need to store the elements somewhere + //~| NOTE required by a bound introduced by this call //~| NOTE try explicitly collecting into a `Vec<{integer}>` //~| NOTE required by a bound in `collect` process_slice(some_generated_vec); diff --git a/tests/ui/iterators/collect-into-slice.stderr b/tests/ui/iterators/collect-into-slice.stderr index e5729a2badc71..b5444a71cc177 100644 --- a/tests/ui/iterators/collect-into-slice.stderr +++ b/tests/ui/iterators/collect-into-slice.stderr @@ -1,8 +1,10 @@ error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size - --> $DIR/collect-into-slice.rs:6:38 + --> $DIR/collect-into-slice.rs:6:31 | LL | let some_generated_vec = (0..10).collect(); - | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` + | ^^^^^ ------- required by a bound introduced by this call + | | + | try explicitly collecting into a `Vec<{integer}>` | = help: the trait `FromIterator<{integer}>` is not implemented for `[i32]` note: required by a bound in `collect` @@ -28,10 +30,12 @@ note: required by an implicit `Sized` bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a slice of type `&[i32]` cannot be built since we need to store the elements somewhere - --> $DIR/collect-into-slice.rs:18:38 + --> $DIR/collect-into-slice.rs:19:31 | LL | let some_generated_vec = (0..10).collect(); - | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>` + | ^^^^^ ------- required by a bound introduced by this call + | | + | try explicitly collecting into a `Vec<{integer}>` | = help: the trait `FromIterator<{integer}>` is not implemented for `&[i32]` note: required by a bound in `collect` diff --git a/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs index 7871612dc8b75..bec92448d1c8c 100644 --- a/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs +++ b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs @@ -4,4 +4,6 @@ fn main() { //~| NOTE expected type `usize` //~| NOTE found struct `RangeTo<{integer}>` //~| NOTE expected `usize`, found `RangeTo<{integer}> + //~| NOTE in this expansion of desugaring of range expression + //~| NOTE in this expansion of desugaring of range expression }