@@ -6,10 +6,12 @@ use rustc_ast::Recovered;
66use rustc_errors:: {
77 Applicability , Diag , EmissionGuarantee , SubdiagMessageOp , Subdiagnostic , SuggestionStyle ,
88} ;
9+ use rustc_hir:: def:: Res ;
10+ use rustc_hir:: def_id:: DefId ;
911use rustc_hir:: { self as hir, HirIdSet } ;
1012use rustc_macros:: LintDiagnostic ;
11- use rustc_middle:: ty:: TyCtxt ;
1213use rustc_middle:: ty:: adjustment:: Adjust ;
14+ use rustc_middle:: ty:: { self , TyCtxt } ;
1315use rustc_session:: lint:: { FutureIncompatibilityReason , LintId } ;
1416use rustc_session:: { declare_lint, impl_lint_pass} ;
1517use rustc_span:: Span ;
@@ -161,7 +163,7 @@ impl IfLetRescope {
161163 let lifetime_end = source_map. end_point ( conseq. span ) ;
162164
163165 if let ControlFlow :: Break ( significant_dropper) =
164- ( FindSignificantDropper { cx } ) . check_if_let_scrutinee ( init)
166+ ( FindSignificantDropper { cx } ) . check_if_let_scrutinee ( init, pat )
165167 {
166168 first_if_to_lint = first_if_to_lint. or_else ( || Some ( ( span, expr. hir_id ) ) ) ;
167169 significant_droppers. push ( significant_dropper) ;
@@ -374,7 +376,52 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
374376 /// of the scrutinee itself, and also recurses into the expression to find any ref
375377 /// exprs (or autoref) which would promote temporaries that would be scoped to the
376378 /// 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+
378425 self . check_promoted_temp_with_drop ( init) ?;
379426 self . visit_expr ( init)
380427 }
@@ -386,23 +433,55 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
386433 /// or is the scrutinee of the `if let`, *and* the expression is not a place
387434 /// expr, and it has a significant drop.
388435 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
391439 . 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+ } )
400464 {
401465 ControlFlow :: Break ( expr. span )
402466 } else {
403467 ControlFlow :: Continue ( ( ) )
404468 }
405469 }
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 ) )
406485}
407486
408487impl < ' tcx > Visitor < ' tcx > for FindSignificantDropper < ' _ , ' tcx > {
0 commit comments