Auto merge of #13255 - Jarcho:get_src_display2, r=Manishearth

Remove more `snippet_opt` calls

First commit is the same as #13244

changelog: none
This commit is contained in:
bors 2024-08-12 18:50:19 +00:00
commit 1984752623
23 changed files with 164 additions and 121 deletions

View File

@ -1,6 +1,6 @@
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
use rustc_ast::{AttrKind, AttrStyle}; use rustc_ast::{AttrKind, AttrStyle};
use rustc_lint::EarlyContext; use rustc_lint::EarlyContext;
use rustc_span::Span; use rustc_span::Span;
@ -26,7 +26,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
item.span.parent(), item.span.parent(),
); );
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
let lines = snippet.split('\n').collect::<Vec<_>>(); let lines = snippet.split('\n').collect::<Vec<_>>();
let lines = without_block_comments(lines); let lines = without_block_comments(lines);

View File

@ -1,6 +1,6 @@
use super::{Attribute, NON_MINIMAL_CFG}; use super::{Attribute, NON_MINIMAL_CFG};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::EarlyContext; use rustc_lint::EarlyContext;
@ -29,8 +29,13 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
meta.span, meta.span,
"unneeded sub `cfg` when there is only one condition", "unneeded sub `cfg` when there is only one condition",
|diag| { |diag| {
if let Some(snippet) = snippet_opt(cx, list[0].span()) { if let Some(snippet) = list[0].span().get_source_text(cx) {
diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect); diag.span_suggestion(
meta.span,
"try",
snippet.to_owned(),
Applicability::MaybeIncorrect,
);
} }
}, },
); );

View File

@ -1,6 +1,7 @@
use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use super::{Attribute, UNNECESSARY_CLIPPY_CFG};
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use itertools::Itertools;
use rustc_ast::AttrStyle; use rustc_ast::AttrStyle;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, Level}; use rustc_lint::{EarlyContext, Level};
@ -31,7 +32,7 @@ pub(super) fn check(
return; return;
} }
if nb_items == clippy_lints.len() { if nb_items == clippy_lints.len() {
if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) { if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
UNNECESSARY_CLIPPY_CFG, UNNECESSARY_CLIPPY_CFG,
@ -47,11 +48,7 @@ pub(super) fn check(
); );
} }
} else { } else {
let snippet = clippy_lints let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(",");
.iter()
.filter_map(|sp| snippet_opt(cx, *sp))
.collect::<Vec<_>>()
.join(",");
span_lint_and_note( span_lint_and_note(
cx, cx,
UNNECESSARY_CLIPPY_CFG, UNNECESSARY_CLIPPY_CFG,

View File

@ -1,7 +1,7 @@
use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::utils::{extract_clippy_lint, is_lint_level, is_word};
use super::{Attribute, USELESS_ATTRIBUTE}; use super::{Attribute, USELESS_ATTRIBUTE};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{first_line_of_span, snippet_opt}; use clippy_utils::source::{first_line_of_span, SpanRangeExt};
use rustc_ast::NestedMetaItem; use rustc_ast::NestedMetaItem;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
@ -69,14 +69,14 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
} }
let line_span = first_line_of_span(cx, attr.span); let line_span = first_line_of_span(cx, attr.span);
if let Some(mut sugg) = snippet_opt(cx, line_span) { if let Some(src) = line_span.get_source_text(cx) {
if sugg.contains("#[") { if src.contains("#[") {
#[expect(clippy::collapsible_span_lint_calls)]
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
sugg = sugg.replacen("#[", "#![", 1);
diag.span_suggestion( diag.span_suggestion(
line_span, line_span,
"if you just forgot a `!`, use", "if you just forgot a `!`, use",
sugg, src.replacen("#[", "#![", 1),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}); });

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
&& let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity() && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
&& let Some(recv) = snippet_opt(cx, receiver.span) && let Some(recv) = receiver.span.get_source_text(cx)
{ {
// `as_mut_ptr` might not exist // `as_mut_ptr` might not exist
let applicability = Applicability::MaybeIncorrect; let applicability = Applicability::MaybeIncorrect;

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
@ -68,8 +68,8 @@ fn check_fn(
header_span, header_span,
"this function can be simplified using the `async fn` syntax", "this function can be simplified using the `async fn` syntax",
|diag| { |diag| {
if let Some(vis_snip) = snippet_opt(cx, *vis_span) if let Some(vis_snip) = vis_span.get_source_text(cx)
&& let Some(header_snip) = snippet_opt(cx, header_span) && let Some(header_snip) = header_span.get_source_text(cx)
&& let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some(ret_pos) = position_before_rarrow(&header_snip)
&& let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output) && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output)
{ {
@ -190,6 +190,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
Some((sugg, String::new())) Some((sugg, String::new()))
} else { } else {
let sugg = "return the output of the future directly"; let sugg = "return the output of the future directly";
snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}"))) output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}")))
} }
} }

