about summary refs log tree commit diff
path: root/src/librustdoc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/clean/mod.rs28
-rw-r--r--src/librustdoc/clean/types.rs14
-rw-r--r--src/librustdoc/clean/utils.rs32
-rw-r--r--src/librustdoc/formats/cache.rs82
-rw-r--r--src/librustdoc/html/format.rs3
-rw-r--r--src/librustdoc/html/layout.rs37
-rw-r--r--src/librustdoc/html/render/context.rs19
-rw-r--r--src/librustdoc/html/render/mod.rs42
-rw-r--r--src/librustdoc/html/render/print_item.rs120
-rw-r--r--src/librustdoc/html/render/search_index.rs33
-rw-r--r--src/librustdoc/html/render/sidebar.rs27
-rw-r--r--src/librustdoc/html/render/write_shared.rs3
-rw-r--r--src/librustdoc/html/sources.rs3
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css86
-rw-r--r--src/librustdoc/html/static/js/main.js68
-rw-r--r--src/librustdoc/html/static/js/search.js19
-rw-r--r--src/librustdoc/html/templates/page.html51
-rw-r--r--src/librustdoc/html/templates/sidebar.html5
-rw-r--r--src/librustdoc/json/conversions.rs18
-rw-r--r--src/librustdoc/lib.rs1
20 files changed, 527 insertions, 164 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index b9d7acee63c..e08318e4f54 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -521,7 +521,7 @@ fn clean_generic_param_def<'tcx>(
                 },
             )
         }
-        ty::GenericParamDefKind::Const { has_default, .. } => (
+        ty::GenericParamDefKind::Const { has_default, is_host_effect } => (
             def.name,
             GenericParamDefKind::Const {
                 ty: Box::new(clean_middle_ty(
@@ -541,6 +541,7 @@ fn clean_generic_param_def<'tcx>(
                     )),
                     false => None,
                 },
+                is_host_effect,
             },
         ),
     };
@@ -597,6 +598,7 @@ fn clean_generic_param<'tcx>(
                 ty: Box::new(clean_ty(ty, cx)),
                 default: default
                     .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
+                is_host_effect: cx.tcx.has_attr(param.def_id, sym::rustc_host),
             },
         ),
     };
@@ -2508,14 +2510,22 @@ fn clean_generic_args<'tcx>(
         let args = generic_args
             .args
             .iter()
-            .map(|arg| match arg {
-                hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
-                    GenericArg::Lifetime(clean_lifetime(*lt, cx))
-                }
-                hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
-                hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
-                hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))),
-                hir::GenericArg::Infer(_inf) => GenericArg::Infer,
+            .filter_map(|arg| {
+                Some(match arg {
+                    hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
+                        GenericArg::Lifetime(clean_lifetime(*lt, cx))
+                    }
+                    hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
+                    hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
+                    // FIXME(effects): This will still emit `<true>` for non-const impls of const traits
+                    hir::GenericArg::Const(ct)
+                        if cx.tcx.has_attr(ct.value.def_id, sym::rustc_host) =>
+                    {
+                        return None;
+                    }
+                    hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))),
+                    hir::GenericArg::Infer(_inf) => GenericArg::Infer,
+                })
             })
             .collect::<Vec<_>>()
             .into();
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index b891dc59cb1..517a51867cf 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1306,7 +1306,7 @@ impl WherePredicate {
 pub(crate) enum GenericParamDefKind {
     Lifetime { outlives: Vec<Lifetime> },
     Type { did: DefId, bounds: Vec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
-    Const { ty: Box<Type>, default: Option<Box<String>> },
+    Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
 }
 
 impl GenericParamDefKind {
@@ -1326,9 +1326,10 @@ impl GenericParamDef {
         Self { name, kind: GenericParamDefKind::Lifetime { outlives: Vec::new() } }
     }
 
-    pub(crate) fn is_synthetic_type_param(&self) -> bool {
+    pub(crate) fn is_synthetic_param(&self) -> bool {
         match self.kind {
-            GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false,
+            GenericParamDefKind::Lifetime { .. } => false,
+            GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect,
             GenericParamDefKind::Type { synthetic, .. } => synthetic,
         }
     }
@@ -2084,9 +2085,8 @@ impl Discriminant {
     pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
         self.expr.map(|body| rendered_const(tcx, body))
     }
-    /// Will always be a machine readable number, without underscores or suffixes.
-    pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
-        print_evaluated_const(tcx, self.value, false).unwrap()
+    pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String {
+        print_evaluated_const(tcx, self.value, with_underscores, false).unwrap()
     }
 }
 
@@ -2331,7 +2331,7 @@ impl ConstantKind {
         match *self {
             ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
             ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
-                print_evaluated_const(tcx, def_id, true)
+                print_evaluated_const(tcx, def_id, true, true)
             }
         }
     }
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 8388f722a7f..c5302570489 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -104,6 +104,10 @@ pub(crate) fn ty_args_to_args<'tcx>(
                     arg: index,
                 }),
             ))),
+            // FIXME(effects): this relies on the host effect being called `host`, which users could also name
+            // their const generics.
+            // FIXME(effects): this causes `host = true` and `host = false` generics to also be emitted.
+            GenericArgKind::Const(ct) if let ty::ConstKind::Param(p) = ct.kind() && p.name == sym::host => None,
             GenericArgKind::Const(ct) => {
                 Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx))))
             }
