@@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
2
2
use clippy_utils:: higher:: VecArgs ;
3
3
use clippy_utils:: last_path_segment;
4
4
use clippy_utils:: macros:: root_macro_call_first_node;
5
+ use clippy_utils:: paths;
5
6
use clippy_utils:: source:: { indent_of, snippet} ;
7
+ use clippy_utils:: ty:: match_type;
6
8
use rustc_errors:: Applicability ;
7
9
use rustc_hir:: { Expr , ExprKind , QPath , TyKind } ;
8
10
use rustc_lint:: { LateContext , LateLintPass } ;
@@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol};
11
13
12
14
declare_clippy_lint ! {
13
15
/// ### 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]`
15
18
///
16
19
/// ### 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 `
18
21
/// is a bit misleading, as it will create references to the same pointer, rather
19
22
/// than different instances.
20
23
///
@@ -26,7 +29,6 @@ declare_clippy_lint! {
26
29
/// ```
27
30
/// Use instead:
28
31
/// ```rust
29
- ///
30
32
/// // Initialize each value separately:
31
33
/// let mut data = Vec::with_capacity(100);
32
34
/// for _ in 0..100 {
@@ -42,34 +44,20 @@ declare_clippy_lint! {
42
44
#[ clippy:: version = "1.62.0" ]
43
45
pub RC_CLONE_IN_VEC_INIT ,
44
46
suspicious,
45
- "initializing `Arc` or `Rc` in `vec![elem; len]`"
47
+ "initializing reference-counted pointer in `vec![elem; len]`"
46
48
}
47
49
declare_lint_pass ! ( RcCloneInVecInit => [ RC_CLONE_IN_VEC_INIT ] ) ;
48
50
49
51
impl LateLintPass < ' _ > for RcCloneInVecInit {
50
52
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
51
53
let Some ( macro_call) = root_macro_call_first_node ( cx, expr) else { return ; } ;
52
54
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 ; } ;
54
56
55
- emit_lint ( cx, symbol, macro_call. span , elem, len) ;
57
+ emit_lint ( cx, symbol, macro_call. span , elem, len, func_span ) ;
56
58
}
57
59
}
58
60
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
-
73
61
fn loop_init_suggestion ( elem : & str , len : & str , indent : & str ) -> String {
74
62
format ! (
75
63
r#"{{
@@ -89,17 +77,17 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String {
89
77
)
90
78
}
91
79
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 ) {
93
81
let symbol_name = symbol. as_str ( ) ;
94
82
95
83
span_lint_and_then (
96
84
cx,
97
85
RC_CLONE_IN_VEC_INIT ,
98
86
lint_span,
99
- & format ! ( "calling `{symbol_name}::new` in `vec![elem; len]`") ,
87
+ "initializing a reference-counted pointer in `vec![elem; len]`",
100
88
|diag| {
101
89
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 ( ) ) , ".." ) ) ;
103
91
let indentation = " " . repeat ( indent_of ( cx, lint_span) . unwrap_or ( 0 ) ) ;
104
92
let loop_init_suggestion = loop_init_suggestion ( & elem_snippet, len_snippet. as_ref ( ) , & indentation) ;
105
93
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<
109
97
lint_span,
110
98
format ! ( "consider initializing each `{symbol_name}` element individually" ) ,
111
99
loop_init_suggestion,
112
- Applicability :: Unspecified ,
100
+ Applicability :: HasPlaceholders ,
113
101
) ;
114
102
diag. span_suggestion (
115
103
lint_span,
116
104
format ! (
117
105
"or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
118
106
) ,
119
107
extract_suggestion,
120
- Applicability :: Unspecified ,
108
+ Applicability :: HasPlaceholders ,
121
109
) ;
122
110
} ,
123
111
) ;
124
112
}
125
113
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 ) > {
128
116
if_chain ! {
129
117
if let ExprKind :: Call ( func, _args) = expr. kind;
130
118
if let ExprKind :: Path ( ref func_path @ QPath :: TypeRelative ( ty, _) ) = func. kind;
131
119
if let TyKind :: Path ( ref ty_path) = ty. kind;
132
120
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;
134
121
135
122
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
+ }
137
135
}
138
136
}
139
137
0 commit comments