diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c88a0539d70e..0ff136ebcae0 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; @@ -23,7 +23,8 @@ pub(super) fn check<'tcx>( cast_from: Ty<'tcx>, cast_to: Ty<'tcx>, ) -> bool { - let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); + let cast_str = cast_expr.span.get_source_text(cx); + let cast_str = cast_str.as_deref().unwrap_or(""); if let ty::RawPtr(..) = cast_from.kind() // check both mutability and type are the same @@ -56,7 +57,7 @@ pub(super) fn check<'tcx>( "casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)" ), "try", - cast_str.clone(), + cast_str.to_string(), Applicability::MaybeIncorrect, ); } @@ -102,7 +103,7 @@ pub(super) fn check<'tcx>( } if let Some(lit) = get_numeric_literal(cast_expr) { - let literal_str = &cast_str; + let literal_str = cast_str; if let LitKind::Int(n, _) = lit.node && let Some(src) = cast_expr.span.get_source_text(cx) @@ -167,7 +168,7 @@ pub(super) fn check<'tcx>( sym::assert_ne_macro, sym::debug_assert_ne_macro, ]; - matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if + matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym))) } @@ -198,7 +199,7 @@ pub(super) fn check<'tcx>( match surrounding { MaybeParenOrBlock::Paren => format!("({cast_str})"), MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"), - MaybeParenOrBlock::Nothing => cast_str, + MaybeParenOrBlock::Nothing => cast_str.to_string(), }, Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 1507f1ed3053..99a327ef7a3f 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::numeric_literal; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt}; @@ -11,6 +11,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; use rustc_session::declare_lint_pass; +use std::borrow::Cow; use std::iter; declare_clippy_lint! { @@ -103,12 +104,13 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { lit.span, "default numeric fallback might occur", |diag| { - let src = if let Some(src) = snippet_opt(self.cx, lit.span) { - src + let src = lit.span.get_source_text(self.cx); + let src: Cow<'_, str> = if let Some(src) = src.as_deref() { + src.into() } else { match lit.node { - LitKind::Int(src, _) => format!("{src}"), - LitKind::Float(src, _) => format!("{src}"), + LitKind::Int(src, _) => format!("{src}").into(), + LitKind::Float(src, _) => format!("{src}").into(), _ => unreachable!("Default numeric fallback never results in other types"), } }; diff --git a/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/clippy_lints/src/doc/include_in_doc_without_cfg.rs index bca1cd03bb7e..3a55dc457be0 100644 --- a/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -15,7 +15,7 @@ pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. - && let Some(snippet) = snippet_opt(cx, attr.span) + && let Some(snippet) = attr.span.get_source_text(cx) // We cannot remove this because a `#[doc = include_str!("...")]` attribute can occupy // several lines. && let Some(start) = snippet.find('[') diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 674690e7e31d..1945646fcd7d 100644 --- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -5,7 +5,7 @@ use rustc_lint::LateContext; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use super::TOO_LONG_FIRST_DOC_PARAGRAPH; @@ -81,15 +81,10 @@ pub(super) fn check( if should_suggest_empty_doc && let Some(second_span) = spans.get(1) && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) - && let Some(snippet) = snippet_opt(cx, new_span) + && let Some(snippet) = new_span.get_source_text(cx) + && let Some(first) = first_span.get_source_text(cx) + && let Some(comment_form) = first.get(..3) { - let Some(first) = snippet_opt(cx, *first_span) else { - return; - }; - let Some(comment_form) = first.get(..3) else { - return; - }; - diag.span_suggestion( new_span, "add an empty line", diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 752f39b4e6dc..15bfa7cf9ca4 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::VecArgs; -use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; use clippy_utils::{ @@ -217,8 +217,8 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).is_some_and(|l| { + if let Some(snippet) = callee.span.get_source_text(cx) { + let snippet = if path_to_local(callee).is_some_and(|l| { // FIXME: Do we really need this `local_used_in` check? // Isn't it checking something like... `callee(callee)`? // If somehow this check is needed, add some test for it, @@ -227,25 +227,24 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }) { match closure_kind { // Mutable closure is used after current expr; we cannot consume it. - ClosureKind::FnMut => snippet = format!("&mut {snippet}"), - ClosureKind::Fn if !callee_ty_raw.is_ref() => { - snippet = format!("&{snippet}"); - }, - _ => (), + ClosureKind::FnMut => format!("&mut {snippet}"), + ClosureKind::Fn if !callee_ty_raw.is_ref() => format!("&{snippet}"), + _ => snippet.to_owned(), } - } else if let n_refs = - callee_ty_adjustments - .iter() - .rev() - .fold(0, |acc, adjustment| match adjustment.kind { + } else { + let n_refs = callee_ty_adjustments.iter().rev().fold(0, |acc, adjustment| { + match adjustment.kind { Adjust::Deref(Some(_)) => acc + 1, Adjust::Deref(_) if acc > 0 => acc + 1, _ => acc, - }) - && n_refs > 0 - { - snippet = format!("{}{snippet}", "*".repeat(n_refs)); - } + } + }); + if n_refs > 0 { + format!("{}{snippet}", "*".repeat(n_refs)) + } else { + snippet.to_owned() + } + }; let replace_with = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => cx.tcx.def_descr(*def), diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 1c751643becb..d2e2bcf856f0 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::is_span_if; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; @@ -170,23 +170,21 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { { let eq_span = lhs.span.between(rhs.span); if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind - && let Some(eq_snippet) = snippet_opt(cx, eq_span) + && eq_span.check_source_text(cx, |eq_snippet| eq_snippet.ends_with('=')) { let op = op.as_str(); let eqop_span = lhs.span.between(sub_rhs.span); - if eq_snippet.ends_with('=') { - span_lint_and_note( - cx, - SUSPICIOUS_ASSIGNMENT_FORMATTING, - eqop_span, - format!( - "this looks like you are trying to use `.. {op}= ..`, but you \ + span_lint_and_note( + cx, + SUSPICIOUS_ASSIGNMENT_FORMATTING, + eqop_span, + format!( + "this looks like you are trying to use `.. {op}= ..`, but you \ really are doing `.. = ({op} ..)`" - ), - None, - format!("to remove this lint, use either `{op}=` or `= {op}`"), - ); - } + ), + None, + format!("to remove this lint, use either `{op}=` or `= {op}`"), + ); } } } @@ -201,11 +199,11 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { && let ExprKind::Unary(op, ref un_rhs) = rhs.kind // from UnOp operator to UnOp operand && let unop_operand_span = rhs.span.until(un_rhs.span) - && let Some(binop_snippet) = snippet_opt(cx, binop_span) - && let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span) && let binop_str = binop.node.as_str() - // no space after BinOp operator and space after UnOp operator - && binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ') + // no space after BinOp operator + && binop_span.check_source_text(cx, |binop_snippet| binop_snippet.ends_with(binop_str)) + // ... and space after UnOp operator + && unop_operand_span.check_source_text(cx, |unop_operand_snippet| unop_operand_snippet.ends_with(' ')) { let unop_str = op.as_str(); let eqop_span = lhs.span.between(un_rhs.span); @@ -239,7 +237,7 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { // the snippet should look like " else \n " with maybe comments anywhere // it’s bad when there is a ‘\n’ after the “else” - && let Some(else_snippet) = snippet_opt(cx, else_span) + && let Some(else_snippet) = else_span.get_source_text(cx) && let Some((pre_else, post_else)) = else_snippet.split_once("else") && !else_snippet.contains('/') && let Some((_, post_else_post_eol)) = post_else.split_once('\n') @@ -293,11 +291,10 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) { && has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span) && let space_span = lhs.span.between(op.span) - && let Some(space_snippet) = snippet_opt(cx, space_span) - && let lint_span = lhs.span.with_lo(lhs.span.hi()) - && space_snippet.contains('\n') + && space_span.check_source_text(cx, |space_snippet| space_snippet.contains('\n')) && indentation(cx, op.span) <= indentation(cx, lhs.span) { + let lint_span = lhs.span.shrink_to_hi(); span_lint_and_note( cx, POSSIBLE_MISSING_COMMA, @@ -322,8 +319,9 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { // If there is a line break between the two expressions, don't lint. // If there is a non-whitespace character, this span came from a proc-macro. && let else_span = first.span.between(second.span) - && let Some(else_snippet) = snippet_opt(cx, else_span) - && !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()) + && else_span.check_source_text(cx, |else_snippet| { + !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()) + }) { let (looks_like, next_thing) = if is_if(second) { ("an `else if`", "the second `if`") diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index c634c12e1877..e6f26d73544b 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -237,7 +237,7 @@ fn check_subtraction( { if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) { // This part of the condition is voluntarily split from the one before to ensure that - // if `snippet_opt` fails, it won't try the next conditions. + // if `Sugg::hir_opt` fails, it won't try the next conditions. if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren) && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 48ce1afc6e69..d539502628c5 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; @@ -96,7 +96,7 @@ impl EarlyLintPass for LargeIncludeFile { && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. - && let Some(snippet) = snippet_opt(cx, attr.span) + && let Some(snippet) = attr.span.get_source_text(cx) // We cannot remove this because a `#[doc = include_str!("...")]` attribute can // occupy several lines. && let Some(start) = snippet.find('[') diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 845edb9cae15..6c70fca801b9 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -1,6 +1,6 @@ use super::WHILE_LET_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet, snippet_indent, snippet_opt}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_indent}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::any_temporaries_need_ordered_drop; use clippy_utils::{higher, peel_blocks}; @@ -89,8 +89,8 @@ fn could_be_while_let<'tcx>( // Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but // keep them if the type has been explicitly specified. && (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some()) - && let Some(pat_str) = snippet_opt(cx, pat.span) - && let Some(init_str) = snippet_opt(cx, peel_blocks(inner_expr).span) + && let Some(pat_str) = pat.span.get_source_text(cx) + && let Some(init_str) = peel_blocks(inner_expr).span.get_source_text(cx) { let ty_str = ty .map(|ty| format!(": {}", snippet(cx, ty.span, "_"))) diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index b036e78cdedc..061864356fdf 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,6 +1,7 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -134,19 +135,22 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { }, ) { - if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) { - span_lint_and_sugg( - cx, - MANUAL_OPTION_AS_SLICE, - span, - "use `Option::as_slice`", - "use", - format!("{snippet}.as_slice()"), - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, MANUAL_OPTION_AS_SLICE, span, "use `Option_as_slice`"); - } + span_lint_and_then( + cx, + MANUAL_OPTION_AS_SLICE, + span, + "manual implementation of `Option::as_slice`", + |diag| { + if let Some(snippet) = callee.span.get_source_text(cx) { + diag.span_suggestion( + span, + "use", + format!("{snippet}.as_slice()"), + Applicability::MachineApplicable, + ); + } + }, + ); } } diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index d43dc23a86b2..d95d74336b1b 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::BindingMode; use rustc_errors::Applicability; @@ -106,14 +106,14 @@ pub(super) fn check<'tcx>( span_lint_and_then(cx, lint, expr.span, msg, |diag| match op { Op::RmCloned | Op::LaterCloned => { let method_span = expr.span.with_lo(cloned_call.span.hi()); - if let Some(mut snip) = snippet_opt(cx, method_span) { - snip.push_str(trailing_clone); + if let Some(snip) = method_span.get_source_text(cx) { + let snip = format!("{snip}{trailing_clone}"); let replace_span = expr.span.with_lo(cloned_recv.span.hi()); diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable); } }, Op::FixClosure(name, predicate_expr) => { - if let Some(predicate) = snippet_opt(cx, predicate_expr.span) { + if let Some(predicate) = predicate_expr.span.get_source_text(cx) { let new_closure = if let ExprKind::Closure(_) = predicate_expr.kind { predicate.replacen('|', "|&", 1) } else { @@ -126,7 +126,7 @@ pub(super) fn check<'tcx>( }, Op::NeedlessMove(_) => { let method_span = expr.span.with_lo(cloned_call.span.hi()); - if let Some(snip) = snippet_opt(cx, method_span) { + if let Some(snip) = method_span.get_source_text(cx) { let replace_span = expr.span.with_lo(cloned_recv.span.hi()); diag.span_suggestion(replace_span, "try", snip, Applicability::MaybeIncorrect); } diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index f988323a8c13..f0e1b7caa606 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -7,7 +7,7 @@ mod unneeded_field_pattern; mod unneeded_wildcard_pattern; mod zero_prefixed_literal; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, Pat}; use rustc_ast::token; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -350,10 +350,11 @@ impl MiscEarlyLints { // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. // See for a regression. // FIXME: Find a better way to detect those cases. - let lit_snip = match snippet_opt(cx, span) { + let lit_snip = match span.get_source_text(cx) { Some(snip) if snip.starts_with(|c: char| c.is_ascii_digit()) => snip, _ => return, }; + let lit_snip = lit_snip.as_str(); let lit_kind = LitKind::from_token_lit(lit); if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind { @@ -362,17 +363,17 @@ impl MiscEarlyLints { LitIntType::Unsigned(ty) => ty.name_str(), LitIntType::Unsuffixed => "", }; - literal_suffix::check(cx, span, &lit_snip, suffix, "integer"); + literal_suffix::check(cx, span, lit_snip, suffix, "integer"); if lit_snip.starts_with("0x") { - mixed_case_hex_literals::check(cx, span, suffix, &lit_snip); + mixed_case_hex_literals::check(cx, span, suffix, lit_snip); } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { // nothing to do } else if value != 0 && lit_snip.starts_with('0') { - zero_prefixed_literal::check(cx, span, &lit_snip); + zero_prefixed_literal::check(cx, span, lit_snip); } } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind { let suffix = float_ty.name_str(); - literal_suffix::check(cx, span, &lit_snip, suffix, "float"); + literal_suffix::check(cx, span, lit_snip, suffix, "float"); } } } diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index 943e662479e9..ae47547e6320 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -72,9 +72,9 @@ impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(format_args) = &expr.kind && !format_args.span.in_external_macro(cx.sess().source_map()) - && format_args.span.check_source_text(cx, |src| src.starts_with('r')) - && let Some(str) = snippet_opt(cx.sess(), format_args.span) - && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count() + && let Some(str) = format_args.span.get_source_text(cx) + && let Some(str) = str.strip_prefix('r') + && let count_hash = str.bytes().take_while(|b| *b == b'#').count() && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1) { self.check_raw_string( diff --git a/clippy_lints/src/types/owned_cow.rs b/clippy_lints/src/types/owned_cow.rs index 8933994d1855..7a7f2685091c 100644 --- a/clippy_lints/src/types/owned_cow.rs +++ b/clippy_lints/src/types/owned_cow.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir}; @@ -38,7 +38,7 @@ fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String) && let [.., last_seg] = path.segments && let Some(args) = last_seg.args && let [t, ..] = args.args - && let Some(snip) = snippet_opt(cx, t.span()) + && let Some(snip) = t.span().get_source_text(cx) { Some((cty.span, format!("[{snip}]"))) } else { diff --git a/clippy_lints/src/unused_trait_names.rs b/clippy_lints/src/unused_trait_names.rs index 12f2804dbaa1..8277f264dd15 100644 --- a/clippy_lints/src/unused_trait_names.rs +++ b/clippy_lints/src/unused_trait_names.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, UseKind}; @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id) && cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id()) && let Some(last_segment) = path.segments.last() - && let Some(snip) = snippet_opt(cx, last_segment.ident.span) + && let Some(snip) = last_segment.ident.span.get_source_text(cx) && self.msrv.meets(cx, msrvs::UNDERSCORE_IMPORTS) && !is_from_proc_macro(cx, &last_segment.ident) { diff --git a/clippy_lints/src/useless_concat.rs b/clippy_lints/src/useless_concat.rs index 96845adb04a2..557f9e820d88 100644 --- a/clippy_lints/src/useless_concat.rs +++ b/clippy_lints/src/useless_concat.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::macro_backtrace; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::SpanRangeExt; use clippy_utils::{sym, tokenize_with_text}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -44,7 +44,7 @@ impl LateLintPass<'_> for UselessConcat { // Check if the `concat` macro from the `core` library. && cx.tcx.is_diagnostic_item(sym::macro_concat, macro_call.def_id) // We get the original code to parse it. - && let Some(original_code) = snippet_opt(cx, macro_call.span) + && let Some(original_code) = macro_call.span.get_source_text(cx) // This check allows us to ensure that the code snippet: // 1. Doesn't come from proc-macro expansion. // 2. Doesn't come from foreign macro expansion. diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 638d32903123..fcd1396493d0 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -393,10 +393,10 @@ pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option { let line_span = line_span(sess, span); - snippet_opt(sess, line_span).and_then(|snip| { + line_span.with_source_text(sess, |snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) - }) + })? } /// Extends the span to the beginning of the spans line, incl. whitespaces. @@ -425,12 +425,13 @@ fn line_span(sess: &impl HasSession, span: Span) -> Span { /// // ^^ -- will return 4 /// ``` pub fn indent_of(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) + line_span(sess, span).with_source_text(sess, |snip| snip.find(|c: char| !c.is_whitespace()))? } /// Gets a snippet of the indentation of the line of a span pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).map(|mut s| { + line_span(sess, span).with_source_text(sess, |s| { + let mut s = s.to_owned(); let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -443,7 +444,7 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) + if let Some(snippet) = span.get_source_text(sess) && snippet.is_empty() { return false; @@ -534,7 +535,11 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) + if let Some(snippet) = span.get_source_text(sess) { + Cow::Owned(snippet.to_owned()) + } else { + Cow::Borrowed(default) + } } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -561,20 +566,14 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - snippet_opt(sess, span).map_or_else( - || { - if *applicability == Applicability::MachineApplicable { - *applicability = Applicability::HasPlaceholders; - } - Cow::Borrowed(default) - }, - From::from, - ) -} - -/// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { - sess.sess().source_map().span_to_snippet(span).ok() + if let Some(snippet) = span.get_source_text(sess) { + Cow::Owned(snippet.to_owned()) + } else { + if *applicability == Applicability::MachineApplicable { + *applicability = Applicability::HasPlaceholders; + } + Cow::Borrowed(default) + } } /// Converts a span (from a block) to a code snippet if available, otherwise use default. diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index a63333c9b48f..9afac857e893 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -1,7 +1,7 @@ //! Contains utility functions to generate suggestions. #![deny(clippy::missing_docs_in_private_items)] -use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_context}; +use crate::source::{SpanRangeExt, snippet, snippet_with_applicability, snippet_with_context}; use crate::ty::expr_sig; use crate::{get_parent_expr_for_hir, higher}; use rustc_ast::util::parser::AssocOp; @@ -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)) + (expr.span.get_source_text(cx)).map(|_| Self::hir_from_snippet(expr, get_snippet)) } /// Convenience function around `hir_opt` for suggestions with a default diff --git a/tests/ui/manual_option_as_slice.stderr b/tests/ui/manual_option_as_slice.stderr index e240ae8eb7d9..846741793de8 100644 --- a/tests/ui/manual_option_as_slice.stderr +++ b/tests/ui/manual_option_as_slice.stderr @@ -1,4 +1,4 @@ -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:5:9 | LL | _ = match x.as_ref() { @@ -12,7 +12,7 @@ LL | | }; = note: `-D clippy::manual-option-as-slice` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_option_as_slice)]` -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:11:9 | LL | _ = if let Some(f) = x.as_ref() { @@ -25,31 +25,31 @@ LL | | &[] LL | | }; | |_____^ help: use: `x.as_slice()` -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:19:9 | LL | _ = x.as_ref().map_or(&[][..], std::slice::from_ref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:22:9 | LL | _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:25:9 | LL | _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:28:9 | LL | _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()` -error: use `Option::as_slice` +error: manual implementation of `Option::as_slice` --> tests/ui/manual_option_as_slice.rs:33:13 | LL | _ = x.as_ref().map_or_else(<&[_]>::default, from_ref);