11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2- use clippy_utils:: source:: { snippet, walk_span_to_context } ;
2+ use clippy_utils:: source:: snippet;
33use clippy_utils:: ty:: implements_trait;
4- use clippy_utils:: { desugar_await, peel_blocks} ;
4+ use clippy_utils:: { desugar_await, desugared_async_block , peel_blocks} ;
55use rustc_errors:: Applicability ;
6- use rustc_hir:: { Closure , ClosureKind , CoroutineDesugaring , CoroutineKind , CoroutineSource , Expr , ExprKind } ;
6+ use rustc_hir:: Expr ;
77use rustc_lint:: { LateContext , LateLintPass } ;
88use rustc_middle:: ty:: UpvarCapture ;
99use rustc_session:: declare_lint_pass;
10+ use rustc_span:: { ExpnKind , Span } ;
1011
1112declare_clippy_lint ! {
1213 /// ### What it does
@@ -41,26 +42,24 @@ declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
4142
4243impl < ' tcx > LateLintPass < ' tcx > for RedundantAsyncBlock {
4344 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
44- let span = expr. span ;
45- if !span. in_external_macro ( cx. tcx . sess . source_map ( ) ) &&
45+ if !expr. span . in_external_macro ( cx. tcx . sess . source_map ( ) ) &&
4646 let Some ( body_expr) = desugar_async_block ( cx, expr) &&
47- let Some ( expr ) = desugar_await ( peel_blocks ( body_expr) ) &&
47+ let Some ( inner_expr ) = desugar_await ( peel_blocks ( body_expr) ) &&
4848 // The await prefix must not come from a macro as its content could change in the future.
49- expr . span . eq_ctxt ( body_expr. span ) &&
49+ ! is_from_macro_within ( inner_expr . span , body_expr. span ) &&
5050 // The await prefix must implement Future, as implementing IntoFuture is not enough.
5151 let Some ( future_trait) = cx. tcx . lang_items ( ) . future_trait ( ) &&
52- implements_trait ( cx, cx. typeck_results ( ) . expr_ty ( expr ) , future_trait, & [ ] ) &&
52+ implements_trait ( cx, cx. typeck_results ( ) . expr_ty ( inner_expr ) , future_trait, & [ ] ) &&
5353 // An async block does not have immediate side-effects from a `.await` point-of-view.
54- ( !expr. can_have_side_effects ( ) || desugar_async_block ( cx, expr) . is_some ( ) ) &&
55- let Some ( shortened_span) = walk_span_to_context ( expr. span , span. ctxt ( ) )
54+ ( !inner_expr. can_have_side_effects ( ) || desugar_async_block ( cx, inner_expr) . is_some ( ) )
5655 {
5756 span_lint_and_sugg (
5857 cx,
5958 REDUNDANT_ASYNC_BLOCK ,
60- span,
59+ expr . span ,
6160 "this async expression only awaits a single future" ,
6261 "you can reduce it to" ,
63- snippet ( cx, shortened_span , ".." ) . into_owned ( ) ,
62+ snippet ( cx, inner_expr . span , ".." ) . into_owned ( ) ,
6463 Applicability :: MachineApplicable ,
6564 ) ;
6665 }
@@ -70,28 +69,41 @@ impl<'tcx> LateLintPass<'tcx> for RedundantAsyncBlock {
7069/// If `expr` is a desugared `async` block, return the original expression if it does not capture
7170/// any variable by ref.
7271fn desugar_async_block < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
73- if let ExprKind :: Closure ( Closure { body, def_id, kind, .. } ) = expr. kind
74- && let body = cx. tcx . hir_body ( * body)
75- && matches ! (
76- kind,
77- ClosureKind :: Coroutine ( CoroutineKind :: Desugared (
78- CoroutineDesugaring :: Async ,
79- CoroutineSource :: Block
80- ) )
81- )
82- {
83- cx. typeck_results ( )
72+ let ( def_id, body) = desugared_async_block ( cx, expr) ?;
73+ if cx. typeck_results ( )
8474 . closure_min_captures
85- . get ( def_id)
75+ . get ( & def_id)
8676 . is_none_or ( |m| {
8777 m. values ( ) . all ( |places| {
8878 places
8979 . iter ( )
9080 . all ( |place| matches ! ( place. info. capture_kind, UpvarCapture :: ByValue ) )
9181 } )
9282 } )
93- . then_some ( body. value )
83+ {
84+ Some ( body. value )
9485 } else {
9586 None
9687 }
9788}
89+
90+ fn is_from_macro_within ( mut span : Span , outer_span : Span ) -> bool {
91+ let outer_ctxt = outer_span. ctxt ( ) ;
92+ loop {
93+ let ctxt = span. ctxt ( ) ;
94+ if ctxt. is_root ( ) || ctxt == outer_ctxt {
95+ break
96+ }
97+
98+ let expn_data = ctxt. outer_expn_data ( ) ;
99+ match expn_data. kind {
100+ ExpnKind :: Macro { .. } => return true ,
101+ ExpnKind :: Root
102+ | ExpnKind :: AstPass ( _)
103+ | ExpnKind :: Desugaring ( _) => { }
104+ }
105+ span = expn_data. call_site ;
106+ }
107+
108+ false
109+ }
0 commit comments