about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-08-10 03:21:23 +0000
committerbors <bors@rust-lang.org>2022-08-10 03:21:23 +0000
commit0459d2fa736a556332ea9613ad0edf073107cb40 (patch)
treede38d91ee5d2918effcc2104d189305a83fe0572 /src
parent34a6cae28e7013ff0e640026a8e46f315426829d (diff)
parent752b9a85dc4f347e52faff39fabf6c84e5425ba7 (diff)
downloadrust-0459d2fa736a556332ea9613ad0edf073107cb40.tar.gz
rust-0459d2fa736a556332ea9613ad0edf073107cb40.zip
Auto merge of #100346 - matthiaskrgr:rollup-6rljn4p, r=matthiaskrgr
Rollup of 14 pull requests

Successful merges:

 - #98775 (rustdoc: improve scroll locking in the rustdoc mobile sidebars)
 - #99479 (rustdoc-json: Remove doc FIXME for Import::id and explain)
 - #100040 (Error on broken pipe but do not backtrace or ICE)
 - #100072 (linker-plugin-lto.md: Correct the name of example c file)
 - #100098 (Some "this expression has a field"-related fixes)
 - #100226 (Do not manually craft a span pointing inside a multibyte character.)
 - #100240 (Fail gracefully when const pattern is not structural match.)
 - #100256 (Add some high-level docs to `FnCtxt` and `ItemCtxt`)
 - #100261 (Set tainted errors bit before emitting coerce suggestions.)
 - #100275 (also update anyhow in codegen_cranelift)
 - #100281 (Remove more Clean trait implementations)
 - #100314 (Mention `unit-test` in MIR opt test README)
 - #100319 (Remove more Clean trait implementations)
 - #100323 ([rustdoc] Don't render impl blocks with doc comment if they only contain private items by default)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'src')
-rw-r--r--src/doc/rustc/src/linker-plugin-lto.md4
-rw-r--r--src/librustdoc/clean/inline.rs8
-rw-r--r--src/librustdoc/clean/mod.rs328
-rw-r--r--src/librustdoc/clean/utils.rs8
-rw-r--r--src/librustdoc/html/static/js/main.js46
-rw-r--r--src/librustdoc/html/static/js/source-script.js20
-rw-r--r--src/librustdoc/json/conversions.rs30
-rw-r--r--src/librustdoc/passes/strip_hidden.rs8
-rw-r--r--src/librustdoc/passes/strip_private.rs10
-rw-r--r--src/librustdoc/passes/stripper.rs53
-rw-r--r--src/rustdoc-json-types/lib.rs7
-rw-r--r--src/test/mir-opt/README.md12
-rw-r--r--src/test/rustdoc-gui/sidebar-mobile-scroll.goml31
-rw-r--r--src/test/rustdoc-gui/sidebar-source-code-display.goml11
-rw-r--r--src/test/rustdoc-json/primitive.rs6
-rw-r--r--src/test/rustdoc/empty-impl-block-private-with-doc.rs44
-rw-r--r--src/test/rustdoc/empty-impl-block-private.rs40
-rw-r--r--src/test/ui/consts/const_in_pattern/incomplete-slice.rs15
-rw-r--r--src/test/ui/consts/const_in_pattern/incomplete-slice.stderr26
-rw-r--r--src/test/ui/copy-a-resource.stderr4
-rw-r--r--src/test/ui/hrtb/issue-30786.stderr8
-rw-r--r--src/test/ui/issues/issue-2823.stderr4
-rw-r--r--src/test/ui/lint/unused_parens_multibyte_recovery.rs11
-rw-r--r--src/test/ui/lint/unused_parens_multibyte_recovery.stderr43
-rw-r--r--src/test/ui/noncopyable-class.stderr8
-rw-r--r--src/test/ui/suggestions/field-access-considering-privacy.rs35
-rw-r--r--src/test/ui/suggestions/field-access-considering-privacy.stderr14
-rw-r--r--src/test/ui/typeck/issue-100246.rs30
-rw-r--r--src/test/ui/typeck/issue-100246.stderr13
29 files changed, 649 insertions, 228 deletions
diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md
index 9c644dd404d..b1854b22a7c 100644
--- a/src/doc/rustc/src/linker-plugin-lto.md
+++ b/src/doc/rustc/src/linker-plugin-lto.md
@@ -30,7 +30,7 @@ Using `rustc` directly:
 # Compile the Rust staticlib
 rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs
 # Compile the C code with `-flto=thin`
-clang -c -O2 -flto=thin -o main.o ./main.c
+clang -c -O2 -flto=thin -o cmain.o ./cmain.c
 # Link everything, making sure that we use an appropriate linker
 clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
 ```
@@ -41,7 +41,7 @@ Using `cargo`:
 # Compile the Rust staticlib
 RUSTFLAGS="-Clinker-plugin-lto" cargo build --release
 # Compile the C code with `-flto=thin`
-clang -c -O2 -flto=thin -o main.o ./main.c
+clang -c -O2 -flto=thin -o cmain.o ./cmain.c
 # Link everything, making sure that we use an appropriate linker
 clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
 ```
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 58d0aedb023..4e9456ba7cb 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -16,8 +16,8 @@ use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
 
 use crate::clean::{
-    self, clean_fn_decl_from_did_and_sig, clean_middle_field, clean_middle_ty,
-    clean_trait_ref_with_bindings, clean_ty, clean_ty_generics, clean_variant_def,
+    self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_field,
+    clean_middle_ty, clean_trait_ref_with_bindings, clean_ty, clean_ty_generics, clean_variant_def,
     clean_visibility, utils, Attributes, AttributesExt, Clean, ImplKind, ItemId, Type, Visibility,
 };
 use crate::core::DocContext;
@@ -426,9 +426,9 @@ pub(crate) fn build_impl(
                         true
                     }
                 })
-                .map(|item| item.clean(cx))
+                .map(|item| clean_impl_item(item, cx))
                 .collect::<Vec<_>>(),
