about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/implicit_return.rs8
-rw-r--r--clippy_lints/src/redundant_async_block.rs26
-rw-r--r--clippy_utils/src/lib.rs49
-rw-r--r--tests/ui/implicit_return.fixed43
-rw-r--r--tests/ui/implicit_return.rs43
-rw-r--r--tests/ui/implicit_return.stderr90
6 files changed, 222 insertions, 37 deletions
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index ee141ddee15..076017a247b 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
 use clippy_utils::visitors::for_each_expr_without_closures;
-use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
+use clippy_utils::{desugar_await, get_async_closure_expr, get_async_fn_body, is_async_fn, is_from_proc_macro};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
@@ -134,6 +134,10 @@ fn lint_implicit_returns(
         },
 
         ExprKind::Match(_, arms, _) => {
+            if let Some(await_expr) = desugar_await(expr) {
+                lint_return(cx, await_expr.hir_id, await_expr.span);
+                return LintLocation::Inner;
+            }
             for arm in arms {
                 let res = lint_implicit_returns(
                     cx,
@@ -241,6 +245,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
                 Some(e) => e,
                 None => return,
             }
+        } else if let Some(expr) = get_async_closure_expr(cx.tcx, body.value) {
+            expr
         } else {
             body.value
         };
diff --git a/clippy_lints/src/redundant_async_block.rs b/clippy_lints/src/redundant_async_block.rs
index 8289ec47bc7..d2442ad0f37 100644
--- a/clippy_lints/src/redundant_async_block.rs
+++ b/clippy_lints/src/redundant_async_block.rs
@@ -1,14 +1,9 @@
-use std::ops::ControlFlow;
-
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::peel_blocks;
 use clippy_utils::source::{snippet, walk_span_to_context};
 use clippy_utils::ty::implements_trait;
-use clippy_utils::visitors::for_each_expr_without_closures;
+use clippy_utils::{desugar_await, peel_blocks};
 use rustc_errors::Applicability;
-use rustc_hir::{
-    Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, MatchSource,
-};
+use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::UpvarCapture;
 use rustc_session::declare_lint_pass;
@@ -99,20 +94,3 @@ fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op
         None
     }
 }
-
-/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
-/// macro expansion.
-fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
-        && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
-        && let ctxt = expr.span.ctxt()
-        && for_each_expr_without_closures(into_future_arg, |e| {
-            walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
-        })
-        .is_none()
-    {
-        Some(into_future_arg)
-    } else {
-        None
-    }
-}
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 76a9b8b9b98..bba4c3179e4 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -104,10 +104,10 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
 use rustc_hir::{
     self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
-    Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem,
-    ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
-    Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitFn, TraitItem,
-    TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
+    CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg,
+    GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource,
+    Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath,
+    Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
 };
 use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
@@ -124,6 +124,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{Ident, Symbol, kw};
 use rustc_span::{InnerSpan, Span, sym};
+use source::walk_span_to_context;
 use visitors::{Visitable, for_each_unconsumed_temporary};
 
 use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
@@ -2131,15 +2132,18 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool {
     }
 }
 
-/// Peels away all the compiler generated code surrounding the body of an async function,
-pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Closure(&Closure { body, .. }) = body.value.kind
+/// Peels away all the compiler generated code surrounding the body of an async closure.
+pub fn get_async_closure_expr<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Closure(&Closure {
+        body,
+        kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
+        ..
+    }) = expr.kind
         && let ExprKind::Block(
             Block {
-                stmts: [],
                 expr:
                     Some(Expr {
-                        kind: ExprKind::DropTemps(expr),
+                        kind: ExprKind::DropTemps(inner_expr),
                         ..
                     }),
                 ..
@@ -2147,9 +2151,15 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t
             _,
         ) = tcx.hir_body(body).value.kind
     {
-        return Some(expr);
+        Some(inner_expr)
+    } else {
+        None
     }
-    None
+}
+
+/// Peels away all the compiler generated code surrounding the body of an async function,
+pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
+    get_async_closure_expr(tcx, body.value)
 }
 
 // check if expr is calling method or function with #[must_use] attribute
@@ -3716,3 +3726,20 @@ pub fn peel_hir_ty_options<'tcx>(cx: &LateContext<'tcx>, mut hir_ty: &'tcx hir::
     }
     hir_ty
 }
+
+/// If `expr` is a desugared `.await`, return the original expression if it does not come from a
+/// macro expansion.
+pub fn desugar_await<'tcx>(expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if let ExprKind::Match(match_value, _, MatchSource::AwaitDesugar) = expr.kind
+        && let ExprKind::Call(_, [into_future_arg]) = match_value.kind
+        && let ctxt = expr.span.ctxt()
+        && for_each_expr_without_closures(into_future_arg, |e| {
+            walk_span_to_context(e.span, ctxt).map_or(ControlFlow::Break(()), |_| ControlFlow::Continue(()))
+        })
+        .is_none()
+    {
+        Some(into_future_arg)
+    } else {
+        None
+    }
+}
diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed
index 1cb639b60a9..728c6e015c1 100644
--- a/tests/ui/implicit_return.fixed
+++ b/tests/ui/implicit_return.fixed
@@ -165,3 +165,46 @@ with_span!(
         x
     }
 );
