fix(rc_buffer): don't touch the path to Rc/Arc in the suggestion (#15803)

Fixes https://github.com/rust-lang/rust-clippy/issues/15802

changelog: [`rc_buffer`]: don't touch the path to `Rc`/`Arc` in the
suggestion
This commit is contained in:
llogiq 2025-11-14 09:52:59 +00:00 committed by GitHub
commit 663ef9b31b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 244 additions and 151 deletions

View file

@ -1,115 +1,74 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::qpath_generic_tys;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, QPath, TyKind};
use rustc_hir::{QPath, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use std::borrow::Cow;
use std::fmt;
use super::RC_BUFFER;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
let app = Applicability::Unspecified;
let name = cx.tcx.get_diagnostic_name(def_id);
if name == Some(sym::Rc) {
if let Some(alternate) = match_buffer_type(cx, qpath) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Rc<T>` when T is a buffer type",
|diag| {
diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app);
},
);
} else {
let Some(ty) = qpath_generic_tys(qpath).next() else {
return false;
};
if !ty.basic_res().is_diag_item(cx, sym::Vec) {
return false;
}
let TyKind::Path(qpath) = &ty.kind else { return false };
let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span,
None => return false,
};
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Rc<T>` when T is a buffer type",
|diag| {
let mut applicability = app;
diag.span_suggestion(
hir_ty.span,
"try",
format!(
"Rc<[{}]>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
app,
);
},
);
return true;
}
} else if name == Some(sym::Arc) {
if let Some(alternate) = match_buffer_type(cx, qpath) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Arc<T>` when T is a buffer type",
|diag| {
diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app);
},
);
} else if let Some(ty) = qpath_generic_tys(qpath).next() {
if !ty.basic_res().is_diag_item(cx, sym::Vec) {
return false;
}
let TyKind::Path(qpath) = &ty.kind else { return false };
let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span,
None => return false,
};
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Arc<T>` when T is a buffer type",
|diag| {
let mut applicability = app;
diag.span_suggestion(
hir_ty.span,
"try",
format!(
"Arc<[{}]>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
app,
);
},
);
return true;
}
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
let mut app = Applicability::Unspecified;
let kind = match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::Rc) => RcKind::Rc,
Some(sym::Arc) => RcKind::Arc,
_ => return false,
};
if let Some(ty) = qpath_generic_tys(qpath).next()
&& let Some(alternate) = match_buffer_type(cx, ty, &mut app)
{
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
format!("usage of `{kind}<T>` when `T` is a buffer type"),
|diag| {
diag.span_suggestion_verbose(ty.span, "try", alternate, app);
},
);
true
} else {
false
}
false
}
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
let ty = qpath_generic_tys(qpath).next()?;
enum RcKind {
Rc,
Arc,
}
impl fmt::Display for RcKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Rc => f.write_str("Rc"),
Self::Arc => f.write_str("Arc"),
}
}
}
fn match_buffer_type(
cx: &LateContext<'_>,
ty: &Ty<'_>,
applicability: &mut Applicability,
) -> Option<Cow<'static, str>> {
let id = ty.basic_res().opt_def_id()?;
let path = match cx.tcx.get_diagnostic_name(id) {
Some(sym::OsString) => "std::ffi::OsStr",
Some(sym::PathBuf) => "std::path::Path",
_ if Some(id) == cx.tcx.lang_items().string() => "str",
Some(sym::OsString) => "std::ffi::OsStr".into(),
Some(sym::PathBuf) => "std::path::Path".into(),
Some(sym::Vec) => {
let TyKind::Path(vec_qpath) = &ty.kind else {
return None;
};
let vec_generic_ty = qpath_generic_tys(vec_qpath).next()?;
let snippet = snippet_with_applicability(cx, vec_generic_ty.span, "_", applicability);
format!("[{snippet}]").into()
},
_ if Some(id) == cx.tcx.lang_items().string() => "str".into(),
_ => return None,
};
Some(path)

View file

