1
1
use crate :: { LateContext , LateLintPass , LintContext } ;
2
2
use rustc_hir as hir;
3
+ use rustc_middle:: ty:: { self , subst:: GenericArgKind } ;
4
+ use rustc_span:: Symbol ;
3
5
4
6
declare_lint ! {
5
7
/// The `let_underscore_drop` lint checks for statements which don't bind
@@ -43,7 +45,53 @@ declare_lint! {
43
45
"non-binding let on a type that implements `Drop`"
44
46
}
45
47
46
- declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_DROP ] ) ;
48
+ declare_lint ! {
49
+ /// The `let_underscore_lock` lint checks for statements which don't bind
50
+ /// a mutex to anything, causing the lock to be released immediately instead
51
+ /// of at end of scope, which is typically incorrect.
52
+ ///
53
+ /// ### Example
54
+ /// ```rust
55
+ /// use std::sync::{Arc, Mutex};
56
+ /// use std::thread;
57
+ /// let data = Arc::new(Mutex::new(0));
58
+ ///
59
+ /// thread::spawn(move || {
60
+ /// // The lock is immediately released instead of at the end of the
61
+ /// // scope, which is probably not intended.
62
+ /// let _ = data.lock().unwrap();
63
+ /// println!("doing some work");
64
+ /// let mut lock = data.lock().unwrap();
65
+ /// *lock += 1;
66
+ /// });
67
+ /// ```
68
+ /// ### Explanation
69
+ ///
70
+ /// Statements which assign an expression to an underscore causes the
71
+ /// expression to immediately drop instead of extending the expression's
72
+ /// lifetime to the end of the scope. This is usually unintended,
73
+ /// especially for types like `MutexGuard`, which are typically used to
74
+ /// lock a mutex for the duration of an entire scope.
75
+ ///
76
+ /// If you want to extend the expression's lifetime to the end of the scope,
77
+ /// assign an underscore-prefixed name (such as `_foo`) to the expression.
78
+ /// If you do actually want to drop the expression immediately, then
79
+ /// calling `std::mem::drop` on the expression is clearer and helps convey
80
+ /// intent.
81
+ pub LET_UNDERSCORE_LOCK ,
82
+ Warn ,
83
+ "non-binding let on a synchronization lock"
84
+ }
85
+
86
+ declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_DROP , LET_UNDERSCORE_LOCK ] ) ;
87
+
88
+ const SYNC_GUARD_PATHS : [ & [ & str ] ; 5 ] = [
89
+ & [ "std" , "sync" , "mutex" , "MutexGuard" ] ,
90
+ & [ "std" , "sync" , "rwlock" , "RwLockReadGuard" ] ,
91
+ & [ "std" , "sync" , "rwlock" , "RwLockWriteGuard" ] ,
92
+ & [ "parking_lot" , "raw_mutex" , "RawMutex" ] ,
93
+ & [ "parking_lot" , "raw_rwlock" , "RawRwLock" ] ,
94
+ ] ;
47
95
48
96
impl < ' tcx > LateLintPass < ' tcx > for LetUnderscore {
49
97
fn check_local ( & mut self , cx : & LateContext < ' _ > , local : & hir:: Local < ' _ > ) {
@@ -53,7 +101,27 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
53
101
if let Some ( init) = local. init {
54
102
let init_ty = cx. typeck_results ( ) . expr_ty ( init) ;
55
103
let needs_drop = init_ty. needs_drop ( cx. tcx , cx. param_env ) ;
56
- if needs_drop {
104
+ let is_sync_lock = init_ty. walk ( ) . any ( |inner| match inner. unpack ( ) {
105
+ GenericArgKind :: Type ( inner_ty) => {
106
+ SYNC_GUARD_PATHS . iter ( ) . any ( |guard_path| match inner_ty. kind ( ) {
107
+ ty:: Adt ( adt, _) => {
108
+ let ty_path = cx. get_def_path ( adt. did ( ) ) ;
109
+ guard_path. iter ( ) . map ( |x| Symbol :: intern ( x) ) . eq ( ty_path. iter ( ) . copied ( ) )
110
+ }
111
+ _ => false ,
112
+ } )
113
+ }
114
+
115
+ GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => false ,
116
+ } ) ;
117
+ if is_sync_lock {
118
+ cx. struct_span_lint ( LET_UNDERSCORE_LOCK , local. span , |lint| {
119
+ lint. build ( "non-binding let on a synchronization lock" )
120
+ . help ( "consider binding to an unused variable" )
121
+ . help ( "consider explicitly droping with `std::mem::drop`" )
122
+ . emit ( ) ;
123
+ } )
124
+ } else if needs_drop {
57
125
cx. struct_span_lint ( LET_UNDERSCORE_DROP , local. span , |lint| {
58
126
lint. build ( "non-binding let on a type that implements `Drop`" )
59
127
. help ( "consider binding to an unused variable" )
0 commit comments