Skip to content

Commit 566a365

Browse files
committed
make useless_vec smarter
1 parent b033883 commit 566a365

File tree

1 file changed

+97
-14
lines changed

1 file changed

+97
-14
lines changed

clippy_lints/src/vec.rs

Lines changed: 97 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1+
use std::ops::ControlFlow;
2+
13
use clippy_utils::consts::{constant, Constant};
24
use clippy_utils::diagnostics::span_lint_and_sugg;
3-
use clippy_utils::higher;
45
use clippy_utils::source::snippet_with_applicability;
56
use clippy_utils::ty::is_copy;
7+
use clippy_utils::visitors::for_each_local_use_after_expr;
8+
use clippy_utils::{get_parent_expr, higher, is_trait_method};
69
use if_chain::if_chain;
10+
use rustc_ast::BindingAnnotation;
711
use rustc_errors::Applicability;
8-
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
12+
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Node, PatKind};
913
use rustc_lint::{LateContext, LateLintPass};
1014
use rustc_middle::ty::layout::LayoutOf;
1115
use rustc_middle::ty::{self, Ty};
1216
use rustc_session::{declare_tool_lint, impl_lint_pass};
1317
use rustc_span::source_map::Span;
18+
use rustc_span::sym;
1419

1520
#[expect(clippy::module_name_repetitions)]
1621
#[derive(Copy, Clone)]
@@ -20,7 +25,7 @@ pub struct UselessVec {
2025

2126
declare_clippy_lint! {
2227
/// ### What it does
23-
/// Checks for usage of `&vec![..]` when using `&[..]` would
28+
/// Checks for usage of `vec![..]` when using `[..]` would
2429
/// be possible.
2530
///
2631
/// ### Why is this bad?
@@ -46,16 +51,70 @@ declare_clippy_lint! {
4651

4752
impl_lint_pass!(UselessVec => [USELESS_VEC]);
4853

54+
fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
55+
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(e).kind() {
56+
ty.is_slice()
57+
} else {
58+
false
59+
}
60+
}
61+
62+
/// Checks if the given expression is a method call to a `Vec` method
63+
/// that also exists on slices. If this returns true, it means that
64+
/// this expression does not actually require a `Vec` and could just work with an array.
65+
fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
66+
const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "as_slice", "is_empty"];
67+
68+
if let ExprKind::MethodCall(path, ..) = e.kind {
69+
ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str())
70+
} else {
71+
is_trait_method(cx, e, sym::IntoIterator)
72+
}
73+
}
74+
4975
impl<'tcx> LateLintPass<'tcx> for UselessVec {
5076
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
5177
// search for `&vec![_]` expressions where the adjusted type is `&[_]`
5278
if_chain! {
53-
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
54-
if let ty::Slice(..) = ty.kind();
79+
if adjusts_to_slice(cx, expr);
5580
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
5681
if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
5782
then {
58-
self.check_vec_macro(cx, &vec_args, mutability, expr.span);
83+
self.check_vec_macro(cx, &vec_args, mutability, expr.span, SuggestSlice::Yes);
84+
}
85+
}
86+
87+
// search for `let foo = vec![_]` expressions where all uses of `foo`
88+
// adjust to slices or call a method that exist on slices (e.g. len)
89+
if let Some(vec_args) = higher::VecArgs::hir(cx, expr)
90+
&& let Node::Local(local) = cx.tcx.hir().get_parent(expr.hir_id)
91+
// for now ignore locals with type annotations.
92+
// this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..];
93+
&& local.ty.is_none()
94+
&& let PatKind::Binding(BindingAnnotation(_, mutbl), id, ..) = local.pat.kind
95+
&& is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr)))
96+
{
97+
let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| {
98+
// allow indexing into a vec and some set of allowed method calls that exist on slices, too
99+
if let Some(parent) = get_parent_expr(cx, expr)
100+
&& (adjusts_to_slice(cx, expr)
101+
|| matches!(parent.kind, ExprKind::Index(..))
102+
|| is_allowed_vec_method(cx, parent))
103+
{
104+
ControlFlow::Continue(())
105+
} else {
106+
ControlFlow::Break(())
107+
}
108+
}).is_continue();
109+
110+
if only_slice_uses {
111+
self.check_vec_macro(
112+
cx,
113+
&vec_args,
114+
mutbl,
115+
expr.span.ctxt().outer_expn_data().call_site,
116+
SuggestSlice::No
117+
);
59118
}
60119
}
61120

