Skip to content

Commit d12b53e

Browse files
committed
Auto merge of #12116 - J-ZhengLi:issue12101, r=Alexendoo
fix suggestion error in [`useless_vec`] fixes: #12101 --- changelog: fix suggestion error in [`useless_vec`] r+ `@matthiaskrgr` since they opened the issue?
2 parents 1c50948 + 929f746 commit d12b53e

File tree

4 files changed

+120
-107
lines changed

4 files changed

+120
-107
lines changed

clippy_lints/src/vec.rs

+105-106
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use std::ops::ControlFlow;
44
use clippy_config::msrvs::{self, Msrv};
55
use clippy_utils::consts::{constant, Constant};
66
use clippy_utils::diagnostics::span_lint_hir_and_then;
7-
use clippy_utils::source::snippet_with_applicability;
7+
use clippy_utils::source::snippet_opt;
88
use clippy_utils::ty::is_copy;
99
use clippy_utils::visitors::for_each_local_use_after_expr;
1010
use clippy_utils::{get_parent_expr, higher, is_trait_method};
1111
use rustc_errors::Applicability;
12-
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind};
12+
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Local, Mutability, Node, Pat, PatKind};
1313
use rustc_lint::{LateContext, LateLintPass};
1414
use rustc_middle::ty;
1515
use rustc_middle::ty::layout::LayoutOf;
@@ -52,35 +52,27 @@ declare_clippy_lint! {
5252

5353
impl_lint_pass!(UselessVec => [USELESS_VEC]);
5454

55-
fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
56-
matches!(cx.typeck_results().expr_ty_adjusted(e).kind(), ty::Ref(_, ty, _) if ty.is_slice())
57-
}
58-
59-
/// Checks if the given expression is a method call to a `Vec` method
60-
/// that also exists on slices. If this returns true, it means that
61-
/// this expression does not actually require a `Vec` and could just work with an array.
62-
pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
63-
const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"];
64-
65-
if let ExprKind::MethodCall(path, ..) = e.kind {
66-
ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str())
67-
} else {
68-
is_trait_method(cx, e, sym::IntoIterator)
69-
}
70-
}
71-
7255
impl<'tcx> LateLintPass<'tcx> for UselessVec {
7356
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
74-
if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) {
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) {
7564
// search for `let foo = vec![_]` expressions where all uses of `foo`
7665
// adjust to slices or call a method that exist on slices (e.g. len)
77-
if let Node::Local(local) = cx.tcx.parent_hir_node(expr.hir_id)
78-
// for now ignore locals with type annotations.
79-
// this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
80-
&& local.ty.is_none()
81-
&& let PatKind::Binding(_, id, ..) = local.pat.kind
82-
{
83-
let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
66+
Node::Local(Local {
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| {
8476
// allow indexing into a vec and some set of allowed method calls that exist on slices, too
8577
if let Some(parent) = get_parent_expr(cx, expr)
8678
&& (adjusts_to_slice(cx, expr)
@@ -94,60 +86,40 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
9486
})
9587
.is_continue();
9688

97-
let span = expr.span.ctxt().outer_expn_data().call_site;
9889
if only_slice_uses {
99-
self.check_vec_macro(cx, &vec_args, span, expr.hir_id, SuggestedType::Array);
90+
self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array);
10091
} else {
101-
self.span_to_lint_map.insert(span, None);
92+
self.span_to_lint_map.insert(callsite, None);
10293
}
103-
}
94+
},
10495
// if the local pattern has a specified type, do not lint.
105-
else if let Some(_) = higher::VecArgs::hir(cx, expr)
106-
&& let Node::Local(local) = cx.tcx.parent_hir_node(expr.hir_id)
107-
&& local.ty.is_some()
108-
{
109-
let span = expr.span.ctxt().outer_expn_data().call_site;
110-
self.span_to_lint_map.insert(span, None);
111-
}
96+
Node::Local(Local { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => {
97+
self.span_to_lint_map.insert(callsite, None);
98+
},
11299
// search for `for _ in vec![...]`
113-
else if let Some(parent) = get_parent_expr(cx, expr)
114-
&& parent.span.is_desugaring(DesugaringKind::ForLoop)
115-
&& self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
100+
Node::Expr(Expr { span, .. })
101+
if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(msrvs::ARRAY_INTO_ITERATOR) =>
116102
{
117-
// report the error around the `vec!` not inside `<std macros>:`
118-
let span = expr.span.ctxt().outer_expn_data().call_site;
119-
self.check_vec_macro(cx, &vec_args, span, expr.hir_id, SuggestedType::Array);
120-
}
103+
let suggest_slice = suggest_type(expr);
104+
self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
105+
},
121106
// search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
122-
else {
123-
let (suggest_slice, span) = if let ExprKind::AddrOf(BorrowKind::Ref, mutability, _) = expr.kind {
124-
// `expr` is `&vec![_]`, so suggest `&[_]` (or `&mut[_]` resp.)
125-
(SuggestedType::SliceRef(mutability), expr.span)
126-
} else {
127-
// `expr` is the `vec![_]` expansion, so suggest `[_]`
128-
// and also use the span of the actual `vec![_]` expression
129-
(SuggestedType::Array, expr.span.ctxt().outer_expn_data().call_site)
130-
};
107+
_ => {
108+
let suggest_slice = suggest_type(expr);
131109

132110
if adjusts_to_slice(cx, expr) {
133-
self.check_vec_macro(cx, &vec_args, span, expr.hir_id, suggest_slice);
111+
self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
134112
} else {
135-
self.span_to_lint_map.insert(span, None);
113+
self.span_to_lint_map.insert(callsite, None);
136114
}
137-
}
115+
},
138116
}
139117
}
140118

