use std::hash::Hash; use std::path::PathBuf; use std::sync::{Arc, OnceLock as OnceCell}; use std::{fmt, iter}; use arrayvec::ArrayVec; use itertools::Either; use rustc_abi::{ExternAbi, VariantIdx}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, ConstStability, Mutability, Stability, StableSince, find_attr}; use rustc_index::IndexVec; use rustc_metadata::rendered_const; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, }; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Symbol, kw, sym}; use rustc_span::{DUMMY_SP, FileName, Loc}; use thin_vec::ThinVec; use tracing::{debug, trace}; use {rustc_ast as ast, rustc_hir as hir}; pub(crate) use self::ItemKind::*; pub(crate) use self::Type::{ Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, RawPointer, SelfTy, Slice, Tuple, UnsafeBinder, }; use crate::clean::cfg::Cfg; use crate::clean::clean_middle_path; use crate::clean::inline::{self, print_inlined_const}; use crate::clean::utils::{is_literal_expr, print_evaluated_const}; use crate::core::DocContext; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::render::Context; use crate::passes::collect_intra_doc_links::UrlFragment; #[cfg(test)] mod tests; pub(crate) type ItemIdSet = FxHashSet; #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] pub(crate) enum ItemId { /// A "normal" item that uses a [`DefId`] for identification. DefId(DefId), /// Identifier that is used for auto traits. Auto { trait_: DefId, for_: DefId }, /// Identifier that is used for blanket implementations. Blanket { impl_id: DefId, for_: DefId }, } impl ItemId { #[inline] pub(crate) fn is_local(self) -> bool { match self { ItemId::Auto { for_: id, .. } | ItemId::Blanket { for_: id, .. } | ItemId::DefId(id) => id.is_local(), } } #[inline] #[track_caller] pub(crate) fn expect_def_id(self) -> DefId { self.as_def_id() .unwrap_or_else(|| panic!("ItemId::expect_def_id: `{self:?}` isn't a DefId")) } #[inline] pub(crate) fn as_def_id(self) -> Option { match self { ItemId::DefId(id) => Some(id), _ => None, } } #[inline] pub(crate) fn as_local_def_id(self) -> Option { self.as_def_id().and_then(|id| id.as_local()) } #[inline] pub(crate) fn krate(self) -> CrateNum { match self { ItemId::Auto { for_: id, .. } | ItemId::Blanket { for_: id, .. } | ItemId::DefId(id) => id.krate, } } } impl From for ItemId { fn from(id: DefId) -> Self { Self::DefId(id) } } /// The crate currently being documented. #[derive(Debug)] pub(crate) struct Crate { pub(crate) module: Item, /// Only here so that they can be filtered through the rustdoc passes. pub(crate) external_traits: Box>, } impl Crate { pub(crate) fn name(&self, tcx: TyCtxt<'_>) -> Symbol { ExternalCrate::LOCAL.name(tcx) } pub(crate) fn src(&self, tcx: TyCtxt<'_>) -> FileName { ExternalCrate::LOCAL.src(tcx) } } #[derive(Copy, Clone, Debug)] pub(crate) struct ExternalCrate { pub(crate) crate_num: CrateNum, } impl ExternalCrate { const LOCAL: Self = Self { crate_num: LOCAL_CRATE }; #[inline] pub(crate) fn def_id(&self) -> DefId { self.crate_num.as_def_id() } pub(crate) fn src(&self, tcx: TyCtxt<'_>) -> FileName { let krate_span = tcx.def_span(self.def_id()); tcx.sess.source_map().span_to_filename(krate_span) } pub(crate) fn name(&self, tcx: TyCtxt<'_>) -> Symbol { tcx.crate_name(self.crate_num) } pub(crate) fn src_root(&self, tcx: TyCtxt<'_>) -> PathBuf { match self.src(tcx) { FileName::Real(ref p) => match p.local_path_if_available().parent() { Some(p) => p.to_path_buf(), None => PathBuf::new(), }, _ => PathBuf::new(), } } /// Attempts to find where an external crate is located, given that we're /// rendering into the specified source destination. pub(crate) fn location( &self, extern_url: Option<&str>, extern_url_takes_precedence: bool, dst: &std::path::Path, tcx: TyCtxt<'_>, ) -> ExternalLocation { use ExternalLocation::*; fn to_remote(url: impl ToString) -> ExternalLocation { let mut url = url.to_string(); if !url.ends_with('/') { url.push('/'); } Remote(url) } // See if there's documentation generated into the local directory // WARNING: since rustdoc creates these directories as it generates documentation, this check is only accurate before rendering starts. // Make sure to call `location()` by that time. let local_location = dst.join(self.name(tcx).as_str()); if local_location.is_dir() { return Local; } if extern_url_takes_precedence && let Some(url) = extern_url { return to_remote(url); } // Failing that, see if there's an attribute specifying where to find this // external crate let did = self.crate_num.as_def_id(); tcx.get_attrs(did, sym::doc) .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) .filter(|a| a.has_name(sym::html_root_url)) .filter_map(|a| a.value_str()) .map(to_remote) .next() .or_else(|| extern_url.map(to_remote)) // NOTE: only matters if `extern_url_takes_precedence` is false .unwrap_or(Unknown) // Well, at least we tried. } fn mapped_root_modules( &self, tcx: TyCtxt<'_>, f: impl Fn(DefId, TyCtxt<'_>) -> Option<(DefId, T)>, ) -> impl Iterator { let root = self.def_id(); if root.is_local() { Either::Left( tcx.hir_root_module() .item_ids .iter() .filter(move |&&id| matches!(tcx.hir_item(id).kind, hir::ItemKind::Mod(..))) .filter_map(move |&id| f(id.owner_id.into(), tcx)), ) } else { Either::Right( tcx.module_children(root) .iter() .filter_map(|item| { if let Res::Def(DefKind::Mod, did) = item.res { Some(did) } else { None } }) .filter_map(move |did| f(did, tcx)), ) } } pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> impl Iterator { self.retrieve_keywords_or_documented_attributes(tcx, sym::keyword) } pub(crate) fn documented_attributes( &self, tcx: TyCtxt<'_>, ) -> impl Iterator { self.retrieve_keywords_or_documented_attributes(tcx, sym::attribute) } fn retrieve_keywords_or_documented_attributes( &self, tcx: TyCtxt<'_>, name: Symbol, ) -> impl Iterator { let as_target = move |did: DefId, tcx: TyCtxt<'_>| -> Option<(DefId, Symbol)> { tcx.get_attrs(did, sym::doc) .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) .filter(|meta| meta.has_name(name)) .find_map(|meta| meta.value_str()) .map(|value| (did, value)) }; self.mapped_root_modules(tcx, as_target) } pub(crate) fn primitives( &self, tcx: TyCtxt<'_>, ) -> impl Iterator { // Collect all inner modules which are tagged as implementations of // primitives. // // Note that this loop only searches the top-level items of the crate, // and this is intentional. If we were to search the entire crate for an // item tagged with `#[rustc_doc_primitive]` then we would also have to // search the entirety of external modules for items tagged // `#[rustc_doc_primitive]`, which is a pretty inefficient process (decoding // all that metadata unconditionally). // // In order to keep the metadata load under control, the // `#[rustc_doc_primitive]` feature is explicitly designed to only allow the // primitive tags to show up as the top level items in a crate. // // Also note that this does not attempt to deal with modules tagged // duplicately for the same primitive. This is handled later on when // rendering by delegating everything to a hash map. fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType)> { tcx.get_attrs(def_id, sym::rustc_doc_primitive).next().map(|attr| { let attr_value = attr.value_str().expect("syntax should already be validated"); let Some(prim) = PrimitiveType::from_symbol(attr_value) else { span_bug!( attr.span(), "primitive `{attr_value}` is not a member of `PrimitiveType`" ); }; (def_id, prim) }) } self.mapped_root_modules(tcx, as_primitive) } } /// Indicates where an external crate can be found. #[derive(Debug)] pub(crate) enum ExternalLocation { /// Remote URL root of the external crate Remote(String), /// This external crate can be found in the local doc/ folder Local, /// The external crate could not be found. Unknown, } /// Anything with a source location and set of attributes and, optionally, a /// name. That is, anything that can be documented. This doesn't correspond /// directly to the AST's concept of an item; it's a strict superset. #[derive(Clone)] pub(crate) struct Item { pub(crate) inner: Box, } // Why does the `Item`/`ItemInner` split exist? `Vec`s are common, and // without the split `Item` would be a large type (100+ bytes) which results in // lots of wasted space in the unused parts of a `Vec`. With the split, // `Item` is just 8 bytes, and the wasted space is avoided, at the cost of an // extra allocation per item. This is a performance win. #[derive(Clone)] pub(crate) struct ItemInner { /// The name of this item. /// Optional because not every item has a name, e.g. impls. pub(crate) name: Option, /// Information about this item that is specific to what kind of item it is. /// E.g., struct vs enum vs function. pub(crate) kind: ItemKind, pub(crate) attrs: Attributes, /// The effective stability, filled out by the `propagate-stability` pass. pub(crate) stability: Option, pub(crate) item_id: ItemId, /// This is the `LocalDefId` of the `use` statement if the item was inlined. /// The crate metadata doesn't hold this information, so the `use` statement /// always belongs to the current crate. pub(crate) inline_stmt_id: Option, pub(crate) cfg: Option>, } impl std::ops::Deref for Item { type Target = ItemInner; fn deref(&self) -> &ItemInner { &self.inner } } /// NOTE: this does NOT unconditionally print every item, to avoid thousands of lines of logs. /// If you want to see the debug output for attributes and the `kind` as well, use `{:#?}` instead of `{:?}`. impl fmt::Debug for Item { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let alternate = f.alternate(); // hand-picked fields that don't bloat the logs too much let mut fmt = f.debug_struct("Item"); fmt.field("name", &self.name).field("item_id", &self.item_id); // allow printing the full item if someone really wants to if alternate { fmt.field("attrs", &self.attrs).field("kind", &self.kind).field("cfg", &self.cfg); } else { fmt.field("kind", &self.type_()); fmt.field("docs", &self.doc_value()); } fmt.finish() } } pub(crate) fn rustc_span(def_id: DefId, tcx: TyCtxt<'_>) -> Span { Span::new(def_id.as_local().map_or_else( || tcx.def_span(def_id), |local| tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(local)), )) } fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let parent = tcx.parent(def_id); match tcx.def_kind(parent) { DefKind::Struct | DefKind::Union => false, DefKind::Variant => true, parent_kind => panic!("unexpected parent kind: {parent_kind:?}"), } } impl Item { /// Returns the effective stability of the item. /// /// This method should only be called after the `propagate-stability` pass has been run. pub(crate) fn stability(&self, tcx: TyCtxt<'_>) -> Option { let stability = self.inner.stability; debug_assert!( stability.is_some() || self.def_id().is_none_or(|did| tcx.lookup_stability(did).is_none()), "missing stability for cleaned item: {self:?}", ); stability } pub(crate) fn const_stability(&self, tcx: TyCtxt<'_>) -> Option { self.def_id().and_then(|did| tcx.lookup_const_stability(did)) } pub(crate) fn deprecation(&self, tcx: TyCtxt<'_>) -> Option { self.def_id().and_then(|did| tcx.lookup_deprecation(did)).or_else(|| { // `allowed_through_unstable_modules` is a bug-compatibility hack for old rustc // versions; the paths that are exposed through it are "deprecated" because they // were never supposed to work at all. let stab = self.stability(tcx)?; if let rustc_hir::StabilityLevel::Stable { allowed_through_unstable_modules: Some(note), .. } = stab.level { Some(Deprecation { since: DeprecatedSince::Unspecified, note: Some(note), suggestion: None, }) } else { None } }) } pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { self.item_id.as_def_id().map(|did| inner_docs(tcx.get_all_attrs(did))).unwrap_or(false) } pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option { let kind = match &self.kind { ItemKind::StrippedItem(k) => k, _ => &self.kind, }; match kind { ItemKind::ModuleItem(Module { span, .. }) => Some(*span), ItemKind::ImplItem(box Impl { kind: ImplKind::Auto, .. }) => None, ItemKind::ImplItem(box Impl { kind: ImplKind::Blanket(_), .. }) => { if let ItemId::Blanket { impl_id, .. } = self.item_id { Some(rustc_span(impl_id, tcx)) } else { panic!("blanket impl item has non-blanket ID") } } _ => self.def_id().map(|did| rustc_span(did, tcx)), } } pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span { span_of_fragments(&self.attrs.doc_strings) .unwrap_or_else(|| self.span(tcx).map_or(DUMMY_SP, |span| span.inner())) } /// Combine all doc strings into a single value handling indentation and newlines as needed. pub(crate) fn doc_value(&self) -> String { self.attrs.doc_value() } /// Combine all doc strings into a single value handling indentation and newlines as needed. /// Returns `None` is there's no documentation at all, and `Some("")` if there is some /// documentation but it is empty (e.g. `#[doc = ""]`). pub(crate) fn opt_doc_value(&self) -> Option { self.attrs.opt_doc_value() } pub(crate) fn from_def_id_and_parts( def_id: DefId, name: Option, kind: ItemKind, cx: &mut DocContext<'_>, ) -> Item { let hir_attrs = cx.tcx.get_all_attrs(def_id); Self::from_def_id_and_attrs_and_parts( def_id, name, kind, Attributes::from_hir(hir_attrs), None, ) } pub(crate) fn from_def_id_and_attrs_and_parts( def_id: DefId, name: Option, kind: ItemKind, attrs: Attributes, cfg: Option>, ) -> Item { trace!("name={name:?}, def_id={def_id:?} cfg={cfg:?}"); Item { inner: Box::new(ItemInner { item_id: def_id.into(), kind, attrs, stability: None, name, cfg, inline_stmt_id: None, }), } } /// If the item has doc comments from a reexport, returns the item id of that reexport, /// otherwise returns returns the item id. /// /// This is used as a key for caching intra-doc link resolution, /// to prevent two reexports of the same item from using the same cache. pub(crate) fn item_or_reexport_id(&self) -> ItemId { // added documentation on a reexport is always prepended. self.attrs .doc_strings .first() .map(|x| x.item_id) .flatten() .map(ItemId::from) .unwrap_or(self.item_id) } pub(crate) fn links(&self, cx: &Context<'_>) -> Vec { use crate::html::format::{href, link_tooltip}; let Some(links) = cx.cache().intra_doc_links.get(&self.item_or_reexport_id()) else { return vec![]; }; links .iter() .filter_map(|ItemLink { link: s, link_text, page_id: id, fragment }| { debug!(?id); if let Ok((mut href, ..)) = href(*id, cx) { debug!(?href); if let Some(ref fragment) = *fragment { fragment.render(&mut href, cx.tcx()) } Some(RenderedLink { original_text: s.clone(), new_text: link_text.clone(), tooltip: link_tooltip(*id, fragment, cx).to_string(), href, }) } else { None } }) .collect() } /// Find a list of all link names, without finding their href. /// /// This is used for generating summary text, which does not include /// the link text, but does need to know which `[]`-bracketed names /// are actually links. pub(crate) fn link_names(&self, cache: &Cache) -> Vec { let Some(links) = cache.intra_doc_links.get(&self.item_id) else { return vec![]; }; links .iter() .map(|ItemLink { link: s, link_text, .. }| RenderedLink { original_text: s.clone(), new_text: link_text.clone(), href: String::new(), tooltip: String::new(), }) .collect() } pub(crate) fn is_crate(&self) -> bool { self.is_mod() && self.def_id().is_some_and(|did| did.is_crate_root()) } pub(crate) fn is_mod(&self) -> bool { self.type_() == ItemType::Module } pub(crate) fn is_struct(&self) -> bool { self.type_() == ItemType::Struct } pub(crate) fn is_enum(&self) -> bool { self.type_() == ItemType::Enum } pub(crate) fn is_variant(&self) -> bool { self.type_() == ItemType::Variant } pub(crate) fn is_associated_type(&self) -> bool { matches!(self.kind, AssocTypeItem(..) | StrippedItem(box AssocTypeItem(..))) } pub(crate) fn is_required_associated_type(&self) -> bool { matches!(self.kind, RequiredAssocTypeItem(..) | StrippedItem(box RequiredAssocTypeItem(..))) } pub(crate) fn is_associated_const(&self) -> bool { matches!(self.kind, ProvidedAssocConstItem(..) | ImplAssocConstItem(..) | StrippedItem(box (ProvidedAssocConstItem(..) | ImplAssocConstItem(..)))) } pub(crate) fn is_required_associated_const(&self) -> bool { matches!(self.kind, RequiredAssocConstItem(..) | StrippedItem(box RequiredAssocConstItem(..))) } pub(crate) fn is_method(&self) -> bool { self.type_() == ItemType::Method } pub(crate) fn is_ty_method(&self) -> bool { self.type_() == ItemType::TyMethod } pub(crate) fn is_primitive(&self) -> bool { self.type_() == ItemType::Primitive } pub(crate) fn is_union(&self) -> bool { self.type_() == ItemType::Union } pub(crate) fn is_import(&self) -> bool { self.type_() == ItemType::Import } pub(crate) fn is_extern_crate(&self) -> bool { self.type_() == ItemType::ExternCrate } pub(crate) fn is_keyword(&self) -> bool { self.type_() == ItemType::Keyword } pub(crate) fn is_attribute(&self) -> bool { self.type_() == ItemType::Attribute } /// Returns `true` if the item kind is one of the following: /// /// * `ItemType::Primitive` /// * `ItemType::Keyword` /// * `ItemType::Attribute` /// /// They are considered fake because they only exist thanks to their /// `#[doc(primitive|keyword|attribute)]` attribute. pub(crate) fn is_fake_item(&self) -> bool { matches!(self.type_(), ItemType::Primitive | ItemType::Keyword | ItemType::Attribute) } pub(crate) fn is_stripped(&self) -> bool { match self.kind { StrippedItem(..) => true, ImportItem(ref i) => !i.should_be_displayed, _ => false, } } pub(crate) fn has_stripped_entries(&self) -> Option { match self.kind { StructItem(ref struct_) => Some(struct_.has_stripped_entries()), UnionItem(ref union_) => Some(union_.has_stripped_entries()), EnumItem(ref enum_) => Some(enum_.has_stripped_entries()), VariantItem(ref v) => v.has_stripped_entries(), TypeAliasItem(ref type_alias) => { type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries()) } _ => None, } } pub(crate) fn stability_class(&self, tcx: TyCtxt<'_>) -> Option { self.stability(tcx).as_ref().and_then(|s| { let mut classes = Vec::with_capacity(2); if s.is_unstable() { classes.push("unstable"); } // FIXME: what about non-staged API items that are deprecated? if self.deprecation(tcx).is_some() { classes.push("deprecated"); } if !classes.is_empty() { Some(classes.join(" ")) } else { None } }) } pub(crate) fn stable_since(&self, tcx: TyCtxt<'_>) -> Option { self.stability(tcx).and_then(|stability| stability.stable_since()) } pub(crate) fn is_non_exhaustive(&self) -> bool { find_attr!(&self.attrs.other_attrs, AttributeKind::NonExhaustive(..)) } /// Returns a documentation-level item type from the item. pub(crate) fn type_(&self) -> ItemType { ItemType::from(self) } pub(crate) fn is_default(&self) -> bool { match self.kind { ItemKind::MethodItem(_, Some(defaultness)) => { defaultness.has_value() && !defaultness.is_final() } _ => false, } } /// Returns a `FnHeader` if `self` is a function item, otherwise returns `None`. pub(crate) fn fn_header(&self, tcx: TyCtxt<'_>) -> Option { fn build_fn_header( def_id: DefId, tcx: TyCtxt<'_>, asyncness: ty::Asyncness, ) -> hir::FnHeader { let sig = tcx.fn_sig(def_id).skip_binder(); let constness = if tcx.is_const_fn(def_id) { // rustc's `is_const_fn` returns `true` for associated functions that have an `impl const` parent // or that have a `const trait` parent. Do not display those as `const` in rustdoc because we // won't be printing correct syntax plus the syntax is unstable. if let Some(assoc) = tcx.opt_associated_item(def_id) && let ty::AssocContainer::Trait | ty::AssocContainer::TraitImpl(_) = assoc.container { hir::Constness::NotConst } else { hir::Constness::Const } } else { hir::Constness::NotConst }; let asyncness = match asyncness { ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP), ty::Asyncness::No => hir::IsAsync::NotAsync, }; hir::FnHeader { safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { hir::HeaderSafety::SafeTargetFeatures } else { sig.safety().into() }, abi: sig.abi(), constness, asyncness, } } let header = match self.kind { ItemKind::ForeignFunctionItem(_, safety) => { let def_id = self.def_id().unwrap(); let abi = tcx.fn_sig(def_id).skip_binder().abi(); hir::FnHeader { safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { hir::HeaderSafety::SafeTargetFeatures } else { safety.into() }, abi, constness: if tcx.is_const_fn(def_id) { hir::Constness::Const } else { hir::Constness::NotConst }, asyncness: hir::IsAsync::NotAsync, } } ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::RequiredMethodItem(_) => { let def_id = self.def_id().unwrap(); build_fn_header(def_id, tcx, tcx.asyncness(def_id)) } _ => return None, }; Some(header) } /// Returns the visibility of the current item. If the visibility is "inherited", then `None` /// is returned. pub(crate) fn visibility(&self, tcx: TyCtxt<'_>) -> Option> { let def_id = match self.item_id { // Anything but DefId *shouldn't* matter, but return a reasonable value anyway. ItemId::Auto { .. } | ItemId::Blanket { .. } => return None, ItemId::DefId(def_id) => def_id, }; match self.kind { // Primitives and Keywords are written in the source code as private modules. // The modules need to be private so that nobody actually uses them, but the // keywords and primitives that they are documenting are public. ItemKind::KeywordItem | ItemKind::PrimitiveItem(_) | ItemKind::AttributeItem => { return Some(Visibility::Public); } // Variant fields inherit their enum's visibility. StructFieldItem(..) if is_field_vis_inherited(tcx, def_id) => { return None; } // Variants always inherit visibility VariantItem(..) | ImplItem(..) => return None, // Trait items inherit the trait's visibility RequiredAssocConstItem(..) | ProvidedAssocConstItem(..) | ImplAssocConstItem(..) | AssocTypeItem(..) | RequiredAssocTypeItem(..) | RequiredMethodItem(..) | MethodItem(..) => { match tcx.associated_item(def_id).container { // Trait impl items always inherit the impl's visibility -- // we don't want to show `pub`. ty::AssocContainer::Trait | ty::AssocContainer::TraitImpl(_) => { return None; } ty::AssocContainer::InherentImpl => {} } } _ => {} } let def_id = match self.inline_stmt_id { Some(inlined) => inlined.to_def_id(), None => def_id, }; Some(tcx.visibility(def_id)) } pub fn is_doc_hidden(&self) -> bool { self.attrs.is_doc_hidden() } pub fn def_id(&self) -> Option { self.item_id.as_def_id() } } #[derive(Clone, Debug)] pub(crate) enum ItemKind { ExternCrateItem { /// The crate's name, *not* the name it's imported as. src: Option, }, ImportItem(Import), StructItem(Struct), UnionItem(Union), EnumItem(Enum), FunctionItem(Box), ModuleItem(Module), TypeAliasItem(Box), StaticItem(Static), TraitItem(Box), TraitAliasItem(TraitAlias), ImplItem(Box), /// A required method in a trait declaration meaning it's only a function signature. RequiredMethodItem(Box), /// A method in a trait impl or a provided method in a trait declaration. /// /// Compared to [RequiredMethodItem], it also contains a method body. MethodItem(Box, Option), StructFieldItem(Type), VariantItem(Variant), /// `fn`s from an extern block ForeignFunctionItem(Box, hir::Safety), /// `static`s from an extern block ForeignStaticItem(Static, hir::Safety), /// `type`s from an extern block ForeignTypeItem, MacroItem(Macro), ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), /// A required associated constant in a trait declaration. RequiredAssocConstItem(Generics, Box), ConstantItem(Box), /// An associated constant in a trait declaration with provided default value. ProvidedAssocConstItem(Box), /// An associated constant in an inherent impl or trait impl. ImplAssocConstItem(Box), /// A required associated type in a trait declaration. /// /// The bounds may be non-empty if there is a `where` clause. RequiredAssocTypeItem(Generics, Vec), /// An associated type in a trait impl or a provided one in a trait declaration. AssocTypeItem(Box, Vec), /// An item that has been stripped by a rustdoc pass StrippedItem(Box), /// This item represents a module with a `#[doc(keyword = "...")]` attribute which is used /// to generate documentation for Rust keywords. KeywordItem, /// This item represents a module with a `#[doc(attribute = "...")]` attribute which is used /// to generate documentation for Rust builtin attributes. AttributeItem, } impl ItemKind { /// Some items contain others such as structs (for their fields) and Enums /// (for their variants). This method returns those contained items. pub(crate) fn inner_items(&self) -> impl Iterator { match self { StructItem(s) => s.fields.iter(), UnionItem(u) => u.fields.iter(), VariantItem(v) => match &v.kind { VariantKind::CLike => [].iter(), VariantKind::Tuple(t) => t.iter(), VariantKind::Struct(s) => s.fields.iter(), }, EnumItem(e) => e.variants.iter(), TraitItem(t) => t.items.iter(), ImplItem(i) => i.items.iter(), ModuleItem(m) => m.items.iter(), ExternCrateItem { .. } | ImportItem(_) | FunctionItem(_) | TypeAliasItem(_) | StaticItem(_) | ConstantItem(_) | TraitAliasItem(_) | RequiredMethodItem(_) | MethodItem(_, _) | StructFieldItem(_) | ForeignFunctionItem(_, _) | ForeignStaticItem(_, _) | ForeignTypeItem | MacroItem(_) | ProcMacroItem(_) | PrimitiveItem(_) | RequiredAssocConstItem(..) | ProvidedAssocConstItem(..) | ImplAssocConstItem(..) | RequiredAssocTypeItem(..) | AssocTypeItem(..) | StrippedItem(_) | KeywordItem | AttributeItem => [].iter(), } } } #[derive(Clone, Debug)] pub(crate) struct Module { pub(crate) items: Vec, pub(crate) span: Span, } pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( attrs: I, name: Symbol, ) -> impl Iterator + use<'a, I> { attrs .into_iter() .filter(move |attr| attr.has_name(name)) .filter_map(ast::attr::AttributeExt::meta_item_list) .flatten() } pub(crate) trait NestedAttributesExt { /// Returns `true` if the attribute list contains a specific `word` fn has_word(self, word: Symbol) -> bool where Self: Sized, { ::get_word_attr(self, word).is_some() } /// Returns `Some(attr)` if the attribute list contains 'attr' /// corresponding to a specific `word` fn get_word_attr(self, word: Symbol) -> Option; } impl> NestedAttributesExt for I { fn get_word_attr(mut self, word: Symbol) -> Option { self.find(|attr| attr.is_word() && attr.has_name(word)) } } /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) struct ItemLink { /// The original link written in the markdown pub(crate) link: Box, /// The link text displayed in the HTML. /// /// This may not be the same as `link` if there was a disambiguator /// in an intra-doc link (e.g. \[`fn@f`\]) pub(crate) link_text: Box, /// The `DefId` of the Item whose **HTML Page** contains the item being /// linked to. This will be different to `item_id` on item's that don't /// have their own page, such as struct fields and enum variants. pub(crate) page_id: DefId, /// The url fragment to append to the link pub(crate) fragment: Option, } pub struct RenderedLink { /// The text the link was original written as. /// /// This could potentially include disambiguators and backticks. pub(crate) original_text: Box, /// The text to display in the HTML pub(crate) new_text: Box, /// The URL to put in the `href` pub(crate) href: String, /// The tooltip. pub(crate) tooltip: String, } /// The attributes on an [`Item`], including attributes like `#[derive(...)]` and `#[inline]`, /// as well as doc comments. #[derive(Clone, Debug, Default)] pub(crate) struct Attributes { pub(crate) doc_strings: Vec, pub(crate) other_attrs: ThinVec, } impl Attributes { pub(crate) fn lists(&self, name: Symbol) -> impl Iterator { hir_attr_lists(&self.other_attrs[..], name) } pub(crate) fn has_doc_flag(&self, flag: Symbol) -> bool { for attr in &self.other_attrs { if !attr.has_name(sym::doc) { continue; } if let Some(items) = attr.meta_item_list() && items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag)) { return true; } } false } pub(crate) fn is_doc_hidden(&self) -> bool { self.has_doc_flag(sym::hidden) } pub(crate) fn from_hir(attrs: &[hir::Attribute]) -> Attributes { Attributes::from_hir_iter(attrs.iter().map(|attr| (attr, None)), false) } pub(crate) fn from_hir_with_additional( attrs: &[hir::Attribute], (additional_attrs, def_id): (&[hir::Attribute], DefId), ) -> Attributes { // Additional documentation should be shown before the original documentation. let attrs1 = additional_attrs.iter().map(|attr| (attr, Some(def_id))); let attrs2 = attrs.iter().map(|attr| (attr, None)); Attributes::from_hir_iter(attrs1.chain(attrs2), false) } pub(crate) fn from_hir_iter<'a>( attrs: impl Iterator)>, doc_only: bool, ) -> Attributes { let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only); Attributes { doc_strings, other_attrs } } /// Combine all doc strings into a single value handling indentation and newlines as needed. pub(crate) fn doc_value(&self) -> String { self.opt_doc_value().unwrap_or_default() } /// Combine all doc strings into a single value handling indentation and newlines as needed. /// Returns `None` is there's no documentation at all, and `Some("")` if there is some /// documentation but it is empty (e.g. `#[doc = ""]`). pub(crate) fn opt_doc_value(&self) -> Option { (!self.doc_strings.is_empty()).then(|| { let mut res = String::new(); for frag in &self.doc_strings { add_doc_fragment(&mut res, frag); } res.pop(); res }) } pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> { let mut aliases = FxIndexSet::default(); for attr in hir_attr_lists(&self.other_attrs[..], sym::doc).filter(|a| a.has_name(sym::alias)) { if let Some(values) = attr.meta_item_list() { for l in values { if let Some(lit) = l.lit() && let ast::LitKind::Str(s, _) = lit.kind { aliases.insert(s); } } } else if let Some(value) = attr.value_str() { aliases.insert(value); } } aliases.into_iter().collect::>().into() } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericBound { TraitBound(PolyTrait, hir::TraitBoundModifiers), Outlives(Lifetime), /// `use<'a, T>` precise-capturing bound syntax Use(Vec), } impl GenericBound { pub(crate) fn sized(cx: &mut DocContext<'_>) -> GenericBound { Self::sized_with(cx, hir::TraitBoundModifiers::NONE) } pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { Self::sized_with( cx, hir::TraitBoundModifiers { polarity: hir::BoundPolarity::Maybe(DUMMY_SP), constness: hir::BoundConstness::Never, }, ) } fn sized_with(cx: &mut DocContext<'_>, modifiers: hir::TraitBoundModifiers) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, DUMMY_SP); let empty = ty::Binder::dummy(ty::GenericArgs::empty()); let path = clean_middle_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifiers) } pub(crate) fn is_trait_bound(&self) -> bool { matches!(self, Self::TraitBound(..)) } pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { self.is_bounded_by_lang_item(cx, LangItem::Sized) } pub(crate) fn is_meta_sized_bound(&self, cx: &DocContext<'_>) -> bool { self.is_bounded_by_lang_item(cx, LangItem::MetaSized) } fn is_bounded_by_lang_item(&self, cx: &DocContext<'_>, lang_item: LangItem) -> bool { if let GenericBound::TraitBound( PolyTrait { ref trait_, .. }, rustc_hir::TraitBoundModifiers::NONE, ) = *self && cx.tcx.is_lang_item(trait_.def_id(), lang_item) { return true; } false } pub(crate) fn get_trait_path(&self) -> Option { if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self { Some(trait_.clone()) } else { None } } } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) struct Lifetime(pub Symbol); impl Lifetime { pub(crate) fn statik() -> Lifetime { Lifetime(kw::StaticLifetime) } pub(crate) fn elided() -> Lifetime { Lifetime(kw::UnderscoreLifetime) } } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) enum PreciseCapturingArg { Lifetime(Lifetime), Param(Symbol), } impl PreciseCapturingArg { pub(crate) fn name(self) -> Symbol { match self { PreciseCapturingArg::Lifetime(lt) => lt.0, PreciseCapturingArg::Param(param) => param, } } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, EqPredicate { lhs: QPathData, rhs: Term }, } impl WherePredicate { pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> { match self { WherePredicate::BoundPredicate { bounds, .. } => Some(bounds), WherePredicate::RegionPredicate { bounds, .. } => Some(bounds), _ => None, } } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericParamDefKind { Lifetime { outlives: ThinVec }, Type { bounds: ThinVec, default: Option>, synthetic: bool }, // Option> makes this type smaller than `Option` would. Const { ty: Box, default: Option> }, } impl GenericParamDefKind { pub(crate) fn is_type(&self) -> bool { matches!(self, GenericParamDefKind::Type { .. }) } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct GenericParamDef { pub(crate) name: Symbol, pub(crate) def_id: DefId, pub(crate) kind: GenericParamDefKind, } impl GenericParamDef { pub(crate) fn lifetime(def_id: DefId, name: Symbol) -> Self { Self { name, def_id, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } } } pub(crate) fn is_synthetic_param(&self) -> bool { match self.kind { GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false, GenericParamDefKind::Type { synthetic, .. } => synthetic, } } pub(crate) fn is_type(&self) -> bool { self.kind.is_type() } pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> { match self.kind { GenericParamDefKind::Type { ref bounds, .. } => Some(bounds), _ => None, } } } // maybe use a Generic enum and use Vec? #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct Generics { pub(crate) params: ThinVec, pub(crate) where_predicates: ThinVec, } impl Generics { pub(crate) fn is_empty(&self) -> bool { self.params.is_empty() && self.where_predicates.is_empty() } } #[derive(Clone, Debug)] pub(crate) struct Function { pub(crate) decl: FnDecl, pub(crate) generics: Generics, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct FnDecl { pub(crate) inputs: Vec, pub(crate) output: Type, pub(crate) c_variadic: bool, } impl FnDecl { pub(crate) fn receiver_type(&self) -> Option<&Type> { self.inputs.first().and_then(|v| v.to_receiver()) } } /// A function parameter. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct Parameter { pub(crate) name: Option, pub(crate) type_: Type, /// This field is used to represent "const" arguments from the `rustc_legacy_const_generics` /// feature. More information in . pub(crate) is_const: bool, } impl Parameter { pub(crate) fn to_receiver(&self) -> Option<&Type> { if self.name == Some(kw::SelfLower) { Some(&self.type_) } else { None } } } #[derive(Clone, Debug)] pub(crate) struct Trait { pub(crate) def_id: DefId, pub(crate) items: Vec, pub(crate) generics: Generics, pub(crate) bounds: Vec, } impl Trait { pub(crate) fn is_auto(&self, tcx: TyCtxt<'_>) -> bool { tcx.trait_is_auto(self.def_id) } pub(crate) fn is_notable_trait(&self, tcx: TyCtxt<'_>) -> bool { tcx.is_doc_notable_trait(self.def_id) } pub(crate) fn safety(&self, tcx: TyCtxt<'_>) -> hir::Safety { tcx.trait_def(self.def_id).safety } pub(crate) fn is_dyn_compatible(&self, tcx: TyCtxt<'_>) -> bool { tcx.is_dyn_compatible(self.def_id) } } #[derive(Clone, Debug)] pub(crate) struct TraitAlias { pub(crate) generics: Generics, pub(crate) bounds: Vec, } /// A trait reference, which may have higher ranked lifetimes. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct PolyTrait { pub(crate) trait_: Path, pub(crate) generic_params: Vec, } /// Rustdoc's representation of types, mostly based on the [`hir::Ty`]. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum Type { /// A named type, which could be a trait. /// /// This is mostly Rustdoc's version of [`hir::Path`]. /// It has to be different because Rustdoc's [`PathSegment`] can contain cleaned generics. Path { path: Path, }, /// A `dyn Trait` object: `dyn for<'a> Trait<'a> + Send + 'static` DynTrait(Vec, Option), /// A type parameter. Generic(Symbol), /// The `Self` type. SelfTy, /// A primitive (aka, builtin) type. Primitive(PrimitiveType), /// A function pointer: `extern "ABI" fn(...) -> ...` BareFunction(Box), /// A tuple type: `(i32, &str)`. Tuple(Vec), /// A slice type (does *not* include the `&`): `[i32]` Slice(Box), /// An array type. /// /// The `String` field is a stringified version of the array's length parameter. Array(Box, Box), Pat(Box, Box), /// A raw pointer type: `*const i32`, `*mut i32` RawPointer(Mutability, Box), /// A reference type: `&i32`, `&'a mut Foo` BorrowedRef { lifetime: Option, mutability: Mutability, type_: Box, }, /// A qualified path to an associated item: `::Name` QPath(Box), /// A type that is inferred: `_` Infer, /// An `impl Trait`: `impl TraitA + TraitB + ...` ImplTrait(Vec), UnsafeBinder(Box), } impl Type { /// When comparing types for equality, it can help to ignore `&` wrapping. pub(crate) fn without_borrowed_ref(&self) -> &Type { let mut result = self; while let Type::BorrowedRef { type_, .. } = result { result = type_; } result } pub(crate) fn is_borrowed_ref(&self) -> bool { matches!(self, Type::BorrowedRef { .. }) } fn is_type_alias(&self) -> bool { matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } }) } /// Check if this type is a subtype of another type for documentation purposes. /// /// This is different from `Eq`, because it knows that things like /// `Infer` and generics have special subtyping rules. /// /// This relation is not commutative when generics are involved: /// /// ```ignore(private) /// # // see types/tests.rs:is_same_generic for the real test /// use rustdoc::format::cache::Cache; /// use rustdoc::clean::types::{Type, PrimitiveType}; /// let cache = Cache::new(false); /// let generic = Type::Generic(rustc_span::symbol::sym::Any); /// let unit = Type::Primitive(PrimitiveType::Unit); /// assert!(!generic.is_doc_subtype_of(&unit, &cache)); /// assert!(unit.is_doc_subtype_of(&generic, &cache)); /// ``` /// /// An owned type is also the same as its borrowed variants (this is commutative), /// but `&T` is not the same as `&mut T`. pub(crate) fn is_doc_subtype_of(&self, other: &Self, cache: &Cache) -> bool { // Strip the references so that it can compare the actual types, unless both are references. // If both are references, leave them alone and compare the mutabilities later. let (self_cleared, other_cleared) = if !self.is_borrowed_ref() || !other.is_borrowed_ref() { (self.without_borrowed_ref(), other.without_borrowed_ref()) } else { (self, other) }; // FIXME: `Cache` does not have the data required to unwrap type aliases, // so we just assume they are equal. // This is only remotely acceptable because we were previously // assuming all types were equal when used // as a generic parameter of a type in `Deref::Target`. if self_cleared.is_type_alias() || other_cleared.is_type_alias() { return true; } match (self_cleared, other_cleared) { // Recursive cases. (Type::Tuple(a), Type::Tuple(b)) => { a.iter().eq_by(b, |a, b| a.is_doc_subtype_of(b, cache)) } (Type::Slice(a), Type::Slice(b)) => a.is_doc_subtype_of(b, cache), (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_doc_subtype_of(b, cache), (Type::RawPointer(mutability, type_), Type::RawPointer(b_mutability, b_type_)) => { mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cache) } ( Type::BorrowedRef { mutability, type_, .. }, Type::BorrowedRef { mutability: b_mutability, type_: b_type_, .. }, ) => mutability == b_mutability && type_.is_doc_subtype_of(b_type_, cache), // Placeholders are equal to all other types. (Type::Infer, _) | (_, Type::Infer) => true, // Generics match everything on the right, but not on the left. // If both sides are generic, this returns true. (_, Type::Generic(_)) => true, (Type::Generic(_), _) => false, // `Self` only matches itself. (Type::SelfTy, Type::SelfTy) => true, // Paths account for both the path itself and its generics. (Type::Path { path: a }, Type::Path { path: b }) => { a.def_id() == b.def_id() && a.generics() .zip(b.generics()) .map(|(ag, bg)| ag.zip(bg).all(|(at, bt)| at.is_doc_subtype_of(bt, cache))) .unwrap_or(true) } // Other cases, such as primitives, just use recursion. (a, b) => a .def_id(cache) .and_then(|a| Some((a, b.def_id(cache)?))) .map(|(a, b)| a == b) .unwrap_or(false), } } pub(crate) fn primitive_type(&self) -> Option { match *self { Primitive(p) | BorrowedRef { type_: box Primitive(p), .. } => Some(p), Slice(..) | BorrowedRef { type_: box Slice(..), .. } => Some(PrimitiveType::Slice), Array(..) | BorrowedRef { type_: box Array(..), .. } => Some(PrimitiveType::Array), Tuple(ref tys) => { if tys.is_empty() { Some(PrimitiveType::Unit) } else { Some(PrimitiveType::Tuple) } } RawPointer(..) => Some(PrimitiveType::RawPointer), BareFunction(..) => Some(PrimitiveType::Fn), _ => None, } } /// Returns the sugared return type for an async function. /// /// For example, if the return type is `impl std::future::Future`, this function /// will return `i32`. /// /// # Panics /// /// This function will panic if the return type does not match the expected sugaring for async /// functions. pub(crate) fn sugared_async_return_type(self) -> Type { if let Type::ImplTrait(mut v) = self && let Some(GenericBound::TraitBound(PolyTrait { mut trait_, .. }, _)) = v.pop() && let Some(segment) = trait_.segments.pop() && let GenericArgs::AngleBracketed { mut constraints, .. } = segment.args && let Some(constraint) = constraints.pop() && let AssocItemConstraintKind::Equality { term } = constraint.kind && let Term::Type(ty) = term { ty } else { panic!("unexpected async fn return type") } } /// Checks if this is a `T::Name` path for an associated type. pub(crate) fn is_assoc_ty(&self) -> bool { match self { Type::Path { path, .. } => path.is_assoc_ty(), _ => false, } } pub(crate) fn is_self_type(&self) -> bool { matches!(*self, Type::SelfTy) } pub(crate) fn generic_args(&self) -> Option<&GenericArgs> { match self { Type::Path { path, .. } => path.generic_args(), _ => None, } } pub(crate) fn generics(&self) -> Option> { match self { Type::Path { path, .. } => path.generics(), _ => None, } } pub(crate) fn is_full_generic(&self) -> bool { matches!(self, Type::Generic(_)) } pub(crate) fn is_unit(&self) -> bool { matches!(self, Type::Tuple(v) if v.is_empty()) } /// Use this method to get the [DefId] of a [clean] AST node, including [PrimitiveType]s. /// /// [clean]: crate::clean pub(crate) fn def_id(&self, cache: &Cache) -> Option { let t: PrimitiveType = match self { Type::Path { path } => return Some(path.def_id()), DynTrait(bounds, _) => return bounds.first().map(|b| b.trait_.def_id()), Primitive(p) => return cache.primitive_locations.get(p).cloned(), BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, BorrowedRef { type_, .. } => return type_.def_id(cache), Tuple(tys) => { if tys.is_empty() { PrimitiveType::Unit } else { PrimitiveType::Tuple } } BareFunction(..) => PrimitiveType::Fn, Slice(..) => PrimitiveType::Slice, Array(..) => PrimitiveType::Array, Type::Pat(..) => PrimitiveType::Pat, RawPointer(..) => PrimitiveType::RawPointer, QPath(box QPathData { self_type, .. }) => return self_type.def_id(cache), Generic(_) | SelfTy | Infer | ImplTrait(_) | UnsafeBinder(_) => return None, }; Primitive(t).def_id(cache) } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct QPathData { pub assoc: PathSegment, pub self_type: Type, /// FIXME: compute this field on demand. pub should_fully_qualify: bool, pub trait_: Option, } /// A primitive (aka, builtin) type. /// /// This represents things like `i32`, `str`, etc. /// /// N.B. This has to be different from [`hir::PrimTy`] because it also includes types that aren't /// paths, like [`Self::Unit`]. #[derive(Clone, PartialEq, Eq, Hash, Copy, Debug)] pub(crate) enum PrimitiveType { Isize, I8, I16, I32, I64, I128, Usize, U8, U16, U32, U64, U128, F16, F32, F64, F128, Char, Bool, Str, Slice, Array, Pat, Tuple, Unit, RawPointer, Reference, Fn, Never, } type SimplifiedTypes = FxIndexMap>; impl PrimitiveType { pub(crate) fn from_hir(prim: hir::PrimTy) -> PrimitiveType { use ast::{FloatTy, IntTy, UintTy}; match prim { hir::PrimTy::Int(IntTy::Isize) => PrimitiveType::Isize, hir::PrimTy::Int(IntTy::I8) => PrimitiveType::I8, hir::PrimTy::Int(IntTy::I16) => PrimitiveType::I16, hir::PrimTy::Int(IntTy::I32) => PrimitiveType::I32, hir::PrimTy::Int(IntTy::I64) => PrimitiveType::I64, hir::PrimTy::Int(IntTy::I128) => PrimitiveType::I128, hir::PrimTy::Uint(UintTy::Usize) => PrimitiveType::Usize, hir::PrimTy::Uint(UintTy::U8) => PrimitiveType::U8, hir::PrimTy::Uint(UintTy::U16) => PrimitiveType::U16, hir::PrimTy::Uint(UintTy::U32) => PrimitiveType::U32, hir::PrimTy::Uint(UintTy::U64) => PrimitiveType::U64, hir::PrimTy::Uint(UintTy::U128) => PrimitiveType::U128, hir::PrimTy::Float(FloatTy::F16) => PrimitiveType::F16, hir::PrimTy::Float(FloatTy::F32) => PrimitiveType::F32, hir::PrimTy::Float(FloatTy::F64) => PrimitiveType::F64, hir::PrimTy::Float(FloatTy::F128) => PrimitiveType::F128, hir::PrimTy::Str => PrimitiveType::Str, hir::PrimTy::Bool => PrimitiveType::Bool, hir::PrimTy::Char => PrimitiveType::Char, } } pub(crate) fn from_symbol(s: Symbol) -> Option { match s { sym::isize => Some(PrimitiveType::Isize), sym::i8 => Some(PrimitiveType::I8), sym::i16 => Some(PrimitiveType::I16), sym::i32 => Some(PrimitiveType::I32), sym::i64 => Some(PrimitiveType::I64), sym::i128 => Some(PrimitiveType::I128), sym::usize => Some(PrimitiveType::Usize), sym::u8 => Some(PrimitiveType::U8), sym::u16 => Some(PrimitiveType::U16), sym::u32 => Some(PrimitiveType::U32), sym::u64 => Some(PrimitiveType::U64), sym::u128 => Some(PrimitiveType::U128), sym::bool => Some(PrimitiveType::Bool), sym::char => Some(PrimitiveType::Char), sym::str => Some(PrimitiveType::Str), sym::f16 => Some(PrimitiveType::F16), sym::f32 => Some(PrimitiveType::F32), sym::f64 => Some(PrimitiveType::F64), sym::f128 => Some(PrimitiveType::F128), sym::array => Some(PrimitiveType::Array), sym::slice => Some(PrimitiveType::Slice), sym::tuple => Some(PrimitiveType::Tuple), sym::unit => Some(PrimitiveType::Unit), sym::pointer => Some(PrimitiveType::RawPointer), sym::reference => Some(PrimitiveType::Reference), kw::Fn => Some(PrimitiveType::Fn), sym::never => Some(PrimitiveType::Never), _ => None, } } pub(crate) fn simplified_types() -> &'static SimplifiedTypes { use PrimitiveType::*; use ty::{FloatTy, IntTy, UintTy}; static CELL: OnceCell = OnceCell::new(); let single = |x| iter::once(x).collect(); CELL.get_or_init(move || { map! { Isize => single(SimplifiedType::Int(IntTy::Isize)), I8 => single(SimplifiedType::Int(IntTy::I8)), I16 => single(SimplifiedType::Int(IntTy::I16)), I32 => single(SimplifiedType::Int(IntTy::I32)), I64 => single(SimplifiedType::Int(IntTy::I64)), I128 => single(SimplifiedType::Int(IntTy::I128)), Usize => single(SimplifiedType::Uint(UintTy::Usize)), U8 => single(SimplifiedType::Uint(UintTy::U8)), U16 => single(SimplifiedType::Uint(UintTy::U16)), U32 => single(SimplifiedType::Uint(UintTy::U32)), U64 => single(SimplifiedType::Uint(UintTy::U64)), U128 => single(SimplifiedType::Uint(UintTy::U128)), F16 => single(SimplifiedType::Float(FloatTy::F16)), F32 => single(SimplifiedType::Float(FloatTy::F32)), F64 => single(SimplifiedType::Float(FloatTy::F64)), F128 => single(SimplifiedType::Float(FloatTy::F128)), Str => single(SimplifiedType::Str), Bool => single(SimplifiedType::Bool), Char => single(SimplifiedType::Char), Array => single(SimplifiedType::Array), Slice => single(SimplifiedType::Slice), // FIXME: If we ever add an inherent impl for tuples // with different lengths, they won't show in rustdoc. // // Either manually update this arrayvec at this point // or start with a more complex refactoring. Tuple => [SimplifiedType::Tuple(1), SimplifiedType::Tuple(2), SimplifiedType::Tuple(3)].into(), Unit => single(SimplifiedType::Tuple(0)), RawPointer => [SimplifiedType::Ptr(Mutability::Not), SimplifiedType::Ptr(Mutability::Mut)].into_iter().collect(), Reference => [SimplifiedType::Ref(Mutability::Not), SimplifiedType::Ref(Mutability::Mut)].into_iter().collect(), // FIXME: This will be wrong if we ever add inherent impls // for function pointers. Fn => single(SimplifiedType::Function(1)), Never => single(SimplifiedType::Never), } }) } pub(crate) fn impls<'tcx>(&self, tcx: TyCtxt<'tcx>) -> impl Iterator + 'tcx { Self::simplified_types() .get(self) .into_iter() .flatten() .flat_map(move |&simp| tcx.incoherent_impls(simp).iter()) .copied() } pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator { Self::simplified_types() .values() .flatten() .flat_map(move |&simp| tcx.incoherent_impls(simp).iter()) .copied() } pub(crate) fn as_sym(&self) -> Symbol { use PrimitiveType::*; match self { Isize => sym::isize, I8 => sym::i8, I16 => sym::i16, I32 => sym::i32, I64 => sym::i64, I128 => sym::i128, Usize => sym::usize, U8 => sym::u8, U16 => sym::u16, U32 => sym::u32, U64 => sym::u64, U128 => sym::u128, F16 => sym::f16, F32 => sym::f32, F64 => sym::f64, F128 => sym::f128, Str => sym::str, Bool => sym::bool, Char => sym::char, Array => sym::array, Pat => sym::pat, Slice => sym::slice, Tuple => sym::tuple, Unit => sym::unit, RawPointer => sym::pointer, Reference => sym::reference, Fn => kw::Fn, Never => sym::never, } } /// Returns the DefId of the module with `rustc_doc_primitive` for this primitive type. /// Panics if there is no such module. /// /// This gives precedence to primitives defined in the current crate, and deprioritizes /// primitives defined in `core`, /// but otherwise, if multiple crates define the same primitive, there is no guarantee of which /// will be picked. /// /// In particular, if a crate depends on both `std` and another crate that also defines /// `rustc_doc_primitive`, then it's entirely random whether `std` or the other crate is picked. /// (no_std crates are usually fine unless multiple dependencies define a primitive.) pub(crate) fn primitive_locations(tcx: TyCtxt<'_>) -> &FxIndexMap { static PRIMITIVE_LOCATIONS: OnceCell> = OnceCell::new(); PRIMITIVE_LOCATIONS.get_or_init(|| { let mut primitive_locations = FxIndexMap::default(); // NOTE: technically this misses crates that are only passed with `--extern` and not loaded when checking the crate. // This is a degenerate case that I don't plan to support. for &crate_num in tcx.crates(()) { let e = ExternalCrate { crate_num }; let crate_name = e.name(tcx); debug!(?crate_num, ?crate_name); for (def_id, prim) in e.primitives(tcx) { // HACK: try to link to std instead where possible if crate_name == sym::core && primitive_locations.contains_key(&prim) { continue; } primitive_locations.insert(prim, def_id); } } let local_primitives = ExternalCrate { crate_num: LOCAL_CRATE }.primitives(tcx); for (def_id, prim) in local_primitives { primitive_locations.insert(prim, def_id); } primitive_locations }) } } impl From for PrimitiveType { fn from(int_ty: ty::IntTy) -> PrimitiveType { match int_ty { ty::IntTy::Isize => PrimitiveType::Isize, ty::IntTy::I8 => PrimitiveType::I8, ty::IntTy::I16 => PrimitiveType::I16, ty::IntTy::I32 => PrimitiveType::I32, ty::IntTy::I64 => PrimitiveType::I64, ty::IntTy::I128 => PrimitiveType::I128, } } } impl From for PrimitiveType { fn from(uint_ty: ty::UintTy) -> PrimitiveType { match uint_ty { ty::UintTy::Usize => PrimitiveType::Usize, ty::UintTy::U8 => PrimitiveType::U8, ty::UintTy::U16 => PrimitiveType::U16, ty::UintTy::U32 => PrimitiveType::U32, ty::UintTy::U64 => PrimitiveType::U64, ty::UintTy::U128 => PrimitiveType::U128, } } } impl From for PrimitiveType { fn from(float_ty: ty::FloatTy) -> PrimitiveType { match float_ty { ty::FloatTy::F16 => PrimitiveType::F16, ty::FloatTy::F32 => PrimitiveType::F32, ty::FloatTy::F64 => PrimitiveType::F64, ty::FloatTy::F128 => PrimitiveType::F128, } } } impl From for PrimitiveType { fn from(prim_ty: hir::PrimTy) -> PrimitiveType { match prim_ty { hir::PrimTy::Int(int_ty) => int_ty.into(), hir::PrimTy::Uint(uint_ty) => uint_ty.into(), hir::PrimTy::Float(float_ty) => float_ty.into(), hir::PrimTy::Str => PrimitiveType::Str, hir::PrimTy::Bool => PrimitiveType::Bool, hir::PrimTy::Char => PrimitiveType::Char, } } } #[derive(Clone, Debug)] pub(crate) struct Struct { pub(crate) ctor_kind: Option, pub(crate) generics: Generics, pub(crate) fields: ThinVec, } impl Struct { pub(crate) fn has_stripped_entries(&self) -> bool { self.fields.iter().any(|f| f.is_stripped()) } } #[derive(Clone, Debug)] pub(crate) struct Union { pub(crate) generics: Generics, pub(crate) fields: Vec, } impl Union { pub(crate) fn has_stripped_entries(&self) -> bool { self.fields.iter().any(|f| f.is_stripped()) } } /// This is a more limited form of the standard Struct, different in that /// it lacks the things most items have (name, id, parameterization). Found /// only as a variant in an enum. #[derive(Clone, Debug)] pub(crate) struct VariantStruct { pub(crate) fields: ThinVec, } impl VariantStruct { pub(crate) fn has_stripped_entries(&self) -> bool { self.fields.iter().any(|f| f.is_stripped()) } } #[derive(Clone, Debug)] pub(crate) struct Enum { pub(crate) variants: IndexVec, pub(crate) generics: Generics, } impl Enum { pub(crate) fn has_stripped_entries(&self) -> bool { self.variants.iter().any(|f| f.is_stripped()) } pub(crate) fn non_stripped_variants(&self) -> impl Iterator { self.variants.iter().filter(|v| !v.is_stripped()) } } #[derive(Clone, Debug)] pub(crate) struct Variant { pub kind: VariantKind, pub discriminant: Option, } #[derive(Clone, Debug)] pub(crate) enum VariantKind { CLike, Tuple(ThinVec), Struct(VariantStruct), } impl Variant { pub(crate) fn has_stripped_entries(&self) -> Option { match &self.kind { VariantKind::Struct(struct_) => Some(struct_.has_stripped_entries()), VariantKind::CLike | VariantKind::Tuple(_) => None, } } } #[derive(Clone, Debug)] pub(crate) struct Discriminant { // In the case of cross crate re-exports, we don't have the necessary information // to reconstruct the expression of the discriminant, only the value. pub(super) expr: Option, pub(super) value: DefId, } impl Discriminant { /// Will be `None` in the case of cross-crate reexports, and may be /// simplified pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option { self.expr .map(|body| rendered_const(tcx, tcx.hir_body(body), tcx.hir_body_owner_def_id(body))) } pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String { print_evaluated_const(tcx, self.value, with_underscores, false).unwrap() } } /// Small wrapper around [`rustc_span::Span`] that adds helper methods /// and enforces calling [`rustc_span::Span::source_callsite()`]. #[derive(Copy, Clone, Debug)] pub(crate) struct Span(rustc_span::Span); impl Span { /// Wraps a [`rustc_span::Span`]. In case this span is the result of a macro expansion, the /// span will be updated to point to the macro invocation instead of the macro definition. /// /// (See rust-lang/rust#39726) pub(crate) fn new(sp: rustc_span::Span) -> Self { Self(sp.source_callsite()) } pub(crate) fn inner(&self) -> rustc_span::Span { self.0 } pub(crate) fn filename(&self, sess: &Session) -> FileName { sess.source_map().span_to_filename(self.0) } pub(crate) fn lo(&self, sess: &Session) -> Loc { sess.source_map().lookup_char_pos(self.0.lo()) } pub(crate) fn hi(&self, sess: &Session) -> Loc { sess.source_map().lookup_char_pos(self.0.hi()) } pub(crate) fn cnum(&self, sess: &Session) -> CrateNum { // FIXME: is there a time when the lo and hi crate would be different? self.lo(sess).file.cnum } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct Path { pub(crate) res: Res, pub(crate) segments: ThinVec, } impl Path { pub(crate) fn def_id(&self) -> DefId { self.res.def_id() } pub(crate) fn last_opt(&self) -> Option { self.segments.last().map(|s| s.name) } pub(crate) fn last(&self) -> Symbol { self.last_opt().expect("segments were empty") } pub(crate) fn whole_name(&self) -> String { self.segments .iter() .map(|s| if s.name == kw::PathRoot { "" } else { s.name.as_str() }) .intersperse("::") .collect() } /// Checks if this is a `T::Name` path for an associated type. pub(crate) fn is_assoc_ty(&self) -> bool { match self.res { Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::Def(DefKind::TyParam, _) if self.segments.len() != 1 => { true } Res::Def(DefKind::AssocTy, _) => true, _ => false, } } pub(crate) fn generic_args(&self) -> Option<&GenericArgs> { self.segments.last().map(|seg| &seg.args) } pub(crate) fn generics(&self) -> Option> { self.segments.last().and_then(|seg| { if let GenericArgs::AngleBracketed { ref args, .. } = seg.args { Some(args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, })) } else { None } }) } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArg { Lifetime(Lifetime), Type(Type), Const(Box), Infer, } impl GenericArg { pub(crate) fn as_lt(&self) -> Option<&Lifetime> { if let Self::Lifetime(lt) = self { Some(lt) } else { None } } pub(crate) fn as_ty(&self) -> Option<&Type> { if let Self::Type(ty) = self { Some(ty) } else { None } } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArgs { /// `` AngleBracketed { args: ThinVec, constraints: ThinVec }, /// `(inputs) -> output` Parenthesized { inputs: ThinVec, output: Option> }, /// `(..)` ReturnTypeNotation, } impl GenericArgs { pub(crate) fn is_empty(&self) -> bool { match self { GenericArgs::AngleBracketed { args, constraints } => { args.is_empty() && constraints.is_empty() } GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), GenericArgs::ReturnTypeNotation => false, } } pub(crate) fn constraints(&self) -> Box + '_> { match self { GenericArgs::AngleBracketed { constraints, .. } => { Box::new(constraints.iter().cloned()) } GenericArgs::Parenthesized { output, .. } => Box::new( output .as_ref() .map(|ty| AssocItemConstraint { assoc: PathSegment { name: sym::Output, args: GenericArgs::AngleBracketed { args: ThinVec::new(), constraints: ThinVec::new(), }, }, kind: AssocItemConstraintKind::Equality { term: Term::Type((**ty).clone()), }, }) .into_iter(), ), GenericArgs::ReturnTypeNotation => Box::new([].into_iter()), } } } impl<'a> IntoIterator for &'a GenericArgs { type IntoIter = Box + 'a>; type Item = GenericArg; fn into_iter(self) -> Self::IntoIter { match self { GenericArgs::AngleBracketed { args, .. } => Box::new(args.iter().cloned()), GenericArgs::Parenthesized { inputs, .. } => { // FIXME: This isn't really right, since `Fn(A, B)` is `Fn<(A, B)>` Box::new(inputs.iter().cloned().map(GenericArg::Type)) } GenericArgs::ReturnTypeNotation => Box::new([].into_iter()), } } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct PathSegment { pub(crate) name: Symbol, pub(crate) args: GenericArgs, } #[derive(Clone, Debug)] pub(crate) enum TypeAliasInnerType { Enum { variants: IndexVec, is_non_exhaustive: bool }, Union { fields: Vec }, Struct { ctor_kind: Option, fields: Vec }, } impl TypeAliasInnerType { fn has_stripped_entries(&self) -> Option { Some(match self { Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()), Self::Union { fields } | Self::Struct { fields, .. } => { fields.iter().any(|f| f.is_stripped()) } }) } } #[derive(Clone, Debug)] pub(crate) struct TypeAlias { pub(crate) type_: Type, pub(crate) generics: Generics, /// Inner `AdtDef` type, ie `type TyKind = IrTyKind`, /// to be shown directly on the typedef page. pub(crate) inner_type: Option, /// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type /// alias instead of the final type. This will always have the final type, regardless of whether /// `type_` came from HIR or from metadata. /// /// If `item_type.is_none()`, `type_` is guaranteed to come from metadata (and therefore hold the /// final type). pub(crate) item_type: Option, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct BareFunctionDecl { pub(crate) safety: hir::Safety, pub(crate) generic_params: Vec, pub(crate) decl: FnDecl, pub(crate) abi: ExternAbi, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct UnsafeBinderTy { pub(crate) generic_params: Vec, pub(crate) ty: Type, } #[derive(Clone, Debug)] pub(crate) struct Static { pub(crate) type_: Box, pub(crate) mutability: Mutability, pub(crate) expr: Option, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Constant { pub(crate) generics: Generics, pub(crate) kind: ConstantKind, pub(crate) type_: Type, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum Term { Type(Type), Constant(ConstantKind), } impl Term { pub(crate) fn ty(&self) -> Option<&Type> { if let Term::Type(ty) = self { Some(ty) } else { None } } } impl From for Term { fn from(ty: Type) -> Self { Term::Type(ty) } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum ConstantKind { /// This is the wrapper around `ty::Const` for a non-local constant. Because it doesn't have a /// `BodyId`, we need to handle it on its own. /// /// Note that `ty::Const` includes generic parameters, and may not always be uniquely identified /// by a DefId. So this field must be different from `Extern`. TyConst { expr: Box }, /// A constant that is just a path (i.e., referring to a const param, free const, etc.). // FIXME: this is an unfortunate representation. rustdoc's logic around consts needs to be improved. Path { path: Box }, /// A constant (expression) that's not an item or associated item. These are usually found /// nested inside types (e.g., array lengths) or expressions (e.g., repeat counts), and also /// used to define explicit discriminant values for enum variants. Anonymous { body: BodyId }, /// A constant from a different crate. Extern { def_id: DefId }, /// `const FOO: u32 = ...;` Local { def_id: DefId, body: BodyId }, /// An inferred constant as in `[10u8; _]`. Infer, } impl ConstantKind { pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String { match *self { ConstantKind::TyConst { ref expr } => expr.to_string(), ConstantKind::Path { ref path } => path.to_string(), ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { rendered_const(tcx, tcx.hir_body(body), tcx.hir_body_owner_def_id(body)) } ConstantKind::Infer => "_".to_string(), } } pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option { match *self { ConstantKind::TyConst { .. } | ConstantKind::Path { .. } | ConstantKind::Anonymous { .. } | ConstantKind::Infer => None, ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { print_evaluated_const(tcx, def_id, true, true) } } } pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { match *self { ConstantKind::TyConst { .. } | ConstantKind::Extern { .. } | ConstantKind::Path { .. } | ConstantKind::Infer => false, ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { is_literal_expr(tcx, body.hir_id) } } } } #[derive(Clone, Debug)] pub(crate) struct Impl { pub(crate) safety: hir::Safety, pub(crate) generics: Generics, pub(crate) trait_: Option, pub(crate) for_: Type, pub(crate) items: Vec, pub(crate) polarity: ty::ImplPolarity, pub(crate) kind: ImplKind, } impl Impl { pub(crate) fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxIndexSet { self.trait_ .as_ref() .map(|t| t.def_id()) .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name()).collect()) .unwrap_or_default() } pub(crate) fn is_negative_trait_impl(&self) -> bool { matches!(self.polarity, ty::ImplPolarity::Negative) } } #[derive(Clone, Debug)] pub(crate) enum ImplKind { Normal, Auto, FakeVariadic, Blanket(Box), } impl ImplKind { pub(crate) fn is_auto(&self) -> bool { matches!(self, ImplKind::Auto) } pub(crate) fn is_blanket(&self) -> bool { matches!(self, ImplKind::Blanket(_)) } pub(crate) fn is_fake_variadic(&self) -> bool { matches!(self, ImplKind::FakeVariadic) } pub(crate) fn as_blanket_ty(&self) -> Option<&Type> { match self { ImplKind::Blanket(ty) => Some(ty), _ => None, } } } #[derive(Clone, Debug)] pub(crate) struct Import { pub(crate) kind: ImportKind, /// The item being re-exported. pub(crate) source: ImportSource, pub(crate) should_be_displayed: bool, } impl Import { pub(crate) fn new_simple( name: Symbol, source: ImportSource, should_be_displayed: bool, ) -> Self { Self { kind: ImportKind::Simple(name), source, should_be_displayed } } pub(crate) fn new_glob(source: ImportSource, should_be_displayed: bool) -> Self { Self { kind: ImportKind::Glob, source, should_be_displayed } } pub(crate) fn imported_item_is_doc_hidden(&self, tcx: TyCtxt<'_>) -> bool { self.source.did.is_some_and(|did| tcx.is_doc_hidden(did)) } } #[derive(Clone, Debug)] pub(crate) enum ImportKind { // use source as str; Simple(Symbol), // use source::*; Glob, } #[derive(Clone, Debug)] pub(crate) struct ImportSource { pub(crate) path: Path, pub(crate) did: Option, } #[derive(Clone, Debug)] pub(crate) struct Macro { pub(crate) source: String, /// Whether the macro was defined via `macro_rules!` as opposed to `macro`. pub(crate) macro_rules: bool, } #[derive(Clone, Debug)] pub(crate) struct ProcMacro { pub(crate) kind: MacroKind, pub(crate) helpers: Vec, } /// A constraint on an associated item. /// /// ### Examples /// /// * the `A = Ty` and `B = Ty` in `Trait` /// * the `G = Ty` in `Trait = Ty>` /// * the `A: Bound` in `Trait` /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy` /// * the `C = { Ct }` in `Trait` (feature `associated_const_equality`) /// * the `f(..): Bound` in `Trait` (feature `return_type_notation`) #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct AssocItemConstraint { pub(crate) assoc: PathSegment, pub(crate) kind: AssocItemConstraintKind, } /// The kind of [associated item constraint][AssocItemConstraint]. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum AssocItemConstraintKind { Equality { term: Term }, Bound { bounds: Vec }, } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { use rustc_data_structures::static_assert_size; use super::*; // tidy-alphabetical-start static_assert_size!(Crate, 16); // frequently moved by-value static_assert_size!(DocFragment, 32); static_assert_size!(GenericArg, 32); static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); static_assert_size!(Item, 8); static_assert_size!(ItemInner, 144); static_assert_size!(ItemKind, 48); static_assert_size!(PathSegment, 32); static_assert_size!(Type, 32); // tidy-alphabetical-end }