]> git.lizzy.rs Git - rust.git/commitdiff
rustdoc: discr. required+provided assoc consts+tys
authorLeón Orell Valerian Liehr <liehr.exchange@gmx.net>
Tue, 29 Mar 2022 17:30:54 +0000 (19:30 +0200)
committerLeón Orell Valerian Liehr <liehr.exchange@gmx.net>
Tue, 12 Apr 2022 13:38:39 +0000 (15:38 +0200)
22 files changed:
src/librustdoc/clean/inline.rs
src/librustdoc/clean/mod.rs
src/librustdoc/clean/types.rs
src/librustdoc/clean/utils.rs
src/librustdoc/fold.rs
src/librustdoc/formats/cache.rs
src/librustdoc/formats/item_type.rs
src/librustdoc/html/format.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/render/mod.rs
src/librustdoc/html/render/print_item.rs
src/librustdoc/html/static/js/search.js
src/librustdoc/json/conversions.rs
src/librustdoc/passes/check_doc_test_visibility.rs
src/librustdoc/passes/collect_trait_impls.rs
src/librustdoc/passes/stripper.rs
src/librustdoc/visit.rs
src/test/rustdoc/assoc-consts.rs
src/test/rustdoc/extern-default-method.no_href_on_anchor.html [new file with mode: 0644]
src/test/rustdoc/extern-default-method.rs
src/test/rustdoc/intra-doc/prim-self.rs
src/test/rustdoc/sidebar-items.rs

index d06e4fa1cc2f57cefef059ac9f2a1675fe3cc2c3..33db6583125efbc9d03ecf9fea4a9244042d72a3 100644 (file)
@@ -79,7 +79,7 @@
         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);
index 85a3e05e8b2136005da7d24c17d0534baae0b77a..a6763d2827cec0fc486919126d422ac006d8a83c 100644 (file)
@@ -964,11 +964,11 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
         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)
@@ -983,11 +983,19 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
                     });
                     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 =
@@ -1004,7 +1012,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
         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) => {
@@ -1016,7 +1024,10 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
                     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(),
+                    )
                 }
             };
 
@@ -1041,13 +1052,17 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item {
         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(
@@ -1181,23 +1196,28 @@ fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
                         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(),
                     )
                 }
             }