-            impl_.generics.clean(cx),
+            clean_generics(impl_.generics, cx),
         ),
         None => (
             tcx.associated_items(did)
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 78b93725537..402e4f29860 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -48,75 +48,68 @@ pub(crate) trait Clean<'tcx, T> {
     fn clean(&self, cx: &mut DocContext<'tcx>) -> T;
 }
 
-impl<'tcx> Clean<'tcx, Item> for DocModule<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
-        let mut items: Vec<Item> = vec![];
-        let mut inserted = FxHashSet::default();
-        items.extend(self.foreigns.iter().map(|(item, renamed)| {
-            let item = clean_maybe_renamed_foreign_item(cx, item, *renamed);
+pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
+    let mut items: Vec<Item> = vec![];
+    let mut inserted = FxHashSet::default();
+    items.extend(doc.foreigns.iter().map(|(item, renamed)| {
+        let item = clean_maybe_renamed_foreign_item(cx, item, *renamed);
+        if let Some(name) = item.name {
+            inserted.insert((item.type_(), name));
+        }
+        item
+    }));
+    items.extend(doc.mods.iter().map(|x| {
+        inserted.insert((ItemType::Module, x.name));
+        clean_doc_module(x, cx)
+    }));
+
+    // Split up imports from all other items.
+    //
+    // This covers the case where somebody does an import which should pull in an item,
+    // but there's already an item with the same namespace and same name. Rust gives
+    // priority to the not-imported one, so we should, too.
+    items.extend(doc.items.iter().flat_map(|(item, renamed)| {
+        // First, lower everything other than imports.
+        if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
+            return Vec::new();
+        }
+        let v = clean_maybe_renamed_item(cx, item, *renamed);
+        for item in &v {
             if let Some(name) = item.name {
                 inserted.insert((item.type_(), name));
             }
-            item
-        }));
-        items.extend(self.mods.iter().map(|x| {
-            inserted.insert((ItemType::Module, x.name));
-            x.clean(cx)
-        }));
-
-        // Split up imports from all other items.
-        //
-        // This covers the case where somebody does an import which should pull in an item,
-        // but there's already an item with the same namespace and same name. Rust gives
-        // priority to the not-imported one, so we should, too.
-        items.extend(self.items.iter().flat_map(|(item, renamed)| {
-            // First, lower everything other than imports.
-            if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
-                return Vec::new();
-            }
-            let v = clean_maybe_renamed_item(cx, item, *renamed);
-            for item in &v {
-                if let Some(name) = item.name {
-                    inserted.insert((item.type_(), name));
-                }
-            }
-            v
-        }));
-        items.extend(self.items.iter().flat_map(|(item, renamed)| {
-            // Now we actually lower the imports, skipping everything else.
-            if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
-                let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id()));
-                clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted)
-            } else {
-                // skip everything else
-                Vec::new()
-            }
-        }));
-
-        // determine if we should display the inner contents or
-        // the outer `mod` item for the source code.
-
-        let span = Span::new({
-            let where_outer = self.where_outer(cx.tcx);
-            let sm = cx.sess().source_map();
-            let outer = sm.lookup_char_pos(where_outer.lo());
-            let inner = sm.lookup_char_pos(self.where_inner.lo());
-            if outer.file.start_pos == inner.file.start_pos {
-                // mod foo { ... }
-                where_outer
-            } else {
-                // mod foo; (and a separate SourceFile for the contents)
-                self.where_inner
-            }
-        });
+        }
+        v
+    }));
+    items.extend(doc.items.iter().flat_map(|(item, renamed)| {
+        // Now we actually lower the imports, skipping everything else.
+        if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
+            let name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id()));
+            clean_use_statement(item, name, path, hir::UseKind::Glob, cx, &mut inserted)
+        } else {
+            // skip everything else
+            Vec::new()
+        }
+    }));
+
+    // determine if we should display the inner contents or
+    // the outer `mod` item for the source code.
+
+    let span = Span::new({
+        let where_outer = doc.where_outer(cx.tcx);
+        let sm = cx.sess().source_map();
+        let outer = sm.lookup_char_pos(where_outer.lo());
+        let inner = sm.lookup_char_pos(doc.where_inner.lo());
+        if outer.file.start_pos == inner.file.start_pos {
+            // mod foo { ... }
+            where_outer
+        } else {
+            // mod foo; (and a separate SourceFile for the contents)
+            doc.where_inner
+        }
+    });
 
-        Item::from_hir_id_and_parts(
-            self.id,
-            Some(self.name),
-            ModuleItem(Module { items, span }),
-            cx,
-        )
-    }
+    Item::from_hir_id_and_parts(doc.id, Some(doc.name), ModuleItem(Module { items, span }), cx)
 }
 
 fn clean_generic_bound<'tcx>(
@@ -568,69 +561,68 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool {
     matches!(param.kind, hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided })
 }
 
-impl<'tcx> Clean<'tcx, Generics> for hir::Generics<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> Generics {
-        let impl_trait_params = self
-            .params
-            .iter()
-            .filter(|param| is_impl_trait(param))
-            .map(|param| {
-                let param = clean_generic_param(cx, Some(self), param);
-                match param.kind {
-                    GenericParamDefKind::Lifetime { .. } => unreachable!(),
-                    GenericParamDefKind::Type { did, ref bounds, .. } => {
-                        cx.impl_trait_bounds.insert(did.into(), bounds.clone());
-                    }
-                    GenericParamDefKind::Const { .. } => unreachable!(),
+pub(crate) fn clean_generics<'tcx>(
+    gens: &hir::Generics<'tcx>,
+    cx: &mut DocContext<'tcx>,
+) -> Generics {
+    let impl_trait_params = gens
+        .params
+        .iter()
+        .filter(|param| is_impl_trait(param))
+        .map(|param| {
+            let param = clean_generic_param(cx, Some(gens), param);
+            match param.kind {
+                GenericParamDefKind::Lifetime { .. } => unreachable!(),
+                GenericParamDefKind::Type { did, ref bounds, .. } => {
+                    cx.impl_trait_bounds.insert(did.into(), bounds.clone());
                 }
-                param
-            })
-            .collect::<Vec<_>>();
+                GenericParamDefKind::Const { .. } => unreachable!(),
+            }
+            param
+        })
+        .collect::<Vec<_>>();
 
-        let mut params = Vec::with_capacity(self.params.len());
-        for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
-            let p = clean_generic_param(cx, Some(self), p);
-            params.push(p);
-        }
-        params.extend(impl_trait_params);
+    let mut params = Vec::with_capacity(gens.params.len());
+    for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
+        let p = clean_generic_param(cx, Some(gens), p);
+        params.push(p);
+    }
+    params.extend(impl_trait_params);
 
