Skip to content

Commit 7d935ac

Browse files
committed
introduce rc_clone_in_vec_init lint
1 parent 0509a96 commit 7d935ac

20 files changed

+269
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3642,6 +3642,7 @@ Released 2018-09-13
36423642
[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
36433643
[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
36443644
[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
3645+
[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
36453646
[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
36463647
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
36473648
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
263263
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
264264
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
265265
LintId::of(ranges::REVERSED_EMPTY_RANGES),
266+
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
266267
LintId::of(redundant_clone::REDUNDANT_CLONE),
267268
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
268269
LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ store.register_lints(&[
446446
ranges::RANGE_PLUS_ONE,
447447
ranges::RANGE_ZIP_WITH_LEN,
448448
ranges::REVERSED_EMPTY_RANGES,
449+
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
449450
redundant_clone::REDUNDANT_CLONE,
450451
redundant_closure_call::REDUNDANT_CLOSURE_CALL,
451452
redundant_else::REDUNDANT_ELSE,

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
2626
LintId::of(methods::SUSPICIOUS_MAP),
2727
LintId::of(mut_key::MUTABLE_KEY_TYPE),
2828
LintId::of(octal_escapes::OCTAL_ESCAPES),
29+
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
2930
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
3031
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
3132
])

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ mod ptr_offset_with_cast;
345345
mod pub_use;
346346
mod question_mark;
347347
mod ranges;
348+
mod rc_clone_in_vec_init;
348349
mod redundant_clone;
349350
mod redundant_closure_call;
350351
mod redundant_else;
@@ -890,6 +891,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
890891
let max_include_file_size = conf.max_include_file_size;
891892
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
892893
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
894+
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
893895
// add lints here, do not remove this comment, it's used in `new_lint`
894896
}
895897

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::higher::VecArgs;
3+
use clippy_utils::last_path_segment;
4+
use clippy_utils::macros::{root_macro_call_first_node, MacroCall};
5+
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_span::{sym, Symbol};
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]`
13+
///
14+
/// ### Why is this bad?
15+
/// This will create `elem` once and clone it `len` times - doing so with `Arc`
16+
/// is a bit misleading, as it will create references to the same pointer, rather
17+
/// than different `Arc` instances.
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// let v = vec![std::sync::Arc::new("some data".to_string()); 100];
22+
/// // or
23+
/// let v = vec![std::rc::Rc::new("some data".to_string()); 100];
24+
/// ```
25+
/// Use instead:
26+
/// ```rust
27+
/// // Create the reference beforehand to clarify it that
28+
/// // this reference should be cloned for each value
29+
/// let data = std::rc::Rc::new("some data".to_string());
30+
/// let v = vec![data; 100];
31+
///
32+
/// // Or initialize each value separately:
33+
/// let mut data = Vec::with_capacity(100);
34+
/// for _ in 0..=100 {
35+
/// data.push(std::rc::Rc::new("some data".to_string()));
36+
/// }
37+
/// ```
38+
#[clippy::version = "1.62.0"]
39+
pub RC_CLONE_IN_VEC_INIT,
40+
suspicious,
41+
"initializing `Arc` or `Rc` in `vec![elem; len]`"
42+
}
43+
declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]);
44+
45+
impl LateLintPass<'_> for RcCloneInVecInit {
46+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
47+
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; };
48+
let Some(VecArgs::Repeat(elem, _)) = VecArgs::hir(cx, expr) else { return; };
49+
let Some(symbol) = new_reference_call(cx, elem) else { return; };
50+
51+
emit_lint(cx, symbol, &macro_call);
52+
}
53+
}
54+
55+
fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, macro_call: &MacroCall) {
56+
let symbol_name = symbol.as_str();
57+
58+
span_lint_and_then(
59+
cx,
60+
RC_CLONE_IN_VEC_INIT,
61+
macro_call.span,
62+
&format!("calling `{symbol_name}::new` in `vec![elem; len]`"),
63+
|diag| {
64+
diag.note(format!("each element will point to the same `{symbol_name}` instance"));
65+
diag.help(format!(
66+
"if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
67+
));
68+
diag.help("or if not, initilaize each element individually");
69+
},
70+
);
71+
}
72+
73+
/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new`
74+
fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
75+
if_chain! {
76+
if let ExprKind::Call(func, _args) = expr.kind;
77+
if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind;
78+
if let TyKind::Path(ref ty_path) = ty.kind;
79+
if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id();
80+
if last_path_segment(func_path).ident.name == sym::new;
81+
82+
then {
83+
return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc);
84+
}
85+
}
86+
87+
None
88+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::sync::Mutex;
3+
4+
fn main() {
5+
let v = vec![
6+
std::sync::Arc::new(Mutex::new({
7+
let x = 1;
8+
dbg!(x);
9+
x
10+
}));
11+
2
12+
];
13+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: calling `Arc::new` in `vec![elem; len]`
2+
--> $DIR/complex_case.rs:5:13
3+
|
4+
LL | let v = vec![
5+
| _____________^
6+
LL | | std::sync::Arc::new(Mutex::new({
7+
LL | | let x = 1;
8+
LL | | dbg!(x);
9+
... |
10+
LL | | 2
11+
LL | | ];
12+
| |_____^
13+
|
14+
= note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
15+
= note: each element will point to the same `Arc` instance
16+
= help: if this is intentional, consider extracting the `Arc` initialization to a variable
17+
= help: or if not, initilaize each element individually
18+
19+
error: aborting due to previous error
20+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
3+
#[derive(Clone)]
4+
struct Arc;
5+
6+
impl Arc {
7+
fn new() -> Self {
8+
Arc
9+
}
10+
}
11+
12+
fn main() {
13+
let v = vec![Arc::new(); 2];
14+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::sync::Arc;
3+
4+
fn main() {
5+
let v = vec![Arc::new("x".to_string()); 2];
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: calling `Arc::new` in `vec![elem; len]`
2+
--> $DIR/simple_case.rs:5:13
3+
|
4+
LL | let v = vec![Arc::new("x".to_string()); 2];
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
8+
= note: each element will point to the same `Arc` instance
9+
= help: if this is intentional, consider extracting the `Arc` initialization to a variable
10+
= help: or if not, initilaize each element individually
11+
12+
error: aborting due to previous error
13+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::sync::{Arc, Mutex};
3+
4+
fn main() {
5+
let v = vec![String::new(); 2];
6+
let v1 = vec![1; 2];
7+
let v2 = vec![
8+
Box::new(std::sync::Arc::new({
9+
let y = 3;
10+
dbg!(y);
11+
y
12+
}));
13+
2
14+
];
15+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::sync::Arc;
3+
4+
fn main() {
5+
let v = vec![Arc::new("x".to_string())];
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::sync::Mutex;
3+
4+
fn main() {
5+
let v = vec![
6+
std::rc::Rc::new(Mutex::new({
7+
let x = 1;
8+
dbg!(x);
9+
x
10+
}));
11+
2
12+
];
13+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: calling `Rc::new` in `vec![elem; len]`
2+
--> $DIR/complex_case.rs:5:13
3+
|
4+
LL | let v = vec![
5+
| _____________^
6+
LL | | std::rc::Rc::new(Mutex::new({
7+
LL | | let x = 1;
8+
LL | | dbg!(x);
9+
... |
10+
LL | | 2
11+
LL | | ];
12+
| |_____^
13+
|
14+
= note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
15+
= note: each element will point to the same `Rc` instance
16+
= help: if this is intentional, consider extracting the `Rc` initialization to a variable
17+
= help: or if not, initilaize each element individually
18+
19+
error: aborting due to previous error
20+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
3+
#[derive(Clone)]
4+
struct Rc;
5+
6+
impl Rc {
7+
fn new() -> Self {
8+
Rc
9+
}
10+
}
11+
12+
fn main() {
13+
let v = vec![Rc::new(); 2];
14+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::rc::Rc;
3+
4+
fn main() {
5+
let v = vec![Rc::new("x".to_string()); 2];
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error: calling `Rc::new` in `vec![elem; len]`
2+
--> $DIR/simple_case.rs:5:13
3+
|
4+
LL | let v = vec![Rc::new("x".to_string()); 2];
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
8+
= note: each element will point to the same `Rc` instance
9+
= help: if this is intentional, consider extracting the `Rc` initialization to a variable
10+
= help: or if not, initilaize each element individually
11+
12+
error: aborting due to previous error
13+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::rc::Rc;
3+
use std::sync::Mutex;
4+
5+
fn main() {
6+
let v = vec![String::new(); 2];
7+
let v1 = vec![1; 2];
8+
let v2 = vec![
9+
Box::new(std::rc::Rc::new({
10+
let y = 3;
11+
dbg!(y);
12+
y
13+
}));
14+
2
15+
];
16+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::rc::Rc;
3+
4+
fn main() {
5+
let v = vec![Rc::new("x".to_string())];
6+
}

0 commit comments

Comments
 (0)