141119
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
142120
for (span, lint_opt) in &self.span_to_lint_map {
143121
if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt {
144-
let help_msg = format!(
145-
"you can use {} directly",
146-
match suggest_slice {
147-
SuggestedType::SliceRef(_) => "a slice",
148-
SuggestedType::Array => "an array",
149-
}
150-
);
122+
let help_msg = format!("you can use {} directly", suggest_slice.desc(),);
151123
span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| {
152124
diag.span_suggestion(*span, help_msg, snippet, *applicability);
153125
});
@@ -158,14 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
158130
extract_msrv_attr!(LateContext);
159131
}
160132

161-
#[derive(Copy, Clone)]
162-
pub(crate) enum SuggestedType {
163-
/// Suggest using a slice `&[..]` / `&mut [..]`
164-
SliceRef(Mutability),
165-
/// Suggest using an array: `[..]`
166-
Array,
167-
}
168-
169133
impl UselessVec {
170134
fn check_vec_macro<'tcx>(
171135
&mut self,
@@ -179,8 +143,6 @@ impl UselessVec {
179143
return;
180144
}
181145

182-
let mut applicability = Applicability::MachineApplicable;
183-
184146
let snippet = match *vec_args {
185147
higher::VecArgs::Repeat(elem, len) => {
186148
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
@@ -194,54 +156,91 @@ impl UselessVec {
194156
return;
195157
}
196158

197-
let elem = snippet_with_applicability(cx, elem.span, "elem", &mut applicability);
198-
let len = snippet_with_applicability(cx, len.span, "len", &mut applicability);
199-
200-
match suggest_slice {
201-
SuggestedType::SliceRef(Mutability::Mut) => format!("&mut [{elem}; {len}]"),
202-
SuggestedType::SliceRef(Mutability::Not) => format!("&[{elem}; {len}]"),
203-
SuggestedType::Array => format!("[{elem}; {len}]"),
204-
}
159+
suggest_slice.snippet(cx, Some(elem.span), Some(len.span))
205160
} else {
206161
return;
207162
}
208163
},
209164
higher::VecArgs::Vec(args) => {
210-
if let Some(last) = args.iter().last() {
165+
let args_span = if let Some(last) = args.iter().last() {
211166
if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
212167
return;
213168
}
214-
let span = args[0].span.source_callsite().to(last.span.source_callsite());
215-
let args = snippet_with_applicability(cx, span, "..", &mut applicability);
216-
217-
match suggest_slice {
218-
SuggestedType::SliceRef(Mutability::Mut) => {
219-
format!("&mut [{args}]")
220-
},
221-
SuggestedType::SliceRef(Mutability::Not) => {
222-
format!("&[{args}]")
223-
},
224-
SuggestedType::Array => {
225-
format!("[{args}]")
226-
},
227-
}
169+
Some(args[0].span.source_callsite().to(last.span.source_callsite()))
228170
} else {
229-
match suggest_slice {
230-
SuggestedType::SliceRef(Mutability::Mut) => "&mut []".to_owned(),
231-
SuggestedType::SliceRef(Mutability::Not) => "&[]".to_owned(),
232-
SuggestedType::Array => "[]".to_owned(),
233-
}
234-
}
171+
None
172+
};
173+
suggest_slice.snippet(cx, args_span, None)
235174
},
236175
};
237176

238-
self.span_to_lint_map
239-
.entry(span)
240-
.or_insert(Some((hir_id, suggest_slice, snippet, applicability)));
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+
}
241213
}
242214
}
243215

244216
fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
245217
let ty = cx.typeck_results().expr_ty_adjusted(expr);
246218
cx.layout_of(ty).map_or(0, |l| l.size.bytes())
247219
}
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+
}

tests/ui/vec.fixed

+4
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,7 @@ fn issue_11958() {
217217
// should not lint, `String` is not `Copy`
218218
f(&vec!["test".to_owned(); 2]);
219219
}
220+
221+
fn issue_12101() {
222+
for a in &[1, 2] {}
223+
}

tests/ui/vec.rs

+4
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,7 @@ fn issue_11958() {
217217
// should not lint, `String` is not `Copy`
218218
f(&vec!["test".to_owned(); 2]);
219219
}
220+
221+
fn issue_12101() {
222+
for a in &(vec![1, 2]) {}
223+
}

tests/ui/vec.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,11 @@ error: useless use of `vec!`
121121
LL | this_macro_doesnt_need_vec!(vec![1]);
122122
| ^^^^^^^ help: you can use an array directly: `[1]`
123123

124-
error: aborting due to 20 previous errors
124+
error: useless use of `vec!`
125+
--> tests/ui/vec.rs:222:14
126+
|
127+
LL | for a in &(vec![1, 2]) {}
128+
| ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
129+
130+
error: aborting due to 21 previous errors
125131

0 commit comments

Comments
 (0)