1- use clippy_utils:: diagnostics:: span_lint_and_note ;
1+ use clippy_utils:: diagnostics:: span_lint_and_then ;
22use clippy_utils:: { match_def_path, paths} ;
33use rustc_hir:: def_id:: DefId ;
44use rustc_hir:: { AsyncGeneratorKind , Body , BodyId , GeneratorKind } ;
@@ -9,8 +9,7 @@ use rustc_span::Span;
99
1010declare_clippy_lint ! {
1111 /// ### 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.
1413 ///
1514 /// ### Why is this bad?
1615 /// The Mutex types found in std::sync and parking_lot
@@ -22,77 +21,110 @@ declare_clippy_lint! {
2221 /// either by introducing a scope or an explicit call to Drop::drop.
2322 ///
2423 /// ### 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.
2627 ///
2728 /// ### Example
28- /// ```rust,ignore
29- /// use std::sync::Mutex;
30- ///
29+ /// ```rust
30+ /// # use std::sync::Mutex;
31+ /// # async fn baz() {}
3132 /// async fn foo(x: &Mutex<u32>) {
32- /// let guard = x.lock().unwrap();
33+ /// let mut guard = x.lock().unwrap();
3334 /// *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;
3543 /// }
3644 /// ```
3745 ///
3846 /// Use instead:
39- /// ```rust,ignore
40- /// use std::sync::Mutex;
41- ///
47+ /// ```rust
48+ /// # use std::sync::Mutex;
49+ /// # async fn baz() {}
4250 /// async fn foo(x: &Mutex<u32>) {
4351 /// {
44- /// let guard = x.lock().unwrap();
52+ /// let mut guard = x.lock().unwrap();
4553 /// *guard += 1;
4654 /// }
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;
4864 /// }
4965 /// ```
5066 #[ clippy:: version = "1.45.0" ]
5167 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` "
5470}
5571
5672declare_clippy_lint ! {
5773 /// ### 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`.
6075 ///
6176 /// ### Why is this bad?
6277 /// `RefCell` refs only check for exclusive mutable access
6378 /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
6479 /// risks panics from a mutable ref shared while other refs are outstanding.
6580 ///
6681 /// ### 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.
6885 ///
6986 /// ### Example
70- /// ```rust,ignore
71- /// use std::cell::RefCell;
72- ///
87+ /// ```rust
88+ /// # use std::cell::RefCell;
89+ /// # async fn baz() {}
7390 /// async fn foo(x: &RefCell<u32>) {
7491 /// let mut y = x.borrow_mut();
7592 /// *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;
77101 /// }
78102 /// ```
79103 ///
80104 /// Use instead:
81- /// ```rust,ignore
82- /// use std::cell::RefCell;
83- ///
105+ /// ```rust
106+ /// # use std::cell::RefCell;
107+ /// # async fn baz() {}
84108 /// async fn foo(x: &RefCell<u32>) {
85109 /// {
86110 /// let mut y = x.borrow_mut();
87111 /// *y += 1;
88112 /// }
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;
90122 /// }
91123 /// ```
92124 #[ clippy:: version = "1.49.0" ]
93125 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` "
96128}
97129
98130declare_lint_pass ! ( AwaitHolding => [ AWAIT_HOLDING_LOCK , AWAIT_HOLDING_REFCELL_REF ] ) ;
@@ -118,23 +150,36 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType
118150 for ty_cause in ty_causes {
119151 if let rustc_middle:: ty:: Adt ( adt, _) = ty_cause. ty . kind ( ) {
120152 if is_mutex_guard ( cx, adt. did ) {
121- span_lint_and_note (
153+ span_lint_and_then (
122154 cx,
123155 AWAIT_HOLDING_LOCK ,
124156 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+ } ,
128168 ) ;
129169 }
130170 if is_refcell_ref ( cx, adt. did ) {
131- span_lint_and_note (
171+ span_lint_and_then (
132172 cx,
133173 AWAIT_HOLDING_REFCELL_REF ,
134174 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+ } ,
138183 ) ;
139184 }
140185 }
0 commit comments