1 use std
::collections
::BTreeMap
;
2 use std
::ops
::ControlFlow
;
4 use clippy_config
::msrvs
::{self, Msrv}
;
5 use clippy_utils
::consts
::{constant, Constant}
;
6 use clippy_utils
::diagnostics
::span_lint_hir_and_then
;
7 use clippy_utils
::source
::snippet_opt
;
8 use clippy_utils
::ty
::is_copy
;
9 use clippy_utils
::visitors
::for_each_local_use_after_expr
;
10 use clippy_utils
::{get_parent_expr, higher, is_trait_method}
;
11 use rustc_errors
::Applicability
;
12 use rustc_hir
::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}
;
13 use rustc_lint
::{LateContext, LateLintPass}
;
15 use rustc_middle
::ty
::layout
::LayoutOf
;
16 use rustc_session
::impl_lint_pass
;
17 use rustc_span
::{sym, DesugaringKind, Span}
;
19 #[expect(clippy::module_name_repetitions)]
21 pub struct UselessVec
{
22 pub too_large_for_stack
: u64,
24 pub span_to_lint_map
: BTreeMap
<Span
, Option
<(HirId
, SuggestedType
, String
, Applicability
)>>,
27 declare_clippy_lint
! {
29 /// Checks for usage of `vec![..]` when using `[..]` would
32 /// ### Why is this bad?
33 /// This is less efficient.
37 /// fn foo(_x: &[u8]) {}
44 /// # fn foo(_x: &[u8]) {}
47 #[clippy::version = "pre 1.29.0"]
53 impl_lint_pass
!(UselessVec
=> [USELESS_VEC
]);
55 impl<'tcx
> LateLintPass
<'tcx
> for UselessVec
{
56 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
57 let Some(vec_args
) = higher
::VecArgs
::hir(cx
, expr
.peel_borrows()) else {
60 // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!`
61 let callsite
= expr
.span
.parent_callsite().unwrap_or(expr
.span
);
63 match cx
.tcx
.parent_hir_node(expr
.hir_id
) {
64 // search for `let foo = vec![_]` expressions where all uses of `foo`
65 // adjust to slices or call a method that exist on slices (e.g. len)
66 Node
::LetStmt(LetStmt
{
70 kind
: PatKind
::Binding(_
, id
, ..),
75 let only_slice_uses
= for_each_local_use_after_expr(cx
, *id
, expr
.hir_id
, |expr
| {
76 // allow indexing into a vec and some set of allowed method calls that exist on slices, too
77 if let Some(parent
) = get_parent_expr(cx
, expr
)
78 && (adjusts_to_slice(cx
, expr
)
79 || matches
!(parent
.kind
, ExprKind
::Index(..))
80 || is_allowed_vec_method(cx
, parent
))
82 ControlFlow
::Continue(())
84 ControlFlow
::Break(())
90 self.check_vec_macro(cx
, &vec_args
, callsite
, expr
.hir_id
, SuggestedType
::Array
);
92 self.span_to_lint_map
.insert(callsite
, None
);
95 // if the local pattern has a specified type, do not lint.
96 Node
::LetStmt(LetStmt { ty: Some(_), .. }
) if higher
::VecArgs
::hir(cx
, expr
).is_some() => {
97 self.span_to_lint_map
.insert(callsite
, None
);
99 // search for `for _ in vec![...]`
100 Node
::Expr(Expr { span, .. }
)
101 if span
.is_desugaring(DesugaringKind
::ForLoop
) && self.msrv
.meets(msrvs
::ARRAY_INTO_ITERATOR
) =>
103 let suggest_slice
= suggest_type(expr
);
104 self.check_vec_macro(cx
, &vec_args
, callsite
, expr
.hir_id
, suggest_slice
);
106 // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
108 let suggest_slice
= suggest_type(expr
);
110 if adjusts_to_slice(cx
, expr
) {
111 self.check_vec_macro(cx
, &vec_args
, callsite
, expr
.hir_id
, suggest_slice
);
113 self.span_to_lint_map
.insert(callsite
, None
);
119 fn check_crate_post(&mut self, cx
: &LateContext
<'tcx
>) {
120 for (span
, lint_opt
) in &self.span_to_lint_map
{
121 if let Some((hir_id
, suggest_slice
, snippet
, applicability
)) = lint_opt
{
122 let help_msg
= format
!("you can use {} directly", suggest_slice
.desc(),);
123 span_lint_hir_and_then(cx
, USELESS_VEC
, *hir_id
, *span
, "useless use of `vec!`", |diag
| {
124 diag
.span_suggestion(*span
, help_msg
, snippet
, *applicability
);
130 extract_msrv_attr
!(LateContext
);
134 fn check_vec_macro
<'tcx
>(
136 cx
: &LateContext
<'tcx
>,
137 vec_args
: &higher
::VecArgs
<'tcx
>,
140 suggest_slice
: SuggestedType
,
142 if span
.from_expansion() {
146 let snippet
= match *vec_args
{
147 higher
::VecArgs
::Repeat(elem
, len
) => {
148 if let Some(Constant
::Int(len_constant
)) = constant(cx
, cx
.typeck_results(), len
) {
149 // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also
150 if !is_copy(cx
, cx
.typeck_results().expr_ty(elem
)) {
154 #[expect(clippy::cast_possible_truncation)]
155 if len_constant
as u64 * size_of(cx
, elem
) > self.too_large_for_stack
{
159 suggest_slice
.snippet(cx
, Some(elem
.span
), Some(len
.span
))
164 higher
::VecArgs
::Vec(args
) => {
165 let args_span
= if let Some(last
) = args
.iter().last() {
166 if args
.len() as u64 * size_of(cx
, last
) > self.too_large_for_stack
{
169 Some(args
[0].span
.source_callsite().to(last
.span
.source_callsite()))
173 suggest_slice
.snippet(cx
, args_span
, None
)
177 self.span_to_lint_map
.entry(span
).or_insert(Some((
181 Applicability
::MachineApplicable
,
186 #[derive(Copy, Clone)]
187 pub(crate) enum SuggestedType
{
188 /// Suggest using a slice `&[..]` / `&mut [..]`
189 SliceRef(Mutability
),
190 /// Suggest using an array: `[..]`
195 fn desc(self) -> &'
static str {
197 Self::SliceRef(_
) => "a slice",
198 Self::Array
=> "an array",
202 fn snippet(self, cx
: &LateContext
<'_
>, args_span
: Option
<Span
>, len_span
: Option
<Span
>) -> String
{
203 let maybe_args
= args_span
.and_then(|sp
| snippet_opt(cx
, sp
)).unwrap_or_default();
204 let maybe_len
= len_span
205 .and_then(|sp
| snippet_opt(cx
, sp
).map(|s
| format
!("; {s}")))
206 .unwrap_or_default();
209 Self::SliceRef(Mutability
::Mut
) => format
!("&mut [{maybe_args}{maybe_len}]"),
210 Self::SliceRef(Mutability
::Not
) => format
!("&[{maybe_args}{maybe_len}]"),
211 Self::Array
=> format
!("[{maybe_args}{maybe_len}]"),
216 fn size_of(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> u64 {
217 let ty
= cx
.typeck_results().expr_ty_adjusted(expr
);
218 cx
.layout_of(ty
).map_or(0, |l
| l
.size
.bytes())
221 fn adjusts_to_slice(cx
: &LateContext
<'_
>, e
: &Expr
<'_
>) -> bool
{
222 matches
!(cx
.typeck_results().expr_ty_adjusted(e
).kind(), ty
::Ref(_
, ty
, _
) if ty
.is_slice())
225 /// Checks if the given expression is a method call to a `Vec` method
226 /// that also exists on slices. If this returns true, it means that
227 /// this expression does not actually require a `Vec` and could just work with an array.
228 pub fn is_allowed_vec_method(cx
: &LateContext
<'_
>, e
: &Expr
<'_
>) -> bool
{
229 const ALLOWED_METHOD_NAMES
: &[&str] = &["len", "as_ptr", "is_empty"];
231 if let ExprKind
::MethodCall(path
, ..) = e
.kind
{
232 ALLOWED_METHOD_NAMES
.contains(&path
.ident
.name
.as_str())
234 is_trait_method(cx
, e
, sym
::IntoIterator
)
238 fn suggest_type(expr
: &Expr
<'_
>) -> SuggestedType
{
239 if let ExprKind
::AddrOf(BorrowKind
::Ref
, mutability
, _
) = expr
.kind
{
240 // `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.)
241 SuggestedType
::SliceRef(mutability
)
243 // `expr` is the `vec![_]` expansion, so suggest `[_]`