@@ -5,7 +5,7 @@ use crate::{
55use rustc_errors:: MultiSpan ;
66use rustc_hir as hir;
77use rustc_middle:: ty;
8- use rustc_span:: Symbol ;
8+ use rustc_span:: { sym , Symbol } ;
99
1010declare_lint ! {
1111 /// The `let_underscore_drop` lint checks for statements which don't bind
@@ -105,46 +105,65 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
105105
106106impl < ' tcx > LateLintPass < ' tcx > for LetUnderscore {
107107 fn check_local ( & mut self , cx : & LateContext < ' _ > , local : & hir:: Local < ' _ > ) {
108- if !matches ! ( local. pat. kind, hir:: PatKind :: Wild ) {
109- return ;
110- }
111- if let Some ( init) = local. init {
112- let init_ty = cx. typeck_results ( ) . expr_ty ( init) ;
108+ let mut top_level = true ;
109+
110+ // We recursively walk through all patterns, so that we can catch cases where the lock is nested in a pattern.
111+ // For the basic `let_underscore_drop` lint, we only look at the top level, since there are many legitimate reasons
112+ // to bind a sub-pattern to an `_`, if we're only interested in the rest.
113+ // But with locks, we prefer having the chance of "false positives" over missing cases, since the effects can be
114+ // quite catastrophic.
115+ local. pat . walk_always ( |pat| {
116+ let is_top_level = top_level;
117+ top_level = false ;
118+
119+ if !matches ! ( pat. kind, hir:: PatKind :: Wild ) {
120+ return ;
121+ }
122+
123+ let ty = cx. typeck_results ( ) . pat_ty ( pat) ;
124+
113125 // If the type has a trivial Drop implementation, then it doesn't
114126 // matter that we drop the value immediately.
115- if !init_ty . needs_drop ( cx. tcx , cx. param_env ) {
127+ if !ty . needs_drop ( cx. tcx , cx. param_env ) {
116128 return ;
117129 }
118- let is_sync_lock = match init_ty. kind ( ) {
130+ // Lint for patterns like `mutex.lock()`, which returns `Result<MutexGuard, _>` as well.
131+ let potential_lock_type = match ty. kind ( ) {
132+ ty:: Adt ( adt, args) if cx. tcx . is_diagnostic_item ( sym:: Result , adt. did ( ) ) => {
133+ args. type_at ( 0 )
134+ }
135+ _ => ty,
136+ } ;
137+ let is_sync_lock = match potential_lock_type. kind ( ) {
119138 ty:: Adt ( adt, _) => SYNC_GUARD_SYMBOLS
120139 . iter ( )
121140 . any ( |guard_symbol| cx. tcx . is_diagnostic_item ( * guard_symbol, adt. did ( ) ) ) ,
122141 _ => false ,
123142 } ;
124143
144+ let can_use_init = is_top_level. then_some ( local. init ) . flatten ( ) ;
145+
125146 let sub = NonBindingLetSub {
126- suggestion : local. pat . span ,
127- multi_suggestion_start : local. span . until ( init. span ) ,
128- multi_suggestion_end : init. span . shrink_to_hi ( ) ,
147+ suggestion : pat. span ,
148+ // We can't suggest `drop()` when we're on the top level.
149+ drop_fn_start_end : can_use_init
150+ . map ( |init| ( local. span . until ( init. span ) , init. span . shrink_to_hi ( ) ) ) ,
129151 } ;
130152 if is_sync_lock {
131- let mut span = MultiSpan :: from_spans ( vec ! [ local . pat. span, init . span ] ) ;
153+ let mut span = MultiSpan :: from_span ( pat. span ) ;
132154 span. push_span_label (
133- local . pat . span ,
155+ pat. span ,
134156 "this lock is not assigned to a binding and is immediately dropped" . to_string ( ) ,
135157 ) ;
136- span. push_span_label (
137- init. span ,
138- "this binding will immediately drop the value assigned to it" . to_string ( ) ,
139- ) ;
140158 cx. emit_spanned_lint ( LET_UNDERSCORE_LOCK , span, NonBindingLet :: SyncLock { sub } ) ;
141- } else {
159+ // Only emit let_underscore_drop for top-level `_` patterns.
160+ } else if can_use_init. is_some ( ) {
142161 cx. emit_spanned_lint (
143162 LET_UNDERSCORE_DROP ,
144163 local. span ,
145164 NonBindingLet :: DropType { sub } ,
146165 ) ;
147166 }
148- }
167+ } ) ;
149168 }
150169}
0 commit comments