@@ -275,7 +279,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
 pub(crate) fn print_evaluated_const(
     tcx: TyCtxt<'_>,
     def_id: DefId,
-    underscores_and_type: bool,
+    with_underscores: bool,
+    with_type: bool,
 ) -> Option<String> {
     tcx.const_eval_poly(def_id).ok().and_then(|val| {
         let ty = tcx.type_of(def_id).instantiate_identity();
@@ -284,7 +289,7 @@ pub(crate) fn print_evaluated_const(
             (mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
             (mir::ConstValue::Scalar(_), _) => {
                 let const_ = mir::Const::from_value(val, ty);
-                Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
+                Some(print_const_with_custom_print_scalar(tcx, const_, with_underscores, with_type))
             }
             _ => None,
         }
@@ -320,32 +325,37 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
 fn print_const_with_custom_print_scalar<'tcx>(
     tcx: TyCtxt<'tcx>,
     ct: mir::Const<'tcx>,
-    underscores_and_type: bool,
+    with_underscores: bool,
+    with_type: bool,
 ) -> String {
     // Use a slightly different format for integer types which always shows the actual value.
     // For all other types, fallback to the original `pretty_print_const`.
     match (ct, ct.ty().kind()) {
         (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
-            if underscores_and_type {
-                format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+            let mut output = if with_underscores {
+                format_integer_with_underscore_sep(&int.to_string())
             } else {
                 int.to_string()
+            };
+            if with_type {
+                output += ui.name_str();
             }
+            output
         }
         (mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
             let ty = ct.ty();
             let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
             let data = int.assert_bits(size);
             let sign_extended_data = size.sign_extend(data) as i128;
-            if underscores_and_type {
-                format!(
-                    "{}{}",
-                    format_integer_with_underscore_sep(&sign_extended_data.to_string()),
-                    i.name_str()
-                )
+            let mut output = if with_underscores {
+                format_integer_with_underscore_sep(&sign_extended_data.to_string())
             } else {
                 sign_extended_data.to_string()
+            };
+            if with_type {
+                output += i.name_str();
             }
+            output
         }
         _ => ct.to_string(),
     }
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 4c6e7dfb987..4ccb5f2be34 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -221,19 +221,25 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
             _ => self.cache.stripped_mod,
         };
 
+        #[inline]
+        fn is_from_private_dep(tcx: TyCtxt<'_>, cache: &Cache, def_id: DefId) -> bool {
+            let krate = def_id.krate;
+
+            cache.masked_crates.contains(&krate) || tcx.is_private_dep(krate)
+        }
+
         // If the impl is from a masked crate or references something from a
         // masked crate then remove it completely.
-        if let clean::ImplItem(ref i) = *item.kind {
-            if self.cache.masked_crates.contains(&item.item_id.krate())
+        if let clean::ImplItem(ref i) = *item.kind &&
+            (self.cache.masked_crates.contains(&item.item_id.krate())
                 || i.trait_
                     .as_ref()
-                    .map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate))
+                    .map_or(false, |t| is_from_private_dep(self.tcx, self.cache, t.def_id()))
                 || i.for_
                     .def_id(self.cache)
-                    .map_or(false, |d| self.cache.masked_crates.contains(&d.krate))
-            {
-                return None;
-            }
+                    .map_or(false, |d| is_from_private_dep(self.tcx, self.cache, d)))
+        {
+            return None;
         }
 
         // Propagate a trait method's documentation to all implementors of the
@@ -334,33 +340,37 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                     // A crate has a module at its root, containing all items,
                     // which should not be indexed. The crate-item itself is
                     // inserted later on when serializing the search-index.
-                    if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root()) {
+                    if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root())
+                        && let ty = item.type_()
+                        && (ty != ItemType::StructField
+                            || u16::from_str_radix(s.as_str(), 10).is_err())
+                    {
                         let desc =
                             short_markdown_summary(&item.doc_value(), &item.link_names(self.cache));
-                        let ty = item.type_();
-                        if ty != ItemType::StructField
-                            || u16::from_str_radix(s.as_str(), 10).is_err()
-                        {
-                            // In case this is a field from a tuple struct, we don't add it into
-                            // the search index because its name is something like "0", which is
-                            // not useful for rustdoc search.
-                            self.cache.search_index.push(IndexItem {
-                                ty,
-                                name: s,
-                                path: join_with_double_colon(path),
-                                desc,
-                                parent,
-                                parent_idx: None,
-                                search_type: get_function_type_for_search(
-                                    &item,
-                                    self.tcx,
-                                    clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
-                                    self.cache,
-                                ),
-                                aliases: item.attrs.get_doc_aliases(),
-                                deprecation: item.deprecation(self.tcx),
-                            });
-                        }
+                        // In case this is a field from a tuple struct, we don't add it into
+                        // the search index because its name is something like "0", which is
+                        // not useful for rustdoc search.
+                        self.cache.search_index.push(IndexItem {
+                            ty,
+                            name: s,
+                            path: join_with_double_colon(path),
+                            desc,
+                            parent,
+                            parent_idx: None,
+                            impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) = self.cache.parent_stack.last() {
+                                item_id.as_def_id()
+                            } else {
+                                None
+                            },
+                            search_type: get_function_type_for_search(
+                                &item,
+                                self.tcx,
+                                clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
+                                self.cache,
+                            ),
+                            aliases: item.attrs.get_doc_aliases(),
+                            deprecation: item.deprecation(self.tcx),
+                        });
                     }
                 }
                 (Some(parent), None) if is_inherent_impl_item => {
@@ -371,6 +381,13 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                         parent,
                         item: item.clone(),
                         impl_generics,
+                        impl_id: if let Some(ParentStackItem::Impl { item_id, .. }) =
+                            self.cache.parent_stack.last()
+                        {
+                            item_id.as_def_id()
+                        } else {
+                            None
+                        },
                     });
                 }
                 _ => {}
