@@ -4,12 +4,12 @@ use std::ops::ControlFlow;
44use clippy_config:: msrvs:: { self , Msrv } ;
55use clippy_utils:: consts:: { constant, Constant } ;
66use clippy_utils:: diagnostics:: span_lint_hir_and_then;
7- use clippy_utils:: source:: snippet_with_applicability ;
7+ use clippy_utils:: source:: snippet_opt ;
88use clippy_utils:: ty:: is_copy;
99use clippy_utils:: visitors:: for_each_local_use_after_expr;
1010use clippy_utils:: { get_parent_expr, higher, is_trait_method} ;
1111use 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 } ;
1313use rustc_lint:: { LateContext , LateLintPass } ;
1414use rustc_middle:: ty;
1515use rustc_middle:: ty:: layout:: LayoutOf ;
@@ -52,35 +52,27 @@ declare_clippy_lint! {
5252
5353impl_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-
7255impl < ' 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-
169133impl 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
244216fn 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+ }
0 commit comments