From 087a571e708560bad84b1dd8de107c8fdc2a3044 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 14 Sep 2023 22:38:07 +0000
Subject: [PATCH 1/2] Record asyncness span in HIR

---
 compiler/rustc_ast_lowering/src/item.rs       |  2 +-
 .../src/diagnostics/region_name.rs            |  2 +-
 compiler/rustc_hir/src/hir.rs                 | 18 +++++------
 .../src/check/compare_impl_item.rs            |  2 +-
 .../rustc_hir_analysis/src/check/entry.rs     |  4 +--
 .../src/collect/resolve_bound_vars.rs         |  2 +-
 compiler/rustc_hir_pretty/src/lib.rs          |  2 +-
 .../src/fn_ctxt/suggestions.rs                |  4 +--
 compiler/rustc_lint/src/builtin.rs            |  3 +-
 compiler/rustc_metadata/src/rmeta/mod.rs      |  2 +-
 compiler/rustc_metadata/src/rmeta/table.rs    |  6 ++--
 compiler/rustc_middle/src/query/erase.rs      |  1 +
 compiler/rustc_middle/src/query/mod.rs        |  2 +-
 compiler/rustc_middle/src/ty/mod.rs           | 13 ++++++++
 compiler/rustc_middle/src/ty/parameterized.rs |  1 +
 .../error_reporting/on_unimplemented.rs       |  6 ++--
 compiler/rustc_ty_utils/src/ty.rs             |  7 ++--
 src/librustdoc/clean/types.rs                 |  8 +++--
 src/librustdoc/html/format.rs                 |  2 +-
 .../src/redundant_closure_call.rs             |  7 ++--
 src/tools/clippy/clippy_utils/src/lib.rs      |  6 ++--
 tests/ui/stats/hir-stats.stderr               | 32 +++++++++----------
 22 files changed, 78 insertions(+), 54 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index a59c83de0f4..edc1e2f0b84 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1308,7 +1308,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync {
         match a {
-            Async::Yes { .. } => hir::IsAsync::Async,
+            Async::Yes { span, .. } => hir::IsAsync::Async(span),
             Async::No => hir::IsAsync::NotAsync,
         }
     }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index b40e89e471d..096bf826cfb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -302,7 +302,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     if free_region.bound_region.is_named() {
                         // A named region that is actually named.
                         Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) })
