@@ -4,7 +4,7 @@ use clippy_utils::get_attr;
44use clippy_utils:: source:: { indent_of, snippet} ;
55use rustc_errors:: { Applicability , Diagnostic } ;
66use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
7- use rustc_hir:: { Expr , ExprKind , MatchSource } ;
7+ use rustc_hir:: { Arm , Expr , ExprKind , MatchSource } ;
88use rustc_lint:: { LateContext , LintContext } ;
99use rustc_middle:: ty:: subst:: GenericArgKind ;
1010use rustc_middle:: ty:: { Ty , TypeAndMut } ;
@@ -16,12 +16,21 @@ pub(super) fn check<'tcx>(
1616 cx : & LateContext < ' tcx > ,
1717 expr : & ' tcx Expr < ' tcx > ,
1818 scrutinee : & ' tcx Expr < ' _ > ,
19+ arms : & ' tcx [ Arm < ' _ > ] ,
1920 source : MatchSource ,
2021) {
2122 if let Some ( ( suggestions, message) ) = has_significant_drop_in_scrutinee ( cx, scrutinee, source) {
2223 for found in suggestions {
2324 span_lint_and_then ( cx, SIGNIFICANT_DROP_IN_SCRUTINEE , found. found_span , message, |diag| {
2425 set_diagnostic ( diag, cx, expr, found) ;
26+ let s = Span :: new ( expr. span . hi ( ) , expr. span . hi ( ) , expr. span . ctxt ( ) , None ) ;
27+ diag. span_label ( s, "original temporary lives until here" ) ;
28+ if let Some ( spans) = has_significant_drop_in_arms ( cx, arms) {
29+ for span in spans {
30+ diag. span_label ( span, "significant drop in arm here" ) ;
31+ }
32+ diag. note ( "this might lead to deadlocks or other unexpected behavior" ) ;
33+ }
2534 } ) ;
2635 }
2736 }
@@ -80,22 +89,77 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>(
8089 let mut helper = SigDropHelper :: new ( cx) ;
8190 helper. find_sig_drop ( scrutinee) . map ( |drops| {
8291 let message = if source == MatchSource :: Normal {
83- "temporary with significant drop in match scrutinee"
92+ "temporary with drop impl with side effects in match scrutinee lives to end of block "
8493 } else {
85- "temporary with significant drop in for loop"
94+ "temporary with drop impl with side effects in for loop condition lives to end of block "
8695 } ;
8796 ( drops, message)
8897 } )
8998}
9099
100+ struct SigDropChecker < ' a , ' tcx > {
101+ seen_types : FxHashSet < Ty < ' tcx > > ,
102+ cx : & ' a LateContext < ' tcx > ,
103+ }
104+
105+ impl < ' a , ' tcx > SigDropChecker < ' a , ' tcx > {
106+ fn new ( cx : & ' a LateContext < ' tcx > ) -> SigDropChecker < ' a , ' tcx > {
107+ SigDropChecker {
108+ seen_types : FxHashSet :: default ( ) ,
109+ cx,
110+ }
111+ }
112+
113+ fn get_type ( & self , ex : & ' tcx Expr < ' _ > ) -> Ty < ' tcx > {
114+ self . cx . typeck_results ( ) . expr_ty ( ex)
115+ }
116+
117+ fn has_seen_type ( & mut self , ty : Ty < ' tcx > ) -> bool {
118+ !self . seen_types . insert ( ty)
119+ }
120+
121+ fn has_sig_drop_attr ( & mut self , cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
122+ if let Some ( adt) = ty. ty_adt_def ( ) {
123+ if get_attr ( cx. sess ( ) , cx. tcx . get_attrs_unchecked ( adt. did ( ) ) , "has_significant_drop" ) . count ( ) > 0 {
124+ return true ;
125+ }
126+ }
127+
128+ match ty. kind ( ) {
129+ rustc_middle:: ty:: Adt ( a, b) => {
130+ for f in a. all_fields ( ) {
131+ let ty = f. ty ( cx. tcx , b) ;
132+ if !self . has_seen_type ( ty) && self . has_sig_drop_attr ( cx, ty) {
133+ return true ;
134+ }
135+ }
136+
137+ for generic_arg in b. iter ( ) {
138+ if let GenericArgKind :: Type ( ty) = generic_arg. unpack ( ) {
139+ if self . has_sig_drop_attr ( cx, ty) {
140+ return true ;
141+ }
142+ }
143+ }
144+ false
145+ } ,
146+ rustc_middle:: ty:: Array ( ty, _)
147+ | rustc_middle:: ty:: RawPtr ( TypeAndMut { ty, .. } )
148+ | rustc_middle:: ty:: Ref ( _, ty, _)
149+ | rustc_middle:: ty:: Slice ( ty) => self . has_sig_drop_attr ( cx, * ty) ,
150+ _ => false ,
151+ }
152+ }
153+ }
154+
91155struct SigDropHelper < ' a , ' tcx > {
92156 cx : & ' a LateContext < ' tcx > ,
93157 is_chain_end : bool ,
94- seen_types : FxHashSet < Ty < ' tcx > > ,
95158 has_significant_drop : bool ,
96159 current_sig_drop : Option < FoundSigDrop > ,
97160 sig_drop_spans : Option < Vec < FoundSigDrop > > ,
98161 special_handling_for_binary_op : bool ,
162+ sig_drop_checker : SigDropChecker < ' a , ' tcx > ,
99163}
100164
101165#[ expect( clippy:: enum_variant_names) ]
@@ -118,11 +182,11 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
118182 SigDropHelper {
119183 cx,
120184 is_chain_end : true ,
121- seen_types : FxHashSet :: default ( ) ,
122185 has_significant_drop : false ,
123186 current_sig_drop : None ,
124187 sig_drop_spans : None ,
125188 special_handling_for_binary_op : false ,
189+ sig_drop_checker : SigDropChecker :: new ( cx) ,
126190 }
127191 }
128192
@@ -163,7 +227,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
163227 if self . current_sig_drop . is_some ( ) {
164228 return ;
165229 }
166- let ty = self . get_type ( expr) ;
230+ let ty = self . sig_drop_checker . get_type ( expr) ;
167231 if ty. is_ref ( ) {
168232 // We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
169233 // but let's avoid any chance of an ICE
@@ -187,14 +251,6 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
187251 }
188252 }
189253
190- fn get_type ( & self , ex : & ' tcx Expr < ' _ > ) -> Ty < ' tcx > {
191- self . cx . typeck_results ( ) . expr_ty ( ex)
192- }
193-
194- fn has_seen_type ( & mut self , ty : Ty < ' tcx > ) -> bool {
195- !self . seen_types . insert ( ty)
196- }
197-
198254 fn visit_exprs_for_binary_ops (
199255 & mut self ,
200256 left : & ' tcx Expr < ' _ > ,
@@ -214,44 +270,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
214270
215271 self . special_handling_for_binary_op = false ;
216272 }
217-
218- fn has_sig_drop_attr ( & mut self , cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
219- if let Some ( adt) = ty. ty_adt_def ( ) {
220- if get_attr ( cx. sess ( ) , cx. tcx . get_attrs_unchecked ( adt. did ( ) ) , "has_significant_drop" ) . count ( ) > 0 {
221- return true ;
222- }
223- }
224-
225- match ty. kind ( ) {
226- rustc_middle:: ty:: Adt ( a, b) => {
227- for f in a. all_fields ( ) {
228- let ty = f. ty ( cx. tcx , b) ;
229- if !self . has_seen_type ( ty) && self . has_sig_drop_attr ( cx, ty) {
230- return true ;
231- }
232- }
233-
234- for generic_arg in b. iter ( ) {
235- if let GenericArgKind :: Type ( ty) = generic_arg. unpack ( ) {
236- if self . has_sig_drop_attr ( cx, ty) {
237- return true ;
238- }
239- }
240- }
241- false
242- } ,
243- rustc_middle:: ty:: Array ( ty, _)
244- | rustc_middle:: ty:: RawPtr ( TypeAndMut { ty, .. } )
245- | rustc_middle:: ty:: Ref ( _, ty, _)
246- | rustc_middle:: ty:: Slice ( ty) => self . has_sig_drop_attr ( cx, * ty) ,
247- _ => false ,
248- }
249- }
250273}
251274
252275impl < ' a , ' tcx > Visitor < ' tcx > for SigDropHelper < ' a , ' tcx > {
253276 fn visit_expr ( & mut self , ex : & ' tcx Expr < ' _ > ) {
254- if !self . is_chain_end && self . has_sig_drop_attr ( self . cx , self . get_type ( ex) ) {
277+ if !self . is_chain_end
278+ && self
279+ . sig_drop_checker
280+ . has_sig_drop_attr ( self . cx , self . sig_drop_checker . get_type ( ex) )
281+ {
255282 self . has_significant_drop = true ;
256283 return ;
257284 }
@@ -330,3 +357,40 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
330357 }
331358 }
332359}
360+
361+ struct ArmSigDropHelper < ' a , ' tcx > {
362+ sig_drop_checker : SigDropChecker < ' a , ' tcx > ,
363+ found_sig_drop_spans : Option < FxHashSet < Span > > ,
364+ }
365+
366+ impl < ' a , ' tcx > ArmSigDropHelper < ' a , ' tcx > {
367+ fn new ( cx : & ' a LateContext < ' tcx > ) -> ArmSigDropHelper < ' a , ' tcx > {
368+ ArmSigDropHelper {
369+ sig_drop_checker : SigDropChecker :: new ( cx) ,
370+ found_sig_drop_spans : None ,
371+ }
372+ }
373+ }
374+
375+ fn has_significant_drop_in_arms < ' tcx , ' a > ( cx : & ' a LateContext < ' tcx > , arms : & ' tcx [ Arm < ' _ > ] ) -> Option < FxHashSet < Span > > {
376+ let mut helper = ArmSigDropHelper :: new ( cx) ;
377+ for arm in arms {
378+ helper. visit_expr ( arm. body ) ;
379+ }
380+ helper. found_sig_drop_spans
381+ }
382+
383+ impl < ' a , ' tcx > Visitor < ' tcx > for ArmSigDropHelper < ' a , ' tcx > {
384+ fn visit_expr ( & mut self , ex : & ' tcx Expr < ' tcx > ) {
385+ if self
386+ . sig_drop_checker
387+ . has_sig_drop_attr ( self . sig_drop_checker . cx , self . sig_drop_checker . get_type ( ex) )
388+ {
389+ self . found_sig_drop_spans
390+ . get_or_insert_with ( FxHashSet :: default)
391+ . insert ( ex. span ) ;
392+ return ;
393+ }
394+ walk_expr ( self , ex) ;
395+ }
396+ }
0 commit comments