Auto merge of #139845 - Zalathar:rollup-u5u5y1v, r=Zalathar

Rollup of 17 pull requests

Successful merges:

 - #138374 (Enable contracts for const functions)
 - #138380 (ci: add runners for vanilla LLVM 20)
 - #138393 (Allow const patterns of matches to contain pattern types)
 - #139517 (std: sys: process: uefi: Use NULL stdin by default)
 - #139554 (std: add Output::exit_ok)
 - #139660 (compiletest: Add an experimental new executor to replace libtest)
 - #139669 (Overhaul `AssocItem`)
 - #139671 (Proc macro span API redesign: Replace proc_macro::SourceFile by Span::{file, local_file})
 - #139750 (std/thread: Use default stack size from menuconfig for NuttX)
 - #139772 (Remove `hir::Map`)
 - #139785 (Let CStrings be either 1 or 2 byte aligned.)
 - #139789 (do not unnecessarily leak auto traits in item bounds)
 - #139791 (drop global where-bounds before merging candidates)
 - #139798 (normalize: prefer `ParamEnv` over `AliasBound` candidates)
 - #139822 (Fix: Map EOPNOTSUPP to ErrorKind::Unsupported on Unix)
 - #139833 (Fix some HIR pretty-printing problems)
 - #139836 (Basic tests of MPMC receiver cloning)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-04-15 08:02:23 +00:00
commit f433fa46b0
173 changed files with 2499 additions and 994 deletions

View file