@@ -1837,14 +1857,11 @@ fn clean_maybe_renamed_item(
             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(),
index 7698c2de24d5c8d9e3b21b6c2d02a07aec0b1252..d2abfc35b932cfa1f3e6be1ef946ec737b985cb4 100644 (file)
@@ -577,10 +577,16 @@ impl Item {
         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
@@ -726,17 +732,18 @@ fn build_fn_header(
     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),
@@ -749,12 +756,16 @@ fn build_fn_header(
     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),
@@ -776,7 +787,7 @@ impl ItemKind {
             ExternCrateItem { .. }
             | ImportItem(_)
             | FunctionItem(_)
-            | TypedefItem(_, _)
+            | TypedefItem(_)
             | OpaqueTyItem(_)
             | StaticItem(_)
             | ConstantItem(_)
@@ -791,7 +802,9 @@ impl ItemKind {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
+            | TyAssocConstItem(_)
             | AssocConstItem(_, _)
+            | TyAssocTypeItem(..)
             | AssocTypeItem(..)
             | StrippedItem(_)
             | KeywordItem(_) => [].iter(),
index c85ef0ac05452c939e4e7d19e2bb6cab305f871b..fe6d680991f80f3e6652cf532a7fb4f065907ae6 100644 (file)
@@ -178,7 +178,7 @@ pub(super) fn external_path(
 
     for item in items {
         let target = match *item.kind {
-            ItemKind::TypedefItem(ref t, true) => &t.type_,
+            ItemKind::AssocTypeItem(ref t, _) => &t.type_,
             _ => continue,
         };
 
index fbb8b572ea43025a54fba2e5a71debace19f275f..95adc4426b585a1edb791d1bcecf6223aff768bd 100644 (file)
@@ -71,7 +71,7 @@ fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind {
             ExternCrateItem { src: _ }
             | ImportItem(_)
             | FunctionItem(_)
-            | TypedefItem(_, _)
+            | TypedefItem(_)
             | OpaqueTyItem(_)
             | StaticItem(_)
             | ConstantItem(_)
@@ -85,7 +85,9 @@ fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
-            | AssocConstItem(_, _)
+            | TyAssocConstItem(..)
+            | AssocConstItem(..)
+            | TyAssocTypeItem(..)
             | AssocTypeItem(..)
             | KeywordItem(_) => kind,
         }
index 53159709586c6826987c39284ba1d4756ed43cc1..663e18fe9129fd594ebae6885b74ee88b7f25e0d 100644 (file)
@@ -242,14 +242,15 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
         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(..) => (
                     (
@@ -258,7 +259,7 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
                     ),
                     false,
                 ),
-                clean::MethodItem(..) | clean::AssocConstItem(..) => {
+                clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
                     if self.cache.parent_stack.is_empty() {
                         ((None, None), false)
                     } else {
@@ -373,7 +374,9 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
             | clean::TyMethodItem(..)
             | clean::MethodItem(..)
             | clean::StructFieldItem(..)
+            | clean::TyAssocConstItem(..)
             | clean::AssocConstItem(..)
+            | clean::TyAssocTypeItem(..)
             | clean::AssocTypeItem(..)
             | clean::StrippedItem(..)
             | clean::KeywordItem(..) => {
index f8660c296cb0bdb02aa0c722ebc0f7c8e265d09f..fb4afb769ad15697ced9f1513db320bc65d13a39 100644 (file)
@@ -14,7 +14,7 @@
 /// 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
@@ -89,8 +89,8 @@ fn from(item: &'a clean::Item) -> ItemType {
             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,
index 5c59609d5b8c600253fb5913c4d1bfe9eea4b646..55b0028180f66fece8164bb2f1c903a36154e7c4 100644 (file)
@@ -527,6 +527,21 @@ fn print<'a, 'tcx: 'a>(
     /// 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,
index 943c521485b18e8b1e4a9b4d5aa0a8d4134bf867..1ebb41b5933d0b0350165494ce61bbe4198cd7d9 100644 (file)
@@ -1452,8 +1452,10 @@ fn init_id_map() -> FxHashMap<String, usize> {
     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);
index 12da16527a0eb574034caa25fce8820aac96382a..9891c4b676fb4d2e32bea23619bed94b2ad05261 100644 (file)
@@ -521,7 +521,7 @@ fn document_short(
         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);
@@ -737,42 +737,82 @@ fn render_impls(
     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(
@@ -787,9 +827,9 @@ 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),
     );
@@ -814,22 +854,6 @@ fn assoc_method(
 ) {
     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.
@@ -843,6 +867,7 @@ fn assoc_method(
     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();
@@ -868,7 +893,7 @@ fn assoc_method(
     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,
@@ -877,8 +902,7 @@ fn assoc_method(
         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),
@@ -968,23 +992,43 @@ fn render_assoc_item(
     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,
@@ -1205,7 +1249,7 @@ fn render_deref_methods(
         .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_),
             }),
@@ -1291,7 +1335,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
                             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 =
@@ -1300,7 +1344,7 @@ fn notable_traits_decl(decl: &clean::FnDecl, cx: &Context<'_>) -> String {
                                     &mut out,
                                     it,
                                     &tydef.generics,
-                                    &[],
+                                    &[], // intentionally leaving out bounds
                                     Some(&tydef.type_),
                                     src_link,
                                     0,
@@ -1439,7 +1483,7 @@ fn doc_impl_item(
                 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 {
@@ -1471,63 +1515,68 @@ fn doc_impl_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,
@@ -1748,13 +1797,13 @@ pub(crate) fn render_impl_summary(
         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,
@@ -1822,7 +1871,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
         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),
         _ => {}
@@ -1917,7 +1966,7 @@ fn get_methods(
                 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
@@ -1937,7 +1986,7 @@ fn get_associated_constants(
         .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,
         })
@@ -2106,7 +2155,7 @@ fn sidebar_deref_methods(
     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_),
             }),
@@ -2281,19 +2330,37 @@ fn print_sidebar_section(
     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(
@@ -2302,7 +2369,7 @@ fn 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(
@@ -2311,7 +2378,7 @@ fn 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();
index 99d7475da33966daae1326fe155ae97ce18323b8..1ed5c662c41cc162280b91829ff0b813b06d92ac 100644 (file)
@@ -141,7 +141,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer,
 
     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)
@@ -150,7 +150,7 @@ pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer,
         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),
@@ -507,13 +507,15 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::
 
 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| {
@@ -554,16 +556,18 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                         ),
                     );
                 }
-                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
@@ -582,28 +586,30 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                         ),
                     );
                 }
-                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,
@@ -614,14 +620,14 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                     );
                     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,
@@ -640,7 +646,8 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                             w.write_str(" { ... }\n");
                         }
                     }
-                    if pos < provided.len() - 1 {
+
+                    if pos < provided_methods.len() - 1 {
                         w.write_str("<span class=\"item-spacer\"></span>");
                     }
                 }
@@ -703,53 +710,77 @@ fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item
         }
     }
 
-    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>");
@@ -933,25 +964,11 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean:
     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_};",
@@ -963,14 +980,7 @@ fn write_content(
         });
     }
 
-    // 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);
 
index b0ce63a4ec1d5d7177feacb2d84198642d807ce5..ab52304491a2bc17841d3515f9227f9c916a57a2 100644 (file)
@@ -3,7 +3,7 @@
 
 (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",
index bc638200533fd88075384a5b7bac7960ba704a2c..a9a6a31fccd0d7c8b3457feda0250b6fab56bc83 100644 (file)
@@ -219,20 +219,23 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         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(_) => {
index 2b17e3457d293eb93e1bd87ca75525a9d9fb7538..b541fb63bd413422e694053c68a2bb86cb28a63f 100644 (file)
@@ -61,9 +61,9 @@ fn add_test(&mut self, _: String, config: LangString, _: usize) {
             *item.kind,
             clean::StructFieldItem(_)
                 | clean::VariantItem(_)
-                | clean::AssocConstItem(_, _)
+                | clean::AssocConstItem(..)
                 | clean::AssocTypeItem(..)
-                | clean::TypedefItem(_, _)
+                | clean::TypedefItem(_)
                 | clean::StaticItem(_)
                 | clean::ConstantItem(_)
                 | clean::ExternCrateItem { .. }
index 2852c3b616df1a4afd84d3606d939d23f2cb0911..4ab942c8f1bdc977ab9d482c714edd34261f06f9 100644 (file)
@@ -93,7 +93,7 @@ fn add_deref_target(
                 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");
index 717dc078b343cf3fd46176df79c8b54d74de2c74..82627aaf7a4cb339ff5deff0dea4f6f703345d8b 100644 (file)
@@ -41,6 +41,7 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
             | clean::ConstantItem(..)
             | clean::UnionItem(..)
             | clean::AssocConstItem(..)
+            | clean::AssocTypeItem(..)
             | clean::TraitAliasItem(..)
             | clean::MacroItem(..)
             | clean::ForeignTypeItem => {
@@ -72,8 +73,8 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
 
             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(..) => {}
@@ -81,9 +82,6 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
             // Primitives are never stripped
             clean::PrimitiveItem(..) => {}
 
-            // Associated types are never stripped
-            clean::AssocTypeItem(..) => {}
-
             // Keywords are never stripped
             clean::KeywordItem(..) => {}
         }
index b16cab1c646f13f6d642d661a92be507f34b606f..ef50292674276c17d6a656f134604f00dc04ca44 100644 (file)
@@ -26,7 +26,7 @@ fn visit_inner_recur(&mut self, kind: &ItemKind) {
             ExternCrateItem { src: _ }
             | ImportItem(_)
             | FunctionItem(_)
-            | TypedefItem(_, _)
+            | TypedefItem(_)
             | OpaqueTyItem(_)
             | StaticItem(_)
             | ConstantItem(_)
@@ -40,7 +40,9 @@ fn visit_inner_recur(&mut self, kind: &ItemKind) {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
-            | AssocConstItem(_, _)
+            | TyAssocConstItem(..)
+            | AssocConstItem(..)
+            | TyAssocTypeItem(..)
             | AssocTypeItem(..)
             | KeywordItem(_) => {}
         }
index ff7fd66391628a4f8707295d851b2511733376f0..0ac6dc763df88bd0e3641fe2f9d3b958915a8ab5 100644 (file)
@@ -1,8 +1,8 @@
 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
diff --git a/src/test/rustdoc/extern-default-method.no_href_on_anchor.html b/src/test/rustdoc/extern-default-method.no_href_on_anchor.html
new file mode 100644 (file)
index 0000000..dab0a64
--- /dev/null
@@ -0,0 +1 @@
+<a class="fnname">provided</a>(&amp;self)
\ No newline at end of file
index 93cf16346b6f7fcd50c3ba887c513128d6c58b38..8139f5b2619b3143a0927cee582b8e4dd3f9c5c7 100644 (file)
@@ -1,9 +1,23 @@
 // 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;
index de053d70f0354cfbca8253343f6b181fe95ea991..c7ce71b15f30c572c8f6b7110c17e69519fee6b5 100644 (file)
@@ -5,6 +5,7 @@
 #![feature(no_core)]
 #![feature(rustdoc_internals)]
 #![feature(inherent_associated_types)]
+#![feature(lang_items)]
 #![no_core]
 
 /// [Self::f]
@@ -35,3 +36,6 @@ mod usize {}
 impl S {
     pub fn f() {}
 }
+
+#[lang = "sized"]
+pub trait Sized {}
index 375cad9da7f88010070b87b326750d55e63c5dc7..b5b681ab085bf7d0b1fe4411e18b9ce7184a2e6c 100644 (file)
@@ -1,3 +1,4 @@
+#![feature(associated_type_defaults)]
 #![crate_name = "foo"]
 
 // @has foo/trait.Foo.html
@@ -5,12 +6,18 @@
 // @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() {}