Skip to content

Commit

Permalink
change catagory to pedantic & add a test case & api adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
J-ZhengLi committed Jan 23, 2024
1 parent d337709 commit 58bc302
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 15 deletions.
57 changes: 44 additions & 13 deletions clippy_lints/src/unnecessary_blocking_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use clippy_utils::{def_path_def_ids, fn_def_id, is_lint_allowed};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::def_id::DefId;
use rustc_hir::{Body, CoroutineKind, Expr, ExprKind};
use rustc_hir::{
Body, BodyId, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, Expr, ExprKind, ImplItem, ImplItemKind,
Item, ItemKind, Node, TraitItem, TraitItemKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::Span;
Expand Down Expand Up @@ -40,23 +43,24 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.74.0"]
pub UNNECESSARY_BLOCKING_OPS,
nursery,
pedantic,
"blocking operations in an async context"
}

pub(crate) struct UnnecessaryBlockingOps {
blocking_ops: Vec<DisallowedPath>,
/// Map of resolved funtion def_id with suggestion string after checking crate
id_with_suggs: FxHashMap<DefId, Option<String>>,
is_in_async: bool,
/// Tracking whether a body is async after entering it.
body_asyncness: Vec<bool>,
}

impl UnnecessaryBlockingOps {
pub(crate) fn new(blocking_ops: Vec<DisallowedPath>) -> Self {
Self {
blocking_ops,
id_with_suggs: FxHashMap::default(),
is_in_async: false,
body_asyncness: vec![],
}
}
}
Expand Down Expand Up @@ -108,14 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryBlockingOps {
if is_lint_allowed(cx, UNNECESSARY_BLOCKING_OPS, body.value.hir_id) {
return;
}

if let Some(CoroutineKind::Async(_)) = body.coroutine_kind() {
self.is_in_async = true;
}
self.body_asyncness.push(in_async_body(cx, body.id()));
}

fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if self.is_in_async
if matches!(self.body_asyncness.last(), Some(true))
&& let ExprKind::Call(call, _) = &expr.kind
&& let Some(call_did) = fn_def_id(cx, expr)
&& let Some(maybe_sugg) = self.id_with_suggs.get(&call_did)
Expand All @@ -134,10 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryBlockingOps {
}
}

fn check_body_post(&mut self, _: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
if !matches!(body.coroutine_kind(), Some(CoroutineKind::Async(_))) {
self.is_in_async = false;
}
fn check_body_post(&mut self, _: &LateContext<'tcx>, _: &'tcx Body<'tcx>) {
self.body_asyncness.pop();
}
}

Expand All @@ -153,3 +152,35 @@ fn make_suggestion(diag: &mut Diagnostic, cx: &LateContext<'_>, expr: &Expr<'_>,
Applicability::Unspecified,
);
}

/// Check whether a body is from an async function/closure.
fn in_async_body(cx: &LateContext<'_>, body_id: BodyId) -> bool {
let Some(parent_node) = cx.tcx.hir().find_parent(body_id.hir_id) else {
return false;
};
match parent_node {
Node::Expr(expr) => matches!(
expr.kind,
ExprKind::Closure(Closure {
kind: ClosureKind::Coroutine(CoroutineKind::Desugared(
CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen,
_
)),
..
})
),
Node::Item(Item {
kind: ItemKind::Fn(fn_sig, ..),
..
})
| Node::ImplItem(ImplItem {
kind: ImplItemKind::Fn(fn_sig, _),
..
})
| Node::TraitItem(TraitItem {
kind: TraitItemKind::Fn(fn_sig, _),
..
}) => fn_sig.header.is_async(),
_ => false,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub async fn async_fn() {
//~^ ERROR: blocking function call detected in an async body
fs::create_dir("").unwrap();
//~^ ERROR: blocking function call detected in an async body
blocking_mod::sleep(Duration::from_secs(1));
//~^ ERROR: blocking function call detected in an async body
}

fn main() {}
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,11 @@ error: blocking function call detected in an async body
LL | fs::create_dir("").unwrap();
| ^^^^^^^^^^^^^^

error: aborting due to 6 previous errors
error: blocking function call detected in an async body
--> $DIR/unnecessary_blocking_ops.rs:33:5
|
LL | blocking_mod::sleep(Duration::from_secs(1));
| ^^^^^^^^^^^^^^^^^^^

error: aborting due to 7 previous errors

7 changes: 7 additions & 0 deletions tests/ui/unnecessary_blocking_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,11 @@ fn closures() {
};
}

fn thread_spawn() {
std::thread::spawn(|| sleep(Duration::from_secs(1)));
std::thread::spawn(async || {});
std::thread::spawn(async || sleep(Duration::from_secs(1)));
//~^ ERROR: blocking function call detected in an async body
}

fn main() {}
8 changes: 7 additions & 1 deletion tests/ui/unnecessary_blocking_ops.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,11 @@ error: blocking function call detected in an async body
LL | sleep(Duration::from_secs(1));
| ^^^^^

error: aborting due to 9 previous errors
error: blocking function call detected in an async body
--> $DIR/unnecessary_blocking_ops.rs:75:33
|
LL | std::thread::spawn(async || sleep(Duration::from_secs(1)));
| ^^^^^

error: aborting due to 10 previous errors

0 comments on commit 58bc302

Please sign in to comment.