Skip to content

Commit a5ece81

Browse files
committed
Auto merge of #8885 - Serial-ATA:rc-clone-in-vec-init-weak, r=llogiq
Support `Weak` in [`rc_clone_in_vec_init`] changelog: Support `Weak` in [`rc_clone_in_vec_init`]
2 parents 6553b98 + fc28f6a commit a5ece81

File tree

5 files changed

+327
-45
lines changed

5 files changed

+327
-45
lines changed

clippy_lints/src/rc_clone_in_vec_init.rs

+27-29
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::higher::VecArgs;
33
use clippy_utils::last_path_segment;
44
use clippy_utils::macros::root_macro_call_first_node;
5+
use clippy_utils::paths;
56
use clippy_utils::source::{indent_of, snippet};
7+
use clippy_utils::ty::match_type;
68
use rustc_errors::Applicability;
79
use rustc_hir::{Expr, ExprKind, QPath, TyKind};
810
use rustc_lint::{LateContext, LateLintPass};
@@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol};
1113

1214
declare_clippy_lint! {
1315
/// ### What it does
14-
/// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]`
16+
/// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`)
17+
/// in `vec![elem; len]`
1518
///
1619
/// ### Why is this bad?
17-
/// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc`
20+
/// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak`
1821
/// is a bit misleading, as it will create references to the same pointer, rather
1922
/// than different instances.
2023
///
@@ -26,7 +29,6 @@ declare_clippy_lint! {
2629
/// ```
2730
/// Use instead:
2831
/// ```rust
29-
///
3032
/// // Initialize each value separately:
3133
/// let mut data = Vec::with_capacity(100);
3234
/// for _ in 0..100 {
@@ -42,34 +44,20 @@ declare_clippy_lint! {
4244
#[clippy::version = "1.62.0"]
4345
pub RC_CLONE_IN_VEC_INIT,
4446
suspicious,
45-
"initializing `Arc` or `Rc` in `vec![elem; len]`"
47+
"initializing reference-counted pointer in `vec![elem; len]`"
4648
}
4749
declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]);
4850

4951
impl LateLintPass<'_> for RcCloneInVecInit {
5052
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
5153
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; };
5254
let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; };
53-
let Some(symbol) = new_reference_call(cx, elem) else { return; };
55+
let Some((symbol, func_span)) = ref_init(cx, elem) else { return; };
5456

55-
emit_lint(cx, symbol, macro_call.span, elem, len);
57+
emit_lint(cx, symbol, macro_call.span, elem, len, func_span);
5658
}
5759
}
5860

59-
fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String {
60-
let elem_snippet = snippet(cx, elem.span, "..").to_string();
61-
if elem_snippet.contains('\n') {
62-
// This string must be found in `elem_snippet`, otherwise we won't be constructing
63-
// the snippet in the first place.
64-
let reference_creation = format!("{symbol_name}::new");
65-
let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap();
66-
67-
return format!("{code_until_reference_creation}{reference_creation}(..)");
68-
}
69-
70-
elem_snippet
71-
}
72-
7361
fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String {
7462
format!(
7563
r#"{{
@@ -89,17 +77,17 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String {
8977
)
9078
}
9179

92-
fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) {
80+
fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) {
9381
let symbol_name = symbol.as_str();
9482

9583
span_lint_and_then(
9684
cx,
9785
RC_CLONE_IN_VEC_INIT,
9886
lint_span,
99-
&format!("calling `{symbol_name}::new` in `vec![elem; len]`"),
87+
"initializing a reference-counted pointer in `vec![elem; len]`",
10088
|diag| {
10189
let len_snippet = snippet(cx, len.span, "..");
102-
let elem_snippet = elem_snippet(cx, elem, symbol_name);
90+
let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), ".."));
10391
let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0));
10492
let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
10593
let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
@@ -109,31 +97,41 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<
10997
lint_span,
11098
format!("consider initializing each `{symbol_name}` element individually"),
11199
loop_init_suggestion,
112-
Applicability::Unspecified,
100+
Applicability::HasPlaceholders,
113101
);
114102
diag.span_suggestion(
115103
lint_span,
116104
format!(
117105
"or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
118106
),
119107
extract_suggestion,
120-
Applicability::Unspecified,
108+
Applicability::HasPlaceholders,
121109
);
122110
},
123111
);
124112
}
125113

