Skip to content

Commit 1a11a49

Browse files
committed
Auto merge of #8769 - yonip23:8719, r=xFrednet
introduce rc_clone_in_vec_init lint Closes #8719 changelog: Introduce [`rc_clone_in_vec_init`] lint
2 parents 77effb7 + feb6d8c commit 1a11a49

File tree

10 files changed

+255
-0
lines changed

10 files changed

+255
-0
lines changed

CHANGELOG.md

+1
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

+1
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

+1
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

+1
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

+2
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ mod ptr_offset_with_cast;
346346
mod pub_use;
347347
mod question_mark;
348348
mod ranges;
349+
mod rc_clone_in_vec_init;
349350
mod redundant_clone;
350351
mod redundant_closure_call;
351352
mod redundant_else;
@@ -900,6 +901,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
900901
let max_include_file_size = conf.max_include_file_size;
901902
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
902903
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
904+
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
903905
// add lints here, do not remove this comment, it's used in `new_lint`
904906
}
905907

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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` or `Rc`
16+
/// is a bit misleading, as it will create references to the same pointer, rather
17+
/// than different 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+
///
28+
/// // Initialize each value separately:
29+
/// let mut data = Vec::with_capacity(100);
30+
/// for _ in 0..100 {
31+
/// data.push(std::rc::Rc::new("some data".to_string()));
32+
/// }
33+
///
34+
/// // Or if you want clones of the same reference,
35+
/// // Create the reference beforehand to clarify that
36+
/// // it should be cloned for each value
37+
/// let data = std::rc::Rc::new("some data".to_string());
38+
/// let v = vec![data; 100];
39+
/// ```
40+
#[clippy::version = "1.62.0"]
41+
pub RC_CLONE_IN_VEC_INIT,
42+
suspicious,
43+
"initializing `Arc` or `Rc` in `vec![elem; len]`"
44+
}
45+
declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]);
46+
47+
impl LateLintPass<'_> for RcCloneInVecInit {
48+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
49+
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; };
50+
let Some(VecArgs::Repeat(elem, _)) = VecArgs::hir(cx, expr) else { return; };
51+
let Some(symbol) = new_reference_call(cx, elem) else { return; };
52+
53+
emit_lint(cx, symbol, &macro_call);
54+
}
55+
}
56+
57+
fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, macro_call: &MacroCall) {
58+
let symbol_name = symbol.as_str();
59+
60+
span_lint_and_then(
61+
cx,
62+
RC_CLONE_IN_VEC_INIT,
63+
macro_call.span,
64+
&format!("calling `{symbol_name}::new` in `vec![elem; len]`"),
65+
|diag| {
66+
diag.note(format!("each element will point to the same `{symbol_name}` instance"));
67+
diag.help(format!(
68+
"if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
69+
));
70+
diag.help("or if not, initialize each element individually");
71+
},
72+
);
73+
}
74+
75+
/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new`
76+
fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
77+
if_chain! {
78+
if let ExprKind::Call(func, _args) = expr.kind;
79+
if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind;
80+
if let TyKind::Path(ref ty_path) = ty.kind;
81+
if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id();
82+
if last_path_segment(func_path).ident.name == sym::new;
83+
84+
then {
85+
return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc);
86+
}
87+
}
88+
89+
None
90+
}

tests/ui/rc_clone_in_vec_init/arc.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::sync::{Arc, Mutex};
3+
4+
fn main() {}
5+
6+
fn should_warn_simple_case() {
7+
let v = vec![Arc::new("x".to_string()); 2];
8+
}
9+
10+
fn should_warn_complex_case() {
11+
let v = vec![
12+
std::sync::Arc::new(Mutex::new({
13+
let x = 1;
14+
dbg!(x);
15+
x
16+
}));
17+
2
18+
];
19+
}
20+
21+
fn should_not_warn_custom_arc() {
22+
#[derive(Clone)]
23+
struct Arc;
24+
25+
impl Arc {
26+
fn new() -> Self {
27+
Arc
28+
}
29+
}
30+
31+
let v = vec![Arc::new(); 2];
32+
}
33+
34+
fn should_not_warn_vec_from_elem_but_not_arc() {
35+
let v = vec![String::new(); 2];
36+
let v1 = vec![1; 2];
37+
let v2 = vec![
38+
Box::new(std::sync::Arc::new({
39+
let y = 3;
40+
dbg!(y);
41+
y
42+
}));
43+
2
44+
];
45+
}
46+
47+
fn should_not_warn_vec_macro_but_not_from_elem() {
48+
let v = vec![Arc::new("x".to_string())];
49+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error: calling `Arc::new` in `vec![elem; len]`
2+
--> $DIR/arc.rs:7: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, initialize each element individually
11+
12+
error: calling `Arc::new` in `vec![elem; len]`
13+
--> $DIR/arc.rs:11:13
14+
|
15+
LL | let v = vec![
16+
| _____________^
17+
LL | | std::sync::Arc::new(Mutex::new({
18+
LL | | let x = 1;
19+
LL | | dbg!(x);
20+
... |
21+
LL | | 2
22+
LL | | ];
23+
| |_____^
24+
|
25+
= note: each element will point to the same `Arc` instance
26+
= help: if this is intentional, consider extracting the `Arc` initialization to a variable
27+
= help: or if not, initialize each element individually
28+
29+
error: aborting due to 2 previous errors
30+

tests/ui/rc_clone_in_vec_init/rc.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::rc::Rc;
3+
use std::sync::Mutex;
4+
5+
fn main() {}
6+
7+
fn should_warn_simple_case() {
8+
let v = vec![Rc::new("x".to_string()); 2];
9+
}
10+
11+
fn should_warn_complex_case() {
12+
let v = vec![
13+
std::rc::Rc::new(Mutex::new({
14+
let x = 1;
15+
dbg!(x);
16+
x
17+
}));
18+
2
19+
];
20+
}
21+
22+
fn should_not_warn_custom_arc() {
23+
#[derive(Clone)]
24+
struct Rc;
25+
26+
impl Rc {
27+
fn new() -> Self {
28+
Rc
29+
}
30+
}
31+
32+
let v = vec![Rc::new(); 2];
33+
}
34+
35+
fn should_not_warn_vec_from_elem_but_not_rc() {
36+
let v = vec![String::new(); 2];
37+
let v1 = vec![1; 2];
38+
let v2 = vec![
39+
Box::new(std::rc::Rc::new({
40+
let y = 3;
41+
dbg!(y);
42+
y
43+
}));
44+
2
45+
];
46+
}
47+
48+
fn should_not_warn_vec_macro_but_not_from_elem() {
49+
let v = vec![Rc::new("x".to_string())];
50+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error: calling `Rc::new` in `vec![elem; len]`
2+
--> $DIR/rc.rs:8: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, initialize each element individually
11+
12+
error: calling `Rc::new` in `vec![elem; len]`
13+
--> $DIR/rc.rs:12:13
14+
|
15+
LL | let v = vec![
16+
| _____________^
17+
LL | | std::rc::Rc::new(Mutex::new({
18+
LL | | let x = 1;
19+
LL | | dbg!(x);
20+
... |
21+
LL | | 2
22+
LL | | ];
23+
| |_____^
24+
|
25+
= note: each element will point to the same `Rc` instance
26+
= help: if this is intentional, consider extracting the `Rc` initialization to a variable
27+
= help: or if not, initialize each element individually
28+
29+
error: aborting due to 2 previous errors
30+

0 commit comments

Comments
 (0)