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
;
5 use clippy_utils
::paths
;
6 use clippy_utils
::source
::{indent_of, snippet}
;
7 use clippy_utils
::ty
::match_type
;
8 use rustc_errors
::Applicability
;
9 use rustc_hir
::{Expr, ExprKind, QPath, TyKind}
;
10 use rustc_lint
::{LateContext, LateLintPass}
;
11 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
12 use rustc_span
::{sym, Span, Symbol}
;
14 declare_clippy_lint
! {
16 /// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`)
17 /// in `vec![elem; len]`
19 /// ### Why is this bad?
20 /// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak`
21 /// is a bit misleading, as it will create references to the same pointer, rather
22 /// than different instances.
26 /// let v = vec![std::sync::Arc::new("some data".to_string()); 100];
28 /// let v = vec![std::rc::Rc::new("some data".to_string()); 100];
32 /// // Initialize each value separately:
33 /// let mut data = Vec::with_capacity(100);
35 /// data.push(std::rc::Rc::new("some data".to_string()));
38 /// // Or if you want clones of the same reference,
39 /// // Create the reference beforehand to clarify that
40 /// // it should be cloned for each value
41 /// let data = std::rc::Rc::new("some data".to_string());
42 /// let v = vec![data; 100];
44 #[clippy::version = "1.62.0"]
45 pub RC_CLONE_IN_VEC_INIT
,
47 "initializing reference-counted pointer in `vec![elem; len]`"
49 declare_lint_pass
!(RcCloneInVecInit
=> [RC_CLONE_IN_VEC_INIT
]);
51 impl LateLintPass
<'_
> for RcCloneInVecInit
{
52 fn check_expr(&mut self, cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) {
53 let Some(macro_call
) = root_macro_call_first_node(cx
, expr
) else { return; }
;
54 let Some(VecArgs
::Repeat(elem
, len
)) = VecArgs
::hir(cx
, expr
) else { return; }
;
55 let Some((symbol
, func_span
)) = ref_init(cx
, elem
) else { return; }
;
57 emit_lint(cx
, symbol
, macro_call
.span
, elem
, len
, func_span
);
61 fn loop_init_suggestion(elem
: &str, len
: &str, indent
: &str) -> String
{
64 {indent} let mut v = Vec::with_capacity({len});
65 {indent} (0..{len}).for_each(|_| v.push({elem}));
71 fn extract_suggestion(elem
: &str, len
: &str, indent
: &str) -> String
{
74 {indent} let data = {elem};
75 {indent} vec![data; {len}]
80 fn emit_lint(cx
: &LateContext
<'_
>, symbol
: Symbol
, lint_span
: Span
, elem
: &Expr
<'_
>, len
: &Expr
<'_
>, func_span
: Span
) {
81 let symbol_name
= symbol
.as_str();
87 "initializing a reference-counted pointer in `vec![elem; len]`",
89 let len_snippet
= snippet(cx
, len
.span
, "..");
90 let elem_snippet
= format
!("{}(..)", snippet(cx
, elem
.span
.with_hi(func_span
.hi()), ".."));
91 let indentation
= " ".repeat(indent_of(cx
, lint_span
).unwrap_or(0));
92 let loop_init_suggestion
= loop_init_suggestion(&elem_snippet
, len_snippet
.as_ref(), &indentation
);
93 let extract_suggestion
= extract_suggestion(&elem_snippet
, len_snippet
.as_ref(), &indentation
);
95 diag
.note(format
!("each element will point to the same `{symbol_name}` instance"));
98 format
!("consider initializing each `{symbol_name}` element individually"),
100 Applicability
::HasPlaceholders
,
102 diag
.span_suggestion(
105 "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
108 Applicability
::HasPlaceholders
,
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
)> {
117 if let ExprKind
::Call(func
, _args
) = expr
.kind
;
118 if let ExprKind
::Path(ref func_path @ QPath
::TypeRelative(ty
, _
)) = func
.kind
;
119 if let TyKind
::Path(ref ty_path
) = ty
.kind
;
120 if let Some(def_id
) = cx
.qpath_res(ty_path
, ty
.hir_id
).opt_def_id();
123 if last_path_segment(func_path
).ident
.name
== sym
::new
124 && let Some(symbol
) = cx
126 .get_diagnostic_name(def_id
)
127 .filter(|symbol
| symbol
== &sym
::Arc
|| symbol
== &sym
::Rc
) {
128 return Some((symbol
, func
.span
));
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
));