126-
/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new`
127-
fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
114+
/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak`
115+
fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> {
128116
if_chain! {
129117
if let ExprKind::Call(func, _args) = expr.kind;
130118
if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind;
131119
if let TyKind::Path(ref ty_path) = ty.kind;
132120
if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id();
133-
if last_path_segment(func_path).ident.name == sym::new;
134121

135122
then {
136-
return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc);
123+
if last_path_segment(func_path).ident.name == sym::new
124+
&& let Some(symbol) = cx
125+
.tcx
126+
.get_diagnostic_name(def_id)
127+
.filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) {
128+
return Some((symbol, func.span));
129+
}
130+
131+
let ty_path = cx.typeck_results().expr_ty(expr);
132+
if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) {
133+
return Some((Symbol::intern("Weak"), func.span));
134+
}
137135
}
138136
}
139137

tests/ui/rc_clone_in_vec_init/arc.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: calling `Arc::new` in `vec![elem; len]`
1+
error: initializing a reference-counted pointer in `vec![elem; len]`
22
--> $DIR/arc.rs:7:13
33
|
44
LL | let v = vec![Arc::new("x".to_string()); 2];
@@ -10,19 +10,19 @@ help: consider initializing each `Arc` element individually
1010
|
1111
LL ~ let v = {
1212
LL + let mut v = Vec::with_capacity(2);
13-
LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string())));
13+
LL + (0..2).for_each(|_| v.push(Arc::new(..)));
1414
LL + v
1515
LL ~ };
1616
|
1717
help: or if this is intentional, consider extracting the `Arc` initialization to a variable
1818
|
1919
LL ~ let v = {
20-
LL + let data = Arc::new("x".to_string());
20+
LL + let data = Arc::new(..);
2121
LL + vec![data; 2]
2222
LL ~ };
2323
|
2424

25-
error: calling `Arc::new` in `vec![elem; len]`
25+
error: initializing a reference-counted pointer in `vec![elem; len]`
2626
--> $DIR/arc.rs:15:21
2727
|
2828
LL | let v = vec![Arc::new("x".to_string()); 2];
@@ -33,19 +33,19 @@ help: consider initializing each `Arc` element individually
3333
|
3434
LL ~ let v = {
3535
LL + let mut v = Vec::with_capacity(2);
36-
LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string())));
36+
LL + (0..2).for_each(|_| v.push(Arc::new(..)));
3737
LL + v
3838
LL ~ };
3939
|
4040
help: or if this is intentional, consider extracting the `Arc` initialization to a variable
4141
|
4242
LL ~ let v = {
43-
LL + let data = Arc::new("x".to_string());
43+
LL + let data = Arc::new(..);
4444
LL + vec![data; 2]
4545
LL ~ };
4646
|
4747

48-
error: calling `Arc::new` in `vec![elem; len]`
48+
error: initializing a reference-counted pointer in `vec![elem; len]`
4949
--> $DIR/arc.rs:21:13
5050
|
5151
LL | let v = vec![
@@ -75,7 +75,7 @@ LL + vec![data; 2]
7575
LL ~ };
7676
|
7777

78-
error: calling `Arc::new` in `vec![elem; len]`
78+
error: initializing a reference-counted pointer in `vec![elem; len]`
7979
--> $DIR/arc.rs:30:14
8080
|
8181
LL | let v1 = vec![

tests/ui/rc_clone_in_vec_init/rc.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: calling `Rc::new` in `vec![elem; len]`
1+
error: initializing a reference-counted pointer in `vec![elem; len]`
22
--> $DIR/rc.rs:8:13
33
|
44
LL | let v = vec![Rc::new("x".to_string()); 2];
@@ -10,19 +10,19 @@ help: consider initializing each `Rc` element individually
1010
|
1111
LL ~ let v = {
1212
LL + let mut v = Vec::with_capacity(2);
13-
LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string())));
13+
LL + (0..2).for_each(|_| v.push(Rc::new(..)));
1414
LL + v
1515
LL ~ };
1616
|
1717
help: or if this is intentional, consider extracting the `Rc` initialization to a variable
1818
|
1919
LL ~ let v = {
20-
LL + let data = Rc::new("x".to_string());
20+
LL + let data = Rc::new(..);
2121
LL + vec![data; 2]
2222
LL ~ };
2323
|
2424

