]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/vec.rs
bump version to 1.80.1+dfsg1-1~bpo12+pve1
[rustc.git] / src / tools / clippy / clippy_lints / src / vec.rs
1 use std::collections::BTreeMap;
2 use std::ops::ControlFlow;
3
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};
14 use rustc_middle::ty;
15 use rustc_middle::ty::layout::LayoutOf;
16 use rustc_session::impl_lint_pass;
17 use rustc_span::{sym, DesugaringKind, Span};
18
19 #[expect(clippy::module_name_repetitions)]
20 #[derive(Clone)]
21 pub struct UselessVec {
22 pub too_large_for_stack: u64,
23 pub msrv: Msrv,
24 pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
25 }
26
27 declare_clippy_lint! {
28 /// ### What it does
29 /// Checks for usage of `vec![..]` when using `[..]` would
30 /// be possible.
31 ///
32 /// ### Why is this bad?
33 /// This is less efficient.
34 ///
35 /// ### Example
36 /// ```no_run
37 /// fn foo(_x: &[u8]) {}
38 ///
39 /// foo(&vec![1, 2]);
40 /// ```
41 ///
42 /// Use instead:
43 /// ```no_run
44 /// # fn foo(_x: &[u8]) {}
45 /// foo(&[1, 2]);
46 /// ```
47 #[clippy::version = "pre 1.29.0"]
48 pub USELESS_VEC,
49 perf,
50 "useless `vec!`"
51 }
52
53 impl_lint_pass!(UselessVec => [USELESS_VEC]);
54
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 {
58 return;
59 };
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);
62
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 {
67 ty: None,
68 pat:
69 Pat {
70 kind: PatKind::Binding(_, id, ..),
71 ..
72 },
73 ..
74 }) => {
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))
81 {
82 ControlFlow::Continue(())
83 } else {
84 ControlFlow::Break(())
85 }
86 })
87 .is_continue();
88
89 if only_slice_uses {
90 self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array);
91 } else {
92 self.span_to_lint_map.insert(callsite, None);
93 }
94 },
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);
98 },
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) =>
102 {
103 let suggest_slice = suggest_type(expr);
104 self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
105 },
106 // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
107 _ => {
108 let suggest_slice = suggest_type(expr);
109
110 if adjusts_to_slice(cx, expr) {
111 self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
112 } else {
113 self.span_to_lint_map.insert(callsite, None);
114 }
115 },
116 }
117 }
118
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);
125 });
126 }
127 }
128 }
129
130 extract_msrv_attr!(LateContext);
131 }
132
133 impl UselessVec {
134 fn check_vec_macro<'tcx>(
135 &mut self,
136 cx: &LateContext<'tcx>,
137 vec_args: &higher::VecArgs<'tcx>,
138 span: Span,
139 hir_id: HirId,
140 suggest_slice: SuggestedType,
141 ) {
142 if span.from_expansion() {
143 return;
144 }
145
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)) {
151 return;
152 }
153
154 #[expect(clippy::cast_possible_truncation)]
155 if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
156 return;
157 }
158
159 suggest_slice.snippet(cx, Some(elem.span), Some(len.span))
160 } else {
161 return;
162 }
163 },
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 {
167 return;
168 }
169 Some(args[0].span.source_callsite().to(last.span.source_callsite()))
170 } else {
171 None
172 };
173 suggest_slice.snippet(cx, args_span, None)
174 },
175 };
176
177 self.span_to_lint_map.entry(span).or_insert(Some((
178 hir_id,
179 suggest_slice,
180 snippet,
181 Applicability::MachineApplicable,
182 )));
183 }
184 }
185
186 #[derive(Copy, Clone)]
187 pub(crate) enum SuggestedType {
188 /// Suggest using a slice `&[..]` / `&mut [..]`
189 SliceRef(Mutability),
190 /// Suggest using an array: `[..]`
191 Array,
192 }
193
194 impl SuggestedType {
195 fn desc(self) -> &'static str {
196 match self {
197 Self::SliceRef(_) => "a slice",
198 Self::Array => "an array",
199 }
200 }
201
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();
207
208 match self {
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}]"),
212 }
213 }
214 }
215
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())
219 }
220
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())
223 }
224
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"];
230
231 if let ExprKind::MethodCall(path, ..) = e.kind {
232 ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str())
233 } else {
234 is_trait_method(cx, e, sym::IntoIterator)
235 }
236 }
237
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)
242 } else {
243 // `expr` is the `vec![_]` expansion, so suggest `[_]`
244 SuggestedType::Array
245 }
246 }