@ -1,5 +1,4 @@
#![warn(clippy::rc_buffer)]
#![allow(dead_code, unused_imports)]
use std::cell::RefCell;
use std::ffi::OsString;
@ -32,4 +31,9 @@ fn func_bad4(_: Rc<std::ffi::OsStr>) {}
// does not trigger lint
fn func_good1(_: Rc<RefCell<String>>) {}
mod issue_15802 {
fn foo(_: std::rc::Rc<[u8]>) {}
//~^ rc_buffer
}
fn main() {}

View file

@ -1,5 +1,4 @@
#![warn(clippy::rc_buffer)]
#![allow(dead_code, unused_imports)]
use std::cell::RefCell;
use std::ffi::OsString;
@ -32,4 +31,9 @@ fn func_bad4(_: Rc<OsString>) {}
// does not trigger lint
fn func_good1(_: Rc<RefCell<String>>) {}
mod issue_15802 {
fn foo(_: std::rc::Rc<Vec<u8>>) {}
//~^ rc_buffer
}
fn main() {}

View file

@ -1,53 +1,112 @@
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:11:11
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:10:11
|
LL | bad1: Rc<String>,
| ^^^^^^^^^^ help: try: `Rc<str>`
| ^^^^^^^^^^
|
= note: `-D clippy::rc-buffer` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::rc_buffer)]`
help: try
|
LL - bad1: Rc<String>,
LL + bad1: Rc<str>,
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:13:11
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:12:11
|
LL | bad2: Rc<PathBuf>,
| ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
| ^^^^^^^^^^^
|
help: try
|
LL - bad2: Rc<PathBuf>,
LL + bad2: Rc<std::path::Path>,
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:15:11
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:14:11
|
LL | bad3: Rc<Vec<u8>>,
| ^^^^^^^^^^^ help: try: `Rc<[u8]>`
| ^^^^^^^^^^^
|
help: try
|
LL - bad3: Rc<Vec<u8>>,
LL + bad3: Rc<[u8]>,
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:17:11
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:16:11
|
LL | bad4: Rc<OsString>,
| ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
| ^^^^^^^^^^^^
|
help: try
|
LL - bad4: Rc<OsString>,
LL + bad4: Rc<std::ffi::OsStr>,
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:24:17
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:23:17
|
LL | fn func_bad1(_: Rc<String>) {}
| ^^^^^^^^^^ help: try: `Rc<str>`
| ^^^^^^^^^^
|
help: try
|
LL - fn func_bad1(_: Rc<String>) {}
LL + fn func_bad1(_: Rc<str>) {}
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:26:17
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:25:17
|
LL | fn func_bad2(_: Rc<PathBuf>) {}
| ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
| ^^^^^^^^^^^
|
help: try
|
LL - fn func_bad2(_: Rc<PathBuf>) {}
LL + fn func_bad2(_: Rc<std::path::Path>) {}
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:28:17
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:27:17
|
LL | fn func_bad3(_: Rc<Vec<u8>>) {}
| ^^^^^^^^^^^ help: try: `Rc<[u8]>`
| ^^^^^^^^^^^
|
help: try
|
LL - fn func_bad3(_: Rc<Vec<u8>>) {}
LL + fn func_bad3(_: Rc<[u8]>) {}
|
error: usage of `Rc<T>` when T is a buffer type
--> tests/ui/rc_buffer.rs:30:17
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:29:17
|
LL | fn func_bad4(_: Rc<OsString>) {}
| ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
| ^^^^^^^^^^^^
|
help: try
|
LL - fn func_bad4(_: Rc<OsString>) {}
LL + fn func_bad4(_: Rc<std::ffi::OsStr>) {}
|
error: aborting due to 8 previous errors
error: usage of `Rc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer.rs:35:15
|
LL | fn foo(_: std::rc::Rc<Vec<u8>>) {}
| ^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - fn foo(_: std::rc::Rc<Vec<u8>>) {}
LL + fn foo(_: std::rc::Rc<[u8]>) {}
|
error: aborting due to 9 previous errors

View file

