@@ -5,7 +5,10 @@ use clippy_utils::{def_path_def_ids, fn_def_id, is_lint_allowed};
55use rustc_data_structures:: fx:: FxHashMap ;
66use rustc_errors:: { Applicability , Diagnostic } ;
77use rustc_hir:: def_id:: DefId ;
8- use rustc_hir:: { Body , CoroutineKind , Expr , ExprKind } ;
8+ use rustc_hir:: {
9+ Body , BodyId , Closure , ClosureKind , CoroutineDesugaring , CoroutineKind , Expr , ExprKind , ImplItem , ImplItemKind ,
10+ Item , ItemKind , Node , TraitItem , TraitItemKind ,
11+ } ;
912use rustc_lint:: { LateContext , LateLintPass } ;
1013use rustc_session:: impl_lint_pass;
1114use rustc_span:: Span ;
@@ -40,23 +43,24 @@ declare_clippy_lint! {
4043 /// ```
4144 #[ clippy:: version = "1.74.0" ]
4245 pub UNNECESSARY_BLOCKING_OPS ,
43- nursery ,
46+ pedantic ,
4447 "blocking operations in an async context"
4548}
4649
4750pub ( crate ) struct UnnecessaryBlockingOps {
4851 blocking_ops : Vec < DisallowedPath > ,
4952 /// Map of resolved funtion def_id with suggestion string after checking crate
5053 id_with_suggs : FxHashMap < DefId , Option < String > > ,
51- is_in_async : bool ,
54+ /// Tracking whether a body is async after entering it.
55+ body_asyncness : Vec < bool > ,
5256}
5357
5458impl UnnecessaryBlockingOps {
5559 pub ( crate ) fn new ( blocking_ops : Vec < DisallowedPath > ) -> Self {
5660 Self {
5761 blocking_ops,
5862 id_with_suggs : FxHashMap :: default ( ) ,
59- is_in_async : false ,
63+ body_asyncness : vec ! [ ] ,
6064 }
6165 }
6266}
@@ -108,14 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryBlockingOps {
108112 if is_lint_allowed ( cx, UNNECESSARY_BLOCKING_OPS , body. value . hir_id ) {
109113 return ;
110114 }
111-
112- if let Some ( CoroutineKind :: Async ( _) ) = body. coroutine_kind ( ) {
113- self . is_in_async = true ;
114- }
115+ self . body_asyncness . push ( in_async_body ( cx, body. id ( ) ) ) ;
115116 }
116117
117118 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
118- if self . is_in_async
119+ if matches ! ( self . body_asyncness . last ( ) , Some ( true ) )
119120 && let ExprKind :: Call ( call, _) = & expr. kind
120121 && let Some ( call_did) = fn_def_id ( cx, expr)
121122 && let Some ( maybe_sugg) = self . id_with_suggs . get ( & call_did)
@@ -134,10 +135,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryBlockingOps {
134135 }
135136 }
136137
137- fn check_body_post ( & mut self , _: & LateContext < ' tcx > , body : & ' tcx Body < ' tcx > ) {
138- if !matches ! ( body. coroutine_kind( ) , Some ( CoroutineKind :: Async ( _) ) ) {
139- self . is_in_async = false ;
140- }
138+ fn check_body_post ( & mut self , _: & LateContext < ' tcx > , _: & ' tcx Body < ' tcx > ) {
139+ self . body_asyncness . pop ( ) ;
141140 }
142141}
143142
@@ -153,3 +152,31 @@ fn make_suggestion(diag: &mut Diagnostic, cx: &LateContext<'_>, expr: &Expr<'_>,
153152 Applicability :: Unspecified ,
154153 ) ;
155154}
155+
156+ fn in_async_body ( cx : & LateContext < ' _ > , body_id : BodyId ) -> bool {
157+ let Some ( parent_node) = cx. tcx . hir ( ) . find_parent ( body_id. hir_id ) else {
158+ return false ;
159+ } ;
160+ match parent_node {
161+ Node :: Expr ( expr) => matches ! (
162+ expr. kind,
163+ ExprKind :: Closure ( Closure {
164+ kind: ClosureKind :: Coroutine ( CoroutineKind :: Desugared ( CoroutineDesugaring :: Async , _) ) ,
165+ ..
166+ } )
167+ ) ,
168+ Node :: Item ( Item {
169+ kind : ItemKind :: Fn ( fn_sig, ..) ,
170+ ..
171+ } )
172+ | Node :: ImplItem ( ImplItem {
173+ kind : ImplItemKind :: Fn ( fn_sig, _) ,
174+ ..
175+ } )
176+ | Node :: TraitItem ( TraitItem {
177+ kind : TraitItemKind :: Fn ( fn_sig, _) ,
178+ ..
179+ } ) => fn_sig. header . is_async ( ) ,
180+ _ => false ,
181+ }
182+ }
0 commit comments