-        let mut generics = Generics {
-            params,
-            where_predicates: self
-                .predicates
-                .iter()
-                .filter_map(|x| clean_where_predicate(x, cx))
-                .collect(),
-        };
+    let mut generics = Generics {
+        params,
+        where_predicates: gens
+            .predicates
+            .iter()
+            .filter_map(|x| clean_where_predicate(x, cx))
+            .collect(),
+    };
 
-        // Some duplicates are generated for ?Sized bounds between type params and where
-        // predicates. The point in here is to move the bounds definitions from type params
-        // to where predicates when such cases occur.
-        for where_pred in &mut generics.where_predicates {
-            match *where_pred {
-                WherePredicate::BoundPredicate {
-                    ty: Generic(ref name), ref mut bounds, ..
-                } => {
-                    if bounds.is_empty() {
-                        for param in &mut generics.params {
-                            match param.kind {
-                                GenericParamDefKind::Lifetime { .. } => {}
-                                GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => {
-                                    if &param.name == name {
-                                        mem::swap(bounds, ty_bounds);
-                                        break;
-                                    }
+    // Some duplicates are generated for ?Sized bounds between type params and where
+    // predicates. The point in here is to move the bounds definitions from type params
+    // to where predicates when such cases occur.
+    for where_pred in &mut generics.where_predicates {
+        match *where_pred {
+            WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds, .. } => {
+                if bounds.is_empty() {
+                    for param in &mut generics.params {
+                        match param.kind {
+                            GenericParamDefKind::Lifetime { .. } => {}
+                            GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => {
+                                if &param.name == name {
+                                    mem::swap(bounds, ty_bounds);
+                                    break;
                                 }
-                                GenericParamDefKind::Const { .. } => {}
                             }
+                            GenericParamDefKind::Const { .. } => {}
                         }
                     }
                 }
-                _ => continue,
             }
+            _ => continue,
         }
-        generics
     }
+    generics
 }
 
 fn clean_ty_generics<'tcx>(
@@ -896,7 +888,7 @@ fn clean_function<'tcx>(
 ) -> Box<Function> {
     let (generics, decl) = enter_impl_trait(cx, |cx| {
         // NOTE: generics must be cleaned before args
-        let generics = generics.clean(cx);
+        let generics = clean_generics(generics, cx);
         let args = clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id);
         let decl = clean_fn_decl_with_args(cx, sig.decl, args);
         (generics, decl)
@@ -1025,7 +1017,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => {
                 let (generics, decl) = enter_impl_trait(cx, |cx| {
                     // NOTE: generics must be cleaned before args
-                    let generics = trait_item.generics.clean(cx);
+                    let generics = clean_generics(trait_item.generics, cx);
                     let args = clean_args_from_types_and_names(cx, sig.decl.inputs, names);
                     let decl = clean_fn_decl_with_args(cx, sig.decl, args);
                     (generics, decl)
@@ -1033,7 +1025,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
                 TyMethodItem(Box::new(Function { decl, generics }))
             }
             hir::TraitItemKind::Type(bounds, Some(default)) => {
-                let generics = enter_impl_trait(cx, |cx| trait_item.generics.clean(cx));
+                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
                 let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
                 let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, default), cx, None);
                 AssocTypeItem(
@@ -1046,7 +1038,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
                 )
             }
             hir::TraitItemKind::Type(bounds, None) => {
-                let generics = enter_impl_trait(cx, |cx| trait_item.generics.clean(cx));
+                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
                 let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
                 TyAssocTypeItem(Box::new(generics), bounds)
             }
@@ -1058,45 +1050,46 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
     })
 }
 
-impl<'tcx> Clean<'tcx, Item> for hir::ImplItem<'tcx> {
-    fn clean(&self, cx: &mut DocContext<'tcx>) -> Item {
-        let local_did = self.def_id.to_def_id();
-        cx.with_param_env(local_did, |cx| {
-            let inner = match self.kind {
-                hir::ImplItemKind::Const(ty, expr) => {
-                    let default = ConstantKind::Local { def_id: local_did, body: expr };
-                    AssocConstItem(clean_ty(ty, cx), default)
-                }
-                hir::ImplItemKind::Fn(ref sig, body) => {
-                    let m = clean_function(cx, sig, self.generics, body);
-                    let defaultness = cx.tcx.impl_defaultness(self.def_id);
-                    MethodItem(m, Some(defaultness))
-                }
-                hir::ImplItemKind::TyAlias(hir_ty) => {
-                    let type_ = clean_ty(hir_ty, cx);
-                    let generics = self.generics.clean(cx);
-                    let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None);
-                    AssocTypeItem(
-                        Box::new(Typedef { type_, generics, item_type: Some(item_type) }),
-                        Vec::new(),
-                    )
-                }
-            };
+pub(crate) fn clean_impl_item<'tcx>(
+    impl_: &hir::ImplItem<'tcx>,
+    cx: &mut DocContext<'tcx>,
+) -> Item {
+    let local_did = impl_.def_id.to_def_id();
+    cx.with_param_env(local_did, |cx| {
+        let inner = match impl_.kind {
+            hir::ImplItemKind::Const(ty, expr) => {
+                let default = ConstantKind::Local { def_id: local_did, body: expr };
+                AssocConstItem(clean_ty(ty, cx), default)
+            }
+            hir::ImplItemKind::Fn(ref sig, body) => {
+                let m = clean_function(cx, sig, impl_.generics, body);
+                let defaultness = cx.tcx.impl_defaultness(impl_.def_id);
+                MethodItem(m, Some(defaultness))
+            }
+            hir::ImplItemKind::TyAlias(hir_ty) => {
+                let type_ = clean_ty(hir_ty, cx);
+                let generics = clean_generics(impl_.generics, cx);
+                let item_type = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None);
+                AssocTypeItem(
+                    Box::new(Typedef { type_, generics, item_type: Some(item_type) }),
+                    Vec::new(),
+                )
+            }
+        };
 
-            let mut what_rustc_thinks =
-                Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx);
+        let mut what_rustc_thinks =
+            Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx);
 
-            let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(self.def_id));
+        let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(impl_.def_id));
 
-            // Trait impl items always inherit the impl's visibility --
-            // we don't want to show `pub`.
-            if impl_ref.is_some() {
-                what_rustc_thinks.visibility = Inherited;
-            }
+        // Trait impl items always inherit the impl's visibility --
+        // we don't want to show `pub`.
+        if impl_ref.is_some() {
+            what_rustc_thinks.visibility = Inherited;
+        }
 
-            what_rustc_thinks
-        })
-    }
+        what_rustc_thinks
+    })
 }
 
 impl<'tcx> Clean<'tcx, Item> for ty::AssocItem {
@@ -1905,32 +1898,32 @@ fn clean_maybe_renamed_item<'tcx>(
             }),
             ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
                 bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
