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 } ;
3
3
use rustc_hir:: { Body , Expr , ExprKind , FnDecl , YieldSource } ;
4
4
use rustc_lint:: { LateContext , LateLintPass } ;
5
5
use rustc_middle:: hir:: nested_filter;
@@ -42,21 +42,43 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
42
42
struct AsyncFnVisitor < ' a , ' tcx > {
43
43
cx : & ' a LateContext < ' tcx > ,
44
44
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 ,
45
49
}
46
50
47
51
impl < ' a , ' tcx > Visitor < ' tcx > for AsyncFnVisitor < ' a , ' tcx > {
48
52
type NestedFilter = nested_filter:: OnlyBodies ;
49
53
50
54
fn visit_expr ( & mut self , ex : & ' tcx Expr < ' tcx > ) {
51
55
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
+ }
53
61
}
54
62
walk_expr ( self , ex) ;
55
63
}
56
64
57
65
fn nested_visit_map ( & mut self ) -> Self :: Map {
58
66
self . cx . tcx . hir ( )
59
67
}
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
+ }
60
82
}
61
83
62
84
impl < ' tcx > LateLintPass < ' tcx > for UnusedAsync {
@@ -70,9 +92,27 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
70
92
def_id : LocalDefId ,
71
93
) {
72
94
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
+ } ;
74
101
walk_fn ( & mut visitor, fn_kind, fn_decl, body. id ( ) , def_id) ;
75
102
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
+ ) ;
76
116
span_lint_and_help (
77
117
cx,
78
118
UNUSED_ASYNC ,
0 commit comments