@ -1,3 +1,7 @@
//! This module used to contain a type called `Map`. That type has since been
//! eliminated, and all its methods are now on `TyCtxt`. But the module name
//! stays as `map` because there isn't an obviously better name for it.
use rustc_abi::ExternAbi;
use rustc_ast::visit::{VisitorResult, walk_list};
use rustc_data_structures::fingerprint::Fingerprint;
@ -18,16 +22,6 @@ use crate::middle::debugger_visualizer::DebuggerVisualizerFile;
use crate::query::LocalCrate;
use crate::ty::TyCtxt;
// FIXME: the structure was necessary in the past but now it
// only serves as "namespace" for HIR-related methods, and can be
// removed if all the methods are reasonably renamed and moved to tcx
// (https://github.com/rust-lang/rust/pull/118256#issuecomment-1826442834).
#[allow(unused)] // FIXME: temporary
#[derive(Copy, Clone)]
pub struct Map<'hir> {
pub(super) tcx: TyCtxt<'hir>,
}
/// An iterator that walks up the ancestor tree of a given `HirId`.
/// Constructed using `tcx.hir_parent_iter(hir_id)`.
struct ParentHirIterator<'tcx> {
@ -335,7 +329,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Returns an iterator of the `DefId`s for all body-owners in this
/// crate. If you would prefer to iterate over the bodies
/// themselves, you can do `self.hir().krate().body_ids.iter()`.
/// themselves, you can do `self.hir_crate(()).body_ids.iter()`.
#[inline]
pub fn hir_body_owners(self) -> impl Iterator<Item = LocalDefId> {
self.hir_crate_items(()).body_owners.iter().copied()

View file

@ -116,11 +116,6 @@ impl ModuleItems {
}
impl<'tcx> TyCtxt<'tcx> {
#[inline(always)]
pub fn hir(self) -> map::Map<'tcx> {
map::Map { tcx: self }
}
pub fn parent_module(self, id: HirId) -> LocalModDefId {
if !id.is_owner() && self.def_kind(id.owner) == DefKind::Mod {
LocalModDefId::new_unchecked(id.owner.def_id)

View file

@ -1636,8 +1636,8 @@ pub fn find_self_call<'tcx>(
&body[block].terminator
&& let Operand::Constant(box ConstOperand { const_, .. }) = func
&& let ty::FnDef(def_id, fn_args) = *const_.ty().kind()
&& let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
tcx.opt_associated_item(def_id)
&& let Some(item) = tcx.opt_associated_item(def_id)
&& item.is_method()
&& let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] =
**args
{

View file

@ -161,11 +161,11 @@ rustc_queries! {
/// Represents crate as a whole (as distinct from the top-level crate module).
///
/// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir_crate()`),
/// we will have to assume that any change means that you need to be recompiled.
/// This is because the `hir_crate` query gives you access to all other items.
/// To avoid this fate, do not call `tcx.hir_crate()`; instead,
/// prefer wrappers like [`TyCtxt::hir_visit_all_item_likes_in_crate`].
/// If you call `tcx.hir_crate(())` we will have to assume that any change
/// means that you need to be recompiled. This is because the `hir_crate`
/// query gives you access to all other items. To avoid this fate, do not
/// call `tcx.hir_crate(())`; instead, prefer wrappers like
/// [`TyCtxt::hir_visit_all_item_likes_in_crate`].
query hir_crate(key: ()) -> &'tcx Crate<'tcx> {
arena_cache
eval_always
@ -197,7 +197,7 @@ rustc_queries! {
/// Gives access to the HIR node's parent for the HIR owner `key`.
///
/// This can be conveniently accessed by methods on `tcx.hir()`.
/// This can be conveniently accessed by `tcx.hir_*` methods.
/// Avoid calling this query directly.
query hir_owner_parent(key: hir::OwnerId) -> hir::HirId {
desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) }
@ -205,7 +205,7 @@ rustc_queries! {
/// Gives access to the HIR nodes and bodies inside `key` if it's a HIR owner.
///
/// This can be conveniently accessed by methods on `tcx.hir()`.
/// This can be conveniently accessed by `tcx.hir_*` methods.
/// Avoid calling this query directly.
query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> {
desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) }
@ -214,7 +214,7 @@ rustc_queries! {
/// Gives access to the HIR attributes inside the HIR owner `key`.
///
/// This can be conveniently accessed by methods on `tcx.hir()`.
/// This can be conveniently accessed by `tcx.hir_*` methods.
/// Avoid calling this query directly.
query hir_attr_map(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> {
desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) }

View file

@ -5,7 +5,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use rustc_span::Span;
use crate::ty::{self, Ty, TyCtxt};
use crate::ty::{Ty, TyCtxt};
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum PointerCoercion {
@ -133,7 +133,7 @@ impl OverloadedDeref {
};
tcx.associated_items(trait_def_id)
.in_definition_order()
.find(|m| m.kind == ty::AssocKind::Fn)
.find(|item| item.is_fn())
.unwrap()
.def_id
}

View file

@ -18,27 +18,33 @@ pub enum AssocItemContainer {
#[derive(Copy, Clone, Debug, PartialEq, HashStable, Eq, Hash, Encodable, Decodable)]
pub struct AssocItem {
pub def_id: DefId,
pub name: Symbol,
pub kind: AssocKind,
pub container: AssocItemContainer,
/// If this is an item in an impl of a trait then this is the `DefId` of
/// the associated item on the trait that this implements.
pub trait_item_def_id: Option<DefId>,
/// Whether this is a method with an explicit self
/// as its first parameter, allowing method calls.
pub fn_has_self_parameter: bool,
/// `Some` if the associated item (an associated type) comes from the
/// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData`
/// provides additional information about its source.
pub opt_rpitit_info: Option<ty::ImplTraitInTraitData>,
}
impl AssocItem {
// Gets the identifier, if it has one.
pub fn opt_name(&self) -> Option<Symbol> {
match self.kind {
ty::AssocKind::Type { data: AssocTypeData::Normal(name) } => Some(name),
ty::AssocKind::Type { data: AssocTypeData::Rpitit(_) } => None,
ty::AssocKind::Const { name } => Some(name),
ty::AssocKind::Fn { name, .. } => Some(name),
}
}
// Gets the identifier name. Aborts if it lacks one, i.e. is an RPITIT
// associated type.
pub fn name(&self) -> Symbol {
self.opt_name().expect("name of non-Rpitit assoc item")
}
pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())
Ident::new(self.name(), tcx.def_ident_span(self.def_id).unwrap())
}
/// Gets the defaultness of the associated item.
@ -78,35 +84,65 @@ impl AssocItem {
pub fn signature(&self, tcx: TyCtxt<'_>) -> String {
match self.kind {
ty::AssocKind::Fn => {
ty::AssocKind::Fn { .. } => {
// We skip the binder here because the binder would deanonymize all
// late-bound regions, and we don't want method signatures to show up
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
tcx.fn_sig(self.def_id).instantiate_identity().skip_binder().to_string()
}
ty::AssocKind::Type => format!("type {};", self.name),
ty::AssocKind::Const => {
format!(
"const {}: {:?};",
self.name,
tcx.type_of(self.def_id).instantiate_identity()
)
ty::AssocKind::Type { .. } => format!("type {};", self.name()),
ty::AssocKind::Const { name } => {
format!("const {}: {:?};", name, tcx.type_of(self.def_id).instantiate_identity())
}
}
}
pub fn descr(&self) -> &'static str {
match self.kind {
ty::AssocKind::Const => "associated const",
ty::AssocKind::Fn if self.fn_has_self_parameter => "method",
ty::AssocKind::Fn => "associated function",
ty::AssocKind::Type => "associated type",
ty::AssocKind::Const { .. } => "associated const",
ty::AssocKind::Fn { has_self: true, .. } => "method",
ty::AssocKind::Fn { has_self: false, .. } => "associated function",
ty::AssocKind::Type { .. } => "associated type",
}
}
pub fn namespace(&self) -> Namespace {
match self.kind {
ty::AssocKind::Type { .. } => Namespace::TypeNS,
ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS,
}
}
pub fn as_def_kind(&self) -> DefKind {
match self.kind {
AssocKind::Const { .. } => DefKind::AssocConst,
AssocKind::Fn { .. } => DefKind::AssocFn,
AssocKind::Type { .. } => DefKind::AssocTy,
}
}
pub fn is_type(&self) -> bool {
matches!(self.kind, ty::AssocKind::Type { .. })
}
pub fn is_fn(&self) -> bool {
matches!(self.kind, ty::AssocKind::Fn { .. })
}
pub fn is_method(&self) -> bool {
matches!(self.kind, ty::AssocKind::Fn { has_self: true, .. })
}
pub fn as_tag(&self) -> AssocTag {
match self.kind {
AssocKind::Const { .. } => AssocTag::Const,
AssocKind::Fn { .. } => AssocTag::Fn,
AssocKind::Type { .. } => AssocTag::Type,
}
}
pub fn is_impl_trait_in_trait(&self) -> bool {
self.opt_rpitit_info.is_some()
matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) })
}
/// Returns true if:
@ -114,7 +150,7 @@ impl AssocItem {
/// - If it is in a trait impl, the item from the original trait has this attribute, or
/// - It is an inherent assoc const.
pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool {
if self.kind != ty::AssocKind::Const {
if !matches!(self.kind, ty::AssocKind::Const { .. }) {
return false;
}
@ -128,26 +164,35 @@ impl AssocItem {
}
}
#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]
pub enum AssocTypeData {
Normal(Symbol),
/// The associated type comes from an RPITIT. It has no name, and the
/// `ImplTraitInTraitData` provides additional information about its
/// source.
Rpitit(ty::ImplTraitInTraitData),
}
#[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]
pub enum AssocKind {
Const,
Fn,
Type,
Const { name: Symbol },
Fn { name: Symbol, has_self: bool },
Type { data: AssocTypeData },
}
impl AssocKind {
pub fn namespace(&self) -> Namespace {
match *self {
ty::AssocKind::Type => Namespace::TypeNS,
ty::AssocKind::Const | ty::AssocKind::Fn => Namespace::ValueNS,
ty::AssocKind::Type { .. } => Namespace::TypeNS,
ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => Namespace::ValueNS,
}
}
pub fn as_def_kind(&self) -> DefKind {
match self {
AssocKind::Const => DefKind::AssocConst,
AssocKind::Fn => DefKind::AssocFn,
AssocKind::Type => DefKind::AssocTy,
AssocKind::Const { .. } => DefKind::AssocConst,
AssocKind::Fn { .. } => DefKind::AssocFn,
AssocKind::Type { .. } => DefKind::AssocTy,
}
}
}
@ -155,15 +200,22 @@ impl AssocKind {
impl std::fmt::Display for AssocKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
// FIXME: fails to distinguish between "associated function" and
// "method" because `has_self` isn't known here.
AssocKind::Fn => write!(f, "method"),
AssocKind::Const => write!(f, "associated const"),
AssocKind::Type => write!(f, "associated type"),
AssocKind::Fn { has_self: true, .. } => write!(f, "method"),
AssocKind::Fn { has_self: false, .. } => write!(f, "associated function"),
AssocKind::Const { .. } => write!(f, "associated const"),
AssocKind::Type { .. } => write!(f, "associated type"),
}
}
}
// Like `AssocKind`, but just the tag, no fields. Used in various kinds of matching.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssocTag {
Const,
Fn,
Type,
}
/// A list of `ty::AssocItem`s in definition order that allows for efficient lookup by name.
///
/// When doing lookup by name, we try to postpone hygienic comparison for as long as possible since
@ -171,17 +223,17 @@ impl std::fmt::Display for AssocKind {
/// done only on items with the same name.
#[derive(Debug, Clone, PartialEq, HashStable)]
pub struct AssocItems {
items: SortedIndexMultiMap<u32, Symbol, ty::AssocItem>,
items: SortedIndexMultiMap<u32, Option<Symbol>, ty::AssocItem>,
}
impl AssocItems {
/// Constructs an `AssociatedItems` map from a series of `ty::AssocItem`s in definition order.
pub fn new(items_in_def_order: impl IntoIterator<Item = ty::AssocItem>) -> Self {
let items = items_in_def_order.into_iter().map(|item| (item.name, item)).collect();
let items = items_in_def_order.into_iter().map(|item| (item.opt_name(), item)).collect();
AssocItems { items }
}
/// Returns a slice of associated items in the order they were defined.
/// Returns an iterator over associated items in the order they were defined.
///
/// New code should avoid relying on definition order. If you need a particular associated item
/// for a known trait, make that trait a lang item instead of indexing this array.
@ -198,7 +250,8 @@ impl AssocItems {
&self,
name: Symbol,
) -> impl '_ + Iterator<Item = &ty::AssocItem> {
self.items.get_by_key(name)
assert!(!name.is_empty());
self.items.get_by_key(Some(name))
}
/// Returns the associated item with the given identifier and `AssocKind`, if one exists.
@ -207,27 +260,14 @@ impl AssocItems {
&self,
tcx: TyCtxt<'_>,
ident: Ident,
kind: AssocKind,
assoc_tag: AssocTag,
parent_def_id: DefId,
) -> Option<&ty::AssocItem> {
self.filter_by_name_unhygienic(ident.name)
.filter(|item| item.kind == kind)
.filter(|item| item.as_tag() == assoc_tag)
.find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
}
/// Returns the associated item with the given identifier and any of `AssocKind`, if one
/// exists. The identifier is matched hygienically.
pub fn find_by_ident_and_kinds(
&self,
tcx: TyCtxt<'_>,
ident: Ident,
// Sorted in order of what kinds to look at
kinds: &[AssocKind],
parent_def_id: DefId,
) -> Option<&ty::AssocItem> {
kinds.iter().find_map(|kind| self.find_by_ident_and_kind(tcx, ident, *kind, parent_def_id))
}
/// Returns the associated item with the given identifier in the given `Namespace`, if one
/// exists. The identifier is matched hygienically.
pub fn find_by_ident_and_namespace(
@ -238,7 +278,7 @@ impl AssocItems {
parent_def_id: DefId,
) -> Option<&ty::AssocItem> {
self.filter_by_name_unhygienic(ident.name)
.filter(|item| item.kind.namespace() == ns)
.filter(|item| item.namespace() == ns)
.find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
}
}

View file

@ -464,7 +464,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> {
self.associated_items(def_id)
.in_definition_order()
.filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type))
.filter(|assoc_item| assoc_item.is_type())
.map(|assoc_item| assoc_item.def_id)
}
@ -2147,7 +2147,7 @@ impl<'tcx> TyCtxt<'tcx> {
return vec![];
};
let mut v = TraitObjectVisitor(vec![], self.hir());
let mut v = TraitObjectVisitor(vec![]);
v.visit_ty_unambig(hir_output);
v.0
}
@ -2160,7 +2160,7 @@ impl<'tcx> TyCtxt<'tcx> {
scope_def_id: LocalDefId,
) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span, Option<Span>)> {
let hir_id = self.local_def_id_to_hir_id(scope_def_id);
let mut v = TraitObjectVisitor(vec![], self.hir());
let mut v = TraitObjectVisitor(vec![]);
// when the return type is a type alias
if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir_fn_decl_by_hir_id(hir_id)
&& let hir::TyKind::Path(hir::QPath::Resolved(

View file

@ -571,7 +571,7 @@ pub fn suggest_constraining_type_params<'a>(
}
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
pub(crate) struct TraitObjectVisitor<'tcx>(pub(crate) Vec<&'tcx hir::Ty<'tcx>>);
impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
@ -592,18 +592,6 @@ impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
}
}
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
pub struct StaticLifetimeVisitor<'tcx>(pub Vec<Span>, pub crate::hir::map::Map<'tcx>);
impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
fn visit_lifetime(&mut self, lt: &'v hir::Lifetime) {
if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static = lt.res
{
self.0.push(lt.ident.span);
}
}
}
pub struct IsSuggestableVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
infer_suggestable: bool,

