1- use clippy_utils:: diagnostics:: span_lint_and_help;
2- use rustc_hir:: intravisit:: { walk_expr, walk_fn, FnKind , Visitor } ;
1+ use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_then } ;
2+ use rustc_hir:: intravisit:: { walk_body , walk_expr, walk_fn, FnKind , Visitor } ;
33use rustc_hir:: { Body , Expr , ExprKind , FnDecl , YieldSource } ;
44use rustc_lint:: { LateContext , LateLintPass } ;
55use rustc_middle:: hir:: nested_filter;
@@ -42,21 +42,43 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
4242struct AsyncFnVisitor < ' a , ' tcx > {
4343 cx : & ' a LateContext < ' tcx > ,
4444 found_await : bool ,
45+ /// Also keep track of `await`s in nested async blocks so we can mention
46+ /// it in a note
47+ found_await_in_async_block : bool ,
48+ async_depth : usize ,
4549}
4650
4751impl < ' a , ' tcx > Visitor < ' tcx > for AsyncFnVisitor < ' a , ' tcx > {
4852 type NestedFilter = nested_filter:: OnlyBodies ;
4953
5054 fn visit_expr ( & mut self , ex : & ' tcx Expr < ' tcx > ) {
5155 if let ExprKind :: Yield ( _, YieldSource :: Await { .. } ) = ex. kind {
52- self . found_await = true ;
56+ if self . async_depth == 1 {
57+ self . found_await = true ;
58+ } else {
59+ self . found_await_in_async_block = true ;
60+ }
5361 }
5462 walk_expr ( self , ex) ;
5563 }
5664
5765 fn nested_visit_map ( & mut self ) -> Self :: Map {
5866 self . cx . tcx . hir ( )
5967 }
68+
69+ fn visit_body ( & mut self , b : & ' tcx Body < ' tcx > ) {
70+ let is_async_block = matches ! ( b. generator_kind, Some ( rustc_hir:: GeneratorKind :: Async ( _) ) ) ;
71+
72+ if is_async_block {
73+ self . async_depth += 1 ;
74+ }
75+
76+ walk_body ( self , b) ;
77+
78+ if is_async_block {
79+ self . async_depth -= 1 ;
80+ }
81+ }
6082}
6183
6284impl < ' tcx > LateLintPass < ' tcx > for UnusedAsync {
@@ -70,9 +92,27 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
7092 def_id : LocalDefId ,
7193 ) {
7294 if !span. from_expansion ( ) && fn_kind. asyncness ( ) . is_async ( ) {
73- let mut visitor = AsyncFnVisitor { cx, found_await : false } ;
95+ let mut visitor = AsyncFnVisitor {
96+ cx,
97+ found_await : false ,
98+ async_depth : 0 ,
99+ found_await_in_async_block : false ,
100+ } ;
74101 walk_fn ( & mut visitor, fn_kind, fn_decl, body. id ( ) , def_id) ;
75102 if !visitor. found_await {
103+ span_lint_and_then (
104+ cx,
105+ UNUSED_ASYNC ,
106+ span,
107+ "unused `async` for function with no await statements" ,
108+ |diag| {
109+ diag. help ( "consider removing the `async` from this function" ) ;
110+
111+ if visitor. found_await_in_async_block {
112+ diag. note ( "`await` used in an async block, which does not require the enclosing function to be `async`" ) ;
113+ }
114+ } ,
115+ ) ;
76116 span_lint_and_help (
77117 cx,
78118 UNUSED_ASYNC ,
0 commit comments