1+ use std:: ops:: ControlFlow ;
2+
13use clippy_utils:: consts:: { constant, Constant } ;
24use clippy_utils:: diagnostics:: span_lint_and_sugg;
3- use clippy_utils:: higher;
45use clippy_utils:: source:: snippet_with_applicability;
56use 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} ;
69use if_chain:: if_chain;
10+ use rustc_ast:: BindingAnnotation ;
711use rustc_errors:: Applicability ;
8- use rustc_hir:: { BorrowKind , Expr , ExprKind , Mutability } ;
12+ use rustc_hir:: { BorrowKind , Expr , ExprKind , Mutability , Node , PatKind } ;
913use rustc_lint:: { LateContext , LateLintPass } ;
1014use rustc_middle:: ty:: layout:: LayoutOf ;
1115use rustc_middle:: ty:: { self , Ty } ;
1216use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1317use 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
2126declare_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
4752impl_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" , "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+
4975impl < ' 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+
76143impl 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