@@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
22use clippy_utils:: higher:: VecArgs ;
33use clippy_utils:: last_path_segment;
44use clippy_utils:: macros:: root_macro_call_first_node;
5+ use clippy_utils:: paths;
56use clippy_utils:: source:: { indent_of, snippet} ;
7+ use clippy_utils:: ty:: match_type;
68use rustc_errors:: Applicability ;
79use rustc_hir:: { Expr , ExprKind , QPath , TyKind } ;
810use rustc_lint:: { LateContext , LateLintPass } ;
@@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol};
1113
1214declare_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}
4749declare_lint_pass ! ( RcCloneInVecInit => [ RC_CLONE_IN_VEC_INIT ] ) ;
4850
4951impl 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-
7361fn 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
0 commit comments