@@ -6,10 +6,12 @@ use rustc_ast::Recovered;
6
6
use rustc_errors:: {
7
7
Applicability , Diag , EmissionGuarantee , SubdiagMessageOp , Subdiagnostic , SuggestionStyle ,
8
8
} ;
9
+ use rustc_hir:: def:: Res ;
10
+ use rustc_hir:: def_id:: DefId ;
9
11
use rustc_hir:: { self as hir, HirIdSet } ;
10
12
use rustc_macros:: LintDiagnostic ;
11
- use rustc_middle:: ty:: TyCtxt ;
12
13
use rustc_middle:: ty:: adjustment:: Adjust ;
14
+ use rustc_middle:: ty:: { self , TyCtxt } ;
13
15
use rustc_session:: lint:: { FutureIncompatibilityReason , LintId } ;
14
16
use rustc_session:: { declare_lint, impl_lint_pass} ;
15
17
use rustc_span:: Span ;
@@ -161,7 +163,7 @@ impl IfLetRescope {
161
163
let lifetime_end = source_map. end_point ( conseq. span ) ;
162
164
163
165
if let ControlFlow :: Break ( significant_dropper) =
164
- ( FindSignificantDropper { cx } ) . check_if_let_scrutinee ( init)
166
+ ( FindSignificantDropper { cx } ) . check_if_let_scrutinee ( init, pat )
165
167
{
166
168
first_if_to_lint = first_if_to_lint. or_else ( || Some ( ( span, expr. hir_id ) ) ) ;
167
169
significant_droppers. push ( significant_dropper) ;
@@ -374,7 +376,52 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
374
376
/// of the scrutinee itself, and also recurses into the expression to find any ref
375
377
/// exprs (or autoref) which would promote temporaries that would be scoped to the
376
378
/// end of this `if`.
377
- fn check_if_let_scrutinee ( & mut self , init : & ' tcx hir:: Expr < ' tcx > ) -> ControlFlow < Span > {
379
+ fn check_if_let_scrutinee (
380
+ & mut self ,
381
+ init : & ' tcx hir:: Expr < ' tcx > ,
382
+ scrut_pat : & ' tcx hir:: Pat < ' tcx > ,
383
+ ) -> ControlFlow < Span > {
384
+ // Try first to match a variant constructor. If that variant constructor's
385
+ // subpatterns are infallible (which for now is just `x` and `_`), then
386
+ // check the fields of the ADT for drop *excluding* that variant, and walk
387
+ // that pattern excluding any of the peeled refs up to that point.
388
+ match & scrut_pat. peel_borrows ( ) . kind {
389
+ rustc_hir:: PatKind :: Struct ( qpath, pat_fields, _) => {
390
+ if let Res :: Def ( _, def_id) =
391
+ self . cx . typeck_results ( ) . qpath_res ( qpath, scrut_pat. hir_id )
392
+ && pat_fields. iter ( ) . all ( |pat| is_infallible ( pat. pat ) )
393
+ {
394
+ // Peel the borrows of the init expr because we want to drill down to
395
+ // the (possibly) temporary value. For example, given something like:
396
+ // `let Enum::Variant(_) = &&&temp()`, we want to drill down to the
397
+ // type of `temp()` so we can check it excluding the variant.
398
+ self . check_promoted_temp_with_drop_excluding_variant (
399
+ init. peel_borrows ( ) ,
400
+ def_id,
401
+ ) ?;
402
+ // Once we've already checked the adt, just visit the expression after
403
+ // all the refs. Otherwise we'll re-encounter the expression and count
404
+ // it as a bad temporary.
405
+ return self . visit_expr ( init. peel_borrows ( ) ) ;
406
+ }
407
+ }
408
+ rustc_hir:: PatKind :: TupleStruct ( qpath, pats, _) => {
409
+ if let Res :: Def ( _, def_id) =
410
+ self . cx . typeck_results ( ) . qpath_res ( qpath, scrut_pat. hir_id )
411
+ && let def_id = self . cx . tcx . parent ( def_id)
412
+ && pats. iter ( ) . all ( |pat| is_infallible ( pat) )
413
+ {
414
+ // See above for the justification here.
415
+ self . check_promoted_temp_with_drop_excluding_variant (
416
+ init. peel_borrows ( ) ,
417
+ def_id,
418
+ ) ?;
419
+ return self . visit_expr ( init. peel_borrows ( ) ) ;
420
+ }
421
+ }
422
+ _ => { }
423
+ }
424
+
378
425
self . check_promoted_temp_with_drop ( init) ?;
379
426
self . visit_expr ( init)
380
427
}
@@ -386,23 +433,55 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
386
433
/// or is the scrutinee of the `if let`, *and* the expression is not a place
387
434
/// expr, and it has a significant drop.
388
435
fn check_promoted_temp_with_drop ( & self , expr : & ' tcx hir:: Expr < ' tcx > ) -> ControlFlow < Span > {
389
- if !expr. is_place_expr ( |base| {
390
- self . cx
436
+ if !self . expr_is_place ( expr)
437
+ && self
438
+ . cx
391
439
. typeck_results ( )
392
- . adjustments ( )
393
- . get ( base. hir_id )
394
- . is_some_and ( |x| x. iter ( ) . any ( |adj| matches ! ( adj. kind, Adjust :: Deref ( _) ) ) )
395
- } ) && self
396
- . cx
397
- . typeck_results ( )
398
- . expr_ty ( expr)
399
- . has_significant_drop ( self . cx . tcx , self . cx . typing_env ( ) )
440
+ . expr_ty ( expr)
441
+ . has_significant_drop ( self . cx . tcx , self . cx . typing_env ( ) )
442
+ {
443
+ ControlFlow :: Break ( expr. span )
444
+ } else {
445
+ ControlFlow :: Continue ( ( ) )
446
+ }
447
+ }
448
+
449
+ /// Same as above, but ignoring the excluded variant def-id.
450
+ fn check_promoted_temp_with_drop_excluding_variant (
451
+ & self ,
452
+ expr : & ' tcx hir:: Expr < ' tcx > ,
453
+ excluded_variant : DefId ,
454
+ ) -> ControlFlow < Span > {
455
+ if !self . expr_is_place ( expr)
456
+ && let ty:: Adt ( def, args) = self . cx . typeck_results ( ) . expr_ty ( expr) . kind ( )
457
+ && def. variants ( ) . iter ( ) . any ( |variant| {
458
+ variant. def_id != excluded_variant
459
+ && variant. fields . iter ( ) . any ( |f| {
460
+ f. ty ( self . cx . tcx , args)
461
+ . has_significant_drop ( self . cx . tcx , self . cx . typing_env ( ) )
462
+ } )
463
+ } )
400
464
{
401
465
ControlFlow :: Break ( expr. span )
402
466
} else {
403
467
ControlFlow :: Continue ( ( ) )
404
468
}
405
469
}
470
+
471
+ fn expr_is_place ( & self , expr : & rustc_hir:: Expr < ' tcx > ) -> bool {
472
+ expr. is_place_expr ( |base| {
473
+ self . cx
474
+ . typeck_results ( )
475
+ . adjustments ( )
476
+ . get ( base. hir_id )
477
+ . is_some_and ( |x| x. iter ( ) . any ( |adj| matches ! ( adj. kind, Adjust :: Deref ( _) ) ) )
478
+ } )
479
+ }
480
+ }
481
+
482
+ // FIXME: This is pretty rudimentary.
483
+ fn is_infallible < ' tcx > ( pat : & ' tcx hir:: Pat < ' tcx > ) -> bool {
484
+ matches ! ( pat. kind, hir:: PatKind :: Wild | hir:: PatKind :: Binding ( _, _, _, None ) )
406
485
}
407
486
408
487
impl < ' tcx > Visitor < ' tcx > for FindSignificantDropper < ' _ , ' tcx > {
0 commit comments