+
+fn desugared_closure_14446() {
+    let _ = async || return 0;
+    //~^ implicit_return
+    #[rustfmt::skip]
+    let _ = async || -> i32 { return 0 };
+    //~^ implicit_return
+    let _ = async |a: i32| return a;
+    //~^ implicit_return
+    #[rustfmt::skip]
+    let _ = async |a: i32| { return a };
+    //~^ implicit_return
+
+    let _ = async || return 0;
+    let _ = async || -> i32 { return 0 };
+    let _ = async |a: i32| return a;
+    #[rustfmt::skip]
+    let _ = async |a: i32| { return a; };
+
+    let _ = async || return foo().await;
+    //~^ implicit_return
+    let _ = async || {
+        foo().await;
+        return foo().await
+    };
+    //~^^ implicit_return
+    #[rustfmt::skip]
+    let _ = async || { return foo().await };
+    //~^ implicit_return
+    let _ = async || -> bool { return foo().await };
+    //~^ implicit_return
+
+    let _ = async || return foo().await;
+    let _ = async || {
+        foo().await;
+        return foo().await;
+    };
+    #[rustfmt::skip]
+    let _ = async || { return foo().await; };
+    let _ = async || -> bool {
+        return foo().await;
+    };
+}
diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs
index 99d75e4987e..3381fffb6e4 100644
--- a/tests/ui/implicit_return.rs
+++ b/tests/ui/implicit_return.rs
@@ -165,3 +165,46 @@ with_span!(
         x
     }
 );
+
+fn desugared_closure_14446() {
+    let _ = async || 0;
+    //~^ implicit_return
+    #[rustfmt::skip]
+    let _ = async || -> i32 { 0 };
+    //~^ implicit_return
+    let _ = async |a: i32| a;
+    //~^ implicit_return
+    #[rustfmt::skip]
+    let _ = async |a: i32| { a };
+    //~^ implicit_return
+
+    let _ = async || return 0;
+    let _ = async || -> i32 { return 0 };
+    let _ = async |a: i32| return a;
+    #[rustfmt::skip]
+    let _ = async |a: i32| { return a; };
+
+    let _ = async || foo().await;
+    //~^ implicit_return
+    let _ = async || {
+        foo().await;
+        foo().await
+    };
+    //~^^ implicit_return
+    #[rustfmt::skip]
+    let _ = async || { foo().await };
+    //~^ implicit_return
+    let _ = async || -> bool { foo().await };
+    //~^ implicit_return
+
+    let _ = async || return foo().await;
+    let _ = async || {
+        foo().await;
+        return foo().await;
+    };
+    #[rustfmt::skip]
+    let _ = async || { return foo().await; };
+    let _ = async || -> bool {
+        return foo().await;
+    };
+}
diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr
index 02044df47ac..05cd7f62583 100644
--- a/tests/ui/implicit_return.stderr
+++ b/tests/ui/implicit_return.stderr
@@ -183,5 +183,93 @@ help: add `return` as shown
 LL |     return true
    |     ++++++
 
-error: aborting due to 16 previous errors
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:170:22
+   |
+LL |     let _ = async || 0;
+   |                      ^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async || return 0;
+   |                      ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:173:31
+   |
+LL |     let _ = async || -> i32 { 0 };
+   |                               ^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async || -> i32 { return 0 };
+   |                               ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:175:28
+   |
+LL |     let _ = async |a: i32| a;
+   |                            ^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async |a: i32| return a;
+   |                            ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:178:30
+   |
+LL |     let _ = async |a: i32| { a };
+   |                              ^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async |a: i32| { return a };
+   |                              ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:187:22
+   |
+LL |     let _ = async || foo().await;
+   |                      ^^^^^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async || return foo().await;
+   |                      ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:191:9
+   |
+LL |         foo().await
+   |         ^^^^^
+   |
+help: add `return` as shown
+   |
+LL |         return foo().await
+   |         ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:195:24
+   |
+LL |     let _ = async || { foo().await };
+   |                        ^^^^^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async || { return foo().await };
+   |                        ++++++
+
+error: missing `return` statement
+  --> tests/ui/implicit_return.rs:197:32
+   |
+LL |     let _ = async || -> bool { foo().await };
+   |                                ^^^^^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = async || -> bool { return foo().await };
+   |                                ++++++
+
+error: aborting due to 24 previous errors