Skip to content

Commit 1b5317f

Browse files
committed
Add rc_buffer lint for Rc<String> and other buffer types
1 parent 231444d commit 1b5317f

File tree

7 files changed

+135
-1
lines changed

7 files changed

+135
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,6 +1775,7 @@ Released 2018-09-13
17751775
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
17761776
[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
17771777
[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
1778+
[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
17781779
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
17791780
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
17801781
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
837837
&types::LET_UNIT_VALUE,
838838
&types::LINKEDLIST,
839839
&types::OPTION_OPTION,
840+
&types::RC_BUFFER,
840841
&types::REDUNDANT_ALLOCATION,
841842
&types::TYPE_COMPLEXITY,
842843
&types::UNIT_ARG,
@@ -1804,6 +1805,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
18041805
LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
18051806
LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE),
18061807
LintId::of(&transmute::USELESS_TRANSMUTE),
1808+
LintId::of(&types::RC_BUFFER),
18071809
LintId::of(&use_self::USE_SELF),
18081810
]);
18091811
}

clippy_lints/src/types.rs

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,41 @@ declare_clippy_lint! {
215215
"redundant allocation"
216216
}
217217

218+
declare_clippy_lint! {
219+
/// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec
220+
///
221+
/// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since
222+
/// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>.
223+
///
224+
/// While mutating a buffer type would still be possible with Rc::get_mut(), it only
225+
/// works if there are no additional references yet, which defeats the purpose of
226+
/// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
227+
/// type with an interior mutable container (such as RefCell or Mutex) would normally
228+
/// be used.
229+
///
230+
/// **Known problems:** None.
231+
///
232+
/// **Example:**
233+
/// ```rust,ignore
234+
/// # use std::rc::Rc;
235+
/// fn foo(interned: Rc<String>) { ... }
236+
/// ```
237+
///
238+
/// Better:
239+
///
240+
/// ```rust,ignore
241+
/// fn foo(interned: Rc<str>) { ... }
242+
/// ```
243+
pub RC_BUFFER,
244+
nursery,
245+
"shared ownership of a buffer type"
246+
}
247+
218248
pub struct Types {
219249
vec_box_size_threshold: u64,
220250
}
221251

222-
impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]);
252+
impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]);
223253

224254
impl<'tcx> LateLintPass<'tcx> for Types {
225255
fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str])
272302
None
273303
}
274304

305+
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
306+
if match_type_parameter(cx, qpath, &paths::STRING).is_some() {
307+
return Some("str");
308+
}
309+
if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() {
310+
return Some("std::ffi::OsStr");
311+
}
312+
if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() {
313+
return Some("std::path::Path");
314+
}
315+
None
316+
}
317+
275318
fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
276319
let last = last_path_segment(qpath);
277320
if_chain! {
@@ -385,6 +428,45 @@ impl Types {
385428
);
386429
return; // don't recurse into the type
387430
}
431+
if let Some(alternate) = match_buffer_type(cx, qpath) {
432+
span_lint_and_sugg(
433+
cx,
434+
RC_BUFFER,
435+
hir_ty.span,
436+
"usage of `Rc<T>` when T is a buffer type",
437+
"try",
438+
format!("Rc<{}>", alternate),
439+
Applicability::MachineApplicable,
440+
);
441+
return; // don't recurse into the type
442+
}
443+
if match_type_parameter(cx, qpath, &paths::VEC).is_some() {
444+
let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
445+
GenericArg::Type(ty) => match &ty.kind {
446+
TyKind::Path(qpath) => qpath,
447+
_ => return,
448+
},
449+
_ => return,
450+
};
451+
let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] {
452+
GenericArg::Type(ty) => ty.span,
453+
_ => return,
454+
};
455+
let mut applicability = Applicability::MachineApplicable;
456+
span_lint_and_sugg(
457+
cx,
458+
RC_BUFFER,
459+
hir_ty.span,
460+
"usage of `Rc<T>` when T is a buffer type",
461+
"try",
462+
format!(
463+
"Rc<[{}]>",
464+
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
465+
),
466+
Applicability::MachineApplicable,
467+
);
468+
return; // don't recurse into the type
469+
}
388470
if let Some(span) = match_borrows_parameter(cx, qpath) {
389471
let mut applicability = Applicability::MachineApplicable;
390472
span_lint_and_sugg(

clippy_lints/src/utils/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
113113
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
114114
pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
115115
pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
116+
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
116117
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
117118
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
118119
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,6 +1851,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
18511851
deprecation: None,
18521852
module: "ranges",
18531853
},
1854+
Lint {
1855+
name: "rc_buffer",
1856+
group: "nursery",
1857+
desc: "shared ownership of a buffer type",
1858+
deprecation: None,
1859+
module: "types",
1860+
},
18541861
Lint {
18551862
name: "redundant_allocation",
18561863
group: "perf",

tests/ui/rc_buffer.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::ffi::OsString;
2+
use std::path::PathBuf;
3+
use std::rc::Rc;
4+
5+
#[warn(clippy::rc_buffer)]
6+
struct S {
7+
a: Rc<String>,
8+
b: Rc<PathBuf>,
9+
c: Rc<Vec<u8>>,
10+
d: Rc<OsString>,
11+
}
12+
13+
fn main() {}

tests/ui/rc_buffer.stderr

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: usage of `Rc<T>` when T is a buffer type
2+
--> $DIR/rc_buffer.rs:7:8
3+
|
4+
LL | a: Rc<String>,
5+
| ^^^^^^^^^^ help: try: `Rc<str>`
6+
|
7+
= note: `-D clippy::rc-buffer` implied by `-D warnings`
8+
9+
error: usage of `Rc<T>` when T is a buffer type
10+
--> $DIR/rc_buffer.rs:8:8
11+
|
12+
LL | b: Rc<PathBuf>,
13+
| ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
14+
15+
error: usage of `Rc<T>` when T is a buffer type
16+
--> $DIR/rc_buffer.rs:9:8
17+
|
18+
LL | c: Rc<Vec<u8>>,
19+
| ^^^^^^^^^^^ help: try: `Rc<[u8]>`
20+
21+
error: usage of `Rc<T>` when T is a buffer type
22+
--> $DIR/rc_buffer.rs:10:8
23+
|
24+
LL | d: Rc<OsString>,
25+
| ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
26+
27+
error: aborting due to 4 previous errors
28+

0 commit comments

Comments
 (0)