-                generics: ty.generics.clean(cx),
+                generics: clean_generics(ty.generics, cx),
             }),
             ItemKind::TyAlias(hir_ty, generics) => {
                 let rustdoc_ty = clean_ty(hir_ty, cx);
                 let ty = clean_middle_ty(hir_ty_to_ty(cx.tcx, hir_ty), cx, None);
                 TypedefItem(Box::new(Typedef {
                     type_: rustdoc_ty,
-                    generics: generics.clean(cx),
+                    generics: clean_generics(generics, cx),
                     item_type: Some(ty),
                 }))
             }
             ItemKind::Enum(ref def, generics) => EnumItem(Enum {
                 variants: def.variants.iter().map(|v| v.clean(cx)).collect(),
-                generics: generics.clean(cx),
+                generics: clean_generics(generics, cx),
             }),
             ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias {
-                generics: generics.clean(cx),
+                generics: clean_generics(generics, cx),
                 bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
             }),
             ItemKind::Union(ref variant_data, generics) => UnionItem(Union {
-                generics: generics.clean(cx),
+                generics: clean_generics(generics, cx),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
             ItemKind::Struct(ref variant_data, generics) => StructItem(Struct {
                 struct_type: CtorKind::from_hir(variant_data),
-                generics: generics.clean(cx),
+                generics: clean_generics(generics, cx),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
             ItemKind::Impl(impl_) => return clean_impl(impl_, item.hir_id(), cx),
@@ -1953,7 +1946,7 @@ fn clean_maybe_renamed_item<'tcx>(
                 TraitItem(Trait {
                     def_id,
                     items,
-                    generics: generics.clean(cx),
+                    generics: clean_generics(generics, cx),
                     bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
                 })
             }
@@ -1988,8 +1981,11 @@ fn clean_impl<'tcx>(
     let tcx = cx.tcx;
     let mut ret = Vec::new();
     let trait_ = impl_.of_trait.as_ref().map(|t| clean_trait_ref(t, cx));
-    let items =
-        impl_.items.iter().map(|ii| tcx.hir().impl_item(ii.id).clean(cx)).collect::<Vec<_>>();
+    let items = impl_
+        .items
+        .iter()
+        .map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx))
+        .collect::<Vec<_>>();
     let def_id = tcx.hir().local_def_id(hir_id);
 
     // If this impl block is an implementation of the Deref trait, then we
@@ -2006,7 +2002,7 @@ fn clean_impl<'tcx>(
     let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| {
         let kind = ImplItem(Box::new(Impl {
             unsafety: impl_.unsafety,
-            generics: impl_.generics.clean(cx),
+            generics: clean_generics(impl_.generics, cx),
             trait_,
             for_,
             items,
@@ -2203,7 +2199,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
             hir::ForeignItemKind::Fn(decl, names, generics) => {
                 let (generics, decl) = enter_impl_trait(cx, |cx| {
                     // NOTE: generics must be cleaned before args
-                    let generics = generics.clean(cx);
+                    let generics = clean_generics(generics, cx);
                     let args = clean_args_from_types_and_names(cx, decl.inputs, names);
                     let decl = clean_fn_decl_with_args(cx, decl, args);
                     (generics, decl)
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 43e71e90a6f..718cbbd2b83 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -2,9 +2,9 @@ use crate::clean::auto_trait::AutoTraitFinder;
 use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::render_macro_matchers::render_macro_matcher;
 use crate::clean::{
-    clean_middle_const, clean_middle_region, clean_middle_ty, inline, Clean, Crate, ExternalCrate,
-    Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, PathSegment,
-    Primitive, PrimitiveType, Type, TypeBinding, Visibility,
+    clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline, Crate,
+    ExternalCrate, Generic, GenericArg, GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path,
+    PathSegment, Primitive, PrimitiveType, Type, TypeBinding, Visibility,
 };
 use crate::core::DocContext;
 use crate::formats::item_type::ItemType;
@@ -37,7 +37,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
 
     // Clean the crate, translating the entire librustc_ast AST to one that is
     // understood by rustdoc.
-    let mut module = module.clean(cx);
+    let mut module = clean_doc_module(&module, cx);
 
     match *module.kind {
         ItemKind::ModuleItem(ref module) => {
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 2e05c4be2f3..5dec610b30c 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -348,8 +348,7 @@ function loadCss(cssFileName) {
 
     function onHashChange(ev) {
         // If we're in mobile mode, we should hide the sidebar in any case.
-        const sidebar = document.getElementsByClassName("sidebar")[0];
-        removeClass(sidebar, "shown");
+        hideSidebar();
         handleHashes(ev);
     }
 
@@ -734,11 +733,50 @@ function loadCss(cssFileName) {
         });
     }());
 
+    let oldSidebarScrollPosition = null;
+
+    function showSidebar() {
+        if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
+            // This is to keep the scroll position on mobile.
+            oldSidebarScrollPosition = window.scrollY;
+            document.body.style.width = `${document.body.offsetWidth}px`;
+            document.body.style.position = "fixed";
+            document.body.style.top = `-${oldSidebarScrollPosition}px`;
+            document.querySelector(".mobile-topbar").style.top = `${oldSidebarScrollPosition}px`;
+            document.querySelector(".mobile-topbar").style.position = "relative";
+        } else {
+            oldSidebarScrollPosition = null;
+        }
+        const sidebar = document.getElementsByClassName("sidebar")[0];
+        addClass(sidebar, "shown");
+    }
+
     function hideSidebar() {
+        if (oldSidebarScrollPosition !== null) {
+            // This is to keep the scroll position on mobile.
+            document.body.style.width = "";
+            document.body.style.position = "";
+            document.body.style.top = "";
+            document.querySelector(".mobile-topbar").style.top = "";
+            document.querySelector(".mobile-topbar").style.position = "";
+            // The scroll position is lost when resetting the style, hence why we store it in
+            // `oldSidebarScrollPosition`.
+            window.scrollTo(0, oldSidebarScrollPosition);
+            oldSidebarScrollPosition = null;
+        }
         const sidebar = document.getElementsByClassName("sidebar")[0];
         removeClass(sidebar, "shown");
     }
 
+    window.addEventListener("resize", () => {
+        if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT &&
+            oldSidebarScrollPosition !== null) {
+            // If the user opens the sidebar in "mobile" mode, and then grows the browser window,
+            // we need to switch away from mobile mode and make the main content area scrollable.
+            hideSidebar();
+        }
+    });
+
     function handleClick(id, f) {
         const elem = document.getElementById(id);
         if (elem) {
@@ -781,9 +819,9 @@ function loadCss(cssFileName) {
         sidebar_menu_toggle.addEventListener("click", () => {
             const sidebar = document.getElementsByClassName("sidebar")[0];
             if (!hasClass(sidebar, "shown")) {
-                addClass(sidebar, "shown");
+                showSidebar();
             } else {
-                removeClass(sidebar, "shown");
+                hideSidebar();
             }
         });
     }
diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js
index c45d614293a..06d15d9e5ff 100644
--- a/src/librustdoc/html/static/js/source-script.js
+++ b/src/librustdoc/html/static/js/source-script.js
@@ -10,7 +10,7 @@
 (function() {
 
 const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-path"].value;
-let oldScrollPosition = 0;
+let oldScrollPosition = null;
 
 const NAME_OFFSET = 0;
 const DIRS_OFFSET = 1;
@@ -75,18 +75,21 @@ function toggleSidebar() {
             oldScrollPosition = window.scrollY;
             document.body.style.position = "fixed";
             document.body.style.top = `-${oldScrollPosition}px`;
+        } else {
+            oldScrollPosition = null;
         }
         addClass(document.documentElement, "source-sidebar-expanded");
         child.innerText = "<";
         updateLocalStorage("source-sidebar-show", "true");
     } else {
-        if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT) {
+        if (window.innerWidth < window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
             // This is to keep the scroll position on mobile.
             document.body.style.position = "";
             document.body.style.top = "";
             // The scroll position is lost when resetting the style, hence why we store it in
-            // `oldScroll`.
+            // `oldScrollPosition`.
             window.scrollTo(0, oldScrollPosition);
+            oldScrollPosition = null;
         }
         removeClass(document.documentElement, "source-sidebar-expanded");
         child.innerText = ">";
@@ -94,6 +97,17 @@ function toggleSidebar() {
     }
 }
 
+window.addEventListener("resize", () => {
+    if (window.innerWidth >= window.RUSTDOC_MOBILE_BREAKPOINT && oldScrollPosition !== null) {
+        // If the user opens the sidebar in "mobile" mode, and then grows the browser window,
+        // we need to switch away from mobile mode and make the main content area scrollable.
+        document.body.style.position = "";
+        document.body.style.top = "";
+        window.scrollTo(0, oldScrollPosition);
+        oldScrollPosition = null;
+    }
+});
+
 function createSidebarToggle() {
     const sidebarToggle = document.createElement("div");
     sidebarToggle.id = "sidebar-toggle";
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 8caba8cb902..4c21fd55328 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -685,24 +685,18 @@ impl FromWithTcx<clean::Variant> for Variant {
 impl FromWithTcx<clean::Import> for Import {
     fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
         use clean::ImportKind::*;
-        match import.kind {
-            Simple(s) => Import {
-                source: import.source.path.whole_name(),
-                name: s.to_string(),
-                id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)),
-                glob: false,
-            },
-            Glob => Import {
-                source: import.source.path.whole_name(),
-                name: import
-                    .source
-                    .path
-                    .last_opt()
-                    .unwrap_or_else(|| Symbol::intern("*"))
-                    .to_string(),
-                id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)),
-                glob: true,
-            },
+        let (name, glob) = match import.kind {
+            Simple(s) => (s.to_string(), false),
+            Glob => (
+                import.source.path.last_opt().unwrap_or_else(|| Symbol::intern("*")).to_string(),
+                true,
+            ),
+        };
+        Import {
+            source: import.source.path.whole_name(),
+            name,
+            id: import.source.did.map(ItemId::from).map(|i| from_item_id(i, tcx)),
+            glob,
         }
     }
 }
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 533e2ce46dd..9914edf3036 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -17,6 +17,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass {
 /// Strip items marked `#[doc(hidden)]`
 pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     let mut retained = ItemIdSet::default();
+    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
 
     // strip all #[doc(hidden)] items
     let krate = {
@@ -25,7 +26,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
     };
 
     // strip all impls referencing stripped items
-    let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
+    let mut stripper = ImplStripper {
+        retained: &retained,
+        cache: &cx.cache,
+        is_json_output,
+        document_private: cx.render_options.document_private,
+    };
     stripper.fold_crate(krate)
 }
 
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index 9ba841a31cf..f3aa3c7ce24 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -17,6 +17,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass {
 pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     // This stripper collects all *retained* nodes.
     let mut retained = ItemIdSet::default();
+    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
 
     // strip all private items
     {
@@ -24,12 +25,17 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
             retained: &mut retained,
             access_levels: &cx.cache.access_levels,
             update_retained: true,
-            is_json_output: cx.output_format.is_json() && !cx.show_coverage,
+            is_json_output,
         };
         krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
     }
 
     // strip all impls referencing private items
-    let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
+    let mut stripper = ImplStripper {
+        retained: &retained,
+        cache: &cx.cache,
+        is_json_output,
+        document_private: cx.render_options.document_private,
+    };
     stripper.fold_crate(krate)
 }
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 0d419042a10..3f069e8393f 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -14,17 +14,19 @@ pub(crate) struct Stripper<'a> {
     pub(crate) is_json_output: bool,
 }
 
-impl<'a> Stripper<'a> {
-    // We need to handle this differently for the JSON output because some non exported items could
-    // be used in public API. And so, we need these items as well. `is_exported` only checks if they
-    // are in the public API, which is not enough.
-    #[inline]
-    fn is_item_reachable(&self, item_id: ItemId) -> bool {
-        if self.is_json_output {
-            self.access_levels.is_reachable(item_id.expect_def_id())
-        } else {
-            self.access_levels.is_exported(item_id.expect_def_id())
-        }
+// We need to handle this differently for the JSON output because some non exported items could
+// be used in public API. And so, we need these items as well. `is_exported` only checks if they
+// are in the public API, which is not enough.
+#[inline]
+fn is_item_reachable(
+    is_json_output: bool,
+    access_levels: &AccessLevels<DefId>,
+    item_id: ItemId,
+) -> bool {
+    if is_json_output {
+        access_levels.is_reachable(item_id.expect_def_id())
+    } else {
+        access_levels.is_exported(item_id.expect_def_id())
     }
 }
 
@@ -61,7 +63,9 @@ impl<'a> DocFolder for Stripper<'a> {
             | clean::MacroItem(..)
             | clean::ForeignTypeItem => {
                 let item_id = i.item_id;
-                if item_id.is_local() && !self.is_item_reachable(item_id) {
+                if item_id.is_local()
+                    && !is_item_reachable(self.is_json_output, self.access_levels, item_id)
+                {
                     debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
                     return None;
                 }
@@ -133,6 +137,8 @@ impl<'a> DocFolder for Stripper<'a> {
 pub(crate) struct ImplStripper<'a> {
     pub(crate) retained: &'a ItemIdSet,
     pub(crate) cache: &'a Cache,
+    pub(crate) is_json_output: bool,
+    pub(crate) document_private: bool,
 }
 
 impl<'a> DocFolder for ImplStripper<'a> {
@@ -140,8 +146,27 @@ impl<'a> DocFolder for ImplStripper<'a> {
         if let clean::ImplItem(ref imp) = *i.kind {
             // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
             // documentation.
-            if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
-                return None;
+            //
+            // There is one special case: if the impl block contains only private items.
+            if imp.trait_.is_none() {
+                // If the only items present are private ones and we're not rendering private items,
+                // we don't document it.
+                if !imp.items.is_empty()
+                    && !self.document_private
+                    && imp.items.iter().all(|i| {
+                        let item_id = i.item_id;
+                        item_id.is_local()
+                            && !is_item_reachable(
+                                self.is_json_output,
+                                &self.cache.access_levels,
+                                item_id,
+                            )
+                    })
+                {
+                    return None;
+                } else if imp.items.is_empty() && i.doc_value().is_none() {
+                    return None;
+                }
             }
             if let Some(did) = imp.for_.def_id(self.cache) {
                 if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index bd4ea98441d..ecdecadd2ef 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -591,8 +591,11 @@ pub struct Import {
     /// May be different from the last segment of `source` when renaming imports:
     /// `use source as name;`
     pub name: String,
-    /// The ID of the item being imported.
-    pub id: Option<Id>, // FIXME is this actually ever None?
+    /// The ID of the item being imported. Will be `None` in case of re-exports of primitives:
+    /// ```rust
+    /// pub use i32 as my_i32;
+    /// ```
+    pub id: Option<Id>,
     /// Whether this import uses a glob: `use source::*;`
     pub glob: bool,
 }
diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md
index a0550466cf0..0721d9f7019 100644
--- a/src/test/mir-opt/README.md
+++ b/src/test/mir-opt/README.md
@@ -14,6 +14,18 @@ presence of pointers in constants or other bit width dependent things. In that c
 
 to your test, causing separate files to be generated for 32bit and 64bit systems.
 
+## Unit testing
+
+If you are only testing the behavior of a particular mir-opt pass on some specific input (as is
+usually the case), you should add
+
+```
+// unit-test: PassName
+```
+
+to the top of the file. This makes sure that other passes don't run which means you'll get the input
+you expected and your test won't break when other code changes.
+
 ## Emit a diff of the mir for a specific optimization
 
 This is what you want most often when you want to see how an optimization changes the MIR.
diff --git a/src/test/rustdoc-gui/sidebar-mobile-scroll.goml b/src/test/rustdoc-gui/sidebar-mobile-scroll.goml
new file mode 100644
index 00000000000..b3bcea25338
--- /dev/null
+++ b/src/test/rustdoc-gui/sidebar-mobile-scroll.goml
@@ -0,0 +1,31 @@
+// This test ensures that the mobile sidebar preserves scroll position.
+goto: file://|DOC_PATH|/test_docs/struct.Foo.html
+// Switching to "mobile view" by reducing the width to 600px.
+size: (600, 600)
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
+// Scroll down.
+scroll-to: "//h2[@id='blanket-implementations']"
+assert-window-property: {"pageYOffset": "702"}
+
+// Open the sidebar menu.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+
+// We are no longer "scrolled". It's important that the user can't
+// scroll the body at all, but these test scripts are run only in Chrome,
+// and we need to use a more complicated solution to this problem because
+// of Mobile Safari...
+assert-window-property: {"pageYOffset": "0"}
+
+// Close the sidebar menu. Make sure the scroll position gets restored.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "-1000px"})
+assert-window-property: {"pageYOffset": "702"}
+
+// Now test that scrollability returns when the browser window is just resized.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+assert-window-property: {"pageYOffset": "0"}
+size: (900, 900)
+assert-window-property: {"pageYOffset": "702"}
diff --git a/src/test/rustdoc-gui/sidebar-source-code-display.goml b/src/test/rustdoc-gui/sidebar-source-code-display.goml
index fa322574fde..e4662a10ed5 100644
--- a/src/test/rustdoc-gui/sidebar-source-code-display.goml
+++ b/src/test/rustdoc-gui/sidebar-source-code-display.goml
@@ -233,6 +233,17 @@ wait-for-css: (".sidebar", {"width": "0px"})
 // The "scrollTop" property should be the same.
 assert-window-property: {"pageYOffset": "2519"}
 
+// We now check that the scroll position is restored if the window is resized.
+size: (500, 700)
+click: "#sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "visible"})
+assert-window-property: {"pageYOffset": "0"}
+size: (900, 900)
+assert-window-property: {"pageYOffset": "2519"}
+size: (500, 700)
+click: "#sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
+
 // We now check that opening the sidebar and clicking a link will close it.
 // The behavior here on mobile is different than the behavior on desktop,
 // but common sense dictates that if you have a list of files that fills the entire screen, and
diff --git a/src/test/rustdoc-json/primitive.rs b/src/test/rustdoc-json/primitive.rs
index b84c2f7c6ac..878a1a4a79c 100644
--- a/src/test/rustdoc-json/primitive.rs
+++ b/src/test/rustdoc-json/primitive.rs
@@ -12,3 +12,9 @@ mod usize {}
 // @has - "$.index[*][?(@.name=='checked_add')]"
 // @!is - "$.index[*][?(@.name=='checked_add')]" $local_crate_id
 // @!has - "$.index[*][?(@.name=='is_ascii_uppercase')]"
+
+// @is - "$.index[*][?(@.kind=='import' && @.inner.name=='my_i32')].inner.id" null
+pub use i32 as my_i32;
+
+// @is - "$.index[*][?(@.kind=='import' && @.inner.name=='u32')].inner.id" null
+pub use u32;
diff --git a/src/test/rustdoc/empty-impl-block-private-with-doc.rs b/src/test/rustdoc/empty-impl-block-private-with-doc.rs
new file mode 100644
index 00000000000..43971996163
--- /dev/null
+++ b/src/test/rustdoc/empty-impl-block-private-with-doc.rs
@@ -0,0 +1,44 @@
+// compile-flags: --document-private-items
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+#![crate_name = "foo"]
+
+// @has 'foo/struct.Foo.html'
+pub struct Foo;
+
+// There are 3 impl blocks with public item and one that should not be displayed
+// by default because it only contains private items (but not in this case because
+// we used `--document-private-items`).
+// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 4
+
+// Impl block only containing private items should not be displayed unless the
+// `--document-private-items` flag is used.
+/// Private
+impl Foo {
+    const BAR: u32 = 0;
+    type FOO = i32;
+    fn hello() {}
+}
+
+// But if any element of the impl block is public, it should be displayed.
+/// Not private
+impl Foo {
+    pub const BAR: u32 = 0;
+    type FOO = i32;
+    fn hello() {}
+}
+
+/// Not private
+impl Foo {
+    const BAR: u32 = 0;
+    pub type FOO = i32;
+    fn hello() {}
+}
+
+/// Not private
+impl Foo {
+    const BAR: u32 = 0;
+    type FOO = i32;
+    pub fn hello() {}
+}
diff --git a/src/test/rustdoc/empty-impl-block-private.rs b/src/test/rustdoc/empty-impl-block-private.rs
new file mode 100644
index 00000000000..5caf020658c
--- /dev/null
+++ b/src/test/rustdoc/empty-impl-block-private.rs
@@ -0,0 +1,40 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+#![crate_name = "foo"]
+
+// @has 'foo/struct.Foo.html'
+pub struct Foo;
+
+// There are 3 impl blocks with public item and one that should not be displayed
+// because it only contains private items.
+// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 3
+
+// Impl block only containing private items should not be displayed.
+/// Private
+impl Foo {
+    const BAR: u32 = 0;
+    type FOO = i32;
+    fn hello() {}
+}
+
+// But if any element of the impl block is public, it should be displayed.
+/// Not private
+impl Foo {
+    pub const BAR: u32 = 0;
+    type FOO = i32;
+    fn hello() {}
+}
+
+/// Not private
+impl Foo {
+    const BAR: u32 = 0;
+    pub type FOO = i32;
+    fn hello() {}
+}
+
+/// Not private
+impl Foo {
+    const BAR: u32 = 0;
+    type FOO = i32;
+    pub fn hello() {}
+}
diff --git a/src/test/ui/consts/const_in_pattern/incomplete-slice.rs b/src/test/ui/consts/const_in_pattern/incomplete-slice.rs
new file mode 100644
index 00000000000..e1ccda71d40
--- /dev/null
+++ b/src/test/ui/consts/const_in_pattern/incomplete-slice.rs
@@ -0,0 +1,15 @@
+#[derive(PartialEq)]
+enum E {
+    A,
+}
+
+const E_SL: &[E] = &[E::A];
+
+fn main() {
+    match &[][..] {
+        //~^ ERROR non-exhaustive patterns: `&_` not covered [E0004]
+        E_SL => {}
+        //~^ WARN to use a constant of type `E` in a pattern, `E` must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARN this was previously accepted by the compiler but is being phased out
+    }
+}
diff --git a/src/test/ui/consts/const_in_pattern/incomplete-slice.stderr b/src/test/ui/consts/const_in_pattern/incomplete-slice.stderr
new file mode 100644
index 00000000000..0ff70837138
--- /dev/null
+++ b/src/test/ui/consts/const_in_pattern/incomplete-slice.stderr
@@ -0,0 +1,26 @@
+warning: to use a constant of type `E` in a pattern, `E` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/incomplete-slice.rs:11:9
+   |
+LL |         E_SL => {}
+   |         ^^^^
+   |
+   = note: `#[warn(indirect_structural_match)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
+
+error[E0004]: non-exhaustive patterns: `&_` not covered
+  --> $DIR/incomplete-slice.rs:9:11
+   |
+LL |     match &[][..] {
+   |           ^^^^^^^ pattern `&_` not covered
+   |
+   = note: the matched value is of type `&[E]`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL ~         E_SL => {}
+LL +         &_ => todo!()
+   |
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/src/test/ui/copy-a-resource.stderr b/src/test/ui/copy-a-resource.stderr
index 128087f1e37..b92449c6e0a 100644
--- a/src/test/ui/copy-a-resource.stderr
+++ b/src/test/ui/copy-a-resource.stderr
@@ -10,6 +10,10 @@ LL |     let _y = x.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _y = x.i.clone();
+   |                ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/hrtb/issue-30786.stderr b/src/test/ui/hrtb/issue-30786.stderr
index bc7b5e914e1..ffe3d7b81f5 100644
--- a/src/test/ui/hrtb/issue-30786.stderr
+++ b/src/test/ui/hrtb/issue-30786.stderr
@@ -18,10 +18,6 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
-help: one of the expressions' fields has a method of the same name
-   |
-LL |     let filter = map.stream.filterx(|x: &_| true);
-   |                      +++++++
 
 error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>`, but its trait bounds were not satisfied
   --> $DIR/issue-30786.rs:130:24
@@ -43,10 +39,6 @@ note: the following trait bounds were not satisfied:
    |
 LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
    |         ---------     -                          ^^^^^^ unsatisfied trait bound introduced here
-help: one of the expressions' fields has a method of the same name
-   |
-LL |     let count = filter.stream.countx();
-   |                        +++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-2823.stderr b/src/test/ui/issues/issue-2823.stderr
index b5a2b2f55a6..208b340d064 100644
--- a/src/test/ui/issues/issue-2823.stderr
+++ b/src/test/ui/issues/issue-2823.stderr
@@ -10,6 +10,10 @@ LL |     let _d = c.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _d = c.x.clone();
+   |                ++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lint/unused_parens_multibyte_recovery.rs b/src/test/ui/lint/unused_parens_multibyte_recovery.rs
new file mode 100644
index 00000000000..8fcfae22a3d
--- /dev/null
+++ b/src/test/ui/lint/unused_parens_multibyte_recovery.rs
@@ -0,0 +1,11 @@
+// ignore-tidy-trailing-newlines
+//
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: format argument must be a string literal
+//
+// Verify that unused parens lint does not try to create a span
+// which points in the middle of a multibyte character.
+
+fn f(){(print!(á
\ No newline at end of file
diff --git a/src/test/ui/lint/unused_parens_multibyte_recovery.stderr b/src/test/ui/lint/unused_parens_multibyte_recovery.stderr
new file mode 100644
index 00000000000..a0302b17e25
--- /dev/null
+++ b/src/test/ui/lint/unused_parens_multibyte_recovery.stderr
@@ -0,0 +1,43 @@
+error: this file contains an unclosed delimiter
+  --> $DIR/unused_parens_multibyte_recovery.rs:11:17
+   |
+LL | fn f(){(print!(á
+   |       --      - ^
+   |       ||      |
+   |       ||      unclosed delimiter
+   |       |unclosed delimiter
+   |       unclosed delimiter
+
+error: this file contains an unclosed delimiter
+  --> $DIR/unused_parens_multibyte_recovery.rs:11:17
+   |
+LL | fn f(){(print!(á
+   |       --      - ^
+   |       ||      |
+   |       ||      unclosed delimiter
+   |       |unclosed delimiter
+   |       unclosed delimiter
+
+error: this file contains an unclosed delimiter
+  --> $DIR/unused_parens_multibyte_recovery.rs:11:17
+   |
+LL | fn f(){(print!(á
+   |       --      - ^
+   |       ||      |
+   |       ||      unclosed delimiter
+   |       |unclosed delimiter
+   |       unclosed delimiter
+
+error: format argument must be a string literal
+  --> $DIR/unused_parens_multibyte_recovery.rs:11:16
+   |
+LL | fn f(){(print!(á
+   |                ^
+   |
+help: you might be missing a string literal to format with
+   |
+LL | fn f(){(print!("{}", á
+   |                +++++
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/noncopyable-class.stderr b/src/test/ui/noncopyable-class.stderr
index 0c696163a26..15e22e946da 100644
--- a/src/test/ui/noncopyable-class.stderr
+++ b/src/test/ui/noncopyable-class.stderr
@@ -10,6 +10,14 @@ LL |     let _y = x.clone();
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `Clone`
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _y = x.i.clone();
+   |                ++
+help: one of the expressions' fields has a method of the same name
+   |
+LL |     let _y = x.j.x.clone();
+   |                ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/field-access-considering-privacy.rs b/src/test/ui/suggestions/field-access-considering-privacy.rs
new file mode 100644
index 00000000000..3de06b21420
--- /dev/null
+++ b/src/test/ui/suggestions/field-access-considering-privacy.rs
@@ -0,0 +1,35 @@
+use a::TyCtxt;
+
+mod a {
+    use std::ops::Deref;
+    pub struct TyCtxt<'tcx> {
+        gcx: &'tcx GlobalCtxt<'tcx>,
+    }
+
+    impl<'tcx> Deref for TyCtxt<'tcx> {
+        type Target = &'tcx GlobalCtxt<'tcx>;
+
+        fn deref(&self) -> &Self::Target {
+            &self.gcx
+        }
+    }
+
+    pub struct GlobalCtxt<'tcx> {
+        pub sess: &'tcx Session,
+        _t: &'tcx (),
+    }
+
+    pub struct Session {
+        pub opts: (),
+    }
+}
+
+mod b {
+    fn foo<'tcx>(tcx: crate::TyCtxt<'tcx>) {
+        tcx.opts;
+        //~^ ERROR no field `opts` on type `TyCtxt<'tcx>`
+        //~| HELP one of the expressions' fields has a field of the same name
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/field-access-considering-privacy.stderr b/src/test/ui/suggestions/field-access-considering-privacy.stderr
new file mode 100644
index 00000000000..cbf6f3d1002
--- /dev/null
+++ b/src/test/ui/suggestions/field-access-considering-privacy.stderr
@@ -0,0 +1,14 @@
+error[E0609]: no field `opts` on type `TyCtxt<'tcx>`
+  --> $DIR/field-access-considering-privacy.rs:29:13
+   |
+LL |         tcx.opts;
+   |             ^^^^ unknown field
+   |
+help: one of the expressions' fields has a field of the same name
+   |
+LL |         tcx.sess.opts;
+   |             +++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0609`.
diff --git a/src/test/ui/typeck/issue-100246.rs b/src/test/ui/typeck/issue-100246.rs
new file mode 100644
index 00000000000..8f0b34bab0c
--- /dev/null
+++ b/src/test/ui/typeck/issue-100246.rs
@@ -0,0 +1,30 @@
+#![recursion_limit = "5"] // To reduce noise
+
+//expect incompatible type error when ambiguous traits are in scope
+//and not an overflow error on the span in the main function.
+
+struct Ratio<T>(T);
+
+pub trait Pow {
+    fn pow(self) -> Self;
+}
+
+impl<'a, T> Pow for &'a Ratio<T>
+where
+    &'a T: Pow,
+{
+    fn pow(self) -> Self {
+        self
+    }
+}
+
+fn downcast<'a, W: ?Sized>() -> std::io::Result<&'a W> {
+    todo!()
+}
+
+struct Other;
+
+fn main() -> std::io::Result<()> {
+    let other: Other = downcast()?;//~ERROR 28:24: 28:35: `?` operator has incompatible types
+    Ok(())
+}
diff --git a/src/test/ui/typeck/issue-100246.stderr b/src/test/ui/typeck/issue-100246.stderr
new file mode 100644
index 00000000000..8b77de94e89
--- /dev/null
+++ b/src/test/ui/typeck/issue-100246.stderr
@@ -0,0 +1,13 @@
+error[E0308]: `?` operator has incompatible types
+  --> $DIR/issue-100246.rs:28:24
+   |
+LL |     let other: Other = downcast()?;
+   |                        ^^^^^^^^^^^ expected struct `Other`, found reference
+   |
+   = note: `?` operator cannot convert from `&_` to `Other`
+   = note: expected struct `Other`
+           found reference `&_`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.