Res::Def(DefKind::TyAlias, did) => {
record_extern_fqn(cx, did, ItemType::Typedef);
build_impls(cx, Some(parent_module), did, attrs, &mut ret);
- clean::TypedefItem(build_type_alias(cx, did), false)
+ clean::TypedefItem(build_type_alias(cx, did))
}
Res::Def(DefKind::Enum, did) => {
record_extern_fqn(cx, did, ItemType::Enum);
let local_did = self.def_id.to_def_id();
cx.with_param_env(local_did, |cx| {
let inner = match self.kind {
- hir::TraitItemKind::Const(ref ty, default) => {
- let default =
- default.map(|e| ConstantKind::Local { def_id: local_did, body: e });
- AssocConstItem(ty.clean(cx), default)
- }
+ hir::TraitItemKind::Const(ref ty, Some(default)) => AssocConstItem(
+ ty.clean(cx),
+ ConstantKind::Local { def_id: local_did, body: default },
+ ),
+ hir::TraitItemKind::Const(ref ty, None) => TyAssocConstItem(ty.clean(cx)),
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, &self.generics, body);
MethodItem(m, None)
});
TyMethodItem(Function { decl, generics })
}
- hir::TraitItemKind::Type(bounds, ref default) => {
+ hir::TraitItemKind::Type(bounds, Some(default)) => {
+ let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
+ let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
+ let item_type = hir_ty_to_ty(cx.tcx, default).clean(cx);
+ AssocTypeItem(
+ Typedef { type_: default.clean(cx), generics, item_type: Some(item_type) },
+ bounds,
+ )
+ }
+ hir::TraitItemKind::Type(bounds, None) => {
let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
- let default = default.map(|t| t.clean(cx));
- AssocTypeItem(Box::new(generics), bounds, default)
+ TyAssocTypeItem(Box::new(generics), bounds)
}
};
let what_rustc_thinks =
cx.with_param_env(local_did, |cx| {
let inner = match self.kind {
hir::ImplItemKind::Const(ref ty, expr) => {
- let default = Some(ConstantKind::Local { def_id: local_did, body: expr });
+ let default = ConstantKind::Local { def_id: local_did, body: expr };
AssocConstItem(ty.clean(cx), default)
}
hir::ImplItemKind::Fn(ref sig, body) => {
let type_ = hir_ty.clean(cx);
let generics = self.generics.clean(cx);
let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
- TypedefItem(Typedef { type_, generics, item_type: Some(item_type) }, true)
+ AssocTypeItem(
+ Typedef { type_, generics, item_type: Some(item_type) },
+ Vec::new(),
+ )
}
};
let tcx = cx.tcx;
let kind = match self.kind {
ty::AssocKind::Const => {
- let ty = tcx.type_of(self.def_id);
- let default = if self.defaultness.has_value() {
- Some(ConstantKind::Extern { def_id: self.def_id })
- } else {
- None
+ let ty = tcx.type_of(self.def_id).clean(cx);
+
+ let provided = match self.container {
+ ty::ImplContainer(_) => true,
+ ty::TraitContainer(_) => self.defaultness.has_value(),
};
- AssocConstItem(ty.clean(cx), default)
+ if provided {
+ AssocConstItem(ty, ConstantKind::Extern { def_id: self.def_id })
+ } else {
+ TyAssocConstItem(ty)
+ }
}
ty::AssocKind::Fn => {
let generics = clean_ty_generics(
None => bounds.push(GenericBound::maybe_sized(cx)),
}
- let ty = if self.defaultness.has_value() {
- Some(tcx.type_of(self.def_id))
+ if self.defaultness.has_value() {
+ AssocTypeItem(
+ Typedef {
+ type_: tcx.type_of(self.def_id).clean(cx),
+ generics,
+ // FIXME: should we obtain the Type from HIR and pass it on here?
+ item_type: None,
+ },
+ bounds,
+ )
} else {
- None
- };
-
- AssocTypeItem(Box::new(generics), bounds, ty.map(|t| t.clean(cx)))
+ TyAssocTypeItem(Box::new(generics), bounds)
+ }
} else {
// FIXME: when could this happen? Associated items in inherent impls?
- let type_ = tcx.type_of(self.def_id).clean(cx);
- TypedefItem(
+ AssocTypeItem(
Typedef {
- type_,
+ type_: tcx.type_of(self.def_id).clean(cx),
generics: Generics { params: Vec::new(), where_predicates: Vec::new() },
item_type: None,
},
- true,
+ Vec::new(),
)
}
}
ItemKind::TyAlias(hir_ty, ref generics) => {
let rustdoc_ty = hir_ty.clean(cx);
let ty = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
- TypedefItem(
- Typedef {
- type_: rustdoc_ty,
- generics: generics.clean(cx),
- item_type: Some(ty),
- },
- false,
- )
+ TypedefItem(Typedef {
+ type_: rustdoc_ty,
+ generics: generics.clean(cx),
+ item_type: Some(ty),
+ })
}
ItemKind::Enum(ref def, ref generics) => EnumItem(Enum {
variants: def.variants.iter().map(|v| v.clean(cx)).collect(),
self.type_() == ItemType::Variant
}
crate fn is_associated_type(&self) -> bool {
- self.type_() == ItemType::AssocType
+ matches!(&*self.kind, AssocTypeItem(..) | StrippedItem(box AssocTypeItem(..)))
+ }
+ crate fn is_ty_associated_type(&self) -> bool {
+ matches!(&*self.kind, TyAssocTypeItem(..) | StrippedItem(box TyAssocTypeItem(..)))
}
crate fn is_associated_const(&self) -> bool {
- self.type_() == ItemType::AssocConst
+ matches!(&*self.kind, AssocConstItem(..) | StrippedItem(box AssocConstItem(..)))
+ }
+ crate fn is_ty_associated_const(&self) -> bool {
+ matches!(&*self.kind, TyAssocConstItem(..) | StrippedItem(box TyAssocConstItem(..)))
}
crate fn is_method(&self) -> bool {
self.type_() == ItemType::Method
EnumItem(Enum),
FunctionItem(Function),
ModuleItem(Module),
- TypedefItem(Typedef, bool /* is associated type */),
+ TypedefItem(Typedef),
OpaqueTyItem(OpaqueTy),
StaticItem(Static),
ConstantItem(Constant),
TraitItem(Trait),
TraitAliasItem(TraitAlias),
ImplItem(Impl),
- /// A method signature only. Used for required methods in traits (ie,
- /// non-default-methods).
+ /// A required method in a trait declaration meaning it's only a function signature.
TyMethodItem(Function),
- /// A method with a body.
+ /// A method in a trait impl or a provided method in a trait declaration.
+ ///
+ /// Compared to [TyMethodItem], it also contains a method body.
MethodItem(Function, Option<hir::Defaultness>),
StructFieldItem(Type),
VariantItem(Variant),
MacroItem(Macro),
ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType),
- AssocConstItem(Type, Option<ConstantKind>),
- /// An associated item in a trait or trait impl.
+ /// 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.
- /// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
- AssocTypeItem(Box<Generics>, Vec<GenericBound>, Option<Type>),
+ TyAssocTypeItem(Box<Generics>, Vec<GenericBound>),
+ /// An associated type in a trait impl or a provided one in a trait declaration.
+ AssocTypeItem(Typedef, Vec<GenericBound>),
/// An item that has been stripped by a rustdoc pass
StrippedItem(Box<ItemKind>),
KeywordItem(Symbol),
ExternCrateItem { .. }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_, _)
+ | TypedefItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
+ | TyAssocConstItem(_)
| AssocConstItem(_, _)
+ | TyAssocTypeItem(..)
| AssocTypeItem(..)
| StrippedItem(_)
| KeywordItem(_) => [].iter(),
for item in items {
let target = match *item.kind {
- ItemKind::TypedefItem(ref t, true) => &t.type_,
+ ItemKind::AssocTypeItem(ref t, _) => &t.type_,
_ => continue,
};
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_, _)
+ | TypedefItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
- | AssocConstItem(_, _)
+ | TyAssocConstItem(..)
+ | AssocConstItem(..)
+ | TyAssocTypeItem(..)
| AssocTypeItem(..)
| KeywordItem(_) => kind,
}
if let Some(ref s) = item.name {
let (parent, is_inherent_impl_item) = match *item.kind {
clean::StrippedItem(..) => ((None, None), false),
- clean::AssocConstItem(..) | clean::TypedefItem(_, true)
+ clean::AssocConstItem(..) | clean::AssocTypeItem(..)
if self.cache.parent_is_trait_impl =>
{
// skip associated items in trait impls
((None, None), false)
}
- clean::AssocTypeItem(..)
- | clean::TyMethodItem(..)
+ clean::TyMethodItem(..)
+ | clean::TyAssocConstItem(..)
+ | clean::TyAssocTypeItem(..)
| clean::StructFieldItem(..)
| clean::VariantItem(..) => (
(
),
false,
),
- clean::MethodItem(..) | clean::AssocConstItem(..) => {
+ clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
if self.cache.parent_stack.is_empty() {
((None, None), false)
} else {
| clean::TyMethodItem(..)
| clean::MethodItem(..)
| clean::StructFieldItem(..)
+ | clean::TyAssocConstItem(..)
| clean::AssocConstItem(..)
+ | clean::TyAssocTypeItem(..)
| clean::AssocTypeItem(..)
| clean::StrippedItem(..)
| clean::KeywordItem(..) => {
/// The search index uses item types encoded as smaller numbers which equal to
/// discriminants. JavaScript then is used to decode them into the original value.
/// Consequently, every change to this type should be synchronized to
-/// the `itemTypes` mapping table in `html/static/main.js`.
+/// the `itemTypes` mapping table in `html/static/js/search.js`.
///
/// In addition, code in `html::render` uses this enum to generate CSS classes, page prefixes, and
/// module headings. If you are adding to this enum and want to ensure that the sidebar also prints
clean::ForeignStaticItem(..) => ItemType::Static, // no ForeignStatic
clean::MacroItem(..) => ItemType::Macro,
clean::PrimitiveItem(..) => ItemType::Primitive,
- clean::AssocConstItem(..) => ItemType::AssocConst,
- clean::AssocTypeItem(..) => ItemType::AssocType,
+ clean::TyAssocConstItem(..) | clean::AssocConstItem(..) => ItemType::AssocConst,
+ clean::TyAssocTypeItem(..) | clean::AssocTypeItem(..) => ItemType::AssocType,
clean::ForeignTypeItem => ItemType::ForeignType,
clean::KeywordItem(..) => ItemType::Keyword,
clean::TraitAliasItem(..) => ItemType::TraitAlias,
/// This item is known to rustdoc, but from a crate that does not have documentation generated.
///
/// This can only happen for non-local items.
+ ///
+ /// # Example
+ ///
+ /// Crate `a` defines a public trait and crate `b` – the target crate that depends on `a` –
+ /// implements it for a local type.
+ /// We document `b` but **not** `a` (we only _build_ the latter – with `rustc`):
+ ///
+ /// ```sh
+ /// rustc a.rs --crate-type=lib
+ /// rustdoc b.rs --crate-type=lib --extern=a=liba.rlib
+ /// ```
+ ///
+ /// Now, the associated items in the trait impl want to link to the corresponding item in the
+ /// trait declaration (see `html::render::assoc_href_attr`) but it's not available since their
+ /// *documentation (was) not built*.
DocumentationNotBuilt,
/// This can only happen for non-local items when `--document-private-items` is not passed.
Private,
map.insert("trait-implementations".to_owned(), 1);
map.insert("synthetic-implementations".to_owned(), 1);
map.insert("blanket-implementations".to_owned(), 1);
- map.insert("associated-types".to_owned(), 1);
- map.insert("associated-const".to_owned(), 1);
+ map.insert("required-associated-types".to_owned(), 1);
+ map.insert("provided-associated-types".to_owned(), 1);
+ map.insert("provided-associated-consts".to_owned(), 1);
+ map.insert("required-associated-consts".to_owned(), 1);
map.insert("required-methods".to_owned(), 1);
map.insert("provided-methods".to_owned(), 1);
map.insert("implementors".to_owned(), 1);
let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string();
if s.contains('\n') {
- let link = format!(r#" <a href="{}">Read more</a>"#, naive_assoc_href(item, link, cx));
+ let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx));
if let Some(idx) = summary_html.rfind("</p>") {
summary_html.insert_str(idx, &link);
w.write_str(&rendered_impls.join(""));
}
-fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
- use crate::formats::item_type::ItemType::*;
+/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
+fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
+ let name = it.name.unwrap();
+ let item_type = it.type_();
- let name = it.name.as_ref().unwrap();
- let ty = match it.type_() {
- Typedef | AssocType => AssocType,
- s => s,
- };
+ let href = match link {
+ AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
+ AssocItemLink::Anchor(None) => Some(format!("#{}.{}", item_type, name)),
+ AssocItemLink::GotoSource(did, provided_methods) => {
+ // We're creating a link from the implementation of an associated item to its
+ // declaration in the trait declaration.
+ let item_type = match item_type {
+ // For historical but not technical reasons, the item type of methods in
+ // trait declarations depends on whether the method is required (`TyMethod`) or
+ // provided (`Method`).
+ ItemType::Method | ItemType::TyMethod => {
+ if provided_methods.contains(&name) {
+ ItemType::Method
+ } else {
+ ItemType::TyMethod
+ }
+ }
+ // For associated types and constants, no such distinction exists.
+ item_type => item_type,
+ };
- let anchor = format!("#{}.{}", ty, name);
- match link {
- AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id),
- AssocItemLink::Anchor(None) => anchor,
- AssocItemLink::GotoSource(did, _) => {
- href(did.expect_def_id(), cx).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
+ match href(did.expect_def_id(), cx) {
+ Ok((url, ..)) => Some(format!("{}#{}.{}", url, item_type, name)),
+ // The link is broken since it points to an external crate that wasn't documented.
+ // Do not create any link in such case. This is better than falling back to a
+ // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
+ // (that used to happen in older versions). Indeed, in most cases this dummy would
+ // coincide with the `id`. However, it would not always do so.
+ // In general, this dummy would be incorrect:
+ // If the type with the trait impl also had an inherent impl with an assoc. item of
+ // the *same* name as this impl item, the dummy would link to that one even though
+ // those two items are distinct!
+ // In this scenario, the actual `id` of this impl item would be
+ // `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
+ Err(HrefError::DocumentationNotBuilt) => None,
+ Err(_) => Some(format!("#{}.{}", item_type, name)),
+ }
}
- }
+ };
+
+ // If there is no `href` for the reason explained above, simply do not render it which is valid:
+ // https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
+ href.map(|href| format!(" href=\"{}\"", href)).unwrap_or_default()
}
fn assoc_const(
w: &mut Buffer,
it: &clean::Item,
ty: &clean::Type,
+ default: Option<&clean::ConstantKind>,
link: AssocItemLink<'_>,
extra: &str,
cx: &Context<'_>,
) {
write!(
w,
- "{}{}const <a href=\"{}\" class=\"constant\">{}</a>: {}",
- extra,
- it.visibility.print_with_space(it.def_id, cx),
- naive_assoc_href(it, link, cx),
- it.name.as_ref().unwrap(),
- ty.print(cx)
+ "{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}",
+ extra = extra,
+ vis = it.visibility.print_with_space(it.def_id, cx),
+ href = assoc_href_attr(it, link, cx),
+ name = it.name.as_ref().unwrap(),
+ ty = ty.print(cx),
);
+ if let Some(default) = default {
+ // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
+ // hood which adds noisy underscores and a type suffix to number literals.
+ // This hurts readability in this context especially when more complex expressions
+ // are involved and it doesn't add much of value.
+ // Find a way to print constants here without all that jazz.
+ write!(w, " = {}", default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx())));
+ }
}
fn assoc_type(
) {
write!(
w,
- "{indent}type <a href=\"{href}\" class=\"associatedtype\">{name}</a>{generics}",
+ "{indent}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
indent = " ".repeat(indent),
- href = naive_assoc_href(it, link, cx),
+ href = assoc_href_attr(it, link, cx),
name = it.name.as_ref().unwrap(),
generics = generics.print(cx),
);
) {
let header = meth.fn_header(cx.tcx()).expect("Trying to get header from a non-function item");
let name = meth.name.as_ref().unwrap();
- let href = match link {
- AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{}", id)),
- AssocItemLink::Anchor(None) => Some(format!("#{}.{}", meth.type_(), name)),
- AssocItemLink::GotoSource(did, provided_methods) => {
- // We're creating a link from an impl-item to the corresponding
- // trait-item and need to map the anchored type accordingly.
- let ty =
- if provided_methods.contains(name) { ItemType::Method } else { ItemType::TyMethod };
-
- match (href(did.expect_def_id(), cx), ty) {
- (Ok(p), ty) => Some(format!("{}#{}.{}", p.0, ty, name)),
- (Err(HrefError::DocumentationNotBuilt), ItemType::TyMethod) => None,
- (Err(_), ty) => Some(format!("#{}.{}", ty, name)),
- }
- }
- };
let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string();
// FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
// this condition.
let unsafety = header.unsafety.print_with_space();
let defaultness = print_default_space(meth.is_default());
let abi = print_abi_with_space(header.abi).to_string();
+ let href = assoc_href_attr(meth, link, cx);
// NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
let generics_len = format!("{:#}", g.print(cx)).len();
w.reserve(header_len + "<a href=\"\" class=\"fnname\">{".len() + "</a>".len());
write!(
w,
- "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a {href} class=\"fnname\">{name}</a>\
+ "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn <a{href} class=\"fnname\">{name}</a>\
{generics}{decl}{notable_traits}{where_clause}",
indent = indent_str,
vis = vis,
unsafety = unsafety,
defaultness = defaultness,
abi = abi,
- // links without a href are valid - https://www.w3schools.com/tags/att_a_href.asp
- href = href.map(|href| format!("href=\"{}\"", href)).unwrap_or_else(|| "".to_string()),
+ href = href,
name = name,
generics = g.print(cx),
decl = d.full_print(header_len, indent, header.asyncness, cx),
cx: &Context<'_>,
render_mode: RenderMode,
) {
- match *item.kind {
+ match &*item.kind {
clean::StrippedItem(..) => {}
- clean::TyMethodItem(ref m) => {
+ clean::TyMethodItem(m) => {
assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
}
- clean::MethodItem(ref m, _) => {
+ clean::MethodItem(m, _) => {
assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
}
- clean::AssocConstItem(ref ty, _) => {
- assoc_const(w, item, ty, link, if parent == ItemType::Trait { " " } else { "" }, cx)
- }
- clean::AssocTypeItem(ref generics, ref bounds, ref default) => assoc_type(
+ kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const(
+ w,
+ item,
+ ty,
+ match kind {
+ clean::TyAssocConstItem(_) => None,
+ clean::AssocConstItem(_, default) => Some(default),
+ _ => unreachable!(),
+ },
+ link,
+ if parent == ItemType::Trait { " " } else { "" },
+ cx,
+ ),
+ clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
w,
item,
generics,
bounds,
- default.as_ref(),
+ None,
+ link,
+ if parent == ItemType::Trait { 4 } else { 0 },
+ cx,
+ ),
+ clean::AssocTypeItem(ref ty, ref bounds) => assoc_type(
+ w,
+ item,
+ &ty.generics,
+ bounds,
+ Some(ty.item_type.as_ref().unwrap_or(&ty.type_)),
link,
if parent == ItemType::Trait { 4 } else { 0 },
cx,
.items
.iter()
.find_map(|item| match *item.kind {
- clean::TypedefItem(ref t, true) => Some(match *t {
+ clean::AssocTypeItem(ref t, _) => Some(match *t {
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
impl_.print(false, cx)
);
for it in &impl_.items {
- if let clean::TypedefItem(ref tydef, _) = *it.kind {
+ if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
out.push_str("<span class=\"where fmt-newline\"> ");
let empty_set = FxHashSet::default();
let src_link =
&mut out,
it,
&tydef.generics,
- &[],
+ &[], // intentionally leaving out bounds
Some(&tydef.type_),
src_link,
0,
if item_type == ItemType::Method { " method-toggle" } else { "" };
write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class);
}
- match *item.kind {
+ match &*item.kind {
clean::MethodItem(..) | clean::TyMethodItem(_) => {
// Only render when the method is not static or we allow static methods
if render_method_item {
w.write_str("</section>");
}
}
- clean::TypedefItem(ref tydef, _) => {
- let source_id = format!("{}.{}", ItemType::AssocType, name);
+ kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => {
+ let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
write!(
w,
"<section id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class
);
+ render_rightside(w, cx, item, containing_item, render_mode);
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
- assoc_type(
+ assoc_const(
w,
item,
- &tydef.generics,
- &[],
- Some(&tydef.type_),
+ ty,
+ match kind {
+ clean::TyAssocConstItem(_) => None,
+ clean::AssocConstItem(_, default) => Some(default),
+ _ => unreachable!(),
+ },
link.anchor(if trait_.is_some() { &source_id } else { &id }),
- 0,
+ "",
cx,
);
w.write_str("</h4>");
w.write_str("</section>");
}
- clean::AssocConstItem(ref ty, _) => {
+ clean::TyAssocTypeItem(generics, bounds) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(
- w,
- "<section id=\"{}\" class=\"{}{} has-srclink\">",
- id, item_type, in_trait_class
- );
- render_rightside(w, cx, item, containing_item, render_mode);
+ write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
- assoc_const(
+ assoc_type(
w,
item,
- ty,
+ generics,
+ bounds,
+ None,
link.anchor(if trait_.is_some() { &source_id } else { &id }),
- "",
+ 0,
cx,
);
w.write_str("</h4>");
w.write_str("</section>");
}
- clean::AssocTypeItem(ref generics, ref bounds, ref default) => {
+ clean::AssocTypeItem(tydef, _bounds) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
+ write!(
+ w,
+ "<section id=\"{}\" class=\"{}{} has-srclink\">",
+ id, item_type, in_trait_class
+ );
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
assoc_type(
w,
item,
- generics,
- bounds,
- default.as_ref(),
+ &tydef.generics,
+ &[], // intentionally leaving out bounds
+ Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
link.anchor(if trait_.is_some() { &source_id } else { &id }),
0,
cx,
write!(w, "{}", i.inner_impl().print(use_absolute, cx));
if show_def_docs {
for it in &i.inner_impl().items {
- if let clean::TypedefItem(ref tydef, _) = *it.kind {
+ if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind {
w.write_str("<span class=\"where fmt-newline\"> ");
assoc_type(
w,
it,
&tydef.generics,
- &[],
+ &[], // intentionally leaving out bounds
Some(&tydef.type_),
AssocItemLink::Anchor(None),
0,
clean::PrimitiveItem(_) => sidebar_primitive(cx, buffer, it),
clean::UnionItem(ref u) => sidebar_union(cx, buffer, it, u),
clean::EnumItem(ref e) => sidebar_enum(cx, buffer, it, e),
- clean::TypedefItem(_, _) => sidebar_typedef(cx, buffer, it),
+ clean::TypedefItem(_) => sidebar_typedef(cx, buffer, it),
clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
clean::ForeignTypeItem => sidebar_foreign_type(cx, buffer, it),
_ => {}
if !for_deref || should_render_item(item, deref_mut, tcx) {
Some(SidebarLink {
name,
- url: get_next_url(used_links, format!("method.{}", name)),
+ url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
})
} else {
None
.filter_map(|item| match item.name {
Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
name,
- url: get_next_url(used_links, format!("associatedconstant.{}", name)),
+ url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
}),
_ => None,
})
debug!("found Deref: {:?}", impl_);
if let Some((target, real_target)) =
impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
- clean::TypedefItem(ref t, true) => Some(match *t {
+ clean::AssocTypeItem(ref t, _) => Some(match *t {
clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
_ => (&t.type_, &t.type_),
}),
print_sidebar_section(
buf,
&t.items,
- "associated-types",
- "Associated Types",
+ "required-associated-types",
+ "Required Associated Types",
+ |m| m.is_ty_associated_type(),
+ |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
+ );
+
+ print_sidebar_section(
+ buf,
+ &t.items,
+ "provided-associated-types",
+ "Provided Associated Types",
|m| m.is_associated_type(),
- |sym| format!("<a href=\"#associatedtype.{0}\">{0}</a>", sym),
+ |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocType),
+ );
+
+ print_sidebar_section(
+ buf,
+ &t.items,
+ "required-associated-consts",
+ "Required Associated Constants",
+ |m| m.is_ty_associated_const(),
+ |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
);
print_sidebar_section(
buf,
&t.items,
- "associated-const",
- "Associated Constants",
+ "provided-associated-consts",
+ "Provided Associated Constants",
|m| m.is_associated_const(),
- |sym| format!("<a href=\"#associatedconstant.{0}\">{0}</a>", sym),
+ |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::AssocConst),
);
print_sidebar_section(
"required-methods",
"Required Methods",
|m| m.is_ty_method(),
- |sym| format!("<a href=\"#tymethod.{0}\">{0}</a>", sym),
+ |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::TyMethod),
);
print_sidebar_section(
"provided-methods",
"Provided Methods",
|m| m.is_method(),
- |sym| format!("<a href=\"#method.{0}\">{0}</a>", sym),
+ |sym| format!("<a href=\"#{1}.{0}\">{0}</a>", sym, ItemType::Method),
);
let cache = cx.cache();
item_vars.render_into(buf).unwrap();
- match *item.kind {
+ match &*item.kind {
clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => {
item_function(buf, cx, item, f)
clean::StructItem(ref s) => item_struct(buf, cx, item, s),
clean::UnionItem(ref s) => item_union(buf, cx, item, s),
clean::EnumItem(ref e) => item_enum(buf, cx, item, e),
- clean::TypedefItem(ref t, is_associated) => item_typedef(buf, cx, item, t, is_associated),
+ clean::TypedefItem(ref t) => item_typedef(buf, cx, item, t),
clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
let bounds = bounds(&t.bounds, false, cx);
- let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
- let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
- let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
- let provided = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>();
- let count_types = types.len();
- let count_consts = consts.len();
- let count_methods = required.len() + provided.len();
+ let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::<Vec<_>>();
+ let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
+ let required_consts = t.items.iter().filter(|m| m.is_ty_associated_const()).collect::<Vec<_>>();
+ let provided_consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
+ let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
+ let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>();
+ let count_types = required_types.len() + provided_types.len();
+ let count_consts = required_consts.len() + provided_consts.len();
+ let count_methods = required_methods.len() + provided_methods.len();
// Output the trait definition
wrap_into_docblock(w, |w| {
),
);
}
- for t in &types {
- render_assoc_item(
- w,
- t,
- AssocItemLink::Anchor(None),
- ItemType::Trait,
- cx,
- RenderMode::Normal,
- );
- w.write_str(";\n");
+ for types in [&required_types, &provided_types] {
+ for t in types {
+ render_assoc_item(
+ w,
+ t,
+ AssocItemLink::Anchor(None),
+ ItemType::Trait,
+ cx,
+ RenderMode::Normal,
+ );
+ w.write_str(";\n");
+ }
}
// If there are too many associated constants, hide everything after them
// We also do this if the types + consts is large because otherwise we could
),
);
}
- if !types.is_empty() && !consts.is_empty() {
+ if count_types != 0 && (count_consts != 0 || count_methods != 0) {
w.write_str("\n");
}
- for t in &consts {
- render_assoc_item(
- w,
- t,
- AssocItemLink::Anchor(None),
- ItemType::Trait,
- cx,
- RenderMode::Normal,
- );
- w.write_str(";\n");
+ for consts in [&required_consts, &provided_consts] {
+ for c in consts {
+ render_assoc_item(
+ w,
+ c,
+ AssocItemLink::Anchor(None),
+ ItemType::Trait,
+ cx,
+ RenderMode::Normal,
+ );
+ w.write_str(";\n");
+ }
}
if !toggle && should_hide_fields(count_methods) {
toggle = true;
toggle_open(w, format_args!("{} methods", count_methods));
}
- if !consts.is_empty() && !required.is_empty() {
+ if count_consts != 0 && count_methods != 0 {
w.write_str("\n");
}
- for (pos, m) in required.iter().enumerate() {
+ for (pos, m) in required_methods.iter().enumerate() {
render_assoc_item(
w,
m,
);
w.write_str(";\n");
- if pos < required.len() - 1 {
+ if pos < required_methods.len() - 1 {
w.write_str("<span class=\"item-spacer\"></span>");
}
}
- if !required.is_empty() && !provided.is_empty() {
+ if !required_methods.is_empty() && !provided_methods.is_empty() {
w.write_str("\n");
}
- for (pos, m) in provided.iter().enumerate() {
+ for (pos, m) in provided_methods.iter().enumerate() {
render_assoc_item(
w,
m,
w.write_str(" { ... }\n");
}
}
- if pos < provided.len() - 1 {
+
+ if pos < provided_methods.len() - 1 {
w.write_str("<span class=\"item-spacer\"></span>");
}
}
}
}
- if !types.is_empty() {
+ if !required_types.is_empty() {
write_small_section_header(
w,
- "associated-types",
- "Associated Types",
+ "required-associated-types",
+ "Required Associated Types",
"<div class=\"methods\">",
);
- for t in types {
+ for t in required_types {
+ trait_item(w, cx, t, it);
+ }
+ w.write_str("</div>");
+ }
+ if !provided_types.is_empty() {
+ write_small_section_header(
+ w,
+ "provided-associated-types",
+ "Provided Associated Types",
+ "<div class=\"methods\">",
+ );
+ for t in provided_types {
trait_item(w, cx, t, it);
}
w.write_str("</div>");
}
- if !consts.is_empty() {
+ if !required_consts.is_empty() {
+ write_small_section_header(
+ w,
+ "required-associated-consts",
+ "Required Associated Constants",
+ "<div class=\"methods\">",
+ );
+ for t in required_consts {
+ trait_item(w, cx, t, it);
+ }
+ w.write_str("</div>");
+ }
+ if !provided_consts.is_empty() {
write_small_section_header(
w,
- "associated-const",
- "Associated Constants",
+ "provided-associated-consts",
+ "Provided Associated Constants",
"<div class=\"methods\">",
);
- for t in consts {
+ for t in provided_consts {
trait_item(w, cx, t, it);
}
w.write_str("</div>");
}
// Output the documentation for each function individually
- if !required.is_empty() {
+ if !required_methods.is_empty() {
write_small_section_header(
w,
"required-methods",
- "Required methods",
+ "Required Methods",
"<div class=\"methods\">",
);
- for m in required {
+ for m in required_methods {
trait_item(w, cx, m, it);
}
w.write_str("</div>");
}
- if !provided.is_empty() {
+ if !provided_methods.is_empty() {
write_small_section_header(
w,
"provided-methods",
- "Provided methods",
+ "Provided Methods",
"<div class=\"methods\">",
);
- for m in provided {
+ for m in provided_methods {
trait_item(w, cx, m, it);
}
w.write_str("</div>");
render_assoc_items(w, cx, it, it.def_id.expect_def_id(), AssocItemRender::All)
}
-fn item_typedef(
- w: &mut Buffer,
- cx: &Context<'_>,
- it: &clean::Item,
- t: &clean::Typedef,
- is_associated: bool,
-) {
- fn write_content(
- w: &mut Buffer,
- cx: &Context<'_>,
- it: &clean::Item,
- t: &clean::Typedef,
- is_associated: bool,
- ) {
+fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
+ fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
wrap_item(w, "typedef", |w| {
render_attributes_in_pre(w, it, "");
- if !is_associated {
- write!(w, "{}", it.visibility.print_with_space(it.def_id, cx));
- }
+ write!(w, "{}", it.visibility.print_with_space(it.def_id, cx));
write!(
w,
"type {}{}{where_clause} = {type_};",
});
}
- // If this is an associated typedef, we don't want to wrap it into a docblock.
- if is_associated {
- write_content(w, cx, it, t, is_associated);
- } else {
- wrap_into_docblock(w, |w| {
- write_content(w, cx, it, t, is_associated);
- });
- }
+ wrap_into_docblock(w, |w| write_content(w, cx, it, t));
document(w, cx, it, None, HeadingOffset::H2);
(function() {
// This mapping table should match the discriminants of
-// `rustdoc::html::item_type::ItemType` type in Rust.
+// `rustdoc::formats::item_type::ItemType` type in Rust.
var itemTypes = [
"mod",
"externcrate",
StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignStaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
ForeignTypeItem => ItemEnum::ForeignType,
- TypedefItem(t, _) => ItemEnum::Typedef(t.into_tcx(tcx)),
+ TypedefItem(t) => ItemEnum::Typedef(t.into_tcx(tcx)),
OpaqueTyItem(t) => ItemEnum::OpaqueTy(t.into_tcx(tcx)),
ConstantItem(c) => ItemEnum::Constant(c.into_tcx(tcx)),
MacroItem(m) => ItemEnum::Macro(m.source),
ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_tcx(tcx)),
PrimitiveItem(p) => ItemEnum::PrimitiveType(p.as_sym().to_string()),
+ TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None },
AssocConstItem(ty, default) => {
- ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: default.map(|c| c.expr(tcx)) }
+ ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
}
- AssocTypeItem(g, b, t) => ItemEnum::AssocType {
+ TyAssocTypeItem(g, b) => ItemEnum::AssocType {
generics: (*g).into_tcx(tcx),
bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(),
- default: t.map(|x| x.into_tcx(tcx)),
+ default: None,
},
+ // FIXME: do not map to Typedef but to a custom variant
+ AssocTypeItem(t, _) => ItemEnum::Typedef(t.into_tcx(tcx)),
// `convert_item` early returns `None` for striped items
StrippedItem(_) => unreachable!(),
KeywordItem(_) => {
*item.kind,
clean::StructFieldItem(_)
| clean::VariantItem(_)
- | clean::AssocConstItem(_, _)
+ | clean::AssocConstItem(..)
| clean::AssocTypeItem(..)
- | clean::TypedefItem(_, _)
+ | clean::TypedefItem(_)
| clean::StaticItem(_)
| clean::ConstantItem(_)
| clean::ExternCrateItem { .. }
let target = items
.iter()
.find_map(|item| match *item.kind {
- TypedefItem(ref t, true) => Some(&t.type_),
+ AssocTypeItem(ref t, _) => Some(&t.type_),
_ => None,
})
.expect("Deref impl without Target type");
| clean::ConstantItem(..)
| clean::UnionItem(..)
| clean::AssocConstItem(..)
+ | clean::AssocTypeItem(..)
| clean::TraitAliasItem(..)
| clean::MacroItem(..)
| clean::ForeignTypeItem => {
clean::ImplItem(..) => {}
- // tymethods have no control over privacy
- clean::TyMethodItem(..) => {}
+ // tymethods etc. have no control over privacy
+ clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
// Proc-macros are always public
clean::ProcMacroItem(..) => {}
// Primitives are never stripped
clean::PrimitiveItem(..) => {}
- // Associated types are never stripped
- clean::AssocTypeItem(..) => {}
-
// Keywords are never stripped
clean::KeywordItem(..) => {}
}
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
- | TypedefItem(_, _)
+ | TypedefItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
- | AssocConstItem(_, _)
+ | TyAssocConstItem(..)
+ | AssocConstItem(..)
+ | TyAssocTypeItem(..)
| AssocTypeItem(..)
| KeywordItem(_) => {}
}
pub trait Foo {
// @has assoc_consts/trait.Foo.html '//*[@class="rust trait"]' \
- // 'const FOO: usize;'
+ // 'const FOO: usize = 13usize;'
// @has - '//*[@id="associatedconstant.FOO"]' 'const FOO: usize'
- const FOO: usize = 12;
+ const FOO: usize = 12 + 1;
// @has - '//*[@id="associatedconstant.FOO_NO_DEFAULT"]' 'const FOO_NO_DEFAULT: bool'
const FOO_NO_DEFAULT: bool;
// @!has - FOO_HIDDEN
--- /dev/null
+<a class="fnname">provided</a>(&self)
\ No newline at end of file
// aux-build:rustdoc-extern-default-method.rs
// ignore-cross-compile
+// ignore-tidy-linelength
extern crate rustdoc_extern_default_method as ext;
+// For this test, the dependency is compiled but not documented.
+//
+// Still, the struct from the external crate and its impl should be documented since
+// the struct is re-exported from this crate.
+// However, the method in the trait impl should *not* have a link (an `href` attribute) to
+// its corresponding item in the trait declaration since it would otherwise be broken.
+//
+// In older versions of rustdoc, the impl item (`a[@class="fnname"]`) used to link to
+// `#method.provided` – i.e. "to itself". Put in quotes since that was actually incorrect in
+// general: If the type `Struct` also had an inherent method called `provided`, the impl item
+// would link to that one even though those two methods are distinct items!
+
// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1
-// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided
+// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]' 1
+// @snapshot no_href_on_anchor - '//*[@id="method.provided"]//a[@class="fnname"]'
// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided
pub use ext::Struct;
#![feature(no_core)]
#![feature(rustdoc_internals)]
#![feature(inherent_associated_types)]
+#![feature(lang_items)]
#![no_core]
/// [Self::f]
impl S {
pub fn f() {}
}
+
+#[lang = "sized"]
+pub trait Sized {}
+#![feature(associated_type_defaults)]
#![crate_name = "foo"]
// @has foo/trait.Foo.html
// @has - '//*[@class="sidebar-elems"]//section//a' 'bar'
// @has - '//*[@class="sidebar-title"]/a[@href="#provided-methods"]' 'Provided Methods'
// @has - '//*[@class="sidebar-elems"]//section//a' 'foo'
-// @has - '//*[@class="sidebar-title"]/a[@href="#associated-const"]' 'Associated Constants'
+// @has - '//*[@class="sidebar-title"]/a[@href="#required-associated-consts"]' 'Required Associated Constants'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'FOO'
+// @has - '//*[@class="sidebar-title"]/a[@href="#provided-associated-consts"]' 'Provided Associated Constants'
// @has - '//*[@class="sidebar-elems"]//section//a' 'BAR'
-// @has - '//*[@class="sidebar-title"]/a[@href="#associated-types"]' 'Associated Types'
+// @has - '//*[@class="sidebar-title"]/a[@href="#required-associated-types"]' 'Required Associated Types'
// @has - '//*[@class="sidebar-elems"]//section//a' 'Output'
+// @has - '//*[@class="sidebar-title"]/a[@href="#provided-associated-types"]' 'Provided Associated Types'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'Extra'
pub trait Foo {
+ const FOO: usize;
const BAR: u32 = 0;
+ type Extra: Copy = ();
type Output: ?Sized;
fn foo() {}