1
- use clippy_utils:: diagnostics:: span_lint_and_note ;
1
+ use clippy_utils:: diagnostics:: span_lint_and_then ;
2
2
use clippy_utils:: { match_def_path, paths} ;
3
3
use rustc_hir:: def_id:: DefId ;
4
4
use rustc_hir:: { AsyncGeneratorKind , Body , BodyId , GeneratorKind } ;
@@ -9,8 +9,7 @@ use rustc_span::Span;
9
9
10
10
declare_clippy_lint ! {
11
11
/// ### What it does
12
- /// Checks for calls to await while holding a
13
- /// non-async-aware MutexGuard.
12
+ /// Checks for calls to await while holding a non-async-aware MutexGuard.
14
13
///
15
14
/// ### Why is this bad?
16
15
/// The Mutex types found in std::sync and parking_lot
@@ -22,77 +21,110 @@ declare_clippy_lint! {
22
21
/// either by introducing a scope or an explicit call to Drop::drop.
23
22
///
24
23
/// ### Known problems
25
- /// Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
24
+ /// Will report false positive for explicitly dropped guards
25
+ /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
26
+ /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
26
27
///
27
28
/// ### Example
28
- /// ```rust,ignore
29
- /// use std::sync::Mutex;
30
- ///
29
+ /// ```rust
30
+ /// # use std::sync::Mutex;
31
+ /// # async fn baz() {}
31
32
/// async fn foo(x: &Mutex<u32>) {
32
- /// let guard = x.lock().unwrap();
33
+ /// let mut guard = x.lock().unwrap();
33
34
/// *guard += 1;
34
- /// bar.await;
35
+ /// baz().await;
36
+ /// }
37
+ ///
38
+ /// async fn bar(x: &Mutex<u32>) {
39
+ /// let mut guard = x.lock().unwrap();
40
+ /// *guard += 1;
41
+ /// drop(guard); // explicit drop
42
+ /// baz().await;
35
43
/// }
36
44
/// ```
37
45
///
38
46
/// Use instead:
39
- /// ```rust,ignore
40
- /// use std::sync::Mutex;
41
- ///
47
+ /// ```rust
48
+ /// # use std::sync::Mutex;
49
+ /// # async fn baz() {}
42
50
/// async fn foo(x: &Mutex<u32>) {
43
51
/// {
44
- /// let guard = x.lock().unwrap();
52
+ /// let mut guard = x.lock().unwrap();
45
53
/// *guard += 1;
46
54
/// }
47
- /// bar.await;
55
+ /// baz().await;
56
+ /// }
57
+ ///
58
+ /// async fn bar(x: &Mutex<u32>) {
59
+ /// {
60
+ /// let mut guard = x.lock().unwrap();
61
+ /// *guard += 1;
62
+ /// } // guard dropped here at end of scope
63
+ /// baz().await;
48
64
/// }
49
65
/// ```
50
66
#[ clippy:: version = "1.45.0" ]
51
67
pub AWAIT_HOLDING_LOCK ,
52
- pedantic ,
53
- "Inside an async function, holding a MutexGuard while calling await"
68
+ suspicious ,
69
+ "inside an async function, holding a ` MutexGuard` while calling ` await` "
54
70
}
55
71
56
72
declare_clippy_lint ! {
57
73
/// ### What it does
58
- /// Checks for calls to await while holding a
59
- /// `RefCell` `Ref` or `RefMut`.
74
+ /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
60
75
///
61
76
/// ### Why is this bad?
62
77
/// `RefCell` refs only check for exclusive mutable access
63
78
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
64
79
/// risks panics from a mutable ref shared while other refs are outstanding.
65
80
///
66
81
/// ### Known problems
67
- /// Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
82
+ /// Will report false positive for explicitly dropped refs
83
+ /// ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is
84
+ /// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
68
85
///
69
86
/// ### Example
70
- /// ```rust,ignore
71
- /// use std::cell::RefCell;
72
- ///
87
+ /// ```rust
88
+ /// # use std::cell::RefCell;
89
+ /// # async fn baz() {}
73
90
/// async fn foo(x: &RefCell<u32>) {
74
91
/// let mut y = x.borrow_mut();
75
92
/// *y += 1;
76
- /// bar.await;
93
+ /// baz().await;
94
+ /// }
95
+ ///
96
+ /// async fn bar(x: &RefCell<u32>) {
97
+ /// let mut y = x.borrow_mut();
98
+ /// *y += 1;
99
+ /// drop(y); // explicit drop
100
+ /// baz().await;
77
101
/// }
78
102
/// ```
79
103
///
80
104
/// Use instead:
81
- /// ```rust,ignore
82
- /// use std::cell::RefCell;
83
- ///
105
+ /// ```rust
106
+ /// # use std::cell::RefCell;
107
+ /// # async fn baz() {}
84
108
/// async fn foo(x: &RefCell<u32>) {
85
109
/// {
86
110
/// let mut y = x.borrow_mut();
87
111
/// *y += 1;
88
112
/// }
89
- /// bar.await;
113
+ /// baz().await;
114
+ /// }
115
+ ///
116
+ /// async fn bar(x: &RefCell<u32>) {
117
+ /// {
118
+ /// let mut y = x.borrow_mut();
119
+ /// *y += 1;
120
+ /// } // y dropped here at end of scope
121
+ /// baz().await;
90
122
/// }
91
123
/// ```
92
124
#[ clippy:: version = "1.49.0" ]
93
125
pub AWAIT_HOLDING_REFCELL_REF ,
94
- pedantic ,
95
- "Inside an async function, holding a RefCell ref while calling await"
126
+ suspicious ,
127
+ "inside an async function, holding a ` RefCell` ref while calling ` await` "
96
128
}
97
129
98
130
declare_lint_pass ! ( AwaitHolding => [ AWAIT_HOLDING_LOCK , AWAIT_HOLDING_REFCELL_REF ] ) ;
@@ -118,23 +150,36 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType
118
150
for ty_cause in ty_causes {
119
151
if let rustc_middle:: ty:: Adt ( adt, _) = ty_cause. ty . kind ( ) {
120
152
if is_mutex_guard ( cx, adt. did ) {
121
- span_lint_and_note (
153
+ span_lint_and_then (
122
154
cx,
123
155
AWAIT_HOLDING_LOCK ,
124
156
ty_cause. span ,
125
- "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await" ,
126
- ty_cause. scope_span . or ( Some ( span) ) ,
127
- "these are all the await points this lock is held through" ,
157
+ "this `MutexGuard` is held across an `await` point" ,
158
+ |diag| {
159
+ diag. help (
160
+ "consider using an async-aware `Mutex` type or ensuring the \
161
+ `MutexGuard` is dropped before calling await",
162
+ ) ;
163
+ diag. span_note (
164
+ ty_cause. scope_span . unwrap_or ( span) ,
165
+ "these are all the `await` points this lock is held through" ,
166
+ ) ;
167
+ } ,
128
168
) ;
129
169
}
130
170
if is_refcell_ref ( cx, adt. did ) {
131
- span_lint_and_note (
171
+ span_lint_and_then (
132
172
cx,
133
173
AWAIT_HOLDING_REFCELL_REF ,
134
174
ty_cause. span ,
135
- "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await" ,
136
- ty_cause. scope_span . or ( Some ( span) ) ,
137
- "these are all the await points this ref is held through" ,
175
+ "this `RefCell` reference is held across an `await` point" ,
176
+ |diag| {
177
+ diag. help ( "ensure the reference is dropped before calling `await`" ) ;
178
+ diag. span_note (
179
+ ty_cause. scope_span . unwrap_or ( span) ,
180
+ "these are all the `await` points this reference is held through" ,
181
+ ) ;
182
+ } ,
138
183
) ;
139
184
}
140
185
}
0 commit comments