@@ -541,6 +558,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
 
 pub(crate) struct OrphanImplItem {
     pub(crate) parent: DefId,
+    pub(crate) impl_id: Option<DefId>,
     pub(crate) item: clean::Item,
     pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>,
 }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 1983bb11e5b..aa3f7184b4e 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -250,8 +250,7 @@ impl clean::Generics {
         cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
         display_fn(move |f| {
-            let mut real_params =
-                self.params.iter().filter(|p| !p.is_synthetic_type_param()).peekable();
+            let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
             if real_params.peek().is_none() {
                 return Ok(());
             }
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 8c5871d9126..d4b4db0f3fd 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -17,6 +17,7 @@ pub(crate) struct Layout {
     pub(crate) external_html: ExternalHtml,
     pub(crate) default_settings: FxHashMap<String, String>,
     pub(crate) krate: String,
+    pub(crate) krate_version: String,
     /// The given user css file which allow to customize the generated
     /// documentation theme.
     pub(crate) css_file_extension: Option<PathBuf>,
@@ -31,6 +32,7 @@ pub(crate) struct Page<'a> {
     pub(crate) static_root_path: Option<&'a str>,
     pub(crate) description: &'a str,
     pub(crate) resource_suffix: &'a str,
+    pub(crate) rust_logo: bool,
 }
 
 impl<'a> Page<'a> {
@@ -54,9 +56,19 @@ struct PageLayout<'a> {
     themes: Vec<String>,
     sidebar: String,
     content: String,
-    krate_with_trailing_slash: String,
     rust_channel: &'static str,
     pub(crate) rustdoc_version: &'a str,
+    // same as layout.krate, except on top-level pages like
+    // Settings, Help, All Crates, and About Scraped Examples,
+    // where these things instead give Rustdoc name and version.
+    //
+    // These are separate from the variables used for the search
+    // engine, because "Rustdoc" isn't necessarily a crate in
+    // the current workspace.
+    display_krate: &'a str,
+    display_krate_with_trailing_slash: String,
+    display_krate_version_number: &'a str,
+    display_krate_version_extra: &'a str,
 }
 
 pub(crate) fn render<T: Print, S: Print>(
@@ -66,12 +78,26 @@ pub(crate) fn render<T: Print, S: Print>(
     t: T,
     style_files: &[StylePath],
 ) -> String {
+    let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
+
+    let (display_krate, display_krate_version, display_krate_with_trailing_slash) =
+        if page.root_path == "./" {
+            // top level pages use Rust branding
+            ("Rustdoc", rustdoc_version, String::new())
+        } else {
+            let display_krate_with_trailing_slash =
+                ensure_trailing_slash(&layout.krate).to_string();
+            (&layout.krate[..], &layout.krate_version[..], display_krate_with_trailing_slash)
+        };
     let static_root_path = page.get_static_root_path();
-    let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
+
+    // bootstrap passes in parts of the version separated by tabs, but other stuff might use spaces
+    let (display_krate_version_number, display_krate_version_extra) =
+        display_krate_version.split_once([' ', '\t']).unwrap_or((display_krate_version, ""));
+
     let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
     themes.sort();
 
-    let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
     let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
     let sidebar = Buffer::html().to_display(sidebar);
     PageLayout {
@@ -82,7 +108,10 @@ pub(crate) fn render<T: Print, S: Print>(
         themes,
         sidebar,
         content,
-        krate_with_trailing_slash,
+        display_krate,
+        display_krate_with_trailing_slash,
+        display_krate_version_number,
+        display_krate_version_extra,
         rust_channel: *crate::clean::utils::DOC_CHANNEL,
         rustdoc_version,
     }
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 97714afaa45..bf8d1a80337 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -24,6 +24,7 @@ use super::{
     sidebar::{sidebar_module_like, Sidebar},
     AllTypes, LinkFromSrc, StylePath,
 };
+use crate::clean::utils::has_doc_flag;
 use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
@@ -277,6 +278,7 @@ impl<'tcx> Context<'tcx> {
                 title: &title,
                 description: &desc,
                 resource_suffix: &clone_shared.resource_suffix,
+                rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
             };
             let mut page_buffer = Buffer::html();
             print_item(self, it, &mut page_buffer, &page);
@@ -528,12 +530,14 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         if let Some(url) = playground_url {
             playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
         }
+        let krate_version = cache.crate_version.as_deref().unwrap_or_default();
         let mut layout = layout::Layout {
             logo: String::new(),
             favicon: String::new(),
             external_html,
             default_settings,
             krate: krate.name(tcx).to_string(),
+            krate_version: krate_version.to_string(),
             css_file_extension: extension_css,
             scrape_examples_extension: !call_locations.is_empty(),
         };
@@ -658,21 +662,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         let shared = Rc::clone(&self.shared);
         let mut page = layout::Page {
             title: "List of all items in this crate",
-            css_class: "mod",
+            css_class: "mod sys",
             root_path: "../",
             static_root_path: shared.static_root_path.as_deref(),
             description: "List of all items in this crate",
             resource_suffix: &shared.resource_suffix,
+            rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
         };
         let all = shared.all.replace(AllTypes::new());
         let mut sidebar = Buffer::html();
 
         let blocks = sidebar_module_like(all.item_sections());
         let bar = Sidebar {
-            title_prefix: "Crate ",
-            title: crate_name.as_str(),
+            title_prefix: "",
+            title: "",
             is_crate: false,
-            version: "",
+            is_mod: false,
             blocks: vec![blocks],
             path: String::new(),
         };
@@ -689,9 +694,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         shared.fs.write(final_file, v)?;
 
         // Generating settings page.
-        page.title = "Rustdoc settings";
+        page.title = "Settings";
         page.description = "Settings of Rustdoc";
         page.root_path = "./";
+        page.rust_logo = true;
 
         let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
         let v = layout::render(
@@ -739,9 +745,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         shared.fs.write(settings_file, v)?;
 
         // Generating help page.
-        page.title = "Rustdoc help";
+        page.title = "Help";
         page.description = "Documentation for Rustdoc";
         page.root_path = "./";
+        page.rust_logo = true;
 
         let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
         let v = layout::render(
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 3e671a64b54..49817f5eb64 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -54,7 +54,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{
     symbol::{sym, Symbol},
     BytePos, FileName, RealFileName,
@@ -102,6 +102,7 @@ pub(crate) struct IndexItem {
     pub(crate) desc: String,
     pub(crate) parent: Option<DefId>,
     pub(crate) parent_idx: Option<isize>,
+    pub(crate) impl_id: Option<DefId>,
     pub(crate) search_type: Option<IndexItemFunctionType>,
     pub(crate) aliases: Box<[Symbol]>,
     pub(crate) deprecation: Option<Deprecation>,
@@ -1877,7 +1878,7 @@ pub(crate) fn render_impl_summary(
     aliases: &[String],
 ) {
     let inner_impl = i.inner_impl();
-    let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx));
+    let id = cx.derive_id(get_id_for_impl(cx.tcx(), i.impl_item.item_id));
     let aliases = if aliases.is_empty() {
         String::new()
     } else {
@@ -1994,21 +1995,35 @@ pub(crate) fn small_url_encode(s: String) -> String {
     }
 }
 
-fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
-    match trait_ {
-        Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
-        None => small_url_encode(format!("impl-{:#}", for_.print(cx))),
-    }
+fn get_id_for_impl<'tcx>(tcx: TyCtxt<'tcx>, impl_id: ItemId) -> String {
+    use rustc_middle::ty::print::with_forced_trimmed_paths;
+    let (type_, trait_) = match impl_id {
+        ItemId::Auto { trait_, for_ } => {
+            let ty = tcx.type_of(for_).skip_binder();
+            (ty, Some(ty::TraitRef::new(tcx, trait_, [ty])))
+        }
+        ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => {
+            match tcx.impl_subject(impl_id).skip_binder() {
+                ty::ImplSubject::Trait(trait_ref) => {
+                    (trait_ref.args[0].expect_ty(), Some(trait_ref))
+                }
+                ty::ImplSubject::Inherent(ty) => (ty, None),
+            }
+        }
+    };
+    with_forced_trimmed_paths!(small_url_encode(if let Some(trait_) = trait_ {
+        format!("impl-{trait_}-for-{type_}", trait_ = trait_.print_only_trait_path())
+    } else {
+        format!("impl-{type_}")
+    }))
 }
 
 fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String, String)> {
     match *item.kind {
-        clean::ItemKind::ImplItem(ref i) => {
-            i.trait_.as_ref().map(|trait_| {
-                // Alternative format produces no URLs,
-                // so this parameter does nothing.
-                (format!("{:#}", i.for_.print(cx)), get_id_for_impl(&i.for_, Some(trait_), cx))
-            })
+        clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => {
+            // Alternative format produces no URLs,
+            // so this parameter does nothing.
+            Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id)))
         }
         _ => None,
     }
@@ -2079,6 +2094,7 @@ impl ItemSection {
     const ALL: &'static [Self] = {
         use ItemSection::*;
         // NOTE: The order here affects the order in the UI.
+        // Keep this synchronized with addSidebarItems in main.js
         &[
             Reexports,
             PrimitiveTypes,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index c6751c9585e..cd31493bf3f 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -5,10 +5,13 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::DefId;
+use rustc_index::IndexVec;
 use rustc_middle::middle::stability;
+use rustc_middle::query::Key;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_target::abi::VariantIdx;
 use std::cell::{RefCell, RefMut};
 use std::cmp::Ordering;
 use std::fmt;
@@ -1247,6 +1250,9 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
         match inner_type {
             clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
                 let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
+                let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
+                let enum_def_id = ty.ty_adt_id().unwrap();
+
                 wrap_item(w, |w| {
                     let variants_len = variants.len();
                     let variants_count = variants_iter().count();
@@ -1257,13 +1263,14 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
                         w,
                         cx,
                         Some(&t.generics),
-                        variants_iter(),
+                        &variants,
                         variants_count,
                         has_stripped_entries,
                         *is_non_exhaustive,
+                        enum_def_id,
                     )
                 });
-                item_variants(w, cx, it, variants_iter());
+                item_variants(w, cx, it, &variants, enum_def_id);
             }
             clean::TypeAliasInnerType::Union { fields } => {
                 wrap_item(w, |w| {
@@ -1416,36 +1423,90 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
             it.name.unwrap(),
             e.generics.print(cx),
         );
+
         render_enum_fields(
             w,
             cx,
             Some(&e.generics),
-            e.variants(),
+            &e.variants,
             count_variants,
             e.has_stripped_entries(),
             it.is_non_exhaustive(),
+            it.def_id().unwrap(),
         );
     });
 
     write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
 
     if count_variants != 0 {
-        item_variants(w, cx, it, e.variants());
+        item_variants(w, cx, it, &e.variants, it.def_id().unwrap());
     }
     let def_id = it.item_id.expect_def_id();
     write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
     write!(w, "{}", document_type_layout(cx, def_id));
 }
 
-fn render_enum_fields<'a>(
+/// It'll return false if any variant is not a C-like variant. Otherwise it'll return true if at
+/// least one of them has an explicit discriminant or if the enum has `#[repr(C)]` or an integer
+/// `repr`.
+fn should_show_enum_discriminant(
+    cx: &Context<'_>,
+    enum_def_id: DefId,
+    variants: &IndexVec<VariantIdx, clean::Item>,
+) -> bool {
+    let mut has_variants_with_value = false;
+    for variant in variants {
+        if let clean::VariantItem(ref var) = *variant.kind &&
+            matches!(var.kind, clean::VariantKind::CLike)
+        {
+            has_variants_with_value |= var.discriminant.is_some();
+        } else {
+            return false;
+        }
+    }
+    if has_variants_with_value {
+        return true;
+    }
+    let repr = cx.tcx().adt_def(enum_def_id).repr();
+    repr.c() || repr.int.is_some()
+}
+
+fn display_c_like_variant(
+    w: &mut Buffer,
+    cx: &mut Context<'_>,
+    item: &clean::Item,
+    variant: &clean::Variant,
+    index: VariantIdx,
+    should_show_enum_discriminant: bool,
+    enum_def_id: DefId,
+) {
+    let name = item.name.unwrap();
+    if let Some(ref value) = variant.discriminant {
+        write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true));
+    } else if should_show_enum_discriminant {
+        let adt_def = cx.tcx().adt_def(enum_def_id);
+        let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
+        if discr.ty.is_signed() {
+            write!(w, "{} = {}", name.as_str(), discr.val as i128);
+        } else {
+            write!(w, "{} = {}", name.as_str(), discr.val);
+        }
+    } else {
+        w.write_str(name.as_str());
+    }
+}
+
+fn render_enum_fields(
     mut w: &mut Buffer,
     cx: &mut Context<'_>,
     g: Option<&clean::Generics>,
-    variants: impl Iterator<Item = &'a clean::Item>,
+    variants: &IndexVec<VariantIdx, clean::Item>,
     count_variants: usize,
     has_stripped_entries: bool,
     is_non_exhaustive: bool,
+    enum_def_id: DefId,
 ) {
+    let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
     if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
         // If there wasn't a `where` clause, we add a whitespace.
         w.write_str(" ");
@@ -1461,15 +1522,24 @@ fn render_enum_fields<'a>(
             toggle_open(&mut w, format_args!("{count_variants} variants"));
         }
         const TAB: &str = "    ";
-        for v in variants {
+        for (index, v) in variants.iter_enumerated() {
+            if v.is_stripped() {
+                continue;
+            }
             w.write_str(TAB);
-            let name = v.name.unwrap();
             match *v.kind {
-                // FIXME(#101337): Show discriminant
                 clean::VariantItem(ref var) => match var.kind {
-                    clean::VariantKind::CLike => w.write_str(name.as_str()),
+                    clean::VariantKind::CLike => display_c_like_variant(
+                        w,
+                        cx,
+                        v,
+                        var,
+                        index,
+                        should_show_enum_discriminant,
+                        enum_def_id,
+                    ),
                     clean::VariantKind::Tuple(ref s) => {
-                        write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
+                        write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s));
                     }
                     clean::VariantKind::Struct(ref s) => {
                         render_struct(w, v, None, None, &s.fields, TAB, false, cx);
@@ -1490,11 +1560,12 @@ fn render_enum_fields<'a>(
     }
 }
 
-fn item_variants<'a>(
+fn item_variants(
     w: &mut Buffer,
     cx: &mut Context<'_>,
     it: &clean::Item,
-    variants: impl Iterator<Item = &'a clean::Item>,
+    variants: &IndexVec<VariantIdx, clean::Item>,
+    enum_def_id: DefId,
 ) {
     let tcx = cx.tcx();
     write!(
@@ -1507,7 +1578,11 @@ fn item_variants<'a>(
         document_non_exhaustive_header(it),
         document_non_exhaustive(it)
     );
-    for variant in variants {
+    let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
+    for (index, variant) in variants.iter_enumerated() {
+        if variant.is_stripped() {
+            continue;
+        }
         let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
         write!(
             w,
@@ -1522,7 +1597,22 @@ fn item_variants<'a>(
             it.const_stable_since(tcx),
             " rightside",
         );
-        write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
+        w.write_str("<h3 class=\"code-header\">");
+        if let clean::VariantItem(ref var) = *variant.kind &&
+            let clean::VariantKind::CLike = var.kind
+        {
+            display_c_like_variant(
+                w,
+                cx,
+                variant,
+                var,
+                index,
+                should_show_enum_discriminant,
+                enum_def_id,
+            );
+        } else {
+            w.write_str(variant.name.unwrap().as_str());
+        }
 
         let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };
 
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 78c443b2257..af1dab59496 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -12,7 +12,7 @@ use crate::formats::cache::{Cache, OrphanImplItem};
 use crate::formats::item_type::ItemType;
 use crate::html::format::join_with_double_colon;
 use crate::html::markdown::short_markdown_summary;
-use crate::html::render::{IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
+use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
 
 /// Builds the search index from the collected metadata
 pub(crate) fn build_index<'tcx>(
@@ -26,7 +26,8 @@ pub(crate) fn build_index<'tcx>(
 
     // Attach all orphan items to the type's definition if the type
     // has since been learned.
-    for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
+    for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items
+    {
         if let Some((fqp, _)) = cache.paths.get(&parent) {
             let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
             cache.search_index.push(IndexItem {
@@ -36,6 +37,7 @@ pub(crate) fn build_index<'tcx>(
                 desc,
                 parent: Some(parent),
                 parent_idx: None,
+                impl_id,
                 search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
                 aliases: item.attrs.get_doc_aliases(),
                 deprecation: item.deprecation(tcx),
@@ -222,6 +224,29 @@ pub(crate) fn build_index<'tcx>(
         })
         .collect();
 
+    // Find associated items that need disambiguators
+    let mut associated_item_duplicates = FxHashMap::<(isize, ItemType, Symbol), usize>::default();
+
+    for &item in &crate_items {
+        if item.impl_id.is_some() && let Some(parent_idx) = item.parent_idx {
+            let count = associated_item_duplicates
+                .entry((parent_idx, item.ty, item.name))
+                .or_insert(0);
+            *count += 1;
+        }
+    }
+
+    let associated_item_disambiguators = crate_items
+        .iter()
+        .enumerate()
+        .filter_map(|(index, item)| {
+            let impl_id = ItemId::DefId(item.impl_id?);
+            let parent_idx = item.parent_idx?;
+            let count = *associated_item_duplicates.get(&(parent_idx, item.ty, item.name))?;
+            if count > 1 { Some((index, render::get_id_for_impl(tcx, impl_id))) } else { None }
+        })
+        .collect::<Vec<_>>();
+
     struct CrateData<'a> {
         doc: String,
         items: Vec<&'a IndexItem>,
@@ -230,6 +255,8 @@ pub(crate) fn build_index<'tcx>(
         //
         // To be noted: the `usize` elements are indexes to `items`.
         aliases: &'a BTreeMap<String, Vec<usize>>,
+        // Used when a type has more than one impl with an associated item with the same name.
+        associated_item_disambiguators: &'a Vec<(usize, String)>,
     }
 
     struct Paths {
@@ -382,6 +409,7 @@ pub(crate) fn build_index<'tcx>(
             crate_data.serialize_field("f", &functions)?;
             crate_data.serialize_field("c", &deprecated)?;
             crate_data.serialize_field("p", &paths)?;
+            crate_data.serialize_field("b", &self.associated_item_disambiguators)?;
             if has_aliases {
                 crate_data.serialize_field("a", &self.aliases)?;
             }
@@ -398,6 +426,7 @@ pub(crate) fn build_index<'tcx>(
             items: crate_items,
             paths: crate_paths,
             aliases: &aliases,
+            associated_item_disambiguators: &associated_item_disambiguators,
         })
         .expect("failed serde conversion")
         // All these `replace` calls are because we have to go through JS string for JSON content.
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 76f63c6f63e..fb429f237e3 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -19,7 +19,7 @@ pub(super) struct Sidebar<'a> {
     pub(super) title_prefix: &'static str,
     pub(super) title: &'a str,
     pub(super) is_crate: bool,
-    pub(super) version: &'a str,
+    pub(super) is_mod: bool,
     pub(super) blocks: Vec<LinkBlock<'a>>,
     pub(super) path: String,
 }
@@ -99,12 +99,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
         || it.is_primitive()
         || it.is_union()
         || it.is_enum()
-        || it.is_mod()
+        // crate title is displayed as part of logo lockup
+        || (it.is_mod() && !it.is_crate())
         || it.is_type_alias()
     {
         (
             match *it.kind {
-                clean::ModuleItem(..) if it.is_crate() => "Crate ",
                 clean::ModuleItem(..) => "Module ",
                 _ => "",
             },
@@ -113,14 +113,22 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
     } else {
         ("", "")
     };
-    let version =
-        if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
-    let path: String = if !it.is_mod() {
-        cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+    // need to show parent path header if:
+    //   - it's a child module, instead of the crate root
+    //   - there's a sidebar section for the item itself
+    //
+    // otherwise, the parent path header is redundant with the big crate
+    // branding area at the top of the sidebar
+    let sidebar_path =
+        if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
+    let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
+        let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
+        if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
     } else {
         "".into()
     };
-    let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+    let sidebar =
+        Sidebar { title_prefix, title, is_mod: it.is_mod(), is_crate: it.is_crate(), blocks, path };
     sidebar.render_into(buffer).unwrap();
 }
 
@@ -503,8 +511,7 @@ fn sidebar_render_assoc_items(
             .iter()
             .filter_map(|it| {
                 let trait_ = it.inner_impl().trait_.as_ref()?;
-                let encoded =
-                    id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+                let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
 
                 let prefix = match it.inner_impl().polarity {
                     ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index e824651e727..e68d5ab2fbd 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -336,11 +336,12 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
             let dst = cx.dst.join("index.html");
             let page = layout::Page {
                 title: "Index of crates",
-                css_class: "mod",
+                css_class: "mod sys",
                 root_path: "./",
                 static_root_path: shared.static_root_path.as_deref(),
                 description: "List of crates",
                 resource_suffix: &shared.resource_suffix,
+                rust_logo: true,
             };
 
             let content = format!(
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 1d6eafe51b9..4a218b9b37c 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -1,4 +1,5 @@
 use crate::clean;
+use crate::clean::utils::has_doc_flag;
 use crate::docfs::PathError;
 use crate::error::Error;
 use crate::html::format;
@@ -13,6 +14,7 @@ use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::source_map::FileName;
+use rustc_span::sym;
 
 use std::cell::RefCell;
 use std::ffi::OsStr;
@@ -231,6 +233,7 @@ impl SourceCollector<'_, '_> {
             static_root_path: shared.static_root_path.as_deref(),
             description: &desc,
             resource_suffix: &shared.resource_suffix,
+            rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
         };
         let v = layout::render(
             &shared.layout,
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 47f9e650281..e2b4cc50dd5 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -461,19 +461,9 @@ img {
 	display: none !important;
 }
 
-.sidebar .logo-container {
-	margin-top: 10px;
-	margin-bottom: 10px;
-	text-align: center;
-}
-
-.version {
-	overflow-wrap: break-word;
-}
-
 .logo-container > img {
-	height: 100px;
-	width: 100px;
+	height: 48px;
+	width: 48px;
 }
 
 ul.block, .block li {
@@ -502,6 +492,7 @@ ul.block, .block li {
 }
 
 .sidebar-elems,
+.sidebar > .version,
 .sidebar > h2 {
 	padding-left: 24px;
 }
@@ -510,6 +501,8 @@ ul.block, .block li {
 	color: var(--sidebar-link-color);
 }
 .sidebar .current,
+.sidebar .current a,
+.sidebar-crate a.logo-container:hover + h2 a,
 .sidebar a:hover:not(.logo-container) {
 	background-color: var(--sidebar-current-link-background-color);
 }
@@ -524,6 +517,75 @@ ul.block, .block li {
 	overflow: hidden;
 }
 
+.sidebar-crate {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	/* there's a 10px padding at the top of <main>, and a 4px margin at the
+		top of the search form. To line them up, add them. */
+	margin: 14px 32px 1rem;
+	row-gap: 10px;
+	column-gap: 32px;
+	flex-wrap: wrap;
+}
+
+.sidebar-crate h2 {
+	flex-grow: 1;
+	/* This setup with the margins and row-gap is designed to make flex-wrap
+		work the way we want. If they're in the side-by-side lockup, there
+		should be a 16px margin to the left of the logo (visually the same as
+		the 24px one on everything else, which are not giant circles) and 8px
+		between it and the crate's name and version. When they're line wrapped,
+		the logo needs to have the same margin on both sides of itself (to
+		center properly) and the crate name and version need 24px on their
+		left margin. */
+	margin: 0 -8px;
+	/* To align this with the search bar, it should not be centered, even when
+		the logo is. */
+	align-self: start;
+}
+
+.sidebar-crate .logo-container {
+	/* The logo is expected to have 8px "slop" along its edges, so we can optically
+		center it. */
+	margin: 0 -16px 0 -16px;
+	text-align: center;
+}
+
+.sidebar-crate h2 a {
+	display: block;
+	margin: 0 calc(-24px + 0.25rem) 0 -0.5rem;
+	/* Align the sidebar crate link with the search bar, which have different
+		font sizes.
+
+		|        | font-size | line-height | total line-height | padding-y |     total    |
+		|:-------|----------:|------------:|------------------:|----------:|-------------:|
+		| crate  |  1.375rem |        1.25 |           1.72rem |         x |   2x+1.72rem |
+		| search |      1rem |        1.15 |           1.15rem |       8px | 1.15rem+16px |
+
+		2x + 1.72rem = 1.15rem + 16px
+		2x = 1.15rem + 16px - 1.72rem
+		2x = 16px - 0.57rem
+		x = ( 16px - 0.57rem ) / 2
+	*/
+	padding: calc( ( 16px - 0.57rem ) / 2 ) 0.25rem;
+	padding-left: 0.5rem;
+}
+
+.sidebar-crate h2 .version {
+	display: block;
+	font-weight: normal;
+	font-size: 1rem;
+	overflow-wrap: break-word;
+	/* opposite of the link padding, cut in half again */
+	margin-top: calc( ( -16px + 0.57rem ) / 2 );
+}
+
+.sidebar-crate + .version {
+	margin-top: -1rem;
+	margin-bottom: 1rem;
+}
+
 .mobile-topbar {
 	display: none;
 }
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index eb256455b08..2e9897ef82b 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -51,9 +51,14 @@ function setMobileTopbar() {
     // but with the current code it's hard to get the right information in the right place.
     const mobileTopbar = document.querySelector(".mobile-topbar");
     const locationTitle = document.querySelector(".sidebar h2.location");
-    if (mobileTopbar && locationTitle) {
+    if (mobileTopbar) {
         const mobileTitle = document.createElement("h2");
-        mobileTitle.innerHTML = locationTitle.innerHTML;
+        mobileTitle.className = "location";
+        if (hasClass(document.body, "crate")) {
+            mobileTitle.innerText = `Crate ${window.currentCrate}`;
+        } else if (locationTitle) {
+            mobileTitle.innerHTML = locationTitle.innerHTML;
+        }
         mobileTopbar.appendChild(mobileTitle);
     }
 }
@@ -354,6 +359,34 @@ function preLoadCss(cssUrl) {
                 expandSection(pageId);
             }
         }
+        if (savedHash.startsWith("impl-")) {
+            // impl-disambiguated links, used by the search engine
+            // format: impl-X[-for-Y]/method.WHATEVER
+            // turn this into method.WHATEVER[-NUMBER]
+            const splitAt = savedHash.indexOf("/");
+            if (splitAt !== -1) {
+                const implId = savedHash.slice(0, splitAt);
+                const assocId = savedHash.slice(splitAt + 1);
+                const implElem = document.getElementById(implId);
+                if (implElem && implElem.parentElement.tagName === "SUMMARY" &&
+                    implElem.parentElement.parentElement.tagName === "DETAILS") {
+                    onEachLazy(implElem.parentElement.parentElement.querySelectorAll(
+                        `[id^="${assocId}"]`),
+                        item => {
+                            const numbered = /([^-]+)-([0-9]+)/.exec(item.id);
+                            if (item.id === assocId || (numbered && numbered[1] === assocId)) {
+                                openParentDetails(item);
+                                item.scrollIntoView();
+                                // Let the section expand itself before trying to highlight
+                                setTimeout(() => {
+                                    window.location.replace("#" + item.id);
+                                }, 0);
+                            }
+                        }
+                    );
+                }
+            }
+        }
     }
 
     function onHashChange(ev) {
@@ -452,22 +485,27 @@ function preLoadCss(cssUrl) {
                 return;
             }
 
+            const modpath = hasClass(document.body, "mod") ? "../" : "";
+
             const h3 = document.createElement("h3");
-            h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
+            h3.innerHTML = `<a href="${modpath}index.html#${id}">${longty}</a>`;
             const ul = document.createElement("ul");
             ul.className = "block " + shortty;
 
             for (const name of filtered) {
                 let path;
                 if (shortty === "mod") {
-                    path = name + "/index.html";
+                    path = `${modpath}${name}/index.html`;
                 } else {
-                    path = shortty + "." + name + ".html";
+                    path = `${modpath}${shortty}.${name}.html`;
+                }
+                let current_page = document.location.href.toString();
+                if (current_page.endsWith("/")) {
+                    current_page += "index.html";
                 }
-                const current_page = document.location.href.split("/").pop();
                 const link = document.createElement("a");
                 link.href = path;
-                if (path === current_page) {
+                if (link.href === current_page) {
                     link.className = "current";
                 }
                 link.textContent = name;
@@ -480,19 +518,33 @@ function preLoadCss(cssUrl) {
         }
 
         if (sidebar) {
+            // keep this synchronized with ItemSection::ALL in html/render/mod.rs
+            // Re-exports aren't shown here, because they don't have child pages
+            //block("reexport", "reexports", "Re-exports");
             block("primitive", "primitives", "Primitive Types");
             block("mod", "modules", "Modules");
             block("macro", "macros", "Macros");
             block("struct", "structs", "Structs");
             block("enum", "enums", "Enums");
-            block("union", "unions", "Unions");
             block("constant", "constants", "Constants");
             block("static", "static", "Statics");
             block("trait", "traits", "Traits");
             block("fn", "functions", "Functions");
             block("type", "types", "Type Aliases");
+            block("union", "unions", "Unions");
+            // No point, because these items don't appear in modules
+            //block("impl", "impls", "Implementations");
+            //block("tymethod", "tymethods", "Type Methods");
+            //block("method", "methods", "Methods");
+            //block("structfield", "fields", "Fields");
+            //block("variant", "variants", "Variants");
+            //block("associatedtype", "associated-types", "Associated Types");
+            //block("associatedconstant", "associated-consts", "Associated Constants");
             block("foreigntype", "foreign-types", "Foreign Types");
             block("keyword", "keywords", "Keywords");
+            block("opaque", "opaque-types", "Opaque Types");
+            block("attr", "attributes", "Attribute Macros");
+            block("derive", "derives", "Derive Macros");
             block("traitalias", "trait-aliases", "Trait Aliases");
         }
     }
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 7a282a99e9c..48c9a53a283 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1752,6 +1752,7 @@ function initSearch(rawSearchIndex) {
                 type: item.type,
                 is_alias: true,
                 deprecated: item.deprecated,
+                implDisambiguator: item.implDisambiguator,
             };
         }
 
@@ -2218,7 +2219,7 @@ function initSearch(rawSearchIndex) {
             href = ROOT_PATH + name + "/index.html";
         } else if (item.parent !== undefined) {
             const myparent = item.parent;
-            let anchor = "#" + type + "." + name;
+            let anchor = type + "." + name;
             const parentType = itemTypes[myparent.ty];
             let pageType = parentType;
             let pageName = myparent.name;
@@ -2232,16 +2233,19 @@ function initSearch(rawSearchIndex) {
                 const enumName = item.path.substr(enumNameIdx + 2);
                 path = item.path.substr(0, enumNameIdx);
                 displayPath = path + "::" + enumName + "::" + myparent.name + "::";
-                anchor = "#variant." + myparent.name + ".field." + name;
+                anchor = "variant." + myparent.name + ".field." + name;
                 pageType = "enum";
                 pageName = enumName;
             } else {
                 displayPath = path + "::" + myparent.name + "::";
             }
+            if (item.implDisambiguator !== null) {
+                anchor = item.implDisambiguator + "/" + anchor;
+            }
             href = ROOT_PATH + path.replace(/::/g, "/") +
                 "/" + pageType +
                 "." + pageName +
-                ".html" + anchor;
+                ".html#" + anchor;
         } else {
             displayPath = item.path + "::";
             href = ROOT_PATH + item.path.replace(/::/g, "/") +
@@ -2727,6 +2731,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
              * Types are also represented as arrays; the first item is an index into the `p`
              * array, while the second is a list of types representing any generic parameters.
              *
+             * b[i] contains an item's impl disambiguator. This is only present if an item
+             * is defined in an impl block and, the impl block's type has more than one associated
+             * item with the same name.
+             *
              * `a` defines aliases with an Array of pairs: [name, offset], where `offset`
              * points into the n/t/d/q/i/f arrays.
              *
@@ -2746,6 +2754,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
              *   i: Array<Number>,
              *   f: Array<RawFunctionSearchType>,
              *   p: Array<Object>,
+             *   b: Array<[Number, String]>,
              *   c: Array<Number>
              * }}
              */
@@ -2766,6 +2775,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 id: id,
                 normalizedName: crate.indexOf("_") === -1 ? crate : crate.replace(/_/g, ""),
                 deprecated: null,
+                implDisambiguator: null,
             };
             id += 1;
             searchIndex.push(crateRow);
@@ -2789,6 +2799,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             const itemFunctionSearchTypes = crateCorpus.f;
             // an array of (Number) indices for the deprecated items
             const deprecatedItems = new Set(crateCorpus.c);
+            // an array of (Number) indices for the deprecated items
+            const implDisambiguator = new Map(crateCorpus.b);
             // an array of [(Number) item type,
             //              (String) name]
             const paths = crateCorpus.p;
@@ -2849,6 +2861,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     id: id,
                     normalizedName: word.indexOf("_") === -1 ? word : word.replace(/_/g, ""),
                     deprecated: deprecatedItems.has(i),
+                    implDisambiguator: implDisambiguator.has(i) ? implDisambiguator.get(i) : null,
                 };
                 id += 1;
                 searchIndex.push(row);
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 579c782be09..ebf817673bf 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -42,6 +42,8 @@
     <script defer src="{{page.root_path|safe}}src-files{{page.resource_suffix}}.js"></script> {# #}
     {% else if !page.css_class.contains("mod") %}
     <script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {# #}
+    {% else if !page.css_class.contains("sys") %}
+    <script defer src="../sidebar-items{{page.resource_suffix}}.js"></script> {# #}
     {% endif %}
     <script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {# #}
     {% if layout.scrape_examples_extension %}
@@ -77,36 +79,51 @@
     {% if page.css_class != "src" %}
     <nav class="mobile-topbar"> {# #}
         <button class="sidebar-menu-toggle">&#9776;</button> {# #}
-        <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
-        {% if !layout.logo.is_empty() %}
-            <img src="{{layout.logo}}" alt="logo"> {# #}
-        {% else %}
-            <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+        {% if !layout.logo.is_empty() || page.rust_logo %}
+        <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+        {% if page.rust_logo %}
+            <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt=""> {# #}
+        {% else if !layout.logo.is_empty() %}
+            <img src="{{layout.logo}}" alt=""> {# #}
         {% endif %}
         </a> {# #}
+        {% endif %}
     </nav>
     {% endif %}
     <nav class="sidebar"> {# #}
         {% if page.css_class != "src" %}
-        <a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
-            {% if !layout.logo.is_empty() %}
-                <img src="{{layout.logo}}" alt="logo"> {# #}
-            {% else %}
-                <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+        <div class="sidebar-crate">
+            {% if !layout.logo.is_empty() || page.rust_logo %}
+            <a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+                {% if page.rust_logo %}
+                    <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+                {% else if !layout.logo.is_empty() %}
+                    <img src="{{layout.logo}}" alt="logo"> {# #}
+                {% endif %}
+            </a> {# #}
             {% endif %}
-        </a> {# #}
+            <h2> {# #}
+                <a href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html">{{display_krate}}</a> {# #}
+                {% if !display_krate_version_number.is_empty() %}
+                    <span class="version">{{+ display_krate_version_number}}</span>
+                {% endif %}
+            </h2> {# #}
+        </div> {# #}
+        {% if !display_krate_version_extra.is_empty() %}
+        <div class="version">{{+ display_krate_version_extra}}</div> {# #}
+        {% endif %}
         {% endif %}
         {{ sidebar|safe }}
     </nav> {# #}
     <main> {# #}
         {% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
             <nav class="sub"> {# #}
-                {% if page.css_class == "src" %}
-                <a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
-                    {% if !layout.logo.is_empty() %}
-                        <img src="{{layout.logo}}" alt="logo"> {# #}
-                    {% else %}
-                        <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
+                {% if page.css_class == "src" && (!layout.logo.is_empty() || page.rust_logo) %}
+                <a class="sub-logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
+                    {% if page.rust_logo %}
+                    <img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="{{display_krate}}"> {# #}
+                    {% else if !layout.logo.is_empty() %}
+                        <img src="{{layout.logo}}" alt="{{display_krate}}"> {# #}
                     {% endif %}
                 </a> {# #}
                 {% endif %}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
index 01d476ad29f..a99198141e2 100644
--- a/src/librustdoc/html/templates/sidebar.html
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -6,9 +6,6 @@
 <div class="sidebar-elems">
     {% if is_crate %}
         <ul class="block">
-            {% if !version.is_empty() %}
-                <li class="version">Version {{+ version}}</li>
-            {% endif %}
             <li><a id="all-types" href="all.html">All Items</a></li> {# #}
         </ul>
     {% endif %}
@@ -32,6 +29,6 @@
         </section>
     {% endif %}
     {% if !path.is_empty() %}
-        <h2><a href="index.html">In {{+ path}}</a></h2>
+        <h2><a href="{% if is_mod %}../{% endif %}index.html">In {{+ path}}</a></h2>
     {% endif %}
 </div>
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 472cde51be5..1420d108664 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -453,7 +453,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
                 default: default.map(|x| (*x).into_tcx(tcx)),
                 synthetic,
             },
-            Const { ty, default } => GenericParamDefKind::Const {
+            Const { ty, default, is_host_effect: _ } => GenericParamDefKind::Const {
                 type_: (*ty).into_tcx(tcx),
                 default: default.map(|x| *x),
             },
@@ -491,12 +491,14 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                                 default: default.map(|ty| (*ty).into_tcx(tcx)),
                                 synthetic,
                             },
-                            clean::GenericParamDefKind::Const { ty, default } => {
-                                GenericParamDefKind::Const {
-                                    type_: (*ty).into_tcx(tcx),
-                                    default: default.map(|d| *d),
-                                }
-                            }
+                            clean::GenericParamDefKind::Const {
+                                ty,
+                                default,
+                                is_host_effect: _,
+                            } => GenericParamDefKind::Const {
+                                type_: (*ty).into_tcx(tcx),
+                                default: default.map(|d| *d),
+                            },
                         };
                         GenericParamDef { name, kind }
                     })
@@ -744,7 +746,7 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
             // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
             // the expr is always some.
             expr: disr.expr(tcx).unwrap(),
-            value: disr.value(tcx),
+            value: disr.value(tcx, false),
         }
     }
 }
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index fc2acb6eaa3..67f5ea5d98b 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -6,6 +6,7 @@
 #![feature(array_methods)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(if_let_guard)]
 #![feature(impl_trait_in_assoc_type)]
 #![feature(iter_intersperse)]
 #![feature(lazy_cell)]