@@ -4,12 +4,12 @@ use std::ops::ControlFlow;
4
4
use clippy_config:: msrvs:: { self , Msrv } ;
5
5
use clippy_utils:: consts:: { constant, Constant } ;
6
6
use clippy_utils:: diagnostics:: span_lint_hir_and_then;
7
- use clippy_utils:: source:: snippet_with_applicability ;
7
+ use clippy_utils:: source:: snippet_opt ;
8
8
use clippy_utils:: ty:: is_copy;
9
9
use clippy_utils:: visitors:: for_each_local_use_after_expr;
10
10
use clippy_utils:: { get_parent_expr, higher, is_trait_method} ;
11
11
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 } ;
13
13
use rustc_lint:: { LateContext , LateLintPass } ;
14
14
use rustc_middle:: ty;
15
15
use rustc_middle:: ty:: layout:: LayoutOf ;
@@ -52,35 +52,27 @@ declare_clippy_lint! {
52
52
53
53
impl_lint_pass ! ( UselessVec => [ USELESS_VEC ] ) ;
54
54
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
-
72
55
impl < ' tcx > LateLintPass < ' tcx > for UselessVec {
73
56
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 ) {
75
64
// search for `let foo = vec![_]` expressions where all uses of `foo`
76
65
// 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| {
84
76
// allow indexing into a vec and some set of allowed method calls that exist on slices, too
85
77
if let Some ( parent) = get_parent_expr ( cx, expr)
86
78
&& ( adjusts_to_slice ( cx, expr)
@@ -94,60 +86,40 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
94
86
} )
95
87
. is_continue ( ) ;
96
88
97
- let span = expr. span . ctxt ( ) . outer_expn_data ( ) . call_site ;
98
89
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 ) ;
100
91
} else {
101
- self . span_to_lint_map . insert ( span , None ) ;
92
+ self . span_to_lint_map . insert ( callsite , None ) ;
102
93
}
103
- }
94
+ } ,
104
95
// 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
+ } ,
112
99
// 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 ) =>
116
102
{
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
+ } ,
121
106
// 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) ;
131
109
132
110
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) ;
134
112
} else {
135
- self . span_to_lint_map . insert ( span , None ) ;
113
+ self . span_to_lint_map . insert ( callsite , None ) ;
136
114
}
137
- }
115
+ } ,
138
116
}
139
117
}
140
118
141
119
fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
142
120
for ( span, lint_opt) in & self . span_to_lint_map {
143
121
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( ) , ) ;
151
123
span_lint_hir_and_then ( cx, USELESS_VEC , * hir_id, * span, "useless use of `vec!`" , |diag| {
152
124
diag. span_suggestion ( * span, help_msg, snippet, * applicability) ;
153
125
} ) ;
@@ -158,14 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
158
130
extract_msrv_attr ! ( LateContext ) ;
159
131
}
160
132
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
-
169
133
impl UselessVec {
170
134
fn check_vec_macro < ' tcx > (
171
135
& mut self ,
@@ -179,8 +143,6 @@ impl UselessVec {
179
143
return ;
180
144
}
181
145
182
- let mut applicability = Applicability :: MachineApplicable ;
183
-
184
146
let snippet = match * vec_args {
185
147
higher:: VecArgs :: Repeat ( elem, len) => {
186
148
if let Some ( Constant :: Int ( len_constant) ) = constant ( cx, cx. typeck_results ( ) , len) {
@@ -194,54 +156,91 @@ impl UselessVec {
194
156
return ;
195
157
}
196
158
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 ) )
205
160
} else {
206
161
return ;
207
162
}
208
163
} ,
209
164
higher:: VecArgs :: Vec ( args) => {
210
- if let Some ( last) = args. iter ( ) . last ( ) {
165
+ let args_span = if let Some ( last) = args. iter ( ) . last ( ) {
211
166
if args. len ( ) as u64 * size_of ( cx, last) > self . too_large_for_stack {
212
167
return ;
213
168
}
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 ( ) ) )
228
170
} 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 )
235
174
} ,
236
175
} ;
237
176
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
+ }
241
213
}
242
214
}
243
215
244
216
fn size_of ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> u64 {
245
217
let ty = cx. typeck_results ( ) . expr_ty_adjusted ( expr) ;
246
218
cx. layout_of ( ty) . map_or ( 0 , |l| l. size . bytes ( ) )
247
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
+ }
0 commit comments