2021-03-16 00:55:45 +00:00
use clippy_utils ::diagnostics ::span_lint_and_note ;
2021-03-16 16:06:34 +00:00
use clippy_utils ::{ match_def_path , paths } ;
2020-04-10 04:50:23 +00:00
use rustc_hir ::def_id ::DefId ;
2020-04-17 06:21:49 +00:00
use rustc_hir ::{ AsyncGeneratorKind , Body , BodyId , GeneratorKind } ;
2020-04-08 04:20:37 +00:00
use rustc_lint ::{ LateContext , LateLintPass } ;
2020-04-17 06:21:49 +00:00
use rustc_middle ::ty ::GeneratorInteriorTypeCause ;
2020-04-08 04:20:37 +00:00
use rustc_session ::{ declare_lint_pass , declare_tool_lint } ;
2020-04-10 04:50:23 +00:00
use rustc_span ::Span ;
2020-04-08 04:20:37 +00:00
declare_clippy_lint! {
2021-07-02 18:37:11 +00:00
/// ### What it does
/// Checks for calls to await while holding a
2020-04-17 06:21:49 +00:00
/// non-async-aware MutexGuard.
2020-04-08 04:20:37 +00:00
///
2021-07-02 18:37:11 +00:00
/// ### Why is this bad?
/// The Mutex types found in std::sync and parking_lot
2020-07-14 12:59:59 +00:00
/// are not designed to operate in an async context across await points.
2020-04-08 04:20:37 +00:00
///
2021-09-28 20:53:12 +00:00
/// There are two potential solutions. One is to use an async-aware Mutex
2020-04-17 06:21:49 +00:00
/// type. Many asynchronous foundation crates provide such a Mutex type. The
/// other solution is to ensure the mutex is unlocked before calling await,
/// either by introducing a scope or an explicit call to Drop::drop.
///
2021-07-02 18:37:11 +00:00
/// ### Known problems
/// Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
2020-04-08 04:20:37 +00:00
///
2021-07-02 18:37:11 +00:00
/// ### Example
2020-04-10 05:12:34 +00:00
/// ```rust,ignore
2020-04-08 04:20:37 +00:00
/// use std::sync::Mutex;
///
/// async fn foo(x: &Mutex<u32>) {
/// let guard = x.lock().unwrap();
/// *guard += 1;
/// bar.await;
/// }
/// ```
2020-04-17 06:21:49 +00:00
///
2020-04-08 04:20:37 +00:00
/// Use instead:
2020-04-10 05:12:34 +00:00
/// ```rust,ignore
2020-04-08 04:20:37 +00:00
/// use std::sync::Mutex;
///
/// async fn foo(x: &Mutex<u32>) {
/// {
/// let guard = x.lock().unwrap();
/// *guard += 1;
/// }
/// bar.await;
/// }
/// ```
Added `clippy::version` attribute to all normal lints
So, some context for this, well, more a story. I'm not used to scripting, I've never really scripted anything, even if it's a valuable skill. I just never really needed it. Now, `@flip1995` correctly suggested using a script for this in `rust-clippy#7813`...
And I decided to write a script using nushell because why not? This was a mistake... I spend way more time on this than I would like to admit. It has definitely been more than 4 hours. It shouldn't take that long, but me being new to scripting and nushell just wasn't a good mixture... Anyway, here is the script that creates another script which adds the versions. Fun...
Just execute this on the `gh-pages` branch and the resulting `replacer.sh` in `clippy_lints` and it should all work.
```nu
mv v0.0.212 rust-1.00.0;
mv beta rust-1.57.0;
mv master rust-1.58.0;
let paths = (open ./rust-1.58.0/lints.json | select id id_span | flatten | select id path);
let versions = (
ls | where name =~ "rust-" | select name | format {name}/lints.json |
each { open $it | select id | insert version $it | str substring "5,11" version} |
group-by id | rotate counter-clockwise id version |
update version {get version | first 1} | flatten | select id version);
$paths | each { |row|
let version = ($versions | where id == ($row.id) | format {version})
let idu = ($row.id | str upcase)
$"sed -i '0,/($idu),/{s/pub ($idu),/#[clippy::version = "($version)"]\n pub ($idu),/}' ($row.path)"
} | str collect ";" | str find-replace --all '1.00.0' 'pre 1.29.0' | save "replacer.sh";
```
And this still has some problems, but at this point I just want to be done -.-
2021-10-21 19:06:26 +00:00
#[ clippy::version = " 1.45.0 " ]
2020-04-08 04:20:37 +00:00
pub AWAIT_HOLDING_LOCK ,
2020-11-23 12:51:04 +00:00
pedantic ,
2020-04-08 04:20:37 +00:00
" Inside an async function, holding a MutexGuard while calling await "
}
2020-10-28 22:36:07 +00:00
declare_clippy_lint! {
2021-07-02 18:37:11 +00:00
/// ### What it does
/// Checks for calls to await while holding a
2020-10-28 22:36:07 +00:00
/// `RefCell` `Ref` or `RefMut`.
///
2021-07-02 18:37:11 +00:00
/// ### Why is this bad?
/// `RefCell` refs only check for exclusive mutable access
2020-10-28 22:36:07 +00:00
/// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
/// risks panics from a mutable ref shared while other refs are outstanding.
///
2021-07-02 18:37:11 +00:00
/// ### Known problems
/// Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
2020-10-28 22:36:07 +00:00
///
2021-07-02 18:37:11 +00:00
/// ### Example
2020-10-28 22:36:07 +00:00
/// ```rust,ignore
/// use std::cell::RefCell;
///
/// async fn foo(x: &RefCell<u32>) {
2020-11-23 12:51:04 +00:00
/// let mut y = x.borrow_mut();
/// *y += 1;
2020-10-28 22:36:07 +00:00
/// bar.await;
/// }
/// ```
///
/// Use instead:
/// ```rust,ignore
/// use std::cell::RefCell;
///
/// async fn foo(x: &RefCell<u32>) {
/// {
2020-11-23 12:51:04 +00:00
/// let mut y = x.borrow_mut();
/// *y += 1;
2020-10-28 22:36:07 +00:00
/// }
/// bar.await;
/// }
/// ```
Added `clippy::version` attribute to all normal lints
So, some context for this, well, more a story. I'm not used to scripting, I've never really scripted anything, even if it's a valuable skill. I just never really needed it. Now, `@flip1995` correctly suggested using a script for this in `rust-clippy#7813`...
And I decided to write a script using nushell because why not? This was a mistake... I spend way more time on this than I would like to admit. It has definitely been more than 4 hours. It shouldn't take that long, but me being new to scripting and nushell just wasn't a good mixture... Anyway, here is the script that creates another script which adds the versions. Fun...
Just execute this on the `gh-pages` branch and the resulting `replacer.sh` in `clippy_lints` and it should all work.
```nu
mv v0.0.212 rust-1.00.0;
mv beta rust-1.57.0;
mv master rust-1.58.0;
let paths = (open ./rust-1.58.0/lints.json | select id id_span | flatten | select id path);
let versions = (
ls | where name =~ "rust-" | select name | format {name}/lints.json |
each { open $it | select id | insert version $it | str substring "5,11" version} |
group-by id | rotate counter-clockwise id version |
update version {get version | first 1} | flatten | select id version);
$paths | each { |row|
let version = ($versions | where id == ($row.id) | format {version})
let idu = ($row.id | str upcase)
$"sed -i '0,/($idu),/{s/pub ($idu),/#[clippy::version = "($version)"]\n pub ($idu),/}' ($row.path)"
} | str collect ";" | str find-replace --all '1.00.0' 'pre 1.29.0' | save "replacer.sh";
```
And this still has some problems, but at this point I just want to be done -.-
2021-10-21 19:06:26 +00:00
#[ clippy::version = " 1.49.0 " ]
2020-10-28 22:36:07 +00:00
pub AWAIT_HOLDING_REFCELL_REF ,
2020-11-23 12:51:04 +00:00
pedantic ,
2020-10-28 22:36:07 +00:00
" Inside an async function, holding a RefCell ref while calling await "
}
2020-04-08 04:20:37 +00:00
2020-10-28 22:36:07 +00:00
declare_lint_pass! ( AwaitHolding = > [ AWAIT_HOLDING_LOCK , AWAIT_HOLDING_REFCELL_REF ] ) ;
impl LateLintPass < '_ > for AwaitHolding {
2020-06-25 20:41:36 +00:00
fn check_body ( & mut self , cx : & LateContext < '_ > , body : & '_ Body < '_ > ) {
2020-04-17 06:21:49 +00:00
use AsyncGeneratorKind ::{ Block , Closure , Fn } ;
2020-06-09 14:36:01 +00:00
if let Some ( GeneratorKind ::Async ( Block | Closure | Fn ) ) = body . generator_kind {
let body_id = BodyId {
hir_id : body . value . hir_id ,
} ;
2021-03-05 16:35:39 +00:00
let typeck_results = cx . tcx . typeck_body ( body_id ) ;
2020-12-20 15:25:54 +00:00
check_interior_types (
cx ,
2021-04-02 21:35:32 +00:00
typeck_results . generator_interior_types . as_ref ( ) . skip_binder ( ) ,
2020-12-20 15:25:54 +00:00
body . value . span ,
) ;
2020-04-08 04:20:37 +00:00
}
2020-04-17 06:21:49 +00:00
}
}
2020-04-08 04:20:37 +00:00
2020-06-25 20:41:36 +00:00
fn check_interior_types ( cx : & LateContext < '_ > , ty_causes : & [ GeneratorInteriorTypeCause < '_ > ] , span : Span ) {
2020-04-17 06:21:49 +00:00
for ty_cause in ty_causes {
2020-08-03 22:18:29 +00:00
if let rustc_middle ::ty ::Adt ( adt , _ ) = ty_cause . ty . kind ( ) {
2020-04-17 06:21:49 +00:00
if is_mutex_guard ( cx , adt . did ) {
span_lint_and_note (
cx ,
AWAIT_HOLDING_LOCK ,
ty_cause . span ,
2021-02-24 13:02:51 +00:00
" this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await " ,
2020-04-22 04:28:23 +00:00
ty_cause . scope_span . or ( Some ( span ) ) ,
2020-04-17 06:21:49 +00:00
" these are all the await points this lock is held through " ,
) ;
2020-04-08 04:20:37 +00:00
}
2020-10-28 22:36:07 +00:00
if is_refcell_ref ( cx , adt . did ) {
span_lint_and_note (
2021-03-01 17:53:33 +00:00
cx ,
AWAIT_HOLDING_REFCELL_REF ,
ty_cause . span ,
" this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await " ,
ty_cause . scope_span . or ( Some ( span ) ) ,
" these are all the await points this ref is held through " ,
) ;
2020-10-28 22:36:07 +00:00
}
2020-04-08 04:20:37 +00:00
}
}
}
2020-06-25 20:41:36 +00:00
fn is_mutex_guard ( cx : & LateContext < '_ > , def_id : DefId ) -> bool {
2020-04-10 04:50:23 +00:00
match_def_path ( cx , def_id , & paths ::MUTEX_GUARD )
| | match_def_path ( cx , def_id , & paths ::RWLOCK_READ_GUARD )
| | match_def_path ( cx , def_id , & paths ::RWLOCK_WRITE_GUARD )
| | match_def_path ( cx , def_id , & paths ::PARKING_LOT_MUTEX_GUARD )
| | match_def_path ( cx , def_id , & paths ::PARKING_LOT_RWLOCK_READ_GUARD )
| | match_def_path ( cx , def_id , & paths ::PARKING_LOT_RWLOCK_WRITE_GUARD )
2020-04-08 04:20:37 +00:00
}
2020-10-28 22:36:07 +00:00
fn is_refcell_ref ( cx : & LateContext < '_ > , def_id : DefId ) -> bool {
match_def_path ( cx , def_id , & paths ::REFCELL_REF ) | | match_def_path ( cx , def_id , & paths ::REFCELL_REFMUT )
}