@@ -67,21 +126,36 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
67126
then {
68127
// report the error around the `vec!` not inside `<std macros>:`
69128
let span = arg.span.ctxt().outer_expn_data().call_site;
70-
self.check_vec_macro(cx, &vec_args, Mutability::Not, span);
129+
self.check_vec_macro(cx, &vec_args, Mutability::Not, span, SuggestSlice::Yes);
71130
}
72131
}
73132
}
74133
}
75134

135+
#[derive(Copy, Clone)]
136+
enum SuggestSlice {
137+
/// Suggest using a slice `&[..]` / `&mut [..]`
138+
Yes,
139+
/// Suggest using an array: `[..]`
140+
No,
141+
}
142+
76143
impl UselessVec {
77144
fn check_vec_macro<'tcx>(
78145
self,
79146
cx: &LateContext<'tcx>,
80147
vec_args: &higher::VecArgs<'tcx>,
81148
mutability: Mutability,
82149
span: Span,
150+
suggest_slice: SuggestSlice,
83151
) {
84152
let mut applicability = Applicability::MachineApplicable;
153+
154+
let (borrow_prefix_mut, borrow_prefix) = match suggest_slice {
155+
SuggestSlice::Yes => ("&mut ", "&"),
156+
SuggestSlice::No => ("", ""),
157+
};
158+
85159
let snippet = match *vec_args {
86160
higher::VecArgs::Repeat(elem, len) => {
87161
if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
@@ -93,14 +167,14 @@ impl UselessVec {
93167
match mutability {
94168
Mutability::Mut => {
95169
format!(
96-
"&mut [{}; {}]",
170+
"{borrow_prefix_mut}[{}; {}]",
97171
snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
98172
snippet_with_applicability(cx, len.span, "len", &mut applicability)
99173
)
100174
},
101175
Mutability::Not => {
102176
format!(
103-
"&[{}; {}]",
177+
"{borrow_prefix}[{}; {}]",
104178
snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
105179
snippet_with_applicability(cx, len.span, "len", &mut applicability)
106180
)
@@ -120,18 +194,21 @@ impl UselessVec {
120194
match mutability {
121195
Mutability::Mut => {
122196
format!(
123-
"&mut [{}]",
197+
"{borrow_prefix_mut}[{}]",
124198
snippet_with_applicability(cx, span, "..", &mut applicability)
125199
)
126200
},
127201
Mutability::Not => {
128-
format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
202+
format!(
203+
"{borrow_prefix}[{}]",
204+
snippet_with_applicability(cx, span, "..", &mut applicability)
205+
)
129206
},
130207
}
131208
} else {
132209
match mutability {
133-
Mutability::Mut => "&mut []".into(),
134-
Mutability::Not => "&[]".into(),
210+
Mutability::Mut => format!("{borrow_prefix_mut}[]"),
211+
Mutability::Not => format!("{borrow_prefix}[]"),
135212
}
136213
}
137214
},
@@ -142,7 +219,13 @@ impl UselessVec {
142219
USELESS_VEC,
143220
span,
144221
"useless use of `vec!`",
145-
"you can use a slice directly",
222+
&format!(
223+
"you can use {} directly",
224+
match suggest_slice {
225+
SuggestSlice::Yes => "a slice",
226+
SuggestSlice::No => "an array",
227+
}
228+
),
146229
snippet,
147230
applicability,
148231
);

0 commit comments

Comments
 (0)