about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/casts/cast_lossless.rs4
-rw-r--r--clippy_lints/src/casts/unnecessary_cast.rs8
-rw-r--r--clippy_lints/src/casts/zero_ptr.rs4
-rw-r--r--clippy_lints/src/functions/must_use.rs4
-rw-r--r--clippy_lints/src/functions/too_many_lines.rs91
-rw-r--r--clippy_lints/src/matches/manual_unwrap_or.rs8
-rw-r--r--clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs12
-rw-r--r--clippy_lints/src/methods/filter_map_bool_then.rs8
-rw-r--r--clippy_lints/src/methods/from_iter_instead_of_collect.rs4
-rw-r--r--clippy_lints/src/methods/join_absolute_paths.rs4
-rw-r--r--clippy_lints/src/methods/manual_ok_or.rs8
-rw-r--r--clippy_lints/src/methods/manual_try_fold.rs10
-rw-r--r--clippy_lints/src/methods/needless_character_iteration.rs6
-rw-r--r--clippy_lints/src/methods/needless_option_as_deref.rs4
-rw-r--r--clippy_lints/src/methods/string_lit_chars_any.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_get_then_check.rs10
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs18
-rw-r--r--clippy_lints/src/methods/unused_enumerate_index.rs12
-rw-r--r--clippy_lints/src/vec.rs8
-rw-r--r--clippy_utils/src/source.rs7
20 files changed, 125 insertions, 109 deletions
diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs
index bd3acc06f4b..346aed7e9f1 100644
--- a/clippy_lints/src/casts/cast_lossless.rs
+++ b/clippy_lints/src/casts/cast_lossless.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_const_context;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_isize_or_usize;
 use rustc_errors::Applicability;
@@ -34,7 +34,7 @@ pub(super) fn check(
             diag.help("an `as` cast can become silently lossy if the types change in the future");
             let mut applicability = Applicability::MachineApplicable;
             let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability);