@ -1,5 +1,4 @@
#![warn(clippy::rc_buffer)]
#![allow(dead_code, unused_imports)]
use std::ffi::OsString;
use std::path::PathBuf;
@ -31,4 +30,9 @@ fn func_bad4(_: Arc<std::ffi::OsStr>) {}
// does not trigger lint
fn func_good1(_: Arc<Mutex<String>>) {}
mod issue_15802 {
fn foo(_: std::sync::Arc<[u8]>) {}
//~^ rc_buffer
}
fn main() {}

View file

@ -1,5 +1,4 @@
#![warn(clippy::rc_buffer)]
#![allow(dead_code, unused_imports)]
use std::ffi::OsString;
use std::path::PathBuf;
@ -31,4 +30,9 @@ fn func_bad4(_: Arc<OsString>) {}
// does not trigger lint
fn func_good1(_: Arc<Mutex<String>>) {}
mod issue_15802 {
fn foo(_: std::sync::Arc<Vec<u8>>) {}
//~^ rc_buffer
}
fn main() {}

View file

@ -1,53 +1,112 @@
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:10:11
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:9:11
|
LL | bad1: Arc<String>,
| ^^^^^^^^^^^ help: try: `Arc<str>`
| ^^^^^^^^^^^
|
= note: `-D clippy::rc-buffer` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::rc_buffer)]`
help: try
|
LL - bad1: Arc<String>,
LL + bad1: Arc<str>,
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:12:11
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:11:11
|
LL | bad2: Arc<PathBuf>,
| ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
| ^^^^^^^^^^^^
|
help: try
|
LL - bad2: Arc<PathBuf>,
LL + bad2: Arc<std::path::Path>,
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:14:11
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:13:11
|
LL | bad3: Arc<Vec<u8>>,
| ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
| ^^^^^^^^^^^^
|
help: try
|
LL - bad3: Arc<Vec<u8>>,
LL + bad3: Arc<[u8]>,
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:16:11
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:15:11
|
LL | bad4: Arc<OsString>,
| ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
| ^^^^^^^^^^^^^
|
help: try
|
LL - bad4: Arc<OsString>,
LL + bad4: Arc<std::ffi::OsStr>,
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:23:17
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:22:17
|
LL | fn func_bad1(_: Arc<String>) {}
| ^^^^^^^^^^^ help: try: `Arc<str>`
| ^^^^^^^^^^^
|
help: try
|
LL - fn func_bad1(_: Arc<String>) {}
LL + fn func_bad1(_: Arc<str>) {}
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:25:17
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:24:17
|
LL | fn func_bad2(_: Arc<PathBuf>) {}
| ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
| ^^^^^^^^^^^^
|
help: try
|
LL - fn func_bad2(_: Arc<PathBuf>) {}
LL + fn func_bad2(_: Arc<std::path::Path>) {}
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:27:17
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:26:17
|
LL | fn func_bad3(_: Arc<Vec<u8>>) {}
| ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
| ^^^^^^^^^^^^
|
help: try
|
LL - fn func_bad3(_: Arc<Vec<u8>>) {}
LL + fn func_bad3(_: Arc<[u8]>) {}
|
error: usage of `Arc<T>` when T is a buffer type
--> tests/ui/rc_buffer_arc.rs:29:17
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:28:17
|
LL | fn func_bad4(_: Arc<OsString>) {}
| ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
| ^^^^^^^^^^^^^
|
help: try
|
LL - fn func_bad4(_: Arc<OsString>) {}
LL + fn func_bad4(_: Arc<std::ffi::OsStr>) {}
|
error: aborting due to 8 previous errors
error: usage of `Arc<T>` when `T` is a buffer type
--> tests/ui/rc_buffer_arc.rs:34:15
|
LL | fn foo(_: std::sync::Arc<Vec<u8>>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - fn foo(_: std::sync::Arc<Vec<u8>>) {}
LL + fn foo(_: std::sync::Arc<[u8]>) {}
|
error: aborting due to 9 previous errors