11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: higher:: { get_vec_init_kind, VecInitKind } ;
33use clippy_utils:: source:: snippet;
4- use clippy_utils:: { path_to_local, path_to_local_id} ;
5- use if_chain:: if_chain;
4+ use clippy_utils:: visitors:: for_each_local_use_after_expr;
5+ use clippy_utils:: { get_parent_expr, path_to_local_id} ;
6+ use core:: ops:: ControlFlow ;
67use rustc_errors:: Applicability ;
7- use rustc_hir:: { BindingAnnotation , Block , Expr , ExprKind , HirId , Local , PatKind , Stmt , StmtKind } ;
8+ use rustc_hir:: def:: Res ;
9+ use rustc_hir:: {
10+ BindingAnnotation , Block , Expr , ExprKind , HirId , Local , Mutability , PatKind , QPath , Stmt , StmtKind , UnOp ,
11+ } ;
812use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
913use rustc_middle:: lint:: in_external_macro;
1014use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
11- use rustc_span:: Span ;
15+ use rustc_span:: { Span , Symbol } ;
1216
1317declare_clippy_lint ! {
1418 /// ### What it does
@@ -43,26 +47,86 @@ pub struct VecInitThenPush {
4347struct VecPushSearcher {
4448 local_id : HirId ,
4549 init : VecInitKind ,
46- lhs_is_local : bool ,
47- lhs_span : Span ,
50+ lhs_is_let : bool ,
51+ let_ty_span : Option < Span > ,
52+ name : Symbol ,
4853 err_span : Span ,
49- found : u64 ,
54+ found : u128 ,
55+ last_push_expr : HirId ,
5056}
5157impl VecPushSearcher {
5258 fn display_err ( & self , cx : & LateContext < ' _ > ) {
53- match self . init {
59+ let min_pushes_for_extension = match self . init {
5460 _ if self . found == 0 => return ,
55- VecInitKind :: WithLiteralCapacity ( x) if x > self . found => return ,
61+ VecInitKind :: WithConstCapacity ( x) if x > self . found => return ,
62+ VecInitKind :: WithConstCapacity ( x) => x,
5663 VecInitKind :: WithExprCapacity ( _) => return ,
57- _ => ( ) ,
64+ _ => 3 ,
5865 } ;
5966
60- let mut s = if self . lhs_is_local {
67+ let mut needs_mut = false ;
68+ let res = for_each_local_use_after_expr ( cx, self . local_id , self . last_push_expr , |e| {
69+ let Some ( parent) = get_parent_expr ( cx, e) else {
70+ return ControlFlow :: Continue ( ( ) )
71+ } ;
72+ let adjusted_ty = cx. typeck_results ( ) . expr_ty_adjusted ( e) ;
73+ let adjusted_mut = adjusted_ty. ref_mutability ( ) . unwrap_or ( Mutability :: Not ) ;
74+ needs_mut |= adjusted_mut == Mutability :: Mut ;
75+ match parent. kind {
76+ ExprKind :: AddrOf ( _, Mutability :: Mut , _) => {
77+ needs_mut = true ;
78+ return ControlFlow :: Break ( true ) ;
79+ } ,
80+ ExprKind :: Unary ( UnOp :: Deref , _) | ExprKind :: Index ( ..) if !needs_mut => {
81+ let mut last_place = parent;
82+ while let Some ( parent) = get_parent_expr ( cx, parent) {
83+ if matches ! ( parent. kind, ExprKind :: Unary ( UnOp :: Deref , _) | ExprKind :: Field ( ..) )
84+ || matches ! ( parent. kind, ExprKind :: Index ( e, _) if e. hir_id == last_place. hir_id)
85+ {
86+ last_place = parent;
87+ } else {
88+ break ;
89+ }
90+ }
91+ needs_mut |= cx. typeck_results ( ) . expr_ty_adjusted ( last_place) . ref_mutability ( )
92+ == Some ( Mutability :: Mut )
93+ || get_parent_expr ( cx, last_place)
94+ . map_or ( false , |e| matches ! ( e. kind, ExprKind :: AddrOf ( _, Mutability :: Mut , _) ) ) ;
95+ } ,
96+ ExprKind :: MethodCall ( _, [ recv, ..] , _)
97+ if recv. hir_id == e. hir_id
98+ && adjusted_mut == Mutability :: Mut
99+ && !adjusted_ty. peel_refs ( ) . is_slice ( ) =>
100+ {
101+ return ControlFlow :: Break ( true ) ;
102+ } ,
103+ ExprKind :: Assign ( lhs, ..) if e. hir_id == lhs. hir_id => {
104+ needs_mut = true ;
105+ return ControlFlow :: Break ( false ) ;
106+ } ,
107+ _ => ( ) ,
108+ }
109+ ControlFlow :: Continue ( ( ) )
110+ } ) ;
111+
112+ // Avoid allocating small `Vec`s when they'll be extended right after.
113+ if res == ControlFlow :: Break ( true ) && self . found <= min_pushes_for_extension {
114+ return ;
115+ }
116+
117+ let mut s = if self . lhs_is_let {
61118 String :: from ( "let " )
62119 } else {
63120 String :: new ( )
64121 } ;
65- s. push_str ( & snippet ( cx, self . lhs_span , ".." ) ) ;
122+ if needs_mut {
123+ s. push_str ( "mut " ) ;
124+ }
125+ s. push_str ( self . name . as_str ( ) ) ;
126+ if let Some ( span) = self . let_ty_span {
127+ s. push_str ( ": " ) ;
128+ s. push_str ( & snippet ( cx, span, "_" ) ) ;
129+ }
66130 s. push_str ( " = vec![..];" ) ;
67131
68132 span_lint_and_sugg (
@@ -83,60 +147,63 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
83147 }
84148
85149 fn check_local ( & mut self , cx : & LateContext < ' tcx > , local : & ' tcx Local < ' tcx > ) {
86- if_chain ! {
87- if !in_external_macro( cx. sess( ) , local. span) ;
88- if let Some ( init) = local. init;
89- if let PatKind :: Binding ( BindingAnnotation :: Mutable , id, _, None ) = local. pat. kind;
90- if let Some ( init_kind) = get_vec_init_kind( cx, init) ;
91- then {
92- self . searcher = Some ( VecPushSearcher {
93- local_id: id,
94- init: init_kind,
95- lhs_is_local: true ,
96- lhs_span: local. ty. map_or( local. pat. span, |t| local. pat. span. to( t. span) ) ,
97- err_span: local. span,
98- found: 0 ,
99- } ) ;
100- }
150+ if let Some ( init_expr) = local. init
151+ && let PatKind :: Binding ( BindingAnnotation :: Mutable , id, name, None ) = local. pat . kind
152+ && !in_external_macro ( cx. sess ( ) , local. span )
153+ && let Some ( init) = get_vec_init_kind ( cx, init_expr)
154+ && !matches ! ( init, VecInitKind :: WithExprCapacity ( _) )
155+ {
156+ self . searcher = Some ( VecPushSearcher {
157+ local_id : id,
158+ init,
159+ lhs_is_let : true ,
160+ name : name. name ,
161+ let_ty_span : local. ty . map ( |ty| ty. span ) ,
162+ err_span : local. span ,
163+ found : 0 ,
164+ last_push_expr : init_expr. hir_id ,
165+ } ) ;
101166 }
102167 }
103168
104169 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
105- if_chain ! {
106- if self . searcher. is_none( ) ;
107- if !in_external_macro( cx. sess( ) , expr. span) ;
108- if let ExprKind :: Assign ( left, right, _) = expr. kind;
109- if let Some ( id) = path_to_local( left) ;
110- if let Some ( init_kind) = get_vec_init_kind( cx, right) ;
111- then {
112- self . searcher = Some ( VecPushSearcher {
113- local_id: id,
114- init: init_kind,
115- lhs_is_local: false ,
116- lhs_span: left. span,
117- err_span: expr. span,
118- found: 0 ,
119- } ) ;
120- }
170+ if self . searcher . is_none ( )
171+ && let ExprKind :: Assign ( left, right, _) = expr. kind
172+ && let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = left. kind
173+ && let [ name] = & path. segments
174+ && let Res :: Local ( id) = path. res
175+ && !in_external_macro ( cx. sess ( ) , expr. span )
176+ && let Some ( init) = get_vec_init_kind ( cx, right)
177+ && !matches ! ( init, VecInitKind :: WithExprCapacity ( _) )
178+ {
179+ self . searcher = Some ( VecPushSearcher {
180+ local_id : id,
181+ init,
182+ lhs_is_let : false ,
183+ let_ty_span : None ,
184+ name : name. ident . name ,
185+ err_span : expr. span ,
186+ found : 0 ,
187+ last_push_expr : expr. hir_id ,
188+ } ) ;
121189 }
122190 }
123191
124192 fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' _ > ) {
125193 if let Some ( searcher) = self . searcher . take ( ) {
126- if_chain ! {
127- if let StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) = stmt. kind;
128- if let ExprKind :: MethodCall ( path, [ self_arg, _] , _) = expr. kind;
129- if path_to_local_id( self_arg, searcher. local_id) ;
130- if path. ident. name. as_str( ) == "push" ;
131- then {
132- self . searcher = Some ( VecPushSearcher {
133- found: searcher. found + 1 ,
134- err_span: searcher. err_span. to( stmt. span) ,
135- .. searcher
136- } ) ;
137- } else {
138- searcher. display_err( cx) ;
139- }
194+ if let StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) = stmt. kind
195+ && let ExprKind :: MethodCall ( name, [ self_arg, _] , _) = expr. kind
196+ && path_to_local_id ( self_arg, searcher. local_id )
197+ && name. ident . as_str ( ) == "push"
198+ {
199+ self . searcher = Some ( VecPushSearcher {
200+ found : searcher. found + 1 ,
201+ err_span : searcher. err_span . to ( stmt. span ) ,
202+ last_push_expr : expr. hir_id ,
203+ .. searcher
204+ } ) ;
205+ } else {
206+ searcher. display_err ( cx) ;
140207 }
141208 }
142209 }
0 commit comments