From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- src/librustdoc/clean/types.rs | 2508 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2508 insertions(+) create mode 100644 src/librustdoc/clean/types.rs (limited to 'src/librustdoc/clean/types.rs') diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs new file mode 100644 index 000000000..0e6de842c --- /dev/null +++ b/src/librustdoc/clean/types.rs @@ -0,0 +1,2508 @@ +use std::cell::RefCell; +use std::default::Default; +use std::hash::Hash; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::OnceLock as OnceCell; +use std::{cmp, fmt, iter}; + +use arrayvec::ArrayVec; + +use rustc_ast::attr; +use rustc_ast::util::comments::beautify_doc_string; +use rustc_ast::{self as ast, AttrStyle}; +use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; +use rustc_const_eval::const_eval::is_unstable_const_fn; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::thin_vec::ThinVec; +use rustc_hir as hir; +use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{BodyId, Mutability}; +use rustc_index::vec::IndexVec; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::Session; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::DUMMY_SP; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{self, FileName, Loc}; +use rustc_target::abi::VariantIdx; +use rustc_target::spec::abi::Abi; +use rustc_typeck::check::intrinsic::intrinsic_operation_unsafety; + +use crate::clean::cfg::Cfg; +use crate::clean::clean_visibility; +use crate::clean::external_path; +use crate::clean::inline::{self, print_inlined_const}; +use crate::clean::utils::{is_literal_expr, print_const_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; + +pub(crate) use self::FnRetTy::*; +pub(crate) use self::ItemKind::*; +pub(crate) use self::SelfTy::*; +pub(crate) use self::Type::{ + Array, BareFunction, BorrowedRef, DynTrait, Generic, ImplTrait, Infer, Primitive, QPath, + RawPointer, Slice, Tuple, +}; +pub(crate) use self::Visibility::{Inherited, Public}; + +#[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 }, + /// Identifier for primitive types. + Primitive(PrimitiveType, CrateNum), +} + +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(), + ItemId::Primitive(_, krate) => krate == LOCAL_CRATE, + } + } + + #[inline] + #[track_caller] + pub(crate) fn expect_def_id(self) -> DefId { + self.as_def_id() + .unwrap_or_else(|| panic!("ItemId::expect_def_id: `{:?}` isn't a DefId", self)) + } + + #[inline] + pub(crate) fn as_def_id(self) -> Option { + match self { + ItemId::DefId(id) => Some(id), + _ => None, + } + } + + #[inline] + pub(crate) fn krate(self) -> CrateNum { + match self { + ItemId::Auto { for_: id, .. } + | ItemId::Blanket { for_: id, .. } + | ItemId::DefId(id) => id.krate, + ItemId::Primitive(_, krate) => krate, + } + } +} + +impl From for ItemId { + fn from(id: DefId) -> Self { + Self::DefId(id) + } +} + +/// The crate currently being documented. +#[derive(Clone, Debug)] +pub(crate) struct Crate { + pub(crate) module: Item, + pub(crate) primitives: ThinVec<(DefId, PrimitiveType)>, + /// Only here so that they can be filtered through the rustdoc passes. + pub(crate) external_traits: Rc>>, +} + +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) + } +} + +/// This struct is used to wrap additional information added by rustdoc on a `trait` item. +#[derive(Clone, Debug)] +pub(crate) struct TraitWithExtraInfo { + pub(crate) trait_: Trait, + pub(crate) is_notable: bool, +} + +#[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 in to 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 { + if 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. + } + + pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, Symbol)> { + let root = self.def_id(); + + let as_keyword = |res: Res| { + if let Res::Def(DefKind::Mod, def_id) = res { + let mut keyword = None; + let meta_items = tcx + .get_attrs(def_id, sym::doc) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()); + for meta in meta_items { + if meta.has_name(sym::keyword) { + if let Some(v) = meta.value_str() { + keyword = Some(v); + break; + } + } + } + return keyword.map(|p| (def_id, p)); + } + None + }; + if root.is_local() { + tcx.hir() + .root_module() + .item_ids + .iter() + .filter_map(|&id| { + let item = tcx.hir().item(id); + match item.kind { + hir::ItemKind::Mod(_) => { + as_keyword(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + } + hir::ItemKind::Use(path, hir::UseKind::Single) + if tcx.visibility(id.def_id).is_public() => + { + as_keyword(path.res.expect_non_local()) + .map(|(_, prim)| (id.def_id.to_def_id(), prim)) + } + _ => None, + } + }) + .collect() + } else { + tcx.module_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect() + } + } + + pub(crate) fn primitives(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, PrimitiveType)> { + let root = self.def_id(); + + // 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 `#[doc(primitive)]` then we would also have to + // search the entirety of external modules for items tagged + // `#[doc(primitive)]`, which is a pretty inefficient process (decoding + // all that metadata unconditionally). + // + // In order to keep the metadata load under control, the + // `#[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. + let as_primitive = |res: Res| { + if let Res::Def(DefKind::Mod, def_id) = res { + let mut prim = None; + let meta_items = tcx + .get_attrs(def_id, sym::doc) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()); + for meta in meta_items { + if let Some(v) = meta.value_str() { + if meta.has_name(sym::primitive) { + prim = PrimitiveType::from_symbol(v); + if prim.is_some() { + break; + } + // FIXME: should warn on unknown primitives? + } + } + } + return prim.map(|p| (def_id, p)); + } + None + }; + + if root.is_local() { + tcx.hir() + .root_module() + .item_ids + .iter() + .filter_map(|&id| { + let item = tcx.hir().item(id); + match item.kind { + hir::ItemKind::Mod(_) => { + as_primitive(Res::Def(DefKind::Mod, id.def_id.to_def_id())) + } + hir::ItemKind::Use(path, hir::UseKind::Single) + if tcx.visibility(id.def_id).is_public() => + { + as_primitive(path.res.expect_non_local()).map(|(_, prim)| { + // Pretend the primitive is local. + (id.def_id.to_def_id(), prim) + }) + } + _ => None, + } + }) + .collect() + } else { + tcx.module_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect() + } + } +} + +/// 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 { + /// The name of this item. + /// Optional because not every item has a name, e.g. impls. + pub(crate) name: Option, + pub(crate) attrs: Box, + pub(crate) visibility: Visibility, + /// Information about this item that is specific to what kind of item it is. + /// E.g., struct vs enum vs function. + pub(crate) kind: Box, + pub(crate) item_id: ItemId, + + pub(crate) cfg: Option>, +} + +/// 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("visibility", &self.visibility) + .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| { + let hir = tcx.hir(); + hir.span_with_body(hir.local_def_id_to_hir_id(local)) + }, + )) +} + +impl Item { + pub(crate) fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option { + self.item_id.as_def_id().and_then(|did| tcx.lookup_stability(did)) + } + + pub(crate) fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option { + self.item_id.as_def_id().and_then(|did| tcx.lookup_const_stability(did)) + } + + pub(crate) fn deprecation(&self, tcx: TyCtxt<'_>) -> Option { + self.item_id.as_def_id().and_then(|did| tcx.lookup_deprecation(did)) + } + + pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { + self.item_id + .as_def_id() + .map(|did| tcx.get_attrs_unchecked(did).inner_docs()) + .unwrap_or(false) + } + + pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Span { + let kind = match &*self.kind { + ItemKind::StrippedItem(k) => k, + _ => &*self.kind, + }; + match kind { + ItemKind::ModuleItem(Module { span, .. }) => *span, + ItemKind::ImplItem(box Impl { kind: ImplKind::Auto, .. }) => Span::dummy(), + ItemKind::ImplItem(box Impl { kind: ImplKind::Blanket(_), .. }) => { + if let ItemId::Blanket { impl_id, .. } = self.item_id { + rustc_span(impl_id, tcx) + } else { + panic!("blanket impl item has non-blanket ID") + } + } + _ => { + self.item_id.as_def_id().map(|did| rustc_span(did, tcx)).unwrap_or_else(Span::dummy) + } + } + } + + pub(crate) fn attr_span(&self, tcx: TyCtxt<'_>) -> rustc_span::Span { + crate::passes::span_of_attrs(&self.attrs).unwrap_or_else(|| self.span(tcx).inner()) + } + + /// Finds the `doc` attribute as a NameValue and returns the corresponding + /// value found. + pub(crate) fn doc_value(&self) -> Option { + self.attrs.doc_value() + } + + /// Convenience wrapper around [`Self::from_def_id_and_parts`] which converts + /// `hir_id` to a [`DefId`] + pub(crate) fn from_hir_id_and_parts( + hir_id: hir::HirId, + name: Option, + kind: ItemKind, + cx: &mut DocContext<'_>, + ) -> Item { + Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx) + } + + pub(crate) fn from_def_id_and_parts( + def_id: DefId, + name: Option, + kind: ItemKind, + cx: &mut DocContext<'_>, + ) -> Item { + let ast_attrs = cx.tcx.get_attrs_unchecked(def_id); + + Self::from_def_id_and_attrs_and_parts( + def_id, + name, + kind, + Box::new(Attributes::from_ast(ast_attrs)), + cx, + ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), + ) + } + + pub(crate) fn from_def_id_and_attrs_and_parts( + def_id: DefId, + name: Option, + kind: ItemKind, + attrs: Box, + cx: &mut DocContext<'_>, + cfg: Option>, + ) -> Item { + trace!("name={:?}, def_id={:?}", name, def_id); + + // 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. + let visibility = if matches!(&kind, ItemKind::KeywordItem | ItemKind::PrimitiveItem(..)) { + Visibility::Public + } else { + clean_visibility(cx.tcx.visibility(def_id)) + }; + + Item { item_id: def_id.into(), kind: Box::new(kind), name, attrs, visibility, cfg } + } + + /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined + /// with newlines. + pub(crate) fn collapsed_doc_value(&self) -> Option { + self.attrs.collapsed_doc_value() + } + + pub(crate) fn links(&self, cx: &Context<'_>) -> Vec { + use crate::html::format::href; + + cx.cache() + .intra_doc_links + .get(&self.item_id) + .map_or(&[][..], |v| v.as_slice()) + .iter() + .filter_map(|ItemLink { link: s, link_text, did, ref fragment }| { + debug!(?did); + if let Ok((mut href, ..)) = href(*did, 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(), + 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 { + cache + .intra_doc_links + .get(&self.item_id) + .map_or(&[][..], |v| v.as_slice()) + .iter() + .map(|ItemLink { link: s, link_text, .. }| RenderedLink { + original_text: s.clone(), + new_text: link_text.clone(), + href: String::new(), + }) + .collect() + } + + pub(crate) fn is_crate(&self) -> bool { + self.is_mod() && self.item_id.as_def_id().map_or(false, |did| did.is_crate_root()) + } + pub(crate) fn is_mod(&self) -> bool { + self.type_() == ItemType::Module + } + pub(crate) fn is_trait(&self) -> bool { + self.type_() == ItemType::Trait + } + 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_ty_associated_type(&self) -> bool { + matches!(&*self.kind, TyAssocTypeItem(..) | StrippedItem(box TyAssocTypeItem(..))) + } + pub(crate) fn is_associated_const(&self) -> bool { + matches!(&*self.kind, AssocConstItem(..) | StrippedItem(box AssocConstItem(..))) + } + pub(crate) fn is_ty_associated_const(&self) -> bool { + matches!(&*self.kind, TyAssocConstItem(..) | StrippedItem(box TyAssocConstItem(..))) + } + 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_typedef(&self) -> bool { + self.type_() == ItemType::Typedef + } + 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_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(), + _ => 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 { + match self.stability(tcx)?.level { + StabilityLevel::Stable { since, .. } => Some(since), + StabilityLevel::Unstable { .. } => None, + } + } + + pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option { + match self.const_stability(tcx)?.level { + StabilityLevel::Stable { since, .. } => Some(since), + StabilityLevel::Unstable { .. } => None, + } + } + + pub(crate) fn is_non_exhaustive(&self) -> bool { + self.attrs.other_attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + } + + /// 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: hir::IsAsync, + ) -> hir::FnHeader { + let sig = tcx.fn_sig(def_id); + let constness = + if tcx.is_const_fn(def_id) && is_unstable_const_fn(tcx, def_id).is_none() { + hir::Constness::Const + } else { + hir::Constness::NotConst + }; + hir::FnHeader { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness } + } + let header = match *self.kind { + ItemKind::ForeignFunctionItem(_) => { + let abi = tcx.fn_sig(self.item_id.as_def_id().unwrap()).abi(); + hir::FnHeader { + unsafety: if abi == Abi::RustIntrinsic { + intrinsic_operation_unsafety(self.name.unwrap()) + } else { + hir::Unsafety::Unsafe + }, + abi, + constness: hir::Constness::NotConst, + asyncness: hir::IsAsync::NotAsync, + } + } + ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) => { + let def_id = self.item_id.as_def_id().unwrap(); + build_fn_header(def_id, tcx, tcx.asyncness(def_id)) + } + ItemKind::TyMethodItem(_) => { + build_fn_header(self.item_id.as_def_id().unwrap(), tcx, hir::IsAsync::NotAsync) + } + _ => return None, + }; + Some(header) + } +} + +#[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), + TypedefItem(Box), + OpaqueTyItem(OpaqueTy), + StaticItem(Static), + ConstantItem(Constant), + TraitItem(Trait), + TraitAliasItem(TraitAlias), + ImplItem(Box), + /// A required method in a trait declaration meaning it's only a function signature. + TyMethodItem(Box), + /// A method in a trait impl or a provided method in a trait declaration. + /// + /// Compared to [TyMethodItem], it also contains a method body. + MethodItem(Box, Option), + StructFieldItem(Type), + VariantItem(Variant), + /// `fn`s from an extern block + ForeignFunctionItem(Box), + /// `static`s from an extern block + ForeignStaticItem(Static), + /// `type`s from an extern block + ForeignTypeItem, + MacroItem(Macro), + ProcMacroItem(ProcMacro), + PrimitiveItem(PrimitiveType), + /// A required associated constant in a trait declaration. + TyAssocConstItem(Type), + /// An associated associated constant in a trait impl or a provided one in a trait declaration. + AssocConstItem(Type, ConstantKind), + /// A required associated type in a trait declaration. + /// + /// The bounds may be non-empty if there is a `where` clause. + TyAssocTypeItem(Box, 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), + KeywordItem, +} + +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(Variant::Struct(v)) => v.fields.iter(), + VariantItem(Variant::Tuple(v)) => v.iter(), + EnumItem(e) => e.variants.iter(), + TraitItem(t) => t.items.iter(), + ImplItem(i) => i.items.iter(), + ModuleItem(m) => m.items.iter(), + ExternCrateItem { .. } + | ImportItem(_) + | FunctionItem(_) + | TypedefItem(_) + | OpaqueTyItem(_) + | StaticItem(_) + | ConstantItem(_) + | TraitAliasItem(_) + | TyMethodItem(_) + | MethodItem(_, _) + | StructFieldItem(_) + | VariantItem(_) + | ForeignFunctionItem(_) + | ForeignStaticItem(_) + | ForeignTypeItem + | MacroItem(_) + | ProcMacroItem(_) + | PrimitiveItem(_) + | TyAssocConstItem(_) + | AssocConstItem(_, _) + | TyAssocTypeItem(..) + | AssocTypeItem(..) + | StrippedItem(_) + | KeywordItem => [].iter(), + } + } +} + +#[derive(Clone, Debug)] +pub(crate) struct Module { + pub(crate) items: Vec, + pub(crate) span: Span, +} + +pub(crate) trait AttributesExt { + type AttributeIterator<'a>: Iterator + where + Self: 'a; + + fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a>; + + fn span(&self) -> Option; + + fn inner_docs(&self) -> bool; + + fn other_attrs(&self) -> Vec; + + fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option>; +} + +impl AttributesExt for [ast::Attribute] { + type AttributeIterator<'a> = impl Iterator + 'a; + + fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> { + self.iter() + .filter(move |attr| attr.has_name(name)) + .filter_map(ast::Attribute::meta_item_list) + .flatten() + } + + /// Return the span of the first doc-comment, if it exists. + fn span(&self) -> Option { + self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span) + } + + /// Returns whether the first doc-comment is an inner attribute. + /// + //// If there are no doc-comments, return true. + /// FIXME(#78591): Support both inner and outer attributes on the same item. + fn inner_docs(&self) -> bool { + self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner) + } + + fn other_attrs(&self) -> Vec { + self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect() + } + + fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option> { + let sess = tcx.sess; + let doc_cfg_active = tcx.features().doc_cfg; + let doc_auto_cfg_active = tcx.features().doc_auto_cfg; + + fn single(it: T) -> Option { + let mut iter = it.into_iter(); + let item = iter.next()?; + if iter.next().is_some() { + return None; + } + Some(item) + } + + let mut cfg = if doc_cfg_active || doc_auto_cfg_active { + let mut doc_cfg = self + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::cfg)) + .peekable(); + if doc_cfg.peek().is_some() && doc_cfg_active { + doc_cfg + .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok()) + .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) + } else if doc_auto_cfg_active { + self.iter() + .filter(|attr| attr.has_name(sym::cfg)) + .filter_map(|attr| single(attr.meta_item_list()?)) + .filter_map(|attr| { + Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten() + }) + .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) + } else { + Cfg::True + } + } else { + Cfg::True + }; + + for attr in self.iter() { + // #[doc] + if attr.doc_str().is_none() && attr.has_name(sym::doc) { + // #[doc(...)] + if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) { + for item in list { + // #[doc(hidden)] + if !item.has_name(sym::cfg) { + continue; + } + // #[doc(cfg(...))] + if let Some(cfg_mi) = item + .meta_item() + .and_then(|item| rustc_expand::config::parse_cfg(item, sess)) + { + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => { + sess.span_err(e.span, e.msg); + } + } + } + } + } + } + } + + // treat #[target_feature(enable = "feat")] attributes as if they were + // #[doc(cfg(target_feature = "feat"))] attributes as well + for attr in self.lists(sym::target_feature) { + if attr.has_name(sym::enable) { + if let Some(feat) = attr.value_str() { + let meta = attr::mk_name_value_item_str( + Ident::with_dummy_span(sym::target_feature), + feat, + DUMMY_SP, + ); + if let Ok(feat_cfg) = Cfg::parse(&meta) { + cfg &= feat_cfg; + } + } + } + } + + if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) } + } +} + +pub(crate) trait NestedAttributesExt { + /// Returns `true` if the attribute list contains a specific `word` + fn has_word(self, word: Symbol) -> bool + where + Self: std::marker::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 portion of documentation, extracted from a `#[doc]` attribute. +/// +/// Each variant contains the line number within the complete doc-comment where the fragment +/// starts, as well as the Span where the corresponding doc comment or attribute is located. +/// +/// Included files are kept separate from inline doc comments so that proper line-number +/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are +/// kept separate because of issue #42760. +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) struct DocFragment { + pub(crate) span: rustc_span::Span, + /// The module this doc-comment came from. + /// + /// This allows distinguishing between the original documentation and a pub re-export. + /// If it is `None`, the item was not re-exported. + pub(crate) parent_module: Option, + pub(crate) doc: Symbol, + pub(crate) kind: DocFragmentKind, + pub(crate) indent: usize, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub(crate) enum DocFragmentKind { + /// A doc fragment created from a `///` or `//!` doc comment. + SugaredDoc, + /// A doc fragment created from a "raw" `#[doc=""]` attribute. + RawDoc, +} + +/// The goal of this function is to apply the `DocFragment` transformation that is required when +/// transforming into the final Markdown, which is applying the computed indent to each line in +/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`). +/// +/// Note: remove the trailing newline where appropriate +fn add_doc_fragment(out: &mut String, frag: &DocFragment) { + let s = frag.doc.as_str(); + let mut iter = s.lines(); + if s.is_empty() { + out.push('\n'); + return; + } + while let Some(line) = iter.next() { + if line.chars().any(|c| !c.is_whitespace()) { + assert!(line.len() >= frag.indent); + out.push_str(&line[frag.indent..]); + } else { + out.push_str(line); + } + out.push('\n'); + } +} + +/// Collapse a collection of [`DocFragment`]s into one string, +/// handling indentation and newlines as needed. +pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { + let mut acc = String::new(); + for frag in doc_strings { + add_doc_fragment(&mut acc, frag); + } + acc.pop(); + acc +} + +/// Removes excess indentation on comments in order for the Markdown +/// to be parsed correctly. This is necessary because the convention for +/// writing documentation is to provide a space between the /// or //! marker +/// and the doc text, but Markdown is whitespace-sensitive. For example, +/// a block of text with four-space indentation is parsed as a code block, +/// so if we didn't unindent comments, these list items +/// +/// /// A list: +/// /// +/// /// - Foo +/// /// - Bar +/// +/// would be parsed as if they were in a code block, which is likely not what the user intended. +fn unindent_doc_fragments(docs: &mut Vec) { + // `add` is used in case the most common sugared doc syntax is used ("/// "). The other + // fragments kind's lines are never starting with a whitespace unless they are using some + // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, + // we need to take into account the fact that the minimum indent minus one (to take this + // whitespace into account). + // + // For example: + // + // /// hello! + // #[doc = "another"] + // + // In this case, you want "hello! another" and not "hello! another". + let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind) + && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc) + { + // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to + // "decide" how much the minimum indent will be. + 1 + } else { + 0 + }; + + // `min_indent` is used to know how much whitespaces from the start of each lines must be + // removed. Example: + // + // /// hello! + // #[doc = "another"] + // + // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum + // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 + // (5 - 1) whitespaces. + let Some(min_indent) = docs + .iter() + .map(|fragment| { + fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { + if line.chars().all(|c| c.is_whitespace()) { + min_indent + } else { + // Compare against either space or tab, ignoring whether they are + // mixed or not. + let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); + cmp::min(min_indent, whitespace) + + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } + } + }) + }) + .min() + else { + return; + }; + + for fragment in docs { + if fragment.doc == kw::Empty { + continue; + } + + let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { + min_indent - add + } else { + min_indent + }; + + fragment.indent = min_indent; + } +} + +/// 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)] +pub(crate) struct ItemLink { + /// The original link written in the markdown + pub(crate) link: String, + /// 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: String, + pub(crate) did: 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: String, + /// The text to display in the HTML + pub(crate) new_text: String, + /// The URL to put in the `href` + pub(crate) href: 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: Vec, +} + +impl Attributes { + pub(crate) fn lists(&self, name: Symbol) -> impl Iterator + '_ { + self.other_attrs.lists(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() { + if items.iter().filter_map(|i| i.meta_item()).any(|it| it.has_name(flag)) { + return true; + } + } + } + + false + } + + pub(crate) fn from_ast(attrs: &[ast::Attribute]) -> Attributes { + Attributes::from_ast_iter(attrs.iter().map(|attr| (attr, None)), false) + } + + pub(crate) fn from_ast_with_additional( + attrs: &[ast::Attribute], + (additional_attrs, def_id): (&[ast::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_ast_iter(attrs1.chain(attrs2), false) + } + + pub(crate) fn from_ast_iter<'a>( + attrs: impl Iterator)>, + doc_only: bool, + ) -> Attributes { + let mut doc_strings = Vec::new(); + let mut other_attrs = Vec::new(); + for (attr, parent_module) in attrs { + if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { + trace!("got doc_str={doc_str:?}"); + let doc = beautify_doc_string(doc_str, comment_kind); + let kind = if attr.is_doc_comment() { + DocFragmentKind::SugaredDoc + } else { + DocFragmentKind::RawDoc + }; + let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 }; + doc_strings.push(fragment); + } else if !doc_only { + other_attrs.push(attr.clone()); + } + } + + unindent_doc_fragments(&mut doc_strings); + + Attributes { doc_strings, other_attrs } + } + + /// Finds the `doc` attribute as a NameValue and returns the corresponding + /// value found. + pub(crate) fn doc_value(&self) -> Option { + let mut iter = self.doc_strings.iter(); + + let ori = iter.next()?; + let mut out = String::new(); + add_doc_fragment(&mut out, ori); + for new_frag in iter { + add_doc_fragment(&mut out, new_frag); + } + out.pop(); + if out.is_empty() { None } else { Some(out) } + } + + /// Return the doc-comments on this item, grouped by the module they came from. + /// The module can be different if this is a re-export with added documentation. + /// + /// The last newline is not trimmed so the produced strings are reusable between + /// early and late doc link resolution regardless of their position. + pub(crate) fn prepare_to_doc_link_resolution(&self) -> FxHashMap, String> { + let mut res = FxHashMap::default(); + for fragment in &self.doc_strings { + let out_str = res.entry(fragment.parent_module).or_default(); + add_doc_fragment(out_str, fragment); + } + res + } + + /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined + /// with newlines. + pub(crate) fn collapsed_doc_value(&self) -> Option { + if self.doc_strings.is_empty() { + None + } else { + Some(collapse_doc_fragments(&self.doc_strings)) + } + } + + pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> { + let mut aliases = FxHashSet::default(); + + for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) { + if let Some(values) = attr.meta_item_list() { + for l in values { + match l.literal().unwrap().kind { + ast::LitKind::Str(s, _) => { + aliases.insert(s); + } + _ => unreachable!(), + } + } + } else { + aliases.insert(attr.value_str().unwrap()); + } + } + aliases.into_iter().collect::>().into() + } +} + +impl PartialEq for Attributes { + fn eq(&self, rhs: &Self) -> bool { + self.doc_strings == rhs.doc_strings + && self + .other_attrs + .iter() + .map(|attr| attr.id) + .eq(rhs.other_attrs.iter().map(|attr| attr.id)) + } +} + +impl Eq for Attributes {} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) enum GenericBound { + TraitBound(PolyTrait, hir::TraitBoundModifier), + Outlives(Lifetime), +} + +impl GenericBound { + pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { + let did = cx.tcx.require_lang_item(LangItem::Sized, None); + let empty = cx.tcx.intern_substs(&[]); + let path = external_path(cx, did, false, vec![], empty); + inline::record_extern_fqn(cx, did, ItemType::Trait); + GenericBound::TraitBound( + PolyTrait { trait_: path, generic_params: Vec::new() }, + hir::TraitBoundModifier::Maybe, + ) + } + + pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { + use rustc_hir::TraitBoundModifier as TBM; + if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self { + if Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait() { + return true; + } + } + false + } + + pub(crate) fn get_poly_trait(&self) -> Option { + if let GenericBound::TraitBound(ref p, _) = *self { + return Some(p.clone()); + } + None + } + + pub(crate) fn get_trait_path(&self) -> Option { + if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self { + Some(trait_.clone()) + } else { + None + } + } +} + +#[derive(Clone, 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, Debug)] +pub(crate) enum WherePredicate { + BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, + RegionPredicate { lifetime: Lifetime, bounds: Vec }, + EqPredicate { lhs: Type, rhs: Term }, +} + +impl WherePredicate { + pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> { + match *self { + WherePredicate::BoundPredicate { ref bounds, .. } => Some(bounds), + WherePredicate::RegionPredicate { ref bounds, .. } => Some(bounds), + _ => None, + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) enum GenericParamDefKind { + Lifetime { outlives: Vec }, + Type { did: DefId, bounds: Vec, default: Option>, synthetic: bool }, + Const { did: DefId, 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) kind: GenericParamDefKind, +} + +impl GenericParamDef { + pub(crate) fn is_synthetic_type_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, Debug, Default)] +pub(crate) struct Generics { + pub(crate) params: Vec, + pub(crate) where_predicates: Vec, +} + +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: Arguments, + pub(crate) output: FnRetTy, + pub(crate) c_variadic: bool, +} + +impl FnDecl { + pub(crate) fn self_type(&self) -> Option { + self.inputs.values.get(0).and_then(|v| v.to_self()) + } + + /// 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) -> FnRetTy { + match &self.output { + FnRetTy::Return(Type::ImplTrait(bounds)) => match &bounds[0] { + GenericBound::TraitBound(PolyTrait { trait_, .. }, ..) => { + let bindings = trait_.bindings().unwrap(); + let ret_ty = bindings[0].term(); + let ty = ret_ty.ty().expect("Unexpected constant return term"); + FnRetTy::Return(ty.clone()) + } + _ => panic!("unexpected desugaring of async function"), + }, + _ => panic!("unexpected desugaring of async function"), + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct Arguments { + pub(crate) values: Vec, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct Argument { + pub(crate) type_: Type, + pub(crate) name: Symbol, + /// This field is used to represent "const" arguments from the `rustc_legacy_const_generics` + /// feature. More information in . + pub(crate) is_const: bool, +} + +#[derive(Clone, PartialEq, Debug)] +pub(crate) enum SelfTy { + SelfValue, + SelfBorrowed(Option, Mutability), + SelfExplicit(Type), +} + +impl Argument { + pub(crate) fn to_self(&self) -> Option { + if self.name != kw::SelfLower { + return None; + } + if self.type_.is_self_type() { + return Some(SelfValue); + } + match self.type_ { + BorrowedRef { ref lifetime, mutability, ref type_ } if type_.is_self_type() => { + Some(SelfBorrowed(lifetime.clone(), mutability)) + } + _ => Some(SelfExplicit(self.type_.clone())), + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) enum FnRetTy { + Return(Type), + DefaultReturn, +} + +impl FnRetTy { + pub(crate) fn as_return(&self) -> Option<&Type> { + match self { + Return(ret) => Some(ret), + DefaultReturn => 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 unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety { + tcx.trait_def(self.def_id).unsafety + } +} + +#[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), + /// 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, String), + /// 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 { + assoc: Box, + self_type: Box, + /// FIXME: compute this field on demand. + should_show_cast: bool, + trait_: Path, + }, + + /// A type that is inferred: `_` + Infer, + + /// An `impl Trait`: `impl TraitA + TraitB + ...` + ImplTrait(Vec), +} + +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 + } + + /// Check if two types are "potentially the same". + /// This is different from `Eq`, because it knows that things like + /// `Placeholder` are possible matches for everything. + pub(crate) fn is_same(&self, other: &Self, cache: &Cache) -> bool { + match (self, other) { + // Recursive cases. + (Type::Tuple(a), Type::Tuple(b)) => { + a.len() == b.len() && a.iter().zip(b).all(|(a, b)| a.is_same(b, cache)) + } + (Type::Slice(a), Type::Slice(b)) => a.is_same(b, cache), + (Type::Array(a, al), Type::Array(b, bl)) => al == bl && a.is_same(b, cache), + (Type::RawPointer(mutability, type_), Type::RawPointer(b_mutability, b_type_)) => { + mutability == b_mutability && type_.is_same(b_type_, cache) + } + ( + Type::BorrowedRef { mutability, type_, .. }, + Type::BorrowedRef { mutability: b_mutability, type_: b_type_, .. }, + ) => mutability == b_mutability && type_.is_same(b_type_, cache), + // Placeholders and generics are equal to all other types. + (Type::Infer, _) | (_, Type::Infer) => true, + (Type::Generic(_), _) | (_, Type::Generic(_)) => 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, + } + } + + /// 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 { + match *self { + Generic(name) => name == kw::SelfUpper, + _ => false, + } + } + + 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_impl_trait(&self) -> bool { + matches!(self, Type::ImplTrait(_)) + } + + pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> { + if let QPath { self_type, trait_, assoc, .. } = self { + Some((self_type, trait_.def_id(), *assoc.clone())) + } else { + None + } + } + + fn inner_def_id(&self, cache: Option<&Cache>) -> Option { + let t: PrimitiveType = match *self { + Type::Path { ref path } => return Some(path.def_id()), + DynTrait(ref bounds, _) => return Some(bounds[0].trait_.def_id()), + Primitive(p) => return cache.and_then(|c| c.primitive_locations.get(&p).cloned()), + BorrowedRef { type_: box Generic(..), .. } => PrimitiveType::Reference, + BorrowedRef { ref type_, .. } => return type_.inner_def_id(cache), + Tuple(ref tys) => { + if tys.is_empty() { + PrimitiveType::Unit + } else { + PrimitiveType::Tuple + } + } + BareFunction(..) => PrimitiveType::Fn, + Slice(..) => PrimitiveType::Slice, + Array(..) => PrimitiveType::Array, + RawPointer(..) => PrimitiveType::RawPointer, + QPath { ref self_type, .. } => return self_type.inner_def_id(cache), + Generic(_) | Infer | ImplTrait(_) => return None, + }; + cache.and_then(|c| Primitive(t).def_id(c)) + } + + /// 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 { + self.inner_def_id(Some(cache)) + } +} + +/// 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, + F32, + F64, + Char, + Bool, + Str, + Slice, + Array, + Tuple, + Unit, + RawPointer, + Reference, + Fn, + Never, +} + +type SimplifiedTypes = FxHashMap>; +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::F32) => PrimitiveType::F32, + hir::PrimTy::Float(FloatTy::F64) => PrimitiveType::F64, + 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::f32 => Some(PrimitiveType::F32), + sym::f64 => Some(PrimitiveType::F64), + 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 ty::fast_reject::SimplifiedTypeGen::*; + use ty::{FloatTy, IntTy, UintTy}; + use PrimitiveType::*; + static CELL: OnceCell = OnceCell::new(); + + let single = |x| iter::once(x).collect(); + CELL.get_or_init(move || { + map! { + Isize => single(IntSimplifiedType(IntTy::Isize)), + I8 => single(IntSimplifiedType(IntTy::I8)), + I16 => single(IntSimplifiedType(IntTy::I16)), + I32 => single(IntSimplifiedType(IntTy::I32)), + I64 => single(IntSimplifiedType(IntTy::I64)), + I128 => single(IntSimplifiedType(IntTy::I128)), + Usize => single(UintSimplifiedType(UintTy::Usize)), + U8 => single(UintSimplifiedType(UintTy::U8)), + U16 => single(UintSimplifiedType(UintTy::U16)), + U32 => single(UintSimplifiedType(UintTy::U32)), + U64 => single(UintSimplifiedType(UintTy::U64)), + U128 => single(UintSimplifiedType(UintTy::U128)), + F32 => single(FloatSimplifiedType(FloatTy::F32)), + F64 => single(FloatSimplifiedType(FloatTy::F64)), + Str => single(StrSimplifiedType), + Bool => single(BoolSimplifiedType), + Char => single(CharSimplifiedType), + Array => single(ArraySimplifiedType), + Slice => single(SliceSimplifiedType), + // 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 => [TupleSimplifiedType(1), TupleSimplifiedType(2), TupleSimplifiedType(3)].into(), + Unit => single(TupleSimplifiedType(0)), + RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into_iter().collect(), + Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into_iter().collect(), + // FIXME: This will be wrong if we ever add inherent impls + // for function pointers. + Fn => single(FunctionSimplifiedType(1)), + Never => single(NeverSimplifiedType), + } + }) + } + + 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)) + .copied() + } + + pub(crate) fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator + '_ { + Self::simplified_types() + .values() + .flatten() + .flat_map(move |&simp| tcx.incoherent_impls(simp)) + .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, + F32 => sym::f32, + F64 => sym::f64, + Str => sym::str, + Bool => sym::bool, + Char => sym::char, + Array => sym::array, + 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 `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 `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<'_>) -> &FxHashMap { + static PRIMITIVE_LOCATIONS: OnceCell> = OnceCell::new(); + PRIMITIVE_LOCATIONS.get_or_init(|| { + let mut primitive_locations = FxHashMap::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: ast::IntTy) -> PrimitiveType { + match int_ty { + ast::IntTy::Isize => PrimitiveType::Isize, + ast::IntTy::I8 => PrimitiveType::I8, + ast::IntTy::I16 => PrimitiveType::I16, + ast::IntTy::I32 => PrimitiveType::I32, + ast::IntTy::I64 => PrimitiveType::I64, + ast::IntTy::I128 => PrimitiveType::I128, + } + } +} + +impl From for PrimitiveType { + fn from(uint_ty: ast::UintTy) -> PrimitiveType { + match uint_ty { + ast::UintTy::Usize => PrimitiveType::Usize, + ast::UintTy::U8 => PrimitiveType::U8, + ast::UintTy::U16 => PrimitiveType::U16, + ast::UintTy::U32 => PrimitiveType::U32, + ast::UintTy::U64 => PrimitiveType::U64, + ast::UintTy::U128 => PrimitiveType::U128, + } + } +} + +impl From for PrimitiveType { + fn from(float_ty: ast::FloatTy) -> PrimitiveType { + match float_ty { + ast::FloatTy::F32 => PrimitiveType::F32, + ast::FloatTy::F64 => PrimitiveType::F64, + } + } +} + +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::F32 => PrimitiveType::F32, + ty::FloatTy::F64 => PrimitiveType::F64, + } + } +} + +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(Copy, Clone, Debug)] +pub(crate) enum Visibility { + /// `pub` + Public, + /// Visibility inherited from parent. + /// + /// For example, this is the visibility of private items and of enum variants. + Inherited, + /// `pub(crate)`, `pub(super)`, or `pub(in path::to::somewhere)` + Restricted(DefId), +} + +impl Visibility { + pub(crate) fn is_public(&self) -> bool { + matches!(self, Visibility::Public) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct Struct { + pub(crate) struct_type: CtorKind, + pub(crate) generics: Generics, + pub(crate) fields: Vec, +} + +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) struct_type: CtorKind, + pub(crate) fields: Vec, +} + +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 variants(&self) -> impl Iterator { + self.variants.iter().filter(|v| !v.is_stripped()) + } +} + +#[derive(Clone, Debug)] +pub(crate) enum Variant { + CLike, + Tuple(Vec), + Struct(VariantStruct), +} + +impl Variant { + pub(crate) fn has_stripped_entries(&self) -> Option { + match *self { + Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()), + Self::CLike | Self::Tuple(_) => None, + } + } +} + +/// 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 dummy() -> Self { + Self(rustc_span::DUMMY_SP) + } + + pub(crate) fn is_dummy(&self) -> bool { + self.0.is_dummy() + } + + 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: Vec, +} + +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::SelfTy { .. } if self.segments.len() != 1 => true, + Res::Def(DefKind::TyParam, _) if self.segments.len() != 1 => true, + Res::Def(DefKind::AssocTy, _) => true, + _ => false, + } + } + + 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, + }) + .collect(), + ) + } else { + None + } + }) + } + + pub(crate) fn bindings(&self) -> Option<&[TypeBinding]> { + self.segments.last().and_then(|seg| { + if let GenericArgs::AngleBracketed { ref bindings, .. } = seg.args { + Some(&**bindings) + } else { + None + } + }) + } +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) enum GenericArg { + Lifetime(Lifetime), + Type(Type), + Const(Box), + Infer, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) enum GenericArgs { + AngleBracketed { args: Box<[GenericArg]>, bindings: ThinVec }, + Parenthesized { inputs: Box<[Type]>, output: Option> }, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct PathSegment { + pub(crate) name: Symbol, + pub(crate) args: GenericArgs, +} + +#[derive(Clone, Debug)] +pub(crate) struct Typedef { + pub(crate) type_: Type, + pub(crate) generics: Generics, + /// `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, Debug)] +pub(crate) struct OpaqueTy { + pub(crate) bounds: Vec, + pub(crate) generics: Generics, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct BareFunctionDecl { + pub(crate) unsafety: hir::Unsafety, + pub(crate) generic_params: Vec, + pub(crate) decl: FnDecl, + pub(crate) abi: Abi, +} + +#[derive(Clone, Debug)] +pub(crate) struct Static { + pub(crate) type_: Type, + pub(crate) mutability: Mutability, + pub(crate) expr: Option, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct Constant { + pub(crate) type_: Type, + pub(crate) kind: ConstantKind, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) enum Term { + Type(Type), + Constant(Constant), +} + +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: String }, + /// 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 }, +} + +impl Constant { + pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String { + self.kind.expr(tcx) + } + + pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option { + self.kind.value(tcx) + } + + pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { + self.kind.is_literal(tcx) + } +} + +impl ConstantKind { + pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> String { + match *self { + ConstantKind::TyConst { ref expr } => expr.clone(), + ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), + ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { + print_const_expr(tcx, body) + } + } + } + + pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option { + match *self { + ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None, + ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { + print_evaluated_const(tcx, def_id) + } + } + } + + pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { + match *self { + ConstantKind::TyConst { .. } => false, + ConstantKind::Extern { def_id } => def_id.as_local().map_or(false, |def_id| { + is_literal_expr(tcx, tcx.hir().local_def_id_to_hir_id(def_id)) + }), + ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { + is_literal_expr(tcx, body.hir_id) + } + } + } +} + +#[derive(Clone, Debug)] +pub(crate) struct Impl { + pub(crate) unsafety: hir::Unsafety, + 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<'_>) -> FxHashSet { + self.trait_ + .as_ref() + .map(|t| t.def_id()) + .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name).collect()) + .unwrap_or_default() + } +} + +#[derive(Clone, Debug)] +pub(crate) enum ImplKind { + Normal, + Auto, + FakeVaradic, + 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::FakeVaradic) + } + + 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, + 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 } + } +} + +#[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, +} + +#[derive(Clone, Debug)] +pub(crate) struct ProcMacro { + pub(crate) kind: MacroKind, + pub(crate) helpers: Vec, +} + +/// An type binding on an associated type (e.g., `A = Bar` in `Foo` or +/// `A: Send + Sync` in `Foo`). +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) struct TypeBinding { + pub(crate) assoc: PathSegment, + pub(crate) kind: TypeBindingKind, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub(crate) enum TypeBindingKind { + Equality { term: Term }, + Constraint { bounds: Vec }, +} + +impl TypeBinding { + pub(crate) fn term(&self) -> &Term { + match self.kind { + TypeBindingKind::Equality { ref term } => term, + _ => panic!("expected equality type binding for parenthesized generic args"), + } + } +} + +/// The type, lifetime, or constant that a private type alias's parameter should be +/// replaced with when expanding a use of that type alias. +/// +/// For example: +/// +/// ``` +/// type PrivAlias = Vec; +/// +/// pub fn public_fn() -> PrivAlias { vec![] } +/// ``` +/// +/// `public_fn`'s docs will show it as returning `Vec`, since `PrivAlias` is private. +/// [`SubstParam`] is used to record that `T` should be mapped to `i32`. +pub(crate) enum SubstParam { + Type(Type), + Lifetime(Lifetime), + Constant(Constant), +} + +impl SubstParam { + pub(crate) fn as_ty(&self) -> Option<&Type> { + if let Self::Type(ty) = self { Some(ty) } else { None } + } + + pub(crate) fn as_lt(&self) -> Option<&Lifetime> { + if let Self::Lifetime(lt) = self { Some(lt) } else { None } + } +} + +// Some nodes are used a lot. Make sure they don't unintentionally get bigger. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +mod size_asserts { + use super::*; + // These are in alphabetical order, which is easy to maintain. + rustc_data_structures::static_assert_size!(Crate, 72); // frequently moved by-value + rustc_data_structures::static_assert_size!(DocFragment, 32); + rustc_data_structures::static_assert_size!(GenericArg, 80); + rustc_data_structures::static_assert_size!(GenericArgs, 32); + rustc_data_structures::static_assert_size!(GenericParamDef, 56); + rustc_data_structures::static_assert_size!(Item, 56); + rustc_data_structures::static_assert_size!(ItemKind, 112); + rustc_data_structures::static_assert_size!(PathSegment, 40); + rustc_data_structures::static_assert_size!(Type, 72); +} -- cgit v1.2.3