1
+ use std:: ops:: ControlFlow ;
2
+
1
3
use clippy_utils:: consts:: { constant, Constant } ;
2
4
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
- use clippy_utils:: higher;
4
5
use clippy_utils:: source:: snippet_with_applicability;
5
6
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} ;
6
9
use if_chain:: if_chain;
10
+ use rustc_ast:: BindingAnnotation ;
7
11
use rustc_errors:: Applicability ;
8
- use rustc_hir:: { BorrowKind , Expr , ExprKind , Mutability } ;
12
+ use rustc_hir:: { BorrowKind , Expr , ExprKind , Mutability , Node , PatKind } ;
9
13
use rustc_lint:: { LateContext , LateLintPass } ;
10
14
use rustc_middle:: ty:: layout:: LayoutOf ;
11
15
use rustc_middle:: ty:: { self , Ty } ;
12
16
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
13
17
use rustc_span:: source_map:: Span ;
18
+ use rustc_span:: sym;
14
19
15
20
#[ expect( clippy:: module_name_repetitions) ]
16
21
#[ derive( Copy , Clone ) ]
@@ -20,7 +25,7 @@ pub struct UselessVec {
20
25
21
26
declare_clippy_lint ! {
22
27
/// ### What it does
23
- /// Checks for usage of `& vec![..]` when using `& [..]` would
28
+ /// Checks for usage of `vec![..]` when using `[..]` would
24
29
/// be possible.
25
30
///
26
31
/// ### Why is this bad?
@@ -46,16 +51,70 @@ declare_clippy_lint! {
46
51
47
52
impl_lint_pass ! ( UselessVec => [ USELESS_VEC ] ) ;
48
53
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" , "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
+
49
75
impl < ' tcx > LateLintPass < ' tcx > for UselessVec {
50
76
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
51
77
// search for `&vec![_]` expressions where the adjusted type is `&[_]`
52
78
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) ;
55
80
if let ExprKind :: AddrOf ( BorrowKind :: Ref , mutability, addressee) = expr. kind;
56
81
if let Some ( vec_args) = higher:: VecArgs :: hir( cx, addressee) ;
57
82
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
+ ) ;
59
118
}
60
119
}
61
120
@@ -67,21 +126,36 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
67
126
then {
68
127
// report the error around the `vec!` not inside `<std macros>:`
69
128
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 ) ;
71
130
}
72
131
}
73
132
}
74
133
}
75
134
135
+ #[ derive( Copy , Clone ) ]
136
+ enum SuggestSlice {
137
+ /// Suggest using a slice `&[..]` / `&mut [..]`
138
+ Yes ,
139
+ /// Suggest using an array: `[..]`
140
+ No ,
141
+ }
142
+
76
143
impl UselessVec {
77
144
fn check_vec_macro < ' tcx > (
78
145
self ,
79
146
cx : & LateContext < ' tcx > ,
80
147
vec_args : & higher:: VecArgs < ' tcx > ,
81
148
mutability : Mutability ,
82
149
span : Span ,
150
+ suggest_slice : SuggestSlice ,
83
151
) {
84
152
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
+
85
159
let snippet = match * vec_args {
86
160
higher:: VecArgs :: Repeat ( elem, len) => {
87
161
if let Some ( Constant :: Int ( len_constant) ) = constant ( cx, cx. typeck_results ( ) , len) {
@@ -93,14 +167,14 @@ impl UselessVec {
93
167
match mutability {
94
168
Mutability :: Mut => {
95
169
format ! (
96
- "&mut [{}; {}]" ,
170
+ "{borrow_prefix_mut} [{}; {}]" ,
97
171
snippet_with_applicability( cx, elem. span, "elem" , & mut applicability) ,
98
172
snippet_with_applicability( cx, len. span, "len" , & mut applicability)
99
173
)
100
174
} ,
101
175
Mutability :: Not => {
102
176
format ! (
103
- "& [{}; {}]" ,
177
+ "{borrow_prefix} [{}; {}]" ,
104
178
snippet_with_applicability( cx, elem. span, "elem" , & mut applicability) ,
105
179
snippet_with_applicability( cx, len. span, "len" , & mut applicability)
106
180
)
@@ -120,18 +194,21 @@ impl UselessVec {
120
194
match mutability {
121
195
Mutability :: Mut => {
122
196
format ! (
123
- "&mut [{}]" ,
197
+ "{borrow_prefix_mut} [{}]" ,
124
198
snippet_with_applicability( cx, span, ".." , & mut applicability)
125
199
)
126
200
} ,
127
201
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
+ )
129
206
} ,
130
207
}
131
208
} else {
132
209
match mutability {
133
- Mutability :: Mut => "&mut []". into ( ) ,
134
- Mutability :: Not => "& []". into ( ) ,
210
+ Mutability :: Mut => format ! ( "{borrow_prefix_mut} []") ,
211
+ Mutability :: Not => format ! ( "{borrow_prefix} []") ,
135
212
}
136
213
}
137
214
} ,
@@ -142,7 +219,13 @@ impl UselessVec {
142
219
USELESS_VEC ,
143
220
span,
144
221
"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
+ ) ,
146
229
snippet,
147
230
applicability,
148
231
) ;
0 commit comments