@@ -3,13 +3,17 @@ use std::mem::swap;
33use rustc_ast:: UnOp ;
44use rustc_hir:: def:: Res ;
55use rustc_hir:: intravisit:: { self , Visitor } ;
6- use rustc_hir:: { self as hir, Block , Expr , ExprKind , LetStmt , Pat , PatKind , QPath , StmtKind } ;
6+ use rustc_hir:: {
7+ self as hir, Block , Expr , ExprKind , HirIdMap , HirIdSet , LetStmt , Pat , PatKind , QPath , StmtKind ,
8+ } ;
79use rustc_macros:: LintDiagnostic ;
8- use rustc_middle:: ty;
10+ use rustc_middle:: hir:: place:: PlaceBase ;
11+ use rustc_middle:: ty:: { self , Ty } ;
912use rustc_session:: lint:: FutureIncompatibilityReason ;
1013use rustc_session:: { declare_lint, declare_lint_pass} ;
1114use rustc_span:: edition:: Edition ;
1215use rustc_span:: Span ;
16+ use tracing:: instrument;
1317
1418use crate :: { LateContext , LateLintPass } ;
1519
@@ -94,35 +98,41 @@ declare_lint! {
9498declare_lint_pass ! ( TailExprDropOrder => [ TAIL_EXPR_DROP_ORDER ] ) ;
9599
96100impl TailExprDropOrder {
101+ #[ instrument( level = "debug" , skip( cx, body) ) ]
97102 fn check_fn_or_closure < ' tcx > (
98103 cx : & LateContext < ' tcx > ,
99104 fn_kind : hir:: intravisit:: FnKind < ' tcx > ,
100105 body : & ' tcx hir:: Body < ' tcx > ,
101106 def_id : rustc_span:: def_id:: LocalDefId ,
102107 ) {
103- let mut locals = vec ! [ ] ;
108+ let consumed = cx. tcx . extract_tail_expr_consuming_nodes ( def_id. to_def_id ( ) ) ;
109+ let mut locals = HirIdMap :: default ( ) ;
104110 if matches ! ( fn_kind, hir:: intravisit:: FnKind :: Closure ) {
105111 for & capture in cx. tcx . closure_captures ( def_id) {
106112 if matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue )
107113 && capture. place . ty ( ) . has_significant_drop ( cx. tcx , cx. param_env )
108114 {
109- locals. push ( capture. var_ident . span ) ;
115+ let hir_id = match capture. place . base {
116+ PlaceBase :: Local ( id) => id,
117+ PlaceBase :: Upvar ( upvar) => upvar. var_path . hir_id ,
118+ PlaceBase :: Rvalue | PlaceBase :: StaticItem => continue ,
119+ } ;
120+ if consumed. contains ( & hir_id) {
121+ continue ;
122+ }
123+ locals. insert ( hir_id, capture. var_ident . span ) ;
110124 }
111125 }
112126 }
113127 for param in body. params {
114- if cx
115- . typeck_results ( )
116- . node_type ( param. hir_id )
117- . has_significant_drop ( cx. tcx , cx. param_env )
118- {
119- locals. push ( param. span ) ;
120- }
128+ LocalCollector { cx, locals : & mut locals, consumed : & consumed } . visit_pat ( param. pat ) ;
121129 }
122130 if let hir:: ExprKind :: Block ( block, _) = body. value . kind {
123- LintVisitor { cx, locals } . check_block_inner ( block) ;
131+ LintVisitor { cx, locals, consumed } . check_block_inner ( block) ;
124132 } else {
125- LintTailExpr { cx, locals : & locals, is_root_tail_expr : true } . visit_expr ( body. value ) ;
133+ let locals: Vec < _ > = locals. values ( ) . copied ( ) . collect ( ) ;
134+ LintTailExpr { cx, locals : & locals, consumed, is_root_tail_expr : true }
135+ . visit_expr ( body. value ) ;
126136 }
127137 }
128138}
@@ -146,21 +156,26 @@ impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder {
146156struct LintVisitor < ' tcx , ' a > {
147157 cx : & ' a LateContext < ' tcx > ,
148158 // We only record locals that have significant drops
149- locals : Vec < Span > ,
159+ locals : HirIdMap < Span > ,
160+ consumed : & ' a HirIdSet ,
150161}
151162
152163struct LocalCollector < ' tcx , ' a > {
153164 cx : & ' a LateContext < ' tcx > ,
154- locals : & ' a mut Vec < Span > ,
165+ locals : & ' a mut HirIdMap < Span > ,
166+ consumed : & ' a HirIdSet ,
155167}
156168
157169impl < ' tcx , ' a > Visitor < ' tcx > for LocalCollector < ' tcx , ' a > {
158170 type Result = ( ) ;
159171 fn visit_pat ( & mut self , pat : & ' tcx Pat < ' tcx > ) {
160172 if let PatKind :: Binding ( _binding_mode, id, ident, pat) = pat. kind {
173+ if self . consumed . contains ( & id) {
174+ return ;
175+ }
161176 let ty = self . cx . typeck_results ( ) . node_type ( id) ;
162177 if ty. has_significant_drop ( self . cx . tcx , self . cx . param_env ) {
163- self . locals . push ( ident. span ) ;
178+ self . locals . insert ( id , ident. span ) ;
164179 }
165180 if let Some ( pat) = pat {
166181 self . visit_pat ( pat) ;
@@ -179,7 +194,8 @@ impl<'tcx, 'a> Visitor<'tcx> for LintVisitor<'tcx, 'a> {
179194 swap ( & mut locals, & mut self . locals ) ;
180195 }
181196 fn visit_local ( & mut self , local : & ' tcx LetStmt < ' tcx > ) {
182- LocalCollector { cx : self . cx , locals : & mut self . locals } . visit_local ( local) ;
197+ LocalCollector { cx : self . cx , locals : & mut self . locals , consumed : & self . consumed }
198+ . visit_local ( local) ;
183199 }
184200}
185201
@@ -200,19 +216,26 @@ impl<'tcx, 'a> LintVisitor<'tcx, 'a> {
200216 if self . locals . is_empty ( ) {
201217 return ;
202218 }
203- LintTailExpr { cx : self . cx , locals : & self . locals , is_root_tail_expr : true }
204- . visit_expr ( tail_expr) ;
219+ let locals: Vec < _ > = self . locals . values ( ) . copied ( ) . collect ( ) ;
220+ LintTailExpr {
221+ cx : self . cx ,
222+ locals : & locals,
223+ is_root_tail_expr : true ,
224+ consumed : self . consumed ,
225+ }
226+ . visit_expr ( tail_expr) ;
205227 }
206228}
207229
208230struct LintTailExpr < ' tcx , ' a > {
209231 cx : & ' a LateContext < ' tcx > ,
210232 is_root_tail_expr : bool ,
211233 locals : & ' a [ Span ] ,
234+ consumed : & ' a HirIdSet ,
212235}
213236
214237impl < ' tcx , ' a > LintTailExpr < ' tcx , ' a > {
215- fn expr_eventually_point_into_local ( mut expr : & Expr < ' tcx > ) -> bool {
238+ fn expr_eventually_point_into_local ( & self , mut expr : & Expr < ' tcx > ) -> bool {
216239 loop {
217240 match expr. kind {
218241 ExprKind :: Index ( access, _, _) | ExprKind :: Field ( access, _) => expr = access,
@@ -231,24 +254,28 @@ impl<'tcx, 'a> LintTailExpr<'tcx, 'a> {
231254 }
232255 }
233256
234- fn expr_generates_nonlocal_droppy_value ( & self , expr : & Expr < ' tcx > ) -> bool {
235- if Self :: expr_eventually_point_into_local ( expr) {
236- return false ;
257+ fn expr_generates_nonlocal_droppy_value ( & self , expr : & Expr < ' tcx > ) -> Option < Ty < ' tcx > > {
258+ if self . expr_eventually_point_into_local ( expr) {
259+ return None ;
260+ }
261+ if self . consumed . contains ( & expr. hir_id ) {
262+ return None ;
237263 }
238- self . cx . typeck_results ( ) . expr_ty ( expr) . has_significant_drop ( self . cx . tcx , self . cx . param_env )
264+ let ty = self . cx . typeck_results ( ) . expr_ty ( expr) ;
265+ if ty. has_significant_drop ( self . cx . tcx , self . cx . param_env ) { Some ( ty) } else { None }
239266 }
240267}
241268
242269impl < ' tcx , ' a > Visitor < ' tcx > for LintTailExpr < ' tcx , ' a > {
243270 fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
244271 if self . is_root_tail_expr {
245272 self . is_root_tail_expr = false ;
246- } else if self . expr_generates_nonlocal_droppy_value ( expr) {
273+ } else if let Some ( ty ) = self . expr_generates_nonlocal_droppy_value ( expr) {
247274 self . cx . tcx . emit_node_span_lint (
248275 TAIL_EXPR_DROP_ORDER ,
249276 expr. hir_id ,
250277 expr. span ,
251- TailExprDropOrderLint { spans : self . locals . to_vec ( ) } ,
278+ TailExprDropOrderLint { spans : self . locals . to_vec ( ) , ty } ,
252279 ) ;
253280 return ;
254281 }
@@ -294,13 +321,15 @@ impl<'tcx, 'a> Visitor<'tcx> for LintTailExpr<'tcx, 'a> {
294321 }
295322 }
296323 fn visit_block ( & mut self , block : & ' tcx Block < ' tcx > ) {
297- LintVisitor { cx : self . cx , locals : <_ >:: default ( ) } . check_block_inner ( block) ;
324+ LintVisitor { cx : self . cx , locals : <_ >:: default ( ) , consumed : & <_ >:: default ( ) }
325+ . check_block_inner ( block) ;
298326 }
299327}
300328
301329#[ derive( LintDiagnostic ) ]
302330#[ diag( lint_tail_expr_drop_order) ]
303- struct TailExprDropOrderLint {
331+ struct TailExprDropOrderLint < ' a > {
304332 #[ label]
305333 pub spans : Vec < Span > ,
334+ pub ty : Ty < ' a > ,
306335}
0 commit comments