25-
error: calling `Rc::new` in `vec![elem; len]`
25+
error: initializing a reference-counted pointer in `vec![elem; len]`
2626
--> $DIR/rc.rs:16:21
2727
|
2828
LL | let v = vec![Rc::new("x".to_string()); 2];
@@ -33,19 +33,19 @@ help: consider initializing each `Rc` element individually
3333
|
3434
LL ~ let v = {
3535
LL + let mut v = Vec::with_capacity(2);
36-
LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string())));
36+
LL + (0..2).for_each(|_| v.push(Rc::new(..)));
3737
LL + v
3838
LL ~ };
3939
|
4040
help: or if this is intentional, consider extracting the `Rc` initialization to a variable
4141
|
4242
LL ~ let v = {
43-
LL + let data = Rc::new("x".to_string());
43+
LL + let data = Rc::new(..);
4444
LL + vec![data; 2]
4545
LL ~ };
4646
|
4747

48-
error: calling `Rc::new` in `vec![elem; len]`
48+
error: initializing a reference-counted pointer in `vec![elem; len]`
4949
--> $DIR/rc.rs:22:13
5050
|
5151
LL | let v = vec![
@@ -75,7 +75,7 @@ LL + vec![data; 2]
7575
LL ~ };
7676
|
7777

78-
error: calling `Rc::new` in `vec![elem; len]`
78+
error: initializing a reference-counted pointer in `vec![elem; len]`
7979
--> $DIR/rc.rs:31:14
8080
|
8181
LL | let v1 = vec![

tests/ui/rc_clone_in_vec_init/weak.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#![warn(clippy::rc_clone_in_vec_init)]
2+
use std::rc::{Rc, Weak as UnSyncWeak};
3+
use std::sync::{Arc, Mutex, Weak as SyncWeak};
4+
5+
fn main() {}
6+
7+
fn should_warn_simple_case() {
8+
let v = vec![SyncWeak::<u32>::new(); 2];
9+
let v2 = vec![UnSyncWeak::<u32>::new(); 2];
10+
11+
let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
12+
let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
13+
}
14+
15+
fn should_warn_simple_case_with_big_indentation() {
16+
if true {
17+
let k = 1;
18+
dbg!(k);
19+
if true {
20+
let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
21+
let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
22+
}
23+
}
24+
}
25+
26+
fn should_warn_complex_case() {
27+
let v = vec![
28+
Arc::downgrade(&Arc::new(Mutex::new({
29+
let x = 1;
30+
dbg!(x);
31+
x
32+
})));
33+
2
34+
];
35+
36+
let v1 = vec![
37+
Rc::downgrade(&Rc::new(Mutex::new({
38+
let x = 1;
39+
dbg!(x);
40+
x
41+
})));
42+
2
43+
];
44+
}
45+
46+
fn should_not_warn_custom_weak() {
47+
#[derive(Clone)]
48+
struct Weak;
49+
50+
impl Weak {
51+
fn new() -> Self {
52+
Weak
53+
}
54+
}
55+
56+
let v = vec![Weak::new(); 2];
57+
}
58+
59+
fn should_not_warn_vec_from_elem_but_not_weak() {
60+
let v = vec![String::new(); 2];
61+
let v1 = vec![1; 2];
62+
let v2 = vec![
63+
Box::new(Arc::downgrade(&Arc::new({
64+
let y = 3;
65+
dbg!(y);
66+
y
67+
})));
68+
2
69+
];
70+
let v3 = vec![
71+
Box::new(Rc::downgrade(&Rc::new({
72+
let y = 3;
73+
dbg!(y);
74+
y
75+
})));
76+
2
77+
];
78+
}
79+
80+
fn should_not_warn_vec_macro_but_not_from_elem() {
81+
let v = vec![Arc::downgrade(&Arc::new("x".to_string()))];
82+
let v = vec![Rc::downgrade(&Rc::new("x".to_string()))];
83+
}

0 commit comments

Comments
 (0)