-                    } else if let hir::IsAsync::Async = tcx.asyncness(self.mir_hir_id().owner) {
+                    } else if tcx.asyncness(self.mir_hir_id().owner).is_async() {
                         // If we spuriously thought that the region is named, we should let the
                         // system generate a true name for error messages. Currently this can
                         // happen if we have an elided name in an async fn for example: the
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 0c4a0e5d4a5..3eec66611ed 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2853,13 +2853,13 @@ impl ImplicitSelfKind {
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
 #[derive(HashStable_Generic)]
 pub enum IsAsync {
-    Async,
+    Async(Span),
     NotAsync,
 }
 
 impl IsAsync {
     pub fn is_async(self) -> bool {
-        self == IsAsync::Async
+        matches!(self, IsAsync::Async(_))
     }
 }
 
@@ -3296,7 +3296,7 @@ pub struct FnHeader {
 
 impl FnHeader {
     pub fn is_async(&self) -> bool {
-        matches!(&self.asyncness, IsAsync::Async)
+        matches!(&self.asyncness, IsAsync::Async(_))
     }
 
     pub fn is_const(&self) -> bool {
@@ -4091,10 +4091,10 @@ mod size_asserts {
     static_assert_size!(GenericBound<'_>, 48);
     static_assert_size!(Generics<'_>, 56);
     static_assert_size!(Impl<'_>, 80);
-    static_assert_size!(ImplItem<'_>, 80);
-    static_assert_size!(ImplItemKind<'_>, 32);
-    static_assert_size!(Item<'_>, 80);
-    static_assert_size!(ItemKind<'_>, 48);
+    static_assert_size!(ImplItem<'_>, 88);
+    static_assert_size!(ImplItemKind<'_>, 40);
+    static_assert_size!(Item<'_>, 88);
+    static_assert_size!(ItemKind<'_>, 56);
     static_assert_size!(Local<'_>, 64);
     static_assert_size!(Param<'_>, 32);
     static_assert_size!(Pat<'_>, 72);
@@ -4105,8 +4105,8 @@ mod size_asserts {
     static_assert_size!(Res, 12);
     static_assert_size!(Stmt<'_>, 32);
     static_assert_size!(StmtKind<'_>, 16);
-    static_assert_size!(TraitItem<'_>, 80);
-    static_assert_size!(TraitItemKind<'_>, 40);
+    static_assert_size!(TraitItem<'_>, 88);
+    static_assert_size!(TraitItemKind<'_>, 48);
     static_assert_size!(Ty<'_>, 48);
     static_assert_size!(TyKind<'_>, 32);
     // tidy-alphabetical-end
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 38f19aa09ad..7be18a36d63 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -595,7 +595,7 @@ fn compare_asyncness<'tcx>(
     trait_m: ty::AssocItem,
     delay: bool,
 ) -> Result<(), ErrorGuaranteed> {
-    if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async {
+    if tcx.asyncness(trait_m.def_id).is_async() {
         match tcx.fn_sig(impl_m.def_id).skip_binder().skip_binder().output().kind() {
             ty::Alias(ty::Opaque, ..) => {
                 // allow both `async fn foo()` and `fn foo() -> impl Future`
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index fcaefe0261b..cede7493093 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -112,7 +112,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
     }
 
     let main_asyncness = tcx.asyncness(main_def_id);
-    if let hir::IsAsync::Async = main_asyncness {
+    if main_asyncness.is_async() {
         let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
         tcx.sess.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span });
         error = true;
@@ -212,7 +212,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
                         });
                         error = true;
                     }
-                    if let hir::IsAsync::Async = sig.header.asyncness {
+                    if sig.header.asyncness.is_async() {
                         let span = tcx.def_span(it.owner_id);
                         tcx.sess.emit_err(errors::StartAsync { span: span });
                         error = true;
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 6869c869603..a39cfd7b6e1 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1213,7 +1213,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         && let Some(generics) = self.tcx.hir().get_generics(self.tcx.local_parent(param_id))
                         && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id)
                         && param.is_elided_lifetime()
-                        && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id)
+                        && !self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id).is_async()
                         && !self.tcx.features().anonymous_lifetime_in_impl_trait
                     {
                         let mut diag =  rustc_session::parse::feature_err(
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 89efdc269c4..8587b009f25 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -2304,7 +2304,7 @@ impl<'a> State<'a> {
 
         match header.asyncness {
             hir::IsAsync::NotAsync => {}
-            hir::IsAsync::Async => self.word_nbsp("async"),
+            hir::IsAsync::Async(_) => self.word_nbsp("async"),
         }
 
         self.print_unsafety(header.unsafety);
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index d2a53ee8b5e..41f815a812a 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -987,10 +987,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let bound_vars = self.tcx.late_bound_vars(fn_id);
             let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
             let ty = match self.tcx.asyncness(fn_id.owner) {
-                hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
+                ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
                     span_bug!(fn_decl.output.span(), "failed to get output type of async function")
                 }),
-                hir::IsAsync::NotAsync => ty,
+                ty::Asyncness::No => ty,
             };
             let ty = self.normalize(expr.span, ty);
             if self.can_coerce(found, ty) {
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index de228bdb850..d38c6b2642a 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -41,7 +41,6 @@ use crate::{
     },
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
 };
-use hir::IsAsync;
 use rustc_ast::attr;
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::visit::{FnCtxt, FnKind};
@@ -1294,7 +1293,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
         span: Span,
         def_id: LocalDefId,
     ) {
-        if fn_kind.asyncness() == IsAsync::Async
+        if fn_kind.asyncness().is_async()
             && !cx.tcx.features().async_fn_track_caller
             // Now, check if the function has the `#[track_caller]` attribute
             && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 71269779d42..f2875bb11b1 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -439,7 +439,7 @@ define_tables! {
     coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,
     mir_const_qualif: Table<DefIndex, LazyValue<mir::ConstQualifs>>,
     rendered_const: Table<DefIndex, LazyValue<String>>,
-    asyncness: Table<DefIndex, hir::IsAsync>,
+    asyncness: Table<DefIndex, ty::Asyncness>,
     fn_arg_names: Table<DefIndex, LazyArray<Ident>>,
     generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
     trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 35987072ed6..d105dab4814 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -205,9 +205,9 @@ fixed_size_enum! {
 }
 
 fixed_size_enum! {
-    hir::IsAsync {
-        ( NotAsync )
-        ( Async    )
+    ty::Asyncness {
+        ( Yes )
+        ( No  )
     }
 }
 
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index d41b38a8b4b..8ba3764bcc3 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -265,6 +265,7 @@ trivial! {
     rustc_middle::ty::adjustment::CoerceUnsizedInfo,
     rustc_middle::ty::AssocItem,
     rustc_middle::ty::AssocItemContainer,
+    rustc_middle::ty::Asyncness,
     rustc_middle::ty::BoundVariableKind,
     rustc_middle::ty::DeducedParamAttrs,
     rustc_middle::ty::Destructor,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 0241820ab72..07160e41c94 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -731,7 +731,7 @@ rustc_queries! {
         separate_provide_extern
     }
 
-    query asyncness(key: DefId) -> hir::IsAsync {
+    query asyncness(key: DefId) -> ty::Asyncness {
         desc { |tcx| "checking if the function is async: `{}`", tcx.def_path_str(key) }
         separate_provide_extern
     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index dbff13fdd8e..b050208b4a6 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -280,6 +280,19 @@ impl fmt::Display for ImplPolarity {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum Asyncness {
+    Yes,
+    No,
+}
+
+impl Asyncness {
+    pub fn is_async(self) -> bool {
+        matches!(self, Asyncness::Yes)
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)]
 pub enum Visibility<Id = LocalDefId> {
     /// Visible everywhere (including in other crates).
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index f1c38984296..f1093e88312 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -62,6 +62,7 @@ trivially_parameterized_over_tcx! {
     crate::middle::resolve_bound_vars::ObjectLifetimeDefault,
     crate::mir::ConstQualifs,
     ty::AssocItemContainer,
+    ty::Asyncness,
     ty::DeducedParamAttrs,
     ty::Generics,
     ty::ImplPolarity,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 4086db2ab55..d645dc033b8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -104,7 +104,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
                 self.describe_generator(*body_id).or_else(|| {
                     Some(match sig.header {
-                        hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function",
+                        hir::FnHeader { asyncness: hir::IsAsync::Async(_), .. } => {
+                            "an async function"
+                        }
                         _ => "a function",
                     })
                 })
@@ -118,7 +120,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 ..
             }) => self.describe_generator(*body_id).or_else(|| {
                 Some(match sig.header {
-                    hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
+                    hir::FnHeader { asyncness: hir::IsAsync::Async(_), .. } => "an async method",
                     _ => "a method",
                 })
             }),
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 2288d36df17..6c23589b39a 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -296,9 +296,12 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<EarlyBinder<Ty<'
 }
 
 /// Check if a function is async.
-fn asyncness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::IsAsync {
+fn asyncness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Asyncness {
     let node = tcx.hir().get_by_def_id(def_id);
-    node.fn_sig().map_or(hir::IsAsync::NotAsync, |sig| sig.header.asyncness)
+    node.fn_sig().map_or(ty::Asyncness::No, |sig| match sig.header.asyncness {
+        hir::IsAsync::Async(_) => ty::Asyncness::Yes,
+        hir::IsAsync::NotAsync => ty::Asyncness::No,
+    })
 }
 
 fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet<u32> {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index ef7794cc41e..b665f684167 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -31,7 +31,7 @@ use rustc_resolve::rustdoc::{
 use rustc_session::Session;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{self, FileName, Loc};
+use rustc_span::{self, FileName, Loc, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi::Abi;
 
@@ -622,7 +622,7 @@ impl Item {
         fn build_fn_header(
             def_id: DefId,
             tcx: TyCtxt<'_>,
-            asyncness: hir::IsAsync,
+            asyncness: ty::Asyncness,
         ) -> hir::FnHeader {
             let sig = tcx.fn_sig(def_id).skip_binder();
             let constness =
@@ -631,6 +631,10 @@ impl Item {
                 } else {
                     hir::Constness::NotConst
                 };
+            let asyncness = match asyncness {
+                ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP),
+                ty::Asyncness::No => hir::IsAsync::NotAsync,
+            };
             hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness }
         }
         let header = match *self.kind {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2f611c31a07..170e559e698 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1599,7 +1599,7 @@ impl PrintWithSpace for hir::Unsafety {
 impl PrintWithSpace for hir::IsAsync {
     fn print_with_space(&self) -> &str {
         match self {
-            hir::IsAsync::Async => "async ",
+            hir::IsAsync::Async(_) => "async ",
             hir::IsAsync::NotAsync => "",
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index fc49b58e0a7..f42836611ca 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -10,6 +10,7 @@ use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -84,7 +85,7 @@ fn find_innermost_closure<'tcx>(
     cx: &LateContext<'tcx>,
     mut expr: &'tcx hir::Expr<'tcx>,
     mut steps: usize,
-) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, hir::IsAsync)> {
+) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, ty::Asyncness)> {
     let mut data = None;
 
     while let hir::ExprKind::Closure(closure) = expr.kind
@@ -98,9 +99,9 @@ fn find_innermost_closure<'tcx>(
     {
         expr = body.value;
         data = Some((body.value, closure.fn_decl, if is_async_closure(body) {
-            hir::IsAsync::Async
+            ty::Asyncness::Yes
         } else {
-            hir::IsAsync::NotAsync
+            ty::Asyncness::No
         }));
         steps -= 1;
     }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 1e464db8087..be1c46319c2 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -90,7 +90,7 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::{
     self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr,
-    ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item,
+    ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item,
     ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy,
     QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
 };
@@ -1958,8 +1958,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
 /// Checks if the given function kind is an async function.
 pub fn is_async_fn(kind: FnKind<'_>) -> bool {
     match kind {
-        FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
-        FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
+        FnKind::ItemFn(_, _, header) => header.asyncness .is_async(),
+        FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
         FnKind::Closure => false,
     }
 }
diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr
index d723ff538a8..813e65e45a2 100644
--- a/tests/ui/stats/hir-stats.stderr
+++ b/tests/ui/stats/hir-stats.stderr
@@ -146,33 +146,33 @@ hir-stats - Trait                    192 ( 2.1%)             4
 hir-stats WherePredicate           192 ( 2.1%)             3            64
 hir-stats - BoundPredicate           192 ( 2.1%)             3
 hir-stats Block                    288 ( 3.2%)             6            48
-hir-stats Pat                      360 ( 4.0%)             5            72
+hir-stats Pat                      360 ( 3.9%)             5            72
 hir-stats - Wild                      72 ( 0.8%)             1
 hir-stats - Struct                    72 ( 0.8%)             1
 hir-stats - Binding                  216 ( 2.4%)             3
 hir-stats GenericParam             400 ( 4.4%)             5            80
-hir-stats Generics                 560 ( 6.2%)            10            56
-hir-stats Ty                       720 ( 8.0%)            15            48
+hir-stats Generics                 560 ( 6.1%)            10            56
+hir-stats Ty                       720 ( 7.9%)            15            48
 hir-stats - Ptr                       48 ( 0.5%)             1
 hir-stats - Ref                       48 ( 0.5%)             1
-hir-stats - Path                     624 ( 6.9%)            13
-hir-stats Expr                     768 ( 8.5%)            12            64
+hir-stats - Path                     624 ( 6.8%)            13
+hir-stats Expr                     768 ( 8.4%)            12            64
 hir-stats - Path                      64 ( 0.7%)             1
 hir-stats - Struct                    64 ( 0.7%)             1
 hir-stats - Match                     64 ( 0.7%)             1
 hir-stats - InlineAsm                 64 ( 0.7%)             1
 hir-stats - Lit                      128 ( 1.4%)             2
 hir-stats - Block                    384 ( 4.2%)             6
-hir-stats Item                     880 ( 9.7%)            11            80
-hir-stats - Trait                     80 ( 0.9%)             1
-hir-stats - Enum                      80 ( 0.9%)             1
-hir-stats - ExternCrate               80 ( 0.9%)             1
-hir-stats - ForeignMod                80 ( 0.9%)             1
-hir-stats - Impl                      80 ( 0.9%)             1
-hir-stats - Fn                       160 ( 1.8%)             2
-hir-stats - Use                      320 ( 3.5%)             4
-hir-stats Path                   1_240 (13.7%)            31            40
-hir-stats PathSegment            1_920 (21.2%)            40            48
+hir-stats Item                     968 (10.6%)            11            88
+hir-stats - Trait                     88 ( 1.0%)             1
+hir-stats - Enum                      88 ( 1.0%)             1
+hir-stats - ExternCrate               88 ( 1.0%)             1
+hir-stats - ForeignMod                88 ( 1.0%)             1
+hir-stats - Impl                      88 ( 1.0%)             1
+hir-stats - Fn                       176 ( 1.9%)             2
+hir-stats - Use                      352 ( 3.9%)             4
+hir-stats Path                   1_240 (13.6%)            31            40
+hir-stats PathSegment            1_920 (21.0%)            40            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  9_048
+hir-stats Total                  9_136
 hir-stats

From 9072415252d6dde7f23acc21a6781f27c703ac9d Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 15 Sep 2023 05:00:39 +0000
Subject: [PATCH 2/2] Suggest desugaring to RPITIT when AFIT is required to be
 an auto trait

---
 .../src/traits/error_reporting/mod.rs         |   1 +
 .../src/traits/error_reporting/suggestions.rs | 136 ++++++++++++++++++
 .../in-trait/auxiliary/foreign-async-fn.rs    |   7 +
 .../in-trait/missing-send-bound.stderr        |   5 +
 .../in-trait/send-on-async-fn-in-trait.fixed  |  20 +++
 .../in-trait/send-on-async-fn-in-trait.rs     |  20 +++
 .../in-trait/send-on-async-fn-in-trait.stderr |  43 ++++++
 .../send-on-foreign-async-fn-in-trait.rs      |  15 ++
 .../send-on-foreign-async-fn-in-trait.stderr  |  23 +++
 9 files changed, 270 insertions(+)
 create mode 100644 tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs
 create mode 100644 tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed
 create mode 100644 tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs
 create mode 100644 tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr
 create mode 100644 tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs
 create mode 100644 tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 7d754cbafa3..d3e0d3b8b18 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -987,6 +987,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         }
 
                         self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
+                        self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref);
 
                         // Return early if the trait is Debug or Display and the invocation
                         // originates within a standard library macro, because the output
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index a08ebe5a9ea..17e0e4fcb48 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -414,6 +414,12 @@ pub trait TypeErrCtxtExt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         cause: &ObligationCause<'tcx>,
     );
+
+    fn suggest_desugaring_async_fn_in_trait(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -4100,6 +4106,136 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             });
         }
     }
+
+    fn suggest_desugaring_async_fn_in_trait(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::PolyTraitRef<'tcx>,
+    ) {
+        // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
+        if self.tcx.features().return_type_notation {
+            return;
+        }
+
+        let trait_def_id = trait_ref.def_id();
+
+        // Only suggest specifying auto traits
+        if !self.tcx.trait_is_auto(trait_def_id) {
+            return;
+        }
+
+        // Look for an RPITIT
+        let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else {
+            return;
+        };
+        let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) =
+            self.tcx.opt_rpitit_info(alias_ty.def_id)
+        else {
+            return;
+        };
+
+        let auto_trait = self.tcx.def_path_str(trait_def_id);
+        // ... which is a local function
+        let Some(fn_def_id) = fn_def_id.as_local() else {
+            // If it's not local, we can at least mention that the method is async, if it is.
+            if self.tcx.asyncness(fn_def_id).is_async() {
+                err.span_note(
+                    self.tcx.def_span(fn_def_id),
+                    format!(
+                        "`{}::{}` is an `async fn` in trait, which does not \
+                    automatically imply that its future is `{auto_trait}`",
+                        alias_ty.trait_ref(self.tcx),
+                        self.tcx.item_name(fn_def_id)
+                    ),
+                );
+            }
+            return;
+        };
+        let Some(hir::Node::TraitItem(item)) = self.tcx.hir().find_by_def_id(fn_def_id) else {
+            return;
+        };
+
+        // ... whose signature is `async` (i.e. this is an AFIT)
+        let (sig, body) = item.expect_fn();
+        let hir::IsAsync::Async(async_span) = sig.header.asyncness else {
+            return;
+        };
+        let Ok(async_span) =
+            self.tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace())
+        else {
+            return;
+        };
+        let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) =
+            sig.decl.output
+        else {
+            // This should never happen, but let's not ICE.
+            return;
+        };
+
+        // Check that this is *not* a nested `impl Future` RPIT in an async fn
+        // (i.e. `async fn foo() -> impl Future`)
+        if def.owner_id.to_def_id() != opaque_def_id {
+            return;
+        }
+
+        let future = self.tcx.hir().item(*def).expect_opaque_ty();
+        let Some(hir::GenericBound::LangItemTrait(_, _, _, generics)) = future.bounds.get(0) else {
+            // `async fn` should always lower to a lang item bound... but don't ICE.
+            return;
+        };
+        let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) =
+            generics.bindings.get(0).map(|binding| binding.kind)
+        else {
+            // Also should never happen.
+            return;
+        };
+
+        let function_name = self.tcx.def_path_str(fn_def_id);
+
+        let mut sugg = if future_output_ty.span.is_empty() {
+            vec![
+                (async_span, String::new()),
+                (
+                    future_output_ty.span,
+                    format!(" -> impl std::future::Future<Output = ()> + {auto_trait}"),
+                ),
+            ]
+        } else {
+            vec![
+                (
+                    future_output_ty.span.shrink_to_lo(),
+                    "impl std::future::Future<Output = ".to_owned(),
+                ),
+                (future_output_ty.span.shrink_to_hi(), format!("> + {auto_trait}")),
+                (async_span, String::new()),
+            ]
+        };
+
+        // If there's a body, we also need to wrap it in `async {}`
+        if let hir::TraitFn::Provided(body) = body {
+            let body = self.tcx.hir().body(*body);
+            let body_span = body.value.span;
+            let body_span_without_braces =
+                body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1));
+            if body_span_without_braces.is_empty() {
+                sugg.push((body_span_without_braces, " async {} ".to_owned()));
+            } else {
+                sugg.extend([
+                    (body_span_without_braces.shrink_to_lo(), "async {".to_owned()),
+                    (body_span_without_braces.shrink_to_hi(), "} ".to_owned()),
+                ]);
+            }
+        }
+
+        err.multipart_suggestion(
+            format!(
+                "`{auto_trait}` can be made part of the associated future's \
+                guarantees for all implementations of `{function_name}`"
+            ),
+            sugg,
+            Applicability::MachineApplicable,
+        );
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs b/tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs
new file mode 100644
index 00000000000..bba886f175e
--- /dev/null
+++ b/tests/ui/async-await/in-trait/auxiliary/foreign-async-fn.rs
@@ -0,0 +1,7 @@
+// edition:2021
+
+#![feature(async_fn_in_trait)]
+
+pub trait Foo {
+    async fn test();
+}
diff --git a/tests/ui/async-await/in-trait/missing-send-bound.stderr b/tests/ui/async-await/in-trait/missing-send-bound.stderr
index 18185b75554..7e59d94d456 100644
--- a/tests/ui/async-await/in-trait/missing-send-bound.stderr
+++ b/tests/ui/async-await/in-trait/missing-send-bound.stderr
@@ -15,6 +15,11 @@ note: required by a bound in `assert_is_send`
    |
 LL | fn assert_is_send(_: impl Send) {}
    |                           ^^^^ required by this bound in `assert_is_send`
+help: `Send` can be made part of the associated future's guarantees for all implementations of `Foo::bar`
+   |
+LL -     async fn bar();
+LL +     fn bar() -> impl std::future::Future<Output = ()> + Send;
+   |
 
 error: aborting due to previous error
 
diff --git a/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed
new file mode 100644
index 00000000000..33c00587439
--- /dev/null
+++ b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.fixed
@@ -0,0 +1,20 @@
+// run-rustfix
+// edition: 2021
+
+#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)]
+#![allow(unused)]
+
+trait Foo {
+    fn test() -> impl std::future::Future<Output = ()> + Send { async {} }
+    fn test2() -> impl std::future::Future<Output = i32> + Send {async { 1 + 2 } }
+}
+
+fn bar<T: Foo>() {
+    fn needs_send(_: impl Send) {}
+    needs_send(T::test());
+    //~^ ERROR `impl Future<Output = ()>` cannot be sent between threads safely
+    needs_send(T::test2());
+    //~^ ERROR `impl Future<Output = i32>` cannot be sent between threads safely
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs
new file mode 100644
index 00000000000..96b623d6988
--- /dev/null
+++ b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+// edition: 2021
+
+#![feature(async_fn_in_trait, return_position_impl_trait_in_trait)]
+#![allow(unused)]
+
+trait Foo {
+    async fn test() -> () {}
+    async fn test2() -> i32 { 1 + 2 }
+}
+
+fn bar<T: Foo>() {
+    fn needs_send(_: impl Send) {}
+    needs_send(T::test());
+    //~^ ERROR `impl Future<Output = ()>` cannot be sent between threads safely
+    needs_send(T::test2());
+    //~^ ERROR `impl Future<Output = i32>` cannot be sent between threads safely
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr
new file mode 100644
index 00000000000..4319a14118b
--- /dev/null
+++ b/tests/ui/async-await/in-trait/send-on-async-fn-in-trait.stderr
@@ -0,0 +1,43 @@
+error[E0277]: `impl Future<Output = ()>` cannot be sent between threads safely
+  --> $DIR/send-on-async-fn-in-trait.rs:14:16
+   |
+LL |     needs_send(T::test());
+   |     ---------- ^^^^^^^^^ `impl Future<Output = ()>` cannot be sent between threads safely
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Send` is not implemented for `impl Future<Output = ()>`
+note: required by a bound in `needs_send`
+  --> $DIR/send-on-async-fn-in-trait.rs:13:27
+   |
+LL |     fn needs_send(_: impl Send) {}
+   |                           ^^^^ required by this bound in `needs_send`
+help: `Send` can be made part of the associated future's guarantees for all implementations of `Foo::test`
+   |
+LL -     async fn test() -> () {}
+LL +     fn test() -> impl std::future::Future<Output = ()> + Send { async {} }
+   |
+
+error[E0277]: `impl Future<Output = i32>` cannot be sent between threads safely
+  --> $DIR/send-on-async-fn-in-trait.rs:16:16
+   |
+LL |     needs_send(T::test2());
+   |     ---------- ^^^^^^^^^^ `impl Future<Output = i32>` cannot be sent between threads safely
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Send` is not implemented for `impl Future<Output = i32>`
+note: required by a bound in `needs_send`
+  --> $DIR/send-on-async-fn-in-trait.rs:13:27
+   |
+LL |     fn needs_send(_: impl Send) {}
+   |                           ^^^^ required by this bound in `needs_send`
+help: `Send` can be made part of the associated future's guarantees for all implementations of `Foo::test2`
+   |
+LL -     async fn test2() -> i32 { 1 + 2 }
+LL +     fn test2() -> impl std::future::Future<Output = i32> + Send {async { 1 + 2 } }
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs
new file mode 100644
index 00000000000..83b69d72a96
--- /dev/null
+++ b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.rs
@@ -0,0 +1,15 @@
+// aux-build:foreign-async-fn.rs
+// edition:2021
+
+#![feature(async_fn_in_trait)]
+
+extern crate foreign_async_fn;
+use foreign_async_fn::Foo;
+
+fn bar<T: Foo>() {
+    fn needs_send(_: impl Send) {}
+    needs_send(T::test());
+    //~^ ERROR `impl Future<Output = ()>` cannot be sent between threads safely
+}
+
+fn main() {}
diff --git a/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr
new file mode 100644
index 00000000000..f337a04ba19
--- /dev/null
+++ b/tests/ui/async-await/in-trait/send-on-foreign-async-fn-in-trait.stderr
@@ -0,0 +1,23 @@
+error[E0277]: `impl Future<Output = ()>` cannot be sent between threads safely
+  --> $DIR/send-on-foreign-async-fn-in-trait.rs:11:16
+   |
+LL |     needs_send(T::test());
+   |     ---------- ^^^^^^^^^ `impl Future<Output = ()>` cannot be sent between threads safely
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Send` is not implemented for `impl Future<Output = ()>`
+note: `<T as Foo>::test` is an `async fn` in trait, which does not automatically imply that its future is `Send`
+  --> $DIR/auxiliary/foreign-async-fn.rs:6:5
+   |
+LL |     async fn test();
+   |     ^^^^^^^^^^^^^^^^
+note: required by a bound in `needs_send`
+  --> $DIR/send-on-foreign-async-fn-in-trait.rs:10:27
+   |
+LL |     fn needs_send(_: impl Send) {}
+   |                           ^^^^ required by this bound in `needs_send`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.