View file

@ -746,7 +746,7 @@ impl<'tcx> Instance<'tcx> {
let call_once = tcx
.associated_items(fn_once)
.in_definition_order()
.find(|it| it.kind == ty::AssocKind::Fn)
.find(|it| it.is_fn())
.unwrap()
.def_id;
let track_caller =

View file

@ -1462,7 +1462,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn provided_trait_methods(self, id: DefId) -> impl 'tcx + Iterator<Item = &'tcx AssocItem> {
self.associated_items(id)
.in_definition_order()
.filter(move |item| item.kind == AssocKind::Fn && item.defaultness(self).has_value())
.filter(move |item| item.is_fn() && item.defaultness(self).has_value())
}
pub fn repr_options_of_def(self, did: LocalDefId) -> ReprOptions {
@ -1608,8 +1608,11 @@ impl<'tcx> TyCtxt<'tcx> {
/// return-position `impl Trait` from a trait, then provide the source info
/// about where that RPITIT came from.
pub fn opt_rpitit_info(self, def_id: DefId) -> Option<ImplTraitInTraitData> {
if let DefKind::AssocTy = self.def_kind(def_id) {
self.associated_item(def_id).opt_rpitit_info
if let DefKind::AssocTy = self.def_kind(def_id)
&& let AssocKind::Type { data: AssocTypeData::Rpitit(rpitit_info) } =
self.associated_item(def_id).kind
{
Some(rpitit_info)
} else {
None
}

View file

@ -1214,7 +1214,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
&& assoc
.trait_container(tcx)
.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Coroutine))
&& assoc.name == rustc_span::sym::Return
&& assoc.opt_name() == Some(rustc_span::sym::Return)
{
if let ty::Coroutine(_, args) = args.type_at(0).kind() {
let return_ty = args.as_coroutine().return_ty();
@ -1237,7 +1237,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
p!(", ");
}
p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name));
p!(write("{} = ", tcx.associated_item(assoc_item_def_id).name()));
match term.unpack() {
TermKind::Ty(ty) => p!(print(ty)),
@ -3291,7 +3291,7 @@ define_print! {
}
ty::ExistentialProjection<'tcx> {
let name = cx.tcx().associated_item(self.def_id).name;
let name = cx.tcx().associated_item(self.def_id).name();
// The args don't contain the self ty (as it has been erased) but the corresp.
// generics do as the trait always has a self ty param. We need to offset.
let args = &self.args[cx.tcx().generics_of(self.def_id).parent_count - 1..];

View file

@ -735,7 +735,7 @@ impl<'tcx> Ty<'tcx> {
.map(|principal| {
tcx.associated_items(principal.def_id())
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.filter(|item| item.is_type())
.filter(|item| !item.is_impl_trait_in_trait())
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
.count()

View file

@ -819,7 +819,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Get an English description for the item's kind.
pub fn def_kind_descr(self, def_kind: DefKind, def_id: DefId) -> &'static str {
match def_kind {
DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method",
DefKind::AssocFn if self.associated_item(def_id).is_method() => "method",
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
match coroutine_kind {
hir::CoroutineKind::Desugared(
@ -873,7 +873,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Gets an English article for the [`TyCtxt::def_kind_descr`].
pub fn def_kind_descr_article(self, def_kind: DefKind, def_id: DefId) -> &'static str {
match def_kind {
DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "a",
DefKind::AssocFn if self.associated_item(def_id).is_method() => "a",
DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
match coroutine_kind {
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, ..) => "an",