View File

@ -22,7 +22,7 @@
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
/// match arms. /// match arms.
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
if let Some(ff) = span.get_source_text(cx) if let Some(ff) = span.get_source_range(cx)
&& let Some(text) = ff.as_str() && let Some(text) = ff.as_str()
{ {
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_self; use clippy_utils::is_self;
use clippy_utils::ptr::get_spans; use clippy_utils::ptr::get_spans;
use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::source::{snippet, SpanRangeExt};
use clippy_utils::ty::{ use clippy_utils::ty::{
implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
}; };
@ -242,8 +242,8 @@ fn check_fn(
for (span, suggestion) in clone_spans { for (span, suggestion) in clone_spans {
diag.span_suggestion( diag.span_suggestion(
span, span,
snippet_opt(cx, span) span.get_source_text(cx)
.map_or("change the call to".into(), |x| format!("change `{x}` to")), .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
suggestion, suggestion,
Applicability::Unspecified, Applicability::Unspecified,
); );
@ -267,8 +267,8 @@ fn check_fn(
for (span, suggestion) in clone_spans { for (span, suggestion) in clone_spans {
diag.span_suggestion( diag.span_suggestion(
span, span,
snippet_opt(cx, span) span.get_source_text(cx)
.map_or("change the call to".into(), |x| format!("change `{x}` to")), .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
suggestion, suggestion,
Applicability::Unspecified, Applicability::Unspecified,
); );

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::path_to_local_id; use clippy_utils::path_to_local_id;
use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::source::{snippet, SpanRangeExt};
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::{LitKind, StrStyle}; use rustc_ast::{LitKind, StrStyle};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -74,7 +74,7 @@ fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option<String> {
&& let Some(arg) = self.arg && let Some(arg) = self.arg
&& let ExprKind::Lit(x) = arg.kind && let ExprKind::Lit(x) = arg.kind
&& let LitKind::Str(_, StrStyle::Cooked) = x.node && let LitKind::Str(_, StrStyle::Cooked) = x.node
&& let Some(s) = snippet_opt(cx, arg.span) && let Some(s) = arg.span.get_source_text(cx)
{ {
Some(format!(" = PathBuf::from({s});")) Some(format!(" = PathBuf::from({s});"))
} else { } else {
@ -84,8 +84,8 @@ fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option<String> {
fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> { fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> {
let arg = self.arg?; let arg = self.arg?;
let arg_str = snippet_opt(cx, arg.span)?; let arg_str = arg.span.get_source_text(cx)?;
let init_val = snippet_opt(cx, self.init_val.span)?; let init_val = self.init_val.span.get_source_text(cx)?;
Some(format!(" = {init_val}.join({arg_str});")) Some(format!(" = {init_val}.join({arg_str});"))
} }

View File

@ -1,7 +1,7 @@
//! Checks for usage of `&Vec[_]` and `&String`. //! Checks for usage of `&Vec[_]` and `&String`.
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::expr_sig; use clippy_utils::ty::expr_sig;
use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::visitors::contains_unsafe_block;
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local}; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
@ -243,7 +243,7 @@ fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
.chain(result.replacements.iter().map(|r| { .chain(result.replacements.iter().map(|r| {
( (
r.expr_span, r.expr_span,
format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement), format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
) )
})) }))
.collect(), .collect(),
@ -372,7 +372,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
DerefTy::Path => f.write_str("Path"), DerefTy::Path => f.write_str("Path"),
DerefTy::Slice(hir_ty, ty) => { DerefTy::Slice(hir_ty, ty) => {
f.write_char('[')?; f.write_char('[')?;
match hir_ty.and_then(|s| snippet_opt(self.0, s)) { match hir_ty.and_then(|s| s.get_source_text(self.0)) {
Some(s) => f.write_str(&s)?, Some(s) => f.write_str(&s)?,
None => ty.fmt(f)?, None => ty.fmt(f)?,
} }
@ -413,6 +413,7 @@ fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx>
} }
} }
#[expect(clippy::too_many_lines)]
fn check_fn_args<'cx, 'tcx: 'cx>( fn check_fn_args<'cx, 'tcx: 'cx>(
cx: &'cx LateContext<'tcx>, cx: &'cx LateContext<'tcx>,
fn_sig: ty::FnSig<'tcx>, fn_sig: ty::FnSig<'tcx>,
@ -488,8 +489,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
return None; return None;
} }
let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
PTR_ARG, PTR_ARG,
@ -500,7 +499,10 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
diag.span_suggestion( diag.span_suggestion(
hir_ty.span, hir_ty.span,
"change this to", "change this to",
format!("&{}{ty_name}", mutability.prefix_str()), match ty.span().get_source_text(cx) {
Some(s) => format!("&{}{s}", mutability.prefix_str()),
None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)),
},
Applicability::Unspecified, Applicability::Unspecified,
); );
}, },

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -120,8 +120,8 @@ fn build_suggestion(
receiver_expr: &Expr<'_>, receiver_expr: &Expr<'_>,
cast_lhs_expr: &Expr<'_>, cast_lhs_expr: &Expr<'_>,
) -> Option<String> { ) -> Option<String> {
let receiver = snippet_opt(cx, receiver_expr.span)?; let receiver = receiver_expr.span.get_source_text(cx)?;
let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?; let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?;
Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
} }

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -208,7 +208,7 @@ fn check_fn(
.assert_crate_local() .assert_crate_local()
.lint_root; .lint_root;
if let Some(snip) = snippet_opt(cx, span) if let Some(snip) = span.get_source_text(cx)
&& let Some(dot) = snip.rfind('.') && let Some(dot) = snip.rfind('.')
{ {
let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap()));

View File

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::source::{snippet_with_applicability, SpanRangeExt};
use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::BytePos; use rustc_span::{BytePos, Span};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -56,11 +56,11 @@ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
{ {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let sugg = if e.span.from_expansion() { let sugg = if e.span.from_expansion() {
if let Some(macro_source) = snippet_opt(cx, e.span) { if let Some(macro_source) = e.span.get_source_text(cx) {
// Remove leading whitespace from the given span // Remove leading whitespace from the given span
// e.g: ` $visitor` turns into `$visitor` // e.g: ` $visitor` turns into `$visitor`
let trim_leading_whitespaces = |span| { let trim_leading_whitespaces = |span: Span| {
snippet_opt(cx, span) span.get_source_text(cx)
.and_then(|snip| { .and_then(|snip| {
#[expect(clippy::cast_possible_truncation)] #[expect(clippy::cast_possible_truncation)]
snip.find(|c: char| !c.is_whitespace()) snip.find(|c: char| !c.is_whitespace())

View File

@ -2,7 +2,7 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use clippy_utils::{def_path_def_ids, path_def_id, paths}; use clippy_utils::{def_path_def_ids, path_def_id, paths};
use rustc_ast::ast::{LitKind, StrStyle}; use rustc_ast::ast::{LitKind, StrStyle};
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
@ -122,7 +122,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: &regex_syntax::Error, unescape
}; };
if let Some((primary, auxiliary, kind)) = parts if let Some((primary, auxiliary, kind)) = parts
&& let Some(literal_snippet) = snippet_opt(cx, base) && let Some(literal_snippet) = base.get_source_text(cx)
&& let Some(inner) = literal_snippet.get(offset as usize..) && let Some(inner) = literal_snippet.get(offset as usize..)
// Only convert to native rustc spans if the parsed regex matches the // Only convert to native rustc spans if the parsed regex matches the
// source snippet exactly, to ensure the span offsets are correct // source snippet exactly, to ensure the span offsets are correct

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::source::{snippet_with_context, SpanRangeExt};
use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::{ use clippy_utils::{
@ -250,20 +250,25 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
|err| { |err| {
err.span_label(local.span, "unnecessary `let` binding"); err.span_label(local.span, "unnecessary `let` binding");
if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { if let Some(src) = initexpr.span.get_source_text(cx) {
if binary_expr_needs_parentheses(initexpr) { let sugg = if binary_expr_needs_parentheses(initexpr) {
if !has_enclosing_paren(&snippet) { if has_enclosing_paren(&src) {
snippet = format!("({snippet})"); src.to_owned()
} else {
format!("({src})")
} }
} else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
if !has_enclosing_paren(&snippet) { if has_enclosing_paren(&src) {
snippet = format!("({snippet})"); format!("{src} as _")
} else {
format!("({src}) as _")
} }
snippet.push_str(" as _"); } else {
} src.to_owned()
};
err.multipart_suggestion( err.multipart_suggestion(
"return the expression directly", "return the expression directly",
vec![(local.span, String::new()), (retexpr.span, snippet)], vec![(local.span, String::new()), (retexpr.span, sugg)],
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} else { } else {

View File

@ -2,7 +2,7 @@
use clippy_utils::get_trait_def_id; use clippy_utils::get_trait_def_id;
use clippy_utils::higher::VecArgs; use clippy_utils::higher::VecArgs;
use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -92,12 +92,12 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if matches!(lang_item, LangItem::Range) if matches!(lang_item, LangItem::Range)
&& let ty = cx.typeck_results().expr_ty(start.expr) && let ty = cx.typeck_results().expr_ty(start.expr)
&& let Some(snippet) = snippet_opt(cx, span) && let Some(snippet) = span.get_source_text(cx)
// `is_from_proc_macro` will skip any `vec![]`. Let's not! // `is_from_proc_macro` will skip any `vec![]`. Let's not!
&& snippet.starts_with(suggested_type.starts_with()) && snippet.starts_with(suggested_type.starts_with())
&& snippet.ends_with(suggested_type.ends_with()) && snippet.ends_with(suggested_type.ends_with())
&& let Some(start_snippet) = snippet_opt(cx, start.span) && let Some(start_snippet) = start.span.get_source_text(cx)
&& let Some(end_snippet) = snippet_opt(cx, end.span) && let Some(end_snippet) = end.span.get_source_text(cx)
{ {
let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"])
&& implements_trait(cx, ty, step_def_id, &[]) && implements_trait(cx, ty, step_def_id, &[])

View File

@ -1,7 +1,7 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
use core::hash::{Hash, Hasher}; use core::hash::{Hash, Hasher};
use itertools::Itertools; use itertools::Itertools;
@ -206,8 +206,7 @@ fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
let fixed_trait_snippet = unique_traits let fixed_trait_snippet = unique_traits
.iter() .iter()
.filter_map(|b| snippet_opt(cx, b.span)) .filter_map(|b| b.span.get_source_text(cx))
.collect::<Vec<_>>()
.join(" + "); .join(" + ");
span_lint_and_sugg( span_lint_and_sugg(
@ -462,9 +461,8 @@ fn rollup_traits(
let traits = comparable_bounds let traits = comparable_bounds
.iter() .iter()
.filter_map(|&(_, span)| snippet_opt(cx, span)) .filter_map(|&(_, span)| span.get_source_text(cx))
.collect::<Vec<_>>(); .join(" + ");
let traits = traits.join(" + ");
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{position_before_rarrow, snippet_opt}; use clippy_utils::source::{position_before_rarrow, SpanRangeExt};
use rustc_ast::visit::FnKind; use rustc_ast::visit::FnKind;
use rustc_ast::{ast, ClosureBinder}; use rustc_ast::{ast, ClosureBinder};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -128,15 +128,17 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
let (ret_span, appl) = let (ret_span, appl) =
snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| { span.with_hi(ty.span.hi())
position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { .get_source_text(cx)
( .map_or((ty.span, Applicability::MaybeIncorrect), |src| {
#[expect(clippy::cast_possible_truncation)] position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), (
Applicability::MachineApplicable, #[expect(clippy::cast_possible_truncation)]
) ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
}) Applicability::MachineApplicable,
}); )
})
});
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
UNUSED_UNIT, UNUSED_UNIT,

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use rustc_ast::ast::{Item, VisibilityKind}; use rustc_ast::ast::{Item, VisibilityKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
@ -82,9 +82,7 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if !in_external_macro(cx.sess(), item.span) if !in_external_macro(cx.sess(), item.span)
&& let VisibilityKind::Restricted { path, shorthand, .. } = &item.vis.kind && let VisibilityKind::Restricted { path, shorthand, .. } = &item.vis.kind
{ {
if **path == kw::SelfLower if **path == kw::SelfLower && !is_from_proc_macro(cx, item.vis.span) {
&& let Some(false) = is_from_proc_macro(cx, item.vis.span)
{
span_lint_and_then( span_lint_and_then(
cx, cx,
NEEDLESS_PUB_SELF, NEEDLESS_PUB_SELF,
@ -104,7 +102,7 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate) if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate)
&& !*shorthand && !*shorthand
&& let [.., last] = &*path.segments && let [.., last] = &*path.segments
&& let Some(false) = is_from_proc_macro(cx, item.vis.span) && !is_from_proc_macro(cx, item.vis.span)
{ {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then( span_lint_and_then(
@ -125,7 +123,7 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if *shorthand if *shorthand
&& let [.., last] = &*path.segments && let [.., last] = &*path.segments
&& let Some(false) = is_from_proc_macro(cx, item.vis.span) && !is_from_proc_macro(cx, item.vis.span)
{ {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then( span_lint_and_then(
@ -147,6 +145,6 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
} }
} }
fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> Option<bool> { fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool {
snippet_opt(cx, span).map(|s| !s.starts_with("pub")) !span.check_source_text(cx, |src| src.starts_with("pub"))
} }

View File

@ -2,7 +2,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::is_in_test; use clippy_utils::is_in_test;
use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt};
use rustc_ast::token::LitKind; use rustc_ast::token::LitKind;
use rustc_ast::{ use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
@ -397,7 +397,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma
format!("using `{name}!()` with a format string that ends in a single newline"), format!("using `{name}!()` with a format string that ends in a single newline"),
|diag| { |diag| {
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let Some(format_snippet) = snippet_opt(cx, format_string_span) else { let Some(format_snippet) = format_string_span.get_source_text(cx) else {
return; return;
}; };
@ -492,7 +492,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
&& let Some(arg) = format_args.arguments.by_index(index) && let Some(arg) = format_args.arguments.by_index(index)
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
&& !arg.expr.span.from_expansion() && !arg.expr.span.from_expansion()
&& let Some(value_string) = snippet_opt(cx, arg.expr.span) && let Some(value_string) = arg.expr.span.get_source_text(cx)
{ {
let (replacement, replace_raw) = match lit.kind { let (replacement, replace_raw) = match lit.kind {
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
@ -515,7 +515,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
_ => continue, _ => continue,
}; };
let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
continue; continue;
}; };
let format_string_is_raw = format_string_snippet.starts_with('r'); let format_string_is_raw = format_string_snippet.starts_with('r');

View File

@ -687,7 +687,7 @@ fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> {
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
&& let expr_lo = expr_span.lo() && let expr_lo = expr_span.lo()
&& expr_lo >= span.lo && expr_lo >= span.lo
&& let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx) && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx)
&& let Some(src) = src.as_str() && let Some(src) = src.as_str()
{ {
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};

View File

@ -1191,9 +1191,9 @@ fn eq_span_tokens(
pred: impl Fn(TokenKind) -> bool, pred: impl Fn(TokenKind) -> bool,
) -> bool { ) -> bool {
fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool { fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
if let Some(lsrc) = left.get_source_text(cx) if let Some(lsrc) = left.get_source_range(cx)
&& let Some(lsrc) = lsrc.as_str() && let Some(lsrc) = lsrc.as_str()
&& let Some(rsrc) = right.get_source_text(cx) && let Some(rsrc) = right.get_source_range(cx)
&& let Some(rsrc) = rsrc.as_str() && let Some(rsrc) = rsrc.as_str()
{ {
let pred = |t: &(_, _)| pred(t.0); let pred = |t: &(_, _)| pred(t.0);

View File

@ -16,7 +16,7 @@
}; };
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::ops::Range; use std::ops::{Deref, Index, Range};
pub trait HasSession { pub trait HasSession {
fn sess(&self) -> &Session; fn sess(&self) -> &Session;
@ -94,10 +94,16 @@ fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
} }
pub trait SpanRangeExt: SpanRange { pub trait SpanRangeExt: SpanRange {
/// Attempts to get a handle to the source text. Returns `None` if either the span is malformed,
/// or the source text is not accessible.
fn get_source_text(self, cx: &impl HasSession) -> Option<SourceText> {
get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new)
}
/// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// Gets the source file, and range in the file, of the given span. Returns `None` if the span
/// extends through multiple files, or is malformed. /// extends through multiple files, or is malformed.
fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> { fn get_source_range(self, cx: &impl HasSession) -> Option<SourceFileRange> {
get_source_text(cx.sess().source_map(), self.into_range()) get_source_range(cx.sess().source_map(), self.into_range())
} }
/// Calls the given function with the source text referenced and returns the value. Returns /// Calls the given function with the source text referenced and returns the value. Returns
@ -144,21 +150,58 @@ fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> { fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
trim_start(cx.sess().source_map(), self.into_range()) trim_start(cx.sess().source_map(), self.into_range())
} }
/// Writes the referenced source text to the given writer. Will return `Err` if the source text
/// could not be retrieved.
fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
}
/// Extracts the referenced source text as an owned string.
fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
self.with_source_text(cx, ToOwned::to_owned)
}
} }
impl<T: SpanRange> SpanRangeExt for T {} impl<T: SpanRange> SpanRangeExt for T {}
fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> { /// Handle to a range of text in a source file.
pub struct SourceText(SourceFileRange);
impl SourceText {
/// Takes ownership of the source file handle if the source text is accessible.
pub fn new(text: SourceFileRange) -> Option<Self> {
if text.as_str().is_some() {
Some(Self(text))
} else {
None
}
}
/// Gets the source text.
pub fn as_str(&self) -> &str {
self.0.as_str().unwrap()
}
/// Converts this into an owned string.
pub fn to_owned(&self) -> String {
self.as_str().to_owned()
}
}
impl Deref for SourceText {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for SourceText {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<T> Index<T> for SourceText
where
str: Index<T>,
{
type Output = <str as Index<T>>::Output;
fn index(&self, idx: T) -> &Self::Output {
&self.as_str()[idx]
}
}
impl fmt::Display for SourceText {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
let start = sm.lookup_byte_offset(sp.start); let start = sm.lookup_byte_offset(sp.start);
let end = sm.lookup_byte_offset(sp.end); let end = sm.lookup_byte_offset(sp.end);
if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos { if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
@ -169,7 +212,7 @@ fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange
} }
fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> { fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
if let Some(src) = get_source_text(sm, sp) if let Some(src) = get_source_range(sm, sp)
&& let Some(src) = src.as_str() && let Some(src) = src.as_str()
{ {
Some(f(src)) Some(f(src))
@ -183,7 +226,7 @@ fn with_source_text_and_range<T>(
sp: Range<BytePos>, sp: Range<BytePos>,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
) -> Option<T> { ) -> Option<T> {
if let Some(src) = get_source_text(sm, sp) if let Some(src) = get_source_range(sm, sp)
&& let Some(text) = &src.sf.src && let Some(text) = &src.sf.src
{ {
Some(f(text, src.range)) Some(f(text, src.range))
@ -198,7 +241,7 @@ fn map_range(
sp: Range<BytePos>, sp: Range<BytePos>,
f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>, f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
) -> Option<Range<BytePos>> { ) -> Option<Range<BytePos>> {
if let Some(src) = get_source_text(sm, sp.clone()) if let Some(src) = get_source_range(sm, sp.clone())
&& let Some(text) = &src.sf.src && let Some(text) = &src.sf.src
&& let Some(range) = f(text, src.range.clone()) && let Some(range) = f(text, src.range.clone())
{ {
@ -232,13 +275,6 @@ fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
.unwrap_or(sp) .unwrap_or(sp)
} }
fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::Write) -> fmt::Result {
match with_source_text(sm, sp, |src| dst.write_str(src)) {
Some(x) => x,
None => Err(fmt::Error),
}
}
pub struct SourceFileRange { pub struct SourceFileRange {
pub sf: Lrc<SourceFile>, pub sf: Lrc<SourceFile>,
pub range: Range<usize>, pub range: Range<usize>,