-            let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else {
+            let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else {
                 return;
             };
             match cast_to_hir.kind {
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index fb0b0cba6a6..566adc83d69 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::snippet_opt;
+use clippy_utils::source::{snippet_opt, SpanRangeExt};
 use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
 use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
@@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
         let literal_str = &cast_str;
 
         if let LitKind::Int(n, _) = lit.node
-            && let Some(src) = snippet_opt(cx, cast_expr.span)
+            && let Some(src) = cast_expr.span.get_source_text(cx)
             && cast_to.is_floating_point()
             && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
             && let from_nbits = 128 - n.get().leading_zeros()
@@ -131,7 +131,7 @@ pub(super) fn check<'tcx>(
             | LitKind::Float(_, LitFloatType::Suffixed(_))
                 if cast_from.kind() == cast_to.kind() =>
             {
-                if let Some(src) = snippet_opt(cx, cast_expr.span) {
+                if let Some(src) = cast_expr.span.get_source_text(cx) {
                     if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
                         lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
                         return true;
@@ -253,7 +253,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
             let res = cx.qpath_res(&qpath, expr.hir_id);
             // Function call
             if let Res::Def(DefKind::Fn, def_id) = res {
-                let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
+                let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else {
                     return ControlFlow::Continue(());
                 };
                 // This is the worst part of this entire function. This is the only way I know of to
diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs
index 3c1c7d2dc3a..c5c4a28646d 100644
--- a/clippy_lints/src/casts/zero_ptr.rs
+++ b/clippy_lints/src/casts/zero_ptr.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 clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability, Ty, TyKind};
@@ -20,7 +20,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>
 
         let sugg = if let TyKind::Infer = mut_ty.ty.kind {
             format!("{std_or_core}::{sugg_fn}()")
-        } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
+        } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
             format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
         } else {
             return;
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index a08415aeb98..93828121047 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -12,7 +12,7 @@ use rustc_span::{sym, Span};
 
 use clippy_utils::attrs::is_proc_macro;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_must_use_ty;
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{return_ty, trait_ref_of_method};
@@ -155,7 +155,7 @@ fn check_must_use_candidate<'tcx>(
         return;
     }
     span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
-        if let Some(snippet) = snippet_opt(cx, fn_span) {
+        if let Some(snippet) = fn_span.get_source_text(cx) {
             diag.span_suggestion(
                 fn_span,
                 "add the attribute",
diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs
index 586ca58d60d..0f5ce340c44 100644
--- a/clippy_lints/src/functions/too_many_lines.rs
+++ b/clippy_lints/src/functions/too_many_lines.rs
@@ -1,12 +1,11 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::SpanRangeExt;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_span::Span;
 
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::snippet_opt;
-
 use super::TOO_MANY_LINES;
 
 pub(super) fn check_fn(
@@ -22,57 +21,57 @@ pub(super) fn check_fn(
         return;
     }
 
-    let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
-        return;
-    };
     let mut line_count: u64 = 0;
-    let mut in_comment = false;
-    let mut code_in_line;
+    let too_many = body.value.span.check_source_text(cx, |src| {
+        let mut in_comment = false;
+        let mut code_in_line;
 
-    let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
-        && code_snippet.as_bytes().first().copied() == Some(b'{')
-        && code_snippet.as_bytes().last().copied() == Some(b'}')
-    {
-        // Removing the braces from the enclosing block
-        &code_snippet[1..code_snippet.len() - 1]
-    } else {
-        &code_snippet
-    }
-    .trim() // Remove leading and trailing blank lines
-    .lines();
+        let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
+            && src.as_bytes().first().copied() == Some(b'{')
+            && src.as_bytes().last().copied() == Some(b'}')
+        {
+            // Removing the braces from the enclosing block
+            &src[1..src.len() - 1]
+        } else {
+            src
+        }
+        .trim() // Remove leading and trailing blank lines
+        .lines();
 
-    for mut line in function_lines {
-        code_in_line = false;
-        loop {
-            line = line.trim_start();
-            if line.is_empty() {
-                break;
-            }
-            if in_comment {
-                if let Some(i) = line.find("*/") {
-                    line = &line[i + 2..];
-                    in_comment = false;
-                    continue;
+        for mut line in function_lines {
+            code_in_line = false;
+            loop {
+                line = line.trim_start();
+                if line.is_empty() {
+                    break;
                 }
-            } else {
-                let multi_idx = line.find("/*").unwrap_or(line.len());
-                let single_idx = line.find("//").unwrap_or(line.len());
-                code_in_line |= multi_idx > 0 && single_idx > 0;
-                // Implies multi_idx is below line.len()
-                if multi_idx < single_idx {
-                    line = &line[multi_idx + 2..];
-                    in_comment = true;
-                    continue;
+                if in_comment {
+                    if let Some(i) = line.find("*/") {
+                        line = &line[i + 2..];
+                        in_comment = false;
+                        continue;
+                    }
+                } else {
+                    let multi_idx = line.find("/*").unwrap_or(line.len());
+                    let single_idx = line.find("//").unwrap_or(line.len());
+                    code_in_line |= multi_idx > 0 && single_idx > 0;
+                    // Implies multi_idx is below line.len()
+                    if multi_idx < single_idx {
+                        line = &line[multi_idx + 2..];
+                        in_comment = true;
+                        continue;
+                    }
                 }
+                break;
+            }
+            if code_in_line {
+                line_count += 1;
             }
-            break;
-        }
-        if code_in_line {
-            line_count += 1;
         }
-    }
+        line_count > too_many_lines_threshold
+    });
 
-    if line_count > too_many_lines_threshold {
+    if too_many {
         span_lint(
             cx,
             TOO_MANY_LINES,
diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs
index 2d4c8daf5cb..d5d83df9347 100644
--- a/clippy_lints/src/matches/manual_unwrap_or.rs
+++ b/clippy_lints/src/matches/manual_unwrap_or.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
 use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg};
@@ -67,11 +67,11 @@ fn check_and_lint<'tcx>(
         && path_to_local_id(peel_blocks(then_expr), binding_hir_id)
         && cx.typeck_results().expr_adjustments(then_expr).is_empty()
         && let Some(ty_name) = find_type_name(cx, ty)
-        && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
+        && let Some(or_body_snippet) = else_expr.span.get_source_text(cx)
         && let Some(indent) = indent_of(cx, expr.span)
         && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some()
     {
-        lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
+        lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent);
     }
 }
 
@@ -110,7 +110,7 @@ fn lint<'tcx>(
     expr: &Expr<'tcx>,
     scrutinee: &'tcx Expr<'_>,
     ty_name: &str,
-    or_body_snippet: String,
+    or_body_snippet: &str,
     indent: usize,
 ) {
     let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent));
diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
index a5df863d800..740cce0a6c2 100644
--- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
+++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -49,10 +49,12 @@ pub(super) fn check<'tcx>(
             "case-sensitive file extension comparison",
             |diag| {
                 diag.help("consider using a case-insensitive comparison instead");
-                if let Some(mut recv_source) = snippet_opt(cx, recv.span) {
-                    if !cx.typeck_results().expr_ty(recv).is_ref() {
-                        recv_source = format!("&{recv_source}");
-                    }
+                if let Some(recv_source) = recv.span.get_source_text(cx) {
+                    let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() {
+                        recv_source.to_owned()
+                    } else {
+                        format!("&{recv_source}")
+                    };
 
                     let suggestion_source = reindent_multiline(
                         format!(
diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs
index 2e43d19a699..4fbf661727d 100644
--- a/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -1,7 +1,7 @@
 use super::FILTER_MAP_BOOL_THEN;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::paths::BOOL_THEN;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
 use rustc_errors::Applicability;
@@ -42,9 +42,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
             .iter()
             .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
             .count()
-        && let Some(param_snippet) = snippet_opt(cx, param.span)
-        && let Some(filter) = snippet_opt(cx, recv.span)
-        && let Some(map) = snippet_opt(cx, then_body.span)
+        && let Some(param_snippet) = param.span.get_source_text(cx)
+        && let Some(filter) = recv.span.get_source_text(cx)
+        && let Some(map) = then_body.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 917a8e33eb9..f4840785584 100644
--- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/clippy_lints/src/methods/from_iter_instead_of_collect.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 clippy_utils::ty::implements_trait;
 use clippy_utils::{is_path_diagnostic_item, sugg};
 use rustc_errors::Applicability;
@@ -39,7 +39,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) ->
     }
 
     let call_site = expr.span.source_callsite();
-    if let Some(snippet) = snippet_opt(cx, call_site)
+    if let Some(snippet) = call_site.get_source_text(cx)
         && let snippet_split = snippet.split("::").collect::<Vec<_>>()
         && let Some((_, elements)) = snippet_split.split_last()
     {
diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs
index aa1ec60d434..2dad7fcf3c1 100644
--- a/clippy_lints/src/methods/join_absolute_paths.rs
+++ b/clippy_lints/src/methods/join_absolute_paths.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::expr_or_init;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a
             join_arg.span,
             "argument to `Path::join` starts with a path separator",
             |diag| {
-                let arg_str = snippet_opt(cx, spanned.span).unwrap_or_else(|| "..".to_string());
+                let arg_str = snippet(cx, spanned.span, "..");
 
                 let no_separator = if sym_str.starts_with('/') {
                     arg_str.replacen('/', "", 1)
diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs
index b1af0083e65..b1a8e1e5e47 100644
--- a/clippy_lints/src/methods/manual_ok_or.rs
+++ b/clippy_lints/src/methods/manual_ok_or.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
 use rustc_errors::Applicability;
@@ -23,11 +23,11 @@ pub(super) fn check<'tcx>(
         && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind
         && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr)
         && is_ok_wrapping(cx, map_expr)
-        && let Some(recv_snippet) = snippet_opt(cx, recv.span)
-        && let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span)
+        && let Some(recv_snippet) = recv.span.get_source_text(cx)
+        && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx)
         && let Some(indent) = indent_of(cx, expr.span)
     {
-        let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+        let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4));
         span_lint_and_sugg(
             cx,
             MANUAL_OK_OR,
diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs
index f93edded729..11fba35c16e 100644
--- a/clippy_lints/src/methods/manual_try_fold.rs
+++ b/clippy_lints/src/methods/manual_try_fold.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_from_proc_macro, is_trait_method};
 use rustc_errors::Applicability;
@@ -31,13 +31,15 @@ pub(super) fn check<'tcx>(
         && let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id)
         && let ExprKind::Closure(closure) = acc.kind
         && !is_from_proc_macro(cx, expr)
-        && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span))
+        && let Some(args_snip) = closure
+            .fn_arg_span
+            .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx))
     {
         let init_snip = rest
             .is_empty()
             .then_some(first.span)
-            .and_then(|span| snippet_opt(cx, span))
-            .unwrap_or("...".to_owned());
+            .and_then(|span| span.get_source_text(cx))
+            .map_or_else(|| "...".to_owned(), |src| src.to_owned());
 
         span_lint_and_sugg(
             cx,
diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs
index e3d78207715..332da722a37 100644
--- a/clippy_lints/src/methods/needless_character_iteration.rs
+++ b/clippy_lints/src/methods/needless_character_iteration.rs
@@ -7,7 +7,7 @@ use rustc_span::Span;
 use super::utils::get_last_chain_binding_hir_id;
 use super::NEEDLESS_CHARACTER_ITERATION;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
 
 fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
@@ -35,7 +35,7 @@ fn handle_expr(
                 && path_to_local_id(receiver, first_param)
                 && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
                 && *char_arg_ty.kind() == ty::Char
-                && let Some(snippet) = snippet_opt(cx, before_chars)
+                && let Some(snippet) = before_chars.get_source_text(cx)
             {
                 span_lint_and_sugg(
                     cx,
@@ -79,7 +79,7 @@ fn handle_expr(
                 && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
                 && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
                 && path_to_local_id(peels_expr_ref(arg), first_param)
-                && let Some(snippet) = snippet_opt(cx, before_chars)
+                && let Some(snippet) = before_chars.get_source_text(cx)
             {
                 span_lint_and_sugg(
                     cx,
diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs
index eaae8613d44..9f714fdd47b 100644
--- a/clippy_lints/src/methods/needless_option_as_deref.rs
+++ b/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::path_res;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::local_used_after_expr;
 use rustc_errors::Applicability;
@@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
             expr.span,
             "derefed type is same as origin",
             "try",
-            snippet_opt(cx, recv.span).unwrap(),
+            recv.span.get_source_text(cx).unwrap().to_owned(),
             Applicability::MachineApplicable,
         );
     }
diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs
index 5f6f027a3b5..cc0d432b799 100644
--- a/clippy_lints/src/methods/string_lit_chars_any.rs
+++ b/clippy_lints/src/methods/string_lit_chars_any.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
 use itertools::Itertools;
 use rustc_ast::LitKind;
@@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
             _ => return,
         }
         && !is_from_proc_macro(cx, expr)
-        && let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span)
+        && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx)
     {
         // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then
         // something like `r"\"` will become `'\'`, which is of course invalid
diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs
index f6184222d8e..64eb8411795 100644
--- a/clippy_lints/src/methods/unnecessary_get_then_check.rs
+++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 
 use rustc_errors::Applicability;
@@ -38,11 +38,11 @@ pub(super) fn check(
         return;
     };
     let both_calls_span = get_call_span.with_hi(call_span.hi());
-    if let Some(snippet) = snippet_opt(cx, both_calls_span)
-        && let Some(arg_snippet) = snippet_opt(cx, arg.span)
+    if let Some(snippet) = both_calls_span.get_source_text(cx)
+        && let Some(arg_snippet) = arg.span.get_source_text(cx)
     {
         let generics_snippet = if let Some(generics) = path.args
-            && let Some(generics_snippet) = snippet_opt(cx, generics.span_ext)
+            && let Some(generics_snippet) = generics.span_ext.get_source_text(cx)
         {
             format!("::{generics_snippet}")
         } else {
@@ -63,7 +63,7 @@ pub(super) fn check(
                 suggestion,
                 Applicability::MaybeIncorrect,
             );
-        } else if let Some(caller_snippet) = snippet_opt(cx, get_caller.span) {
+        } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) {
             let full_span = get_caller.span.with_hi(call_span.hi());
 
             span_lint_and_then(
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index fed2b128dcf..69c5bc57e29 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -2,7 +2,7 @@ use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{
@@ -133,7 +133,7 @@ fn check_addr_of_expr(
         && (*referent_ty != receiver_ty
             || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
             || is_cow_into_owned(cx, method_name, method_def_id))
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
             span_lint_and_sugg(
@@ -167,7 +167,7 @@ fn check_addr_of_expr(
                     parent.span,
                     format!("unnecessary use of `{method_name}`"),
                     "use",
-                    receiver_snippet,
+                    receiver_snippet.to_owned(),
                     Applicability::MachineApplicable,
                 );
             } else {
@@ -217,7 +217,7 @@ fn check_into_iter_call_arg(
         && let parent_ty = cx.typeck_results().expr_ty(parent)
         && implements_trait(cx, parent_ty, iterator_trait_id, &[])
         && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
             return true;
@@ -309,8 +309,8 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
     if let Some(parent) = get_parent_expr(cx, expr)
         && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent)
         && fn_name.as_str() == "split"
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
-        && let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
+        && let Some(arg_snippet) = argument_expr.span.get_source_text(cx)
     {
         // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x`
         // implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to
@@ -405,7 +405,7 @@ fn check_other_call_arg<'tcx>(
             None
         }
         && can_change_type(cx, maybe_arg, receiver_ty)
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
@@ -695,7 +695,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
         && let arg_ty = arg_ty.peel_refs()
         // For now we limit this lint to `String` and `Vec`.
         && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty))
-        && let Some(snippet) = snippet_opt(cx, caller.span)
+        && let Some(snippet) = caller.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
@@ -706,7 +706,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
             if original_arg_ty.is_array() {
                 format!("{snippet}.as_slice()")
             } else {
-                snippet
+                snippet.to_owned()
             },
             Applicability::MaybeIncorrect,
         );
diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs
index 3004d9c4233..ee5177d1ffa 100644
--- a/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
@@ -81,8 +81,14 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
         let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
             // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
             // Fallback to `..` if we fail getting either snippet.
-            Some(ty_span) => snippet_opt(cx, elem.span)
-                .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}")))
+            Some(ty_span) => elem
+                .span
+                .get_source_text(cx)
+                .and_then(|binding_name| {
+                    ty_span
+                        .get_source_text(cx)
+                        .map(|ty_name| format!("{binding_name}: {ty_name}"))
+                })
                 .unwrap_or_else(|| "..".to_string()),
             // Otherwise, we have no explicit type. We can replace with the binding name of the element.
             None => snippet(cx, elem.span, "..").into_owned(),
diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs
index 228db14d1b7..d3e49bff422 100644
--- a/clippy_lints/src/vec.rs
+++ b/clippy_lints/src/vec.rs
@@ -5,7 +5,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
@@ -214,9 +214,11 @@ impl SuggestedType {
     }
 
     fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String {
-        let maybe_args = args_span.and_then(|sp| snippet_opt(cx, sp)).unwrap_or_default();
+        let maybe_args = args_span
+            .and_then(|sp| sp.get_source_text(cx))
+            .map_or(String::new(), |x| x.to_owned());
         let maybe_len = len_span
-            .and_then(|sp| snippet_opt(cx, sp).map(|s| format!("; {s}")))
+            .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}")))
             .unwrap_or_default();
 
         match self {
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 8b49b013e03..d6830b53b15 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -207,6 +207,7 @@ fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRang
     if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
         return None;
     }
+    sm.ensure_source_file_source_present(&start.sf);
     let range = start.pos.to_usize()..end.pos.to_usize();
     Some(SourceFileRange { sf: start.sf, range })
 }
@@ -283,7 +284,11 @@ impl SourceFileRange {
     /// Attempts to get the text from the source file. This can fail if the source text isn't
     /// loaded.
     pub fn as_str(&self) -> Option<&str> {
-        self.sf.src.as_ref().and_then(|x| x.get(self.range.clone()))
+        self.sf
+            .src
+            .as_ref()
+            .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source()))
+            .and_then(|x| x.get(self.range.clone()))
     }
 }