blob: 88ee4e3a2abdc7296febc2ac9f620203e362362a [file] [log] [blame]
use std::borrow::Cow;
use std::cell::RefCell;
use std::hash::Hash;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::OnceLock as OnceCell;
use std::{fmt, iter};
use arrayvec::ArrayVec;
use thin_vec::ThinVec;
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel, StableSince};
use rustc_const_eval::const_eval::is_unstable_const_fn;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
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_hir_analysis::check::intrinsic::intrinsic_operation_unsafety;
use rustc_index::IndexVec;
use rustc_metadata::rendered_const;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, TyCtxt, Visibility};
use rustc_resolve::rustdoc::{
add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, DocFragment,
};
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{self, FileName, Loc, DUMMY_SP};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;
use crate::clean::cfg::Cfg;
use crate::clean::external_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;
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,
};
#[cfg(test)]
mod tests;
pub(crate) type ItemIdSet = FxHashSet<ItemId>;
#[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<DefId> {
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,
}
}
}
impl From<DefId> 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,
/// Only here so that they can be filtered through the rustdoc passes.
pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, Trait>>>,
}
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.
}
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.owner_id.to_def_id()))
}
_ => 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 `#[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.
let as_primitive = |res: Res<!>| {
let Res::Def(DefKind::Mod, def_id) = res else { return None };
tcx.get_attrs(def_id, sym::rustc_doc_primitive).find_map(|attr| {
// FIXME: should warn on unknown primitives?
Some((def_id, PrimitiveType::from_symbol(attr.value_str()?)?))
})
};
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.owner_id.to_def_id()))
}
_ => 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<Symbol>,
pub(crate) attrs: Box<Attributes>,
/// 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<ItemKind>,
pub(crate) item_id: ItemId,
/// This is the `DefId` of the `use` statement if the item was inlined.
pub(crate) inline_stmt_id: Option<DefId>,
pub(crate) cfg: Option<Arc<Cfg>>,
}
/// 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| {
let hir = tcx.hir();
hir.span_with_body(hir.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 {
pub(crate) fn stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<Stability> {
self.def_id().and_then(|did| tcx.lookup_stability(did))
}
pub(crate) fn const_stability<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<ConstStability> {
self.def_id().and_then(|did| tcx.lookup_const_stability(did))
}
pub(crate) fn deprecation(&self, tcx: TyCtxt<'_>) -> Option<Deprecation> {
self.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| inner_docs(tcx.get_attrs_unchecked(did)))
.unwrap_or(false)
}
pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option<Span> {
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(rustc_span::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<String> {
self.attrs.opt_doc_value()
}
pub(crate) fn from_def_id_and_parts(
def_id: DefId,
name: Option<Symbol>,
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)),
ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
)
}
pub(crate) fn from_def_id_and_attrs_and_parts(
def_id: DefId,
name: Option<Symbol>,
kind: ItemKind,
attrs: Box<Attributes>,
cfg: Option<Arc<Cfg>>,
) -> Item {
trace!("name={name:?}, def_id={def_id:?} cfg={cfg:?}");
Item {
item_id: def_id.into(),
kind: Box::new(kind),
name,
attrs,
cfg,
inline_stmt_id: None,
}
}
pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> {
use crate::html::format::{href, link_tooltip};
let Some(links) = cx.cache().intra_doc_links.get(&self.item_id) else { return vec![] };
links
.iter()
.filter_map(|ItemLink { link: s, link_text, page_id: id, ref 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),
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<RenderedLink> {
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().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_type_alias(&self) -> bool {
self.type_() == ItemType::TypeAlias
}
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<bool> {
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<String> {
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<StableSince> {
match self.stability(tcx)?.level {
StabilityLevel::Stable { since, .. } => Some(since),
StabilityLevel::Unstable { .. } => None,
}
}
pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
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<hir::FnHeader> {
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) && is_unstable_const_fn(tcx, def_id).is_none() {
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 { unsafety: sig.unsafety(), abi: sig.abi(), constness, asyncness }
}
let header = match *self.kind {
ItemKind::ForeignFunctionItem(_) => {
let def_id = self.def_id().unwrap();
let abi = tcx.fn_sig(def_id).skip_binder().abi();
hir::FnHeader {
unsafety: if abi == Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, self.def_id().unwrap())
} else {
hir::Unsafety::Unsafe
},
abi,
constness: if abi == Abi::RustIntrinsic
&& tcx.is_const_fn(def_id)
&& is_unstable_const_fn(tcx, def_id).is_none()
{
hir::Constness::Const
} else {
hir::Constness::NotConst
},
asyncness: hir::IsAsync::NotAsync,
}
}
ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::TyMethodItem(_) => {
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<Visibility<DefId>> {
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(_) => 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
AssocConstItem(..) | TyAssocConstItem(..) | AssocTypeItem(..) | TyAssocTypeItem(..)
| TyMethodItem(..) | MethodItem(..) => {
let assoc_item = tcx.associated_item(def_id);
let is_trait_item = match assoc_item.container {
ty::TraitContainer => true,
ty::ImplContainer => {
// Trait impl items always inherit the impl's visibility --
// we don't want to show `pub`.
tcx.impl_trait_ref(tcx.parent(assoc_item.def_id)).is_some()
}
};
if is_trait_item {
return None;
}
}
_ => {}
}
let def_id = match self.inline_stmt_id {
Some(inlined) => inlined,
None => def_id,
};
Some(tcx.visibility(def_id))
}
pub(crate) fn attributes(
&self,
tcx: TyCtxt<'_>,
cache: &Cache,
keep_as_is: bool,
) -> Vec<String> {
const ALLOWED_ATTRIBUTES: &[Symbol] =
&[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
use rustc_abi::IntegerType;
let mut attrs: Vec<String> = self
.attrs
.other_attrs
.iter()
.filter_map(|attr| {
if keep_as_is {
Some(pprust::attribute_to_string(attr))
} else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
Some(
pprust::attribute_to_string(attr)
.replace("\\\n", "")
.replace('\n', "")
.replace(" ", " "),
)
} else {
None
}
})
.collect();
if !keep_as_is
&& let Some(def_id) = self.def_id()
&& let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
{
let adt = tcx.adt_def(def_id);
let repr = adt.repr();
let mut out = Vec::new();
if repr.c() {
out.push("C");
}
if repr.transparent() {
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
// field is public in case all fields are 1-ZST fields.
let render_transparent = cache.document_private
|| adt
.all_fields()
.find(|field| {
let ty =
field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
tcx.layout_of(tcx.param_env(field.did).and(ty))
.is_ok_and(|layout| !layout.is_1zst())
})
.map_or_else(
|| adt.all_fields().any(|field| field.vis.is_public()),
|field| field.vis.is_public(),
);
if render_transparent {
out.push("transparent");
}
}
if repr.simd() {
out.push("simd");
}
let pack_s;
if let Some(pack) = repr.pack {
pack_s = format!("packed({})", pack.bytes());
out.push(&pack_s);
}
let align_s;
if let Some(align) = repr.align {
align_s = format!("align({})", align.bytes());
out.push(&align_s);
}
let int_s;
if let Some(int) = repr.int {
int_s = match int {
IntegerType::Pointer(is_signed) => {
format!("{}size", if is_signed { 'i' } else { 'u' })
}
IntegerType::Fixed(size, is_signed) => {
format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8)
}
};
out.push(&int_s);
}
if !out.is_empty() {
attrs.push(format!("#[repr({})]", out.join(", ")));
}
}
attrs
}
pub fn is_doc_hidden(&self) -> bool {
self.attrs.is_doc_hidden()
}
pub fn def_id(&self) -> Option<DefId> {
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<Symbol>,
},
ImportItem(Import),
StructItem(Struct),
UnionItem(Union),
EnumItem(Enum),
FunctionItem(Box<Function>),
ModuleItem(Module),
TypeAliasItem(Box<TypeAlias>),
OpaqueTyItem(OpaqueTy),
StaticItem(Static),
ConstantItem(Constant),
TraitItem(Box<Trait>),
TraitAliasItem(TraitAlias),
ImplItem(Box<Impl>),
/// A required method in a trait declaration meaning it's only a function signature.
TyMethodItem(Box<Function>),
/// 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<Function>, Option<hir::Defaultness>),
StructFieldItem(Type),
VariantItem(Variant),
/// `fn`s from an extern block
ForeignFunctionItem(Box<Function>),
/// `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(Generics, Box<Type>),
/// An associated constant in a trait impl or a provided one in a trait declaration.
AssocConstItem(Generics, Box<Type>, ConstantKind),
/// A required associated type in a trait declaration.
///
/// The bounds may be non-empty if there is a `where` clause.
TyAssocTypeItem(Generics, Vec<GenericBound>),
/// An associated type in a trait impl or a provided one in a trait declaration.
AssocTypeItem(Box<TypeAlias>, Vec<GenericBound>),
/// An item that has been stripped by a rustdoc pass
StrippedItem(Box<ItemKind>),
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<Item = &Item> {
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(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
| TraitAliasItem(_)
| TyMethodItem(_)
| MethodItem(_, _)
| StructFieldItem(_)
| ForeignFunctionItem(_)
| ForeignStaticItem(_)
| ForeignTypeItem
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
| TyAssocConstItem(..)
| AssocConstItem(..)
| TyAssocTypeItem(..)
| AssocTypeItem(..)
| StrippedItem(_)
| KeywordItem => [].iter(),
}
}
/// Returns `true` if this item does not appear inside an impl block.
pub(crate) fn is_non_assoc(&self) -> bool {
matches!(
self,
StructItem(_)
| UnionItem(_)
| EnumItem(_)
| TraitItem(_)
| ModuleItem(_)
| ExternCrateItem { .. }
| FunctionItem(_)
| TypeAliasItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
| TraitAliasItem(_)
| ForeignFunctionItem(_)
| ForeignStaticItem(_)
| ForeignTypeItem
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
)
}
}
#[derive(Clone, Debug)]
pub(crate) struct Module {
pub(crate) items: Vec<Item>,
pub(crate) span: Span,
}
pub(crate) trait AttributesExt {
type AttributeIterator<'a>: Iterator<Item = ast::NestedMetaItem>
where
Self: 'a;
type Attributes<'a>: Iterator<Item = &'a ast::Attribute>
where
Self: 'a;
fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a>;
fn iter<'a>(&'a self) -> Self::Attributes<'a>;
fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
let sess = tcx.sess;
let doc_cfg_active = tcx.features().doc_cfg;
let doc_auto_cfg_active = tcx.features().doc_auto_cfg;
fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
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 {
// If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because
// `doc(cfg())` overrides `cfg()`).
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 attr.value_str().is_some() {
// Clone `enable = "feat"`, change to `target_feature = "feat"`.
// Unwrap is safe because `value_str` succeeded above.
let mut meta = attr.meta_item().unwrap().clone();
meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
if let Ok(feat_cfg) = Cfg::parse(&meta) {
cfg &= feat_cfg;
}
}
}
}
if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) }
}
}
impl AttributesExt for [ast::Attribute] {
type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + '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()
}
fn iter<'a>(&'a self) -> Self::Attributes<'a> {
self.into_iter()
}
}
impl AttributesExt for [(Cow<'_, ast::Attribute>, Option<DefId>)] {
type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a
where Self: 'a;
type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a
where Self: 'a;
fn lists<'a>(&'a self, name: Symbol) -> Self::AttributeIterator<'a> {
AttributesExt::iter(self)
.filter(move |attr| attr.has_name(name))
.filter_map(ast::Attribute::meta_item_list)
.flatten()
}
fn iter<'a>(&'a self) -> Self::Attributes<'a> {
self.into_iter().map(move |(attr, _)| match attr {
Cow::Borrowed(attr) => *attr,
Cow::Owned(attr) => attr,
})
}
}
pub(crate) trait NestedAttributesExt {
/// Returns `true` if the attribute list contains a specific `word`
fn has_word(self, word: Symbol) -> bool
where
Self: Sized,
{
<Self as NestedAttributesExt>::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<ast::NestedMetaItem>;
}
impl<I: Iterator<Item = ast::NestedMetaItem>> NestedAttributesExt for I {
fn get_word_attr(mut self, word: Symbol) -> Option<ast::NestedMetaItem> {
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<str>,
/// 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<str>,
/// 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<UrlFragment>,
}
pub struct RenderedLink {
/// The text the link was original written as.
///
/// This could potentially include disambiguators and backticks.
pub(crate) original_text: Box<str>,
/// The text to display in the HTML
pub(crate) new_text: Box<str>,
/// 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<DocFragment>,
pub(crate) other_attrs: ast::AttrVec,
}
impl Attributes {
pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::NestedMetaItem> + '_ {
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
}
fn is_doc_hidden(&self) -> bool {
self.has_doc_flag(sym::hidden)
}
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<Item = (&'a ast::Attribute, Option<DefId>)>,
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<String> {
(!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 = 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.lit().unwrap().kind {
ast::LitKind::Str(s, _) => {
aliases.insert(s);
}
_ => unreachable!(),
}
}
} else {
aliases.insert(attr.value_str().unwrap());
}
}
aliases.into_iter().collect::<Vec<_>>().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 sized(cx: &mut DocContext<'_>) -> GenericBound {
Self::sized_with(cx, hir::TraitBoundModifier::None)
}
pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound {
Self::sized_with(cx, hir::TraitBoundModifier::Maybe)
}
fn sized_with(cx: &mut DocContext<'_>, modifier: hir::TraitBoundModifier) -> GenericBound {
let did = cx.tcx.require_lang_item(LangItem::Sized, None);
let empty = ty::Binder::dummy(ty::GenericArgs::empty());
let path = external_path(cx, did, false, ThinVec::new(), empty);
inline::record_extern_fqn(cx, did, ItemType::Trait);
GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifier)
}
pub(crate) fn is_trait_bound(&self) -> bool {
matches!(self, Self::TraitBound(..))
}
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 &&
Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait()
{
return true;
}
false
}
pub(crate) fn get_poly_trait(&self) -> Option<PolyTrait> {
if let GenericBound::TraitBound(ref p, _) = *self {
return Some(p.clone());
}
None
}
pub(crate) fn get_trait_path(&self) -> Option<Path> {
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, PartialEq, Eq, Hash, Debug)]
pub(crate) enum WherePredicate {
BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
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: ThinVec<Lifetime> },
Type { did: DefId, bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
// Option<Box<String>> makes this type smaller than `Option<String>` would.
Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
}
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 lifetime(name: Symbol) -> Self {
Self { name, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
}
pub(crate) fn is_synthetic_param(&self) -> bool {
match self.kind {
GenericParamDefKind::Lifetime { .. } => false,
GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect,
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<Generic>?
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct Generics {
pub(crate) params: ThinVec<GenericParamDef>,
pub(crate) where_predicates: ThinVec<WherePredicate>,
}
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: Type,
pub(crate) c_variadic: bool,
}
impl FnDecl {
pub(crate) fn self_type(&self) -> Option<SelfTy> {
self.inputs.values.get(0).and_then(|v| v.to_self())
}
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) struct Arguments {
pub(crate) values: Vec<Argument>,
}
#[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 <https://github.com/rust-lang/rust/issues/83167>.
pub(crate) is_const: bool,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum SelfTy {
SelfValue,
SelfBorrowed(Option<Lifetime>, Mutability),
SelfExplicit(Type),
}
impl Argument {
pub(crate) fn to_self(&self) -> Option<SelfTy> {
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, Debug)]
pub(crate) struct Trait {
pub(crate) def_id: DefId,
pub(crate) items: Vec<Item>,
pub(crate) generics: Generics,
pub(crate) bounds: Vec<GenericBound>,
}
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 unsafety(&self, tcx: TyCtxt<'_>) -> hir::Unsafety {
tcx.trait_def(self.def_id).unsafety
}
pub(crate) fn is_object_safe(&self, tcx: TyCtxt<'_>) -> bool {
tcx.check_is_object_safe(self.def_id)
}
}
#[derive(Clone, Debug)]
pub(crate) struct TraitAlias {
pub(crate) generics: Generics,
pub(crate) bounds: Vec<GenericBound>,
}
/// 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<GenericParamDef>,
}
/// 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<PolyTrait>, Option<Lifetime>),
/// A type parameter.
Generic(Symbol),
/// A primitive (aka, builtin) type.
Primitive(PrimitiveType),
/// A function pointer: `extern "ABI" fn(...) -> ...`
BareFunction(Box<BareFunctionDecl>),
/// A tuple type: `(i32, &str)`.
Tuple(Vec<Type>),
/// A slice type (does *not* include the `&`): `[i32]`
Slice(Box<Type>),
/// An array type.
///
/// The `String` field is a stringified version of the array's length parameter.
Array(Box<Type>, Box<str>),
/// A raw pointer type: `*const i32`, `*mut i32`
RawPointer(Mutability, Box<Type>),
/// A reference type: `&i32`, `&'a mut Foo`
BorrowedRef { lifetime: Option<Lifetime>, mutability: Mutability, type_: Box<Type> },
/// A qualified path to an associated item: `<Type as Trait>::Name`
QPath(Box<QPathData>),
/// A type that is inferred: `_`
Infer,
/// An `impl Trait`: `impl TraitA + TraitB + ...`
ImplTrait(Vec<GenericBound>),
}
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 { .. })
}
/// Check if two types are "the same" for documentation purposes.
///
/// This is different from `Eq`, because it knows that things like
/// `Placeholder` are possible matches for everything.
///
/// 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_same(&unit, &cache));
/// assert!(unit.is_same(&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)
};
match (self_cleared, other_cleared) {
// Recursive cases.
(Type::Tuple(a), Type::Tuple(b)) => {
a.len() == b.len() && a.iter().zip(b).all(|(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,
// 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.iter().zip(bg.iter()).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<PrimitiveType> {
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<Output = i32>`, 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 bindings, .. } = segment.args
&& let Some(binding) = bindings.pop()
&& let TypeBindingKind::Equality { term } = binding.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 {
match *self {
Generic(name) => name == kw::SelfUpper,
_ => false,
}
}
pub(crate) fn generics(&self) -> Option<Vec<&Type>> {
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())
}
pub(crate) fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
if let QPath(box QPathData { self_type, trait_, assoc, .. }) = self {
Some((self_type, trait_.as_ref()?.def_id(), assoc.clone()))
} else {
None
}
}
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
let t: PrimitiveType = match *self {
Type::Path { ref path } => return Some(path.def_id()),
DynTrait(ref bounds, _) => return bounds.get(0).map(|b| b.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(box QPathData { 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<DefId> {
self.inner_def_id(Some(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_show_cast: bool,
pub trait_: Option<Path>,
}
/// 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<PrimitiveType, ArrayVec<SimplifiedType, 3>>;
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<PrimitiveType> {
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::{FloatTy, IntTy, UintTy};
use PrimitiveType::*;
static CELL: OnceCell<SimplifiedTypes> = 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)),
F32 => single(SimplifiedType::Float(FloatTy::F32)),
F64 => single(SimplifiedType::Float(FloatTy::F64)),
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<Item = DefId> + '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<Item = DefId> + '_ {
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 `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<'_>) -> &FxHashMap<PrimitiveType, DefId> {
static PRIMITIVE_LOCATIONS: OnceCell<FxHashMap<PrimitiveType, DefId>> = 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<ast::IntTy> 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<ast::UintTy> 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<ast::FloatTy> for PrimitiveType {
fn from(float_ty: ast::FloatTy) -> PrimitiveType {
match float_ty {
ast::FloatTy::F32 => PrimitiveType::F32,
ast::FloatTy::F64 => PrimitiveType::F64,
}
}
}
impl From<ty::IntTy> 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<ty::UintTy> 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<ty::FloatTy> for PrimitiveType {
fn from(float_ty: ty::FloatTy) -> PrimitiveType {
match float_ty {
ty::FloatTy::F32 => PrimitiveType::F32,
ty::FloatTy::F64 => PrimitiveType::F64,
}
}
}
impl From<hir::PrimTy> 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<CtorKind>,
pub(crate) generics: Generics,
pub(crate) fields: Vec<Item>,
}
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<Item>,
}
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: Vec<Item>,
}
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<VariantIdx, Item>,
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<Item = &Item> {
self.variants.iter().filter(|v| !v.is_stripped())
}
}
#[derive(Clone, Debug)]
pub(crate) struct Variant {
pub kind: VariantKind,
pub discriminant: Option<Discriminant>,
}
#[derive(Clone, Debug)]
pub(crate) enum VariantKind {
CLike,
Tuple(Vec<Item>),
Struct(VariantStruct),
}
impl Variant {
pub(crate) fn has_stripped_entries(&self) -> Option<bool> {
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<BodyId>,
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<String> {
self.expr.map(|body| rendered_const(tcx, 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<PathSegment>,
}
impl Path {
pub(crate) fn def_id(&self) -> DefId {
self.res.def_id()
}
pub(crate) fn last_opt(&self) -> Option<Symbol> {
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 generics(&self) -> Option<Vec<&Type>> {
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
}
})
}
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericArg {
Lifetime(Lifetime),
Type(Type),
Const(Box<Constant>),
Infer,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericArgs {
AngleBracketed { args: Box<[GenericArg]>, bindings: ThinVec<TypeBinding> },
Parenthesized { inputs: Box<[Type]>, output: Option<Box<Type>> },
}
impl GenericArgs {
pub(crate) fn is_empty(&self) -> bool {
match self {
GenericArgs::AngleBracketed { args, bindings } => {
args.is_empty() && bindings.is_empty()
}
GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(),
}
}
}
#[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<VariantIdx, Item>, is_non_exhaustive: bool },
Union { fields: Vec<Item> },
Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
}
#[derive(Clone, Debug)]
pub(crate) struct TypeAlias {
pub(crate) type_: Type,
pub(crate) generics: Generics,
/// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
/// to be shown directly on the typedef page.
pub(crate) inner_type: Option<TypeAliasInnerType>,
/// `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<Type>,
}
#[derive(Clone, Debug)]
pub(crate) struct OpaqueTy {
pub(crate) bounds: Vec<GenericBound>,
pub(crate) generics: Generics,
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) struct BareFunctionDecl {
pub(crate) unsafety: hir::Unsafety,
pub(crate) generic_params: Vec<GenericParamDef>,
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<BodyId>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Constant {
pub(crate) type_: Box<Type>,
pub(crate) generics: Generics,
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<Type> 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<str> },
/// 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<String> {
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.to_string(),
ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id),
ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
rendered_const(tcx, body)
}
}
}
pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> Option<String> {
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => 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 { .. } => false,
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<Path>,
pub(crate) for_: Type,
pub(crate) items: Vec<Item>,
pub(crate) polarity: ty::ImplPolarity,
pub(crate) kind: ImplKind,
}
impl Impl {
pub(crate) fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxHashSet<Symbol> {
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,
FakeVariadic,
Blanket(Box<Type>),
}
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.map_or(false, |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<DefId>,
}
#[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<Symbol>,
}
/// An type binding on an associated type (e.g., `A = Bar` in `Foo<A = Bar>` or
/// `A: Send + Sync` in `Foo<A: Send + Sync>`).
#[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<GenericBound> },
}
/// 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<T> = Vec<T>;
///
/// pub fn public_fn() -> PrivAlias<i32> { vec![] }
/// ```
///
/// `public_fn`'s docs will show it as returning `Vec<i32>`, 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::*;
use rustc_data_structures::static_assert_size;
// tidy-alphabetical-start
static_assert_size!(Crate, 64); // frequently moved by-value
static_assert_size!(DocFragment, 32);
static_assert_size!(GenericArg, 32);
static_assert_size!(GenericArgs, 32);
static_assert_size!(GenericParamDef, 40);
static_assert_size!(Generics, 16);
static_assert_size!(Item, 56);
static_assert_size!(ItemKind, 56);
static_assert_size!(PathSegment, 40);
static_assert_size!(Type, 32);
// tidy-alphabetical-end
}