about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-11-15 13:19:05 +0000
committerbors <bors@rust-lang.org>2020-11-15 13:19:05 +0000
commit5fab31e5ddf5f2613bf57a0a7286dc6f5887e1cb (patch)
treebf2540f955bf3e3adb49330b1fd06ee628d9fc35 /src
parent04688459242356c0f6b9fdad3ba76c9ec4dcc354 (diff)
parent568354f01f22148709e51fe1130826addb455e18 (diff)
downloadrust-5fab31e5ddf5f2613bf57a0a7286dc6f5887e1cb.tar.gz
rust-5fab31e5ddf5f2613bf57a0a7286dc6f5887e1cb.zip
Auto merge of #79070 - jonas-schievink:rollup-wacn2b8, r=jonas-schievink
Rollup of 13 pull requests

Successful merges:

 - #77802 (Allow making `RUSTC_BOOTSTRAP` conditional on the crate name)
 - #79004 (Add `--color` support to bootstrap)
 - #79005 (cleanup: Remove `ParseSess::injected_crate_name`)
 - #79016 (Make `_` an expression, to discard values in destructuring assignments)
 - #79019 (astconv: extract closures into a separate trait)
 - #79026 (Implement BTreeMap::retain and BTreeSet::retain)
 - #79031 (Validate that locals have a corresponding `LocalDecl`)
 - #79034 (rustc_resolve: Make `macro_rules` scope chain compression lazy)
 - #79036 (Move Steal to rustc_data_structures.)
 - #79041 (Rename clean::{ItemEnum -> ItemKind}, clean::Item::{inner -> kind})
 - #79058 (Move likely/unlikely argument outside of invisible unsafe block)
 - #79059 (Print 'checking cranelift artifacts' to easily separate it from other artifacts)
 - #79063 (Update rustfmt to v1.4.26)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/builder.rs12
-rw-r--r--src/bootstrap/check.rs5
-rw-r--r--src/bootstrap/config.rs4
-rw-r--r--src/bootstrap/flags.rs30
-rw-r--r--src/librustdoc/clean/auto_trait.rs2
-rw-r--r--src/librustdoc/clean/blanket_impl.rs2
-rw-r--r--src/librustdoc/clean/inline.rs10
-rw-r--r--src/librustdoc/clean/mod.rs62
-rw-r--r--src/librustdoc/clean/types.rs37
-rw-r--r--src/librustdoc/clean/utils.rs18
-rw-r--r--src/librustdoc/config.rs16
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/librustdoc/doctest.rs3
-rw-r--r--src/librustdoc/externalfiles.rs4
-rw-r--r--src/librustdoc/fold.rs16
-rw-r--r--src/librustdoc/formats/cache.rs18
-rw-r--r--src/librustdoc/formats/item_type.rs6
-rw-r--r--src/librustdoc/formats/mod.rs2
-rw-r--r--src/librustdoc/formats/renderer.rs2
-rw-r--r--src/librustdoc/html/render/cache.rs2
-rw-r--r--src/librustdoc/html/render/mod.rs61
-rw-r--r--src/librustdoc/markdown.rs5
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs8
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs8
-rw-r--r--src/librustdoc/passes/doc_test_lints.rs6
-rw-r--r--src/librustdoc/passes/html_tags.rs3
-rw-r--r--src/librustdoc/passes/non_autolinks.rs3
-rw-r--r--src/librustdoc/passes/strip_hidden.rs2
-rw-r--r--src/librustdoc/passes/stripper.rs8
-rw-r--r--src/test/ui/cross/cross-file-errors/main.rs3
-rw-r--r--src/test/ui/cross/cross-file-errors/main.stderr22
-rw-r--r--src/test/ui/destructuring-assignment/nested_destructure.rs3
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure.rs2
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure_fail.rs1
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure_fail.stderr8
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure.rs6
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure_fail.rs2
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure_fail.stderr32
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure.rs2
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure_fail.rs1
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr13
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure.rs4
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs4
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr28
-rw-r--r--src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs2
-rw-r--r--src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr13
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs18
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr89
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/sugg.rs1
m---------src/tools/rustfmt10
51 files changed, 420 insertions, 203 deletions
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 3d724c14842..508d785834f 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -19,7 +19,7 @@ use crate::compile;
 use crate::config::TargetSelection;
 use crate::dist;
 use crate::doc;
-use crate::flags::Subcommand;
+use crate::flags::{Color, Subcommand};
 use crate::install;
 use crate::native;
 use crate::run;
@@ -811,6 +811,16 @@ impl<'a> Builder<'a> {
             cargo.env("REAL_LIBRARY_PATH", e);
         }
 
+        match self.build.config.color {
+            Color::Always => {
+                cargo.arg("--color=always");
+            }
+            Color::Never => {
+                cargo.arg("--color=never");
+            }
+            Color::Auto => {} // nothing to do
+        }
+
         if cmd != "install" {
             cargo.arg("--target").arg(target.rustc_target_arg());
         } else {
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index ecca12108b6..f65b2b2c79f 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -232,6 +232,11 @@ impl Step for CodegenBackend {
             .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
         rustc_cargo_env(builder, &mut cargo, target);
 
+        builder.info(&format!(
+            "Checking {} artifacts ({} -> {})",
+            backend, &compiler.host.triple, target.triple
+        ));
+
         run_cargo(
             builder,
             cargo,
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index c0753d88504..94319a6d1e9 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -13,8 +13,8 @@ use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
 use crate::cache::{Interned, INTERNER};
-use crate::flags::Flags;
 pub use crate::flags::Subcommand;
+use crate::flags::{Color, Flags};
 use crate::util::exe;
 use build_helper::t;
 use merge::Merge;
@@ -67,6 +67,7 @@ pub struct Config {
     pub json_output: bool,
     pub test_compare_mode: bool,
     pub llvm_libunwind: Option<LlvmLibunwind>,
+    pub color: Color,
 
     pub on_fail: Option<String>,
     pub stage: u32,
@@ -577,6 +578,7 @@ impl Config {
         config.keep_stage = flags.keep_stage;
         config.keep_stage_std = flags.keep_stage_std;
         config.bindir = "bin".into(); // default
+        config.color = flags.color;
         if let Some(value) = flags.deny_warnings {
             config.deny_warnings = value;
         }
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index dbfcf4df9b4..5a8096674c6 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -15,6 +15,31 @@ use crate::config::{Config, TargetSelection};
 use crate::setup::Profile;
 use crate::{Build, DocTests};
 
+pub enum Color {
+    Always,
+    Never,
+    Auto,
+}
+
+impl Default for Color {
+    fn default() -> Self {
+        Self::Auto
+    }
+}
+
+impl std::str::FromStr for Color {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.to_lowercase().as_str() {
+            "always" => Ok(Self::Always),
+            "never" => Ok(Self::Never),
+            "auto" => Ok(Self::Auto),
+            _ => Err(()),
+        }
+    }
+}
+
 /// Deserialized version of all flags for this compile.
 pub struct Flags {
     pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
@@ -34,6 +59,7 @@ pub struct Flags {
     pub rustc_error_format: Option<String>,
     pub json_output: bool,
     pub dry_run: bool,
+    pub color: Color,
 
     // This overrides the deny-warnings configuration option,
     // which passes -Dwarnings to the compiler invocations.
@@ -184,6 +210,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         );
         opts.optopt("", "error-format", "rustc error format", "FORMAT");
         opts.optflag("", "json-output", "use message-format=json");
+        opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE");
         opts.optopt(
             "",
             "llvm-skip-rebuild",
@@ -644,6 +671,9 @@ Arguments:
             llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
                 |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
             ),
+            color: matches
+                .opt_get_default("color", Color::Auto)
+                .expect("`color` should be `always`, `never`, or `auto`"),
         }
     }
 }
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index f39b53f3c82..a07d6b73f06 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -125,7 +125,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                     def_id: self.cx.next_def_id(param_env_def_id.krate),
                     stability: None,
                     deprecation: None,
-                    inner: ImplItem(Impl {
+                    kind: ImplItem(Impl {
                         unsafety: hir::Unsafety::Normal,
                         generics: new_generics,
                         provided_trait_methods: Default::default(),
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 7030fd9b7f2..f15142f4983 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -114,7 +114,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     def_id: self.cx.next_def_id(impl_def_id.krate),
                     stability: None,
                     deprecation: None,
-                    inner: ImplItem(Impl {
+                    kind: ImplItem(Impl {
                         unsafety: hir::Unsafety::Normal,
                         generics: (
                             self.cx.tcx.generics_of(impl_def_id),
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index b3de70e5905..d6f8870c859 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -54,7 +54,7 @@ crate fn try_inline(
     debug!("attrs={:?}", attrs);
     let attrs_clone = attrs;
 
-    let inner = match res {
+    let kind = match res {
         Res::Def(DefKind::Trait, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Trait);
             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
@@ -128,7 +128,7 @@ crate fn try_inline(
         source: cx.tcx.def_span(did).clean(cx),
         name: Some(name.clean(cx)),
         attrs,
-        inner,
+        kind,
         visibility: clean::Public,
         stability: cx.tcx.lookup_stability(did).cloned(),
         deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
@@ -446,7 +446,7 @@ crate fn build_impl(
     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
 
     ret.push(clean::Item {
-        inner: clean::ImplItem(clean::Impl {
+        kind: clean::ImplItem(clean::Impl {
             unsafety: hir::Unsafety::Normal,
             generics,
             provided_trait_methods: provided,
@@ -498,7 +498,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>)
                         visibility: clean::Public,
                         stability: None,
                         deprecation: None,
-                        inner: clean::ImportItem(clean::Import::new_simple(
+                        kind: clean::ImportItem(clean::Import::new_simple(
                             item.ident.to_string(),
                             clean::ImportSource {
                                 path: clean::Path {
@@ -555,7 +555,7 @@ fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static
     }
 }
 
-fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemEnum {
+fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind {
     let imported_from = cx.tcx.original_crate_name(did.krate);
     match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
         LoadedMacro::MacroDef(def, _) => {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 366548d5b5f..56ce0bae8bb 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -42,7 +42,7 @@ use utils::*;
 pub use utils::{get_auto_trait_and_blanket_impls, krate, register_res};
 
 pub use self::types::FnRetTy::*;
-pub use self::types::ItemEnum::*;
+pub use self::types::ItemKind::*;
 pub use self::types::SelfTy::*;
 pub use self::types::Type::*;
 pub use self::types::Visibility::{Inherited, Public};
@@ -276,7 +276,7 @@ impl Clean<Item> for doctree::Module<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
-            inner: ModuleItem(Module { is_crate: self.is_crate, items }),
+            kind: ModuleItem(Module { is_crate: self.is_crate, items }),
         }
     }
 }
@@ -916,7 +916,7 @@ impl Clean<Item> for doctree::Function<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: did.to_def_id(),
-            inner: FunctionItem(Function {
+            kind: FunctionItem(Function {
                 decl,
                 generics,
                 header: hir::FnHeader { constness, ..self.header },
@@ -1023,7 +1023,7 @@ impl Clean<Item> for doctree::Trait<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: TraitItem(Trait {
+            kind: TraitItem(Trait {
                 auto: self.is_auto.clean(cx),
                 unsafety: self.unsafety,
                 items: self.items.iter().map(|ti| ti.clean(cx)).collect(),
@@ -1047,7 +1047,7 @@ impl Clean<Item> for doctree::TraitAlias<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: TraitAliasItem(TraitAlias {
+            kind: TraitAliasItem(TraitAlias {
                 generics: self.generics.clean(cx),
                 bounds: self.bounds.clean(cx),
             }),
@@ -1102,7 +1102,7 @@ impl Clean<TypeKind> for hir::def::DefKind {
 impl Clean<Item> for hir::TraitItem<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
         let local_did = cx.tcx.hir().local_def_id(self.hir_id);
-        let inner = match self.kind {
+        let kind = match self.kind {
             hir::TraitItemKind::Const(ref ty, default) => {
                 AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e)))
             }
@@ -1140,7 +1140,7 @@ impl Clean<Item> for hir::TraitItem<'_> {
             visibility: Visibility::Inherited,
             stability: get_stability(cx, local_did.to_def_id()),
             deprecation: get_deprecation(cx, local_did.to_def_id()),
-            inner,
+            kind,
         }
     }
 }
@@ -1148,7 +1148,7 @@ impl Clean<Item> for hir::TraitItem<'_> {
 impl Clean<Item> for hir::ImplItem<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
         let local_did = cx.tcx.hir().local_def_id(self.hir_id);
-        let inner = match self.kind {
+        let kind = match self.kind {
             hir::ImplItemKind::Const(ref ty, expr) => {
                 AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr)))
             }
@@ -1175,14 +1175,14 @@ impl Clean<Item> for hir::ImplItem<'_> {
             visibility: self.vis.clean(cx),
             stability: get_stability(cx, local_did.to_def_id()),
             deprecation: get_deprecation(cx, local_did.to_def_id()),
-            inner,
+            kind,
         }
     }
 }
 
 impl Clean<Item> for ty::AssocItem {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
-        let inner = match self.kind {
+        let kind = match self.kind {
             ty::AssocKind::Const => {
                 let ty = cx.tcx.type_of(self.def_id);
                 let default = if self.defaultness.has_value() {
@@ -1343,7 +1343,7 @@ impl Clean<Item> for ty::AssocItem {
             def_id: self.def_id,
             attrs: inline::load_attrs(cx, self.def_id).clean(cx),
             source: cx.tcx.def_span(self.def_id).clean(cx),
-            inner,
+            kind,
         }
     }
 }
@@ -1784,7 +1784,7 @@ impl Clean<Item> for hir::StructField<'_> {
             stability: get_stability(cx, local_did.to_def_id()),
             deprecation: get_deprecation(cx, local_did.to_def_id()),
             def_id: local_did.to_def_id(),
-            inner: StructFieldItem(self.ty.clean(cx)),
+            kind: StructFieldItem(self.ty.clean(cx)),
         }
     }
 }
@@ -1799,7 +1799,7 @@ impl Clean<Item> for ty::FieldDef {
             stability: get_stability(cx, self.did),
             deprecation: get_deprecation(cx, self.did),
             def_id: self.did,
-            inner: StructFieldItem(cx.tcx.type_of(self.did).clean(cx)),
+            kind: StructFieldItem(cx.tcx.type_of(self.did).clean(cx)),
         }
     }
 }
@@ -1835,7 +1835,7 @@ impl Clean<Item> for doctree::Struct<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: StructItem(Struct {
+            kind: StructItem(Struct {
                 struct_type: self.struct_type,
                 generics: self.generics.clean(cx),
                 fields: self.fields.clean(cx),
@@ -1855,7 +1855,7 @@ impl Clean<Item> for doctree::Union<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: UnionItem(Union {
+            kind: UnionItem(Union {
                 struct_type: self.struct_type,
                 generics: self.generics.clean(cx),
                 fields: self.fields.clean(cx),
@@ -1885,7 +1885,7 @@ impl Clean<Item> for doctree::Enum<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: EnumItem(Enum {
+            kind: EnumItem(Enum {
                 variants: self.variants.iter().map(|v| v.clean(cx)).collect(),
                 generics: self.generics.clean(cx),
                 variants_stripped: false,
@@ -1904,7 +1904,7 @@ impl Clean<Item> for doctree::Variant<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
-            inner: VariantItem(Variant { kind: self.def.clean(cx) }),
+            kind: VariantItem(Variant { kind: self.def.clean(cx) }),
         }
     }
 }
@@ -1930,7 +1930,7 @@ impl Clean<Item> for ty::VariantDef {
                         def_id: field.did,
                         stability: get_stability(cx, field.did),
                         deprecation: get_deprecation(cx, field.did),
-                        inner: StructFieldItem(cx.tcx.type_of(field.did).clean(cx)),
+                        kind: StructFieldItem(cx.tcx.type_of(field.did).clean(cx)),
                     })
                     .collect(),
             }),
@@ -1941,7 +1941,7 @@ impl Clean<Item> for ty::VariantDef {
             source: cx.tcx.def_span(self.def_id).clean(cx),
             visibility: Inherited,
             def_id: self.def_id,
-            inner: VariantItem(Variant { kind }),
+            kind: VariantItem(Variant { kind }),
             stability: get_stability(cx, self.def_id),
             deprecation: get_deprecation(cx, self.def_id),
         }
@@ -2057,7 +2057,7 @@ impl Clean<Item> for doctree::Typedef<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
+            kind: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
         }
     }
 }
@@ -2072,7 +2072,7 @@ impl Clean<Item> for doctree::OpaqueTy<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: OpaqueTyItem(OpaqueTy {
+            kind: OpaqueTyItem(OpaqueTy {
                 bounds: self.opaque_ty.bounds.clean(cx),
                 generics: self.opaque_ty.generics.clean(cx),
             }),
@@ -2100,7 +2100,7 @@ impl Clean<Item> for doctree::Static<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: StaticItem(Static {
+            kind: StaticItem(Static {
                 type_: self.type_.clean(cx),
                 mutability: self.mutability,
                 expr: print_const_expr(cx, self.expr),
@@ -2121,7 +2121,7 @@ impl Clean<Item> for doctree::Constant<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: ConstantItem(Constant {
+            kind: ConstantItem(Constant {
                 type_: self.type_.clean(cx),
                 expr: print_const_expr(cx, self.expr),
                 value: print_evaluated_const(cx, def_id.to_def_id()),
@@ -2175,7 +2175,7 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: ImplItem(Impl {
+            kind: ImplItem(Impl {
                 unsafety: self.unsafety,
                 generics: self.generics.clean(cx),
                 provided_trait_methods: provided.clone(),
@@ -2231,7 +2231,7 @@ impl Clean<Vec<Item>> for doctree::ExternCrate<'_> {
             visibility: self.vis.clean(cx),
             stability: None,
             deprecation: None,
-            inner: ExternCrateItem(self.name.clean(cx), self.path.clone()),
+            kind: ExternCrateItem(self.name.clean(cx), self.path.clone()),
         }]
     }
 }
@@ -2302,7 +2302,7 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
                         visibility: self.vis.clean(cx),
                         stability: None,
                         deprecation: None,
-                        inner: ImportItem(Import::new_simple(
+                        kind: ImportItem(Import::new_simple(
                             self.name.clean(cx),
                             resolve_use_source(cx, path),
                             false,
@@ -2322,14 +2322,14 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
             visibility: self.vis.clean(cx),
             stability: None,
             deprecation: None,
-            inner: ImportItem(inner),
+            kind: ImportItem(inner),
         }]
     }
 }
 
 impl Clean<Item> for doctree::ForeignItem<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
-        let inner = match self.kind {
+        let kind = match self.kind {
             hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => {
                 let abi = cx.tcx.hir().get_foreign_abi(self.id);
                 let (generics, decl) =
@@ -2364,7 +2364,7 @@ impl Clean<Item> for doctree::ForeignItem<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner,
+            kind,
         }
     }
 }
@@ -2380,7 +2380,7 @@ impl Clean<Item> for doctree::Macro<'_> {
             stability: cx.stability(self.hid),
             deprecation: cx.deprecation(self.hid).clean(cx),
             def_id: self.def_id,
-            inner: MacroItem(Macro {
+            kind: MacroItem(Macro {
                 source: format!(
                     "macro_rules! {} {{\n{}}}",
                     name,
@@ -2405,7 +2405,7 @@ impl Clean<Item> for doctree::ProcMacro<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
-            inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }),
+            kind: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }),
         }
     }
 }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 32b3f69ecd4..3060cf79cd5 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -41,7 +41,7 @@ use crate::formats::item_type::ItemType;
 use crate::html::render::cache::ExternalLocation;
 
 use self::FnRetTy::*;
-use self::ItemEnum::*;
+use self::ItemKind::*;
 use self::SelfTy::*;
 use self::Type::*;
 
@@ -81,7 +81,7 @@ pub struct Item {
     /// Not everything has a name. E.g., impls
     pub name: Option<String>,
     pub attrs: Attributes,
-    pub inner: ItemEnum,
+    pub kind: ItemKind,
     pub visibility: Visibility,
     pub def_id: DefId,
     pub stability: Option<Stability>,
@@ -90,14 +90,13 @@ pub struct Item {
 
 impl fmt::Debug for Item {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let fake = self.is_fake();
-        let def_id: &dyn fmt::Debug = if fake { &"**FAKE**" } else { &self.def_id };
+        let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id };
 
         fmt.debug_struct("Item")
             .field("source", &self.source)
             .field("name", &self.name)
             .field("attrs", &self.attrs)
-            .field("inner", &self.inner)
+            .field("kind", &self.kind)
             .field("visibility", &self.visibility)
             .field("def_id", def_id)
             .field("stability", &self.stability)
@@ -124,7 +123,7 @@ impl Item {
     }
 
     pub fn is_crate(&self) -> bool {
-        match self.inner {
+        match self.kind {
             StrippedItem(box ModuleItem(Module { is_crate: true, .. }))
             | ModuleItem(Module { is_crate: true, .. }) => true,
             _ => false,
@@ -176,14 +175,14 @@ impl Item {
         self.type_() == ItemType::Keyword
     }
     pub fn is_stripped(&self) -> bool {
-        match self.inner {
+        match self.kind {
             StrippedItem(..) => true,
             ImportItem(ref i) => !i.should_be_displayed,
             _ => false,
         }
     }
     pub fn has_stripped_fields(&self) -> Option<bool> {
-        match self.inner {
+        match self.kind {
             StructItem(ref _struct) => Some(_struct.fields_stripped),
             UnionItem(ref union) => Some(union.fields_stripped),
             VariantItem(Variant { kind: VariantKind::Struct(ref vstruct) }) => {
@@ -227,8 +226,8 @@ impl Item {
     }
 
     pub fn is_default(&self) -> bool {
-        match self.inner {
-            ItemEnum::MethodItem(ref meth) => {
+        match self.kind {
+            ItemKind::MethodItem(ref meth) => {
                 if let Some(defaultness) = meth.defaultness {
                     defaultness.has_value() && !defaultness.is_final()
                 } else {
@@ -248,7 +247,7 @@ impl Item {
 }
 
 #[derive(Clone, Debug)]
-pub enum ItemEnum {
+pub enum ItemKind {
     ExternCrateItem(String, Option<String>),
     ImportItem(Import),
     StructItem(Struct),
@@ -282,23 +281,23 @@ pub enum ItemEnum {
     AssocConstItem(Type, Option<String>),
     AssocTypeItem(Vec<GenericBound>, Option<Type>),
     /// An item that has been stripped by a rustdoc pass
-    StrippedItem(Box<ItemEnum>),
+    StrippedItem(Box<ItemKind>),
     KeywordItem(String),
 }
 
-impl ItemEnum {
+impl ItemKind {
     pub fn is_type_alias(&self) -> bool {
         match *self {
-            ItemEnum::TypedefItem(_, _) | ItemEnum::AssocTypeItem(_, _) => true,
+            ItemKind::TypedefItem(_, _) | ItemKind::AssocTypeItem(_, _) => true,
             _ => false,
         }
     }
 
     pub fn as_assoc_kind(&self) -> Option<AssocKind> {
         match *self {
-            ItemEnum::AssocConstItem(..) => Some(AssocKind::Const),
-            ItemEnum::AssocTypeItem(..) => Some(AssocKind::Type),
-            ItemEnum::TyMethodItem(..) | ItemEnum::MethodItem(..) => Some(AssocKind::Fn),
+            ItemKind::AssocConstItem(..) => Some(AssocKind::Const),
+            ItemKind::AssocTypeItem(..) => Some(AssocKind::Type),
+            ItemKind::TyMethodItem(..) | ItemKind::MethodItem(..) => Some(AssocKind::Fn),
             _ => None,
         }
     }
@@ -681,7 +680,9 @@ impl Attributes {
                                 }
                                 Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
                                 Some(&(_, _, ExternalLocation::Unknown)) | None => String::from(
-                                    if UnstableFeatures::from_environment().is_nightly_build() {
+                                    // NOTE: intentionally doesn't pass crate name to avoid having
+                                    // different primitive links between crates
+                                    if UnstableFeatures::from_environment(None).is_nightly_build() {
                                         "https://doc.rust-lang.org/nightly"
                                     } else {
                                         "https://doc.rust-lang.org"
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index f6258221e32..e5fb656cbb9 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -2,7 +2,7 @@ use crate::clean::auto_trait::AutoTraitFinder;
 use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::{
     inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg,
-    GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime,
+    GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemKind, Lifetime,
     MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Type, TypeBinding,
     TypeKind, Visibility, WherePredicate,
 };
@@ -44,8 +44,8 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
     let mut module = module.clean(cx);
     let mut masked_crates = FxHashSet::default();
 
-    match module.inner {
-        ItemEnum::ModuleItem(ref module) => {
+    match module.kind {
+        ItemKind::ModuleItem(ref module) => {
             for it in &module.items {
                 // `compiler_builtins` should be masked too, but we can't apply
                 // `#[doc(masked)]` to the injected `extern crate` because it's unstable.
@@ -62,8 +62,8 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
 
     let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx);
     {
-        let m = match module.inner {
-            ItemEnum::ModuleItem(ref mut m) => m,
+        let m = match module.kind {
+            ItemKind::ModuleItem(ref mut m) => m,
             _ => unreachable!(),
         };
         m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| Item {
@@ -74,7 +74,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
             stability: get_stability(cx, def_id),
             deprecation: get_deprecation(cx, def_id),
             def_id,
-            inner: ItemEnum::PrimitiveItem(prim),
+            kind: ItemKind::PrimitiveItem(prim),
         }));
         m.items.extend(keywords.into_iter().map(|(def_id, kw, attrs)| Item {
             source: Span::empty(),
@@ -84,7 +84,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
             stability: get_stability(cx, def_id),
             deprecation: get_deprecation(cx, def_id),
             def_id,
-            inner: ItemEnum::KeywordItem(kw),
+            kind: ItemKind::KeywordItem(kw),
         }));
     }
 
@@ -355,8 +355,8 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V
     let tcx = cx.tcx;
 
     for item in items {
-        let target = match item.inner {
-            ItemEnum::TypedefItem(ref t, true) => &t.type_,
+        let target = match item.kind {
+            ItemKind::TypedefItem(ref t, true) => &t.type_,
             _ => continue,
         };
         let primitive = match *target {
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index c248d57a9dd..f0fc0dc6514 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -257,6 +257,7 @@ pub struct RenderOptions {
     pub document_private: bool,
     /// Document items that have `doc(hidden)`.
     pub document_hidden: bool,
+    pub unstable_features: rustc_feature::UnstableFeatures,
 }
 
 /// Temporary storage for data obtained during `RustdocVisitor::clean()`.
@@ -299,7 +300,7 @@ impl Options {
                 println_condition(p.condition);
             }
 
-            if nightly_options::is_nightly_build() {
+            if nightly_options::match_is_nightly_build(matches) {
                 println!("\nPasses run with `--show-coverage`:");
                 for p in passes::COVERAGE_PASSES {
                     print!("{:>20}", p.pass.name);
@@ -483,6 +484,7 @@ impl Options {
             &matches.opt_strs("html-after-content"),
             &matches.opt_strs("markdown-before-content"),
             &matches.opt_strs("markdown-after-content"),
+            nightly_options::match_is_nightly_build(&matches),
             &diag,
             &mut id_map,
             edition,
@@ -539,7 +541,9 @@ impl Options {
         let output_format = match matches.opt_str("output-format") {
             Some(s) => match OutputFormat::try_from(s.as_str()) {
                 Ok(o) => {
-                    if o.is_json() && !(show_coverage || nightly_options::is_nightly_build()) {
+                    if o.is_json()
+                        && !(show_coverage || nightly_options::match_is_nightly_build(matches))
+                    {
                         diag.struct_err("json output format isn't supported for doc generation")
                             .emit();
                         return Err(1);
@@ -591,7 +595,6 @@ impl Options {
 
         Ok(Options {
             input,
-            crate_name,
             proc_macro_crate,
             error_format,
             libs,
@@ -643,7 +646,11 @@ impl Options {
                 generate_search_filter,
                 document_private,
                 document_hidden,
+                unstable_features: rustc_feature::UnstableFeatures::from_environment(
+                    crate_name.as_deref(),
+                ),
             },
+            crate_name,
             output_format,
         })
     }
@@ -661,7 +668,8 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han
     for flag in deprecated_flags.iter() {
         if matches.opt_present(flag) {
             if *flag == "output-format"
-                && (matches.opt_present("show-coverage") || nightly_options::is_nightly_build())
+                && (matches.opt_present("show-coverage")
+                    || nightly_options::match_is_nightly_build(matches))
             {
                 continue;
             }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 4cad6418d6a..14a2def1383 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -371,7 +371,7 @@ pub fn run_core(
         cg: codegen_options,
         externs,
         target_triple: target,
-        unstable_features: UnstableFeatures::from_environment(),
+        unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
         actually_rustdoc: true,
         debugging_opts,
         error_format,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index eb33890fb5f..5e40e6b151d 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,7 +1,6 @@
 use rustc_ast as ast;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::ErrorReported;
-use rustc_feature::UnstableFeatures;
 use rustc_hir as hir;
 use rustc_hir::intravisit;
 use rustc_hir::{HirId, CRATE_HIR_ID};
@@ -70,7 +69,7 @@ pub fn run(options: Options) -> Result<(), ErrorReported> {
         lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)),
         cg: options.codegen_options.clone(),
         externs: options.externs.clone(),
-        unstable_features: UnstableFeatures::from_environment(),
+        unstable_features: options.render_options.unstable_features,
         actually_rustdoc: true,
         debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() },
         edition: options.edition,
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index c8121d39d0f..900821dbf4a 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -1,6 +1,5 @@
 use crate::html::markdown::{ErrorCodes, IdMap, Markdown, Playground};
 use crate::rustc_span::edition::Edition;
-use rustc_feature::UnstableFeatures;
 use std::fs;
 use std::path::Path;
 use std::str;
@@ -25,12 +24,13 @@ impl ExternalHtml {
         after_content: &[String],
         md_before_content: &[String],
         md_after_content: &[String],
+        nightly_build: bool,
         diag: &rustc_errors::Handler,
         id_map: &mut IdMap,
         edition: Edition,
         playground: &Option<Playground>,
     ) -> Option<ExternalHtml> {
-        let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+        let codes = ErrorCodes::from(nightly_build);
         let ih = load_external_files(in_header, diag)?;
         let bc = load_external_files(before_content, diag)?;
         let m_bc = load_external_files(md_before_content, diag)?;
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index d4ada3278e6..694051aa54f 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -5,9 +5,9 @@ pub struct StripItem(pub Item);
 impl StripItem {
     pub fn strip(self) -> Option<Item> {
         match self.0 {
-            Item { inner: StrippedItem(..), .. } => Some(self.0),
+            Item { kind: StrippedItem(..), .. } => Some(self.0),
             mut i => {
-                i.inner = StrippedItem(box i.inner);
+                i.kind = StrippedItem(box i.kind);
                 Some(i)
             }
         }
@@ -20,8 +20,8 @@ pub trait DocFolder: Sized {
     }
 
     /// don't override!
-    fn fold_inner_recur(&mut self, inner: ItemEnum) -> ItemEnum {
-        match inner {
+    fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind {
+        match kind {
             StrippedItem(..) => unreachable!(),
             ModuleItem(i) => ModuleItem(self.fold_mod(i)),
             StructItem(mut i) => {
@@ -72,14 +72,14 @@ pub trait DocFolder: Sized {
 
     /// don't override!
     fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
-        let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item;
+        let Item { attrs, name, source, visibility, def_id, kind, stability, deprecation } = item;
 
-        let inner = match inner {
+        let kind = match kind {
             StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)),
-            _ => self.fold_inner_recur(inner),
+            _ => self.fold_inner_recur(kind),
         };
 
-        Some(Item { attrs, name, source, inner, visibility, stability, deprecation, def_id })
+        Some(Item { attrs, name, source, kind, visibility, stability, deprecation, def_id })
     }
 
     fn fold_mod(&mut self, m: Module) -> Module {
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index b99321e8484..277571b11f5 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -218,7 +218,7 @@ impl DocFolder for Cache {
 
         // If this is a stripped module,
         // we don't want it or its children in the search index.
-        let orig_stripped_mod = match item.inner {
+        let orig_stripped_mod = match item.kind {
             clean::StrippedItem(box clean::ModuleItem(..)) => {
                 mem::replace(&mut self.stripped_mod, true)
             }
@@ -227,7 +227,7 @@ impl DocFolder for Cache {
 
         // 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.inner {
+        if let clean::ImplItem(ref i) = item.kind {
             if self.masked_crates.contains(&item.def_id.krate)
                 || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate))
                 || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate))
@@ -238,12 +238,12 @@ impl DocFolder for Cache {
 
         // Propagate a trait method's documentation to all implementors of the
         // trait.
-        if let clean::TraitItem(ref t) = item.inner {
+        if let clean::TraitItem(ref t) = item.kind {
             self.traits.entry(item.def_id).or_insert_with(|| t.clone());
         }
 
         // Collect all the implementors of traits.
-        if let clean::ImplItem(ref i) = item.inner {
+        if let clean::ImplItem(ref i) = item.kind {
             if let Some(did) = i.trait_.def_id() {
                 if i.blanket_impl.is_none() {
                     self.implementors
@@ -256,7 +256,7 @@ impl DocFolder for Cache {
 
         // Index this method for searching later on.
         if let Some(ref s) = item.name {
-            let (parent, is_inherent_impl_item) = match item.inner {
+            let (parent, is_inherent_impl_item) = match item.kind {
                 clean::StrippedItem(..) => ((None, None), false),
                 clean::AssocConstItem(..) | clean::TypedefItem(_, true)
                     if self.parent_is_trait_impl =>
@@ -345,7 +345,7 @@ impl DocFolder for Cache {
             _ => false,
         };
 
-        match item.inner {
+        match item.kind {
             clean::StructItem(..)
             | clean::EnumItem(..)
             | clean::TypedefItem(..)
@@ -384,7 +384,7 @@ impl DocFolder for Cache {
 
         // Maintain the parent stack
         let orig_parent_is_trait_impl = self.parent_is_trait_impl;
-        let parent_pushed = match item.inner {
+        let parent_pushed = match item.kind {
             clean::TraitItem(..)
             | clean::EnumItem(..)
             | clean::ForeignTypeItem
@@ -422,12 +422,12 @@ impl DocFolder for Cache {
         // Once we've recursively found all the generics, hoard off all the
         // implementations elsewhere.
         let ret = self.fold_item_recur(item).and_then(|item| {
-            if let clean::Item { inner: clean::ImplItem(_), .. } = item {
+            if let clean::Item { kind: clean::ImplItem(_), .. } = item {
                 // Figure out the id of this impl. This may map to a
                 // primitive rather than always to a struct/enum.
                 // Note: matching twice to restrict the lifetime of the `i` borrow.
                 let mut dids = FxHashSet::default();
-                if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
+                if let clean::Item { kind: clean::ImplItem(ref i), .. } = item {
                     match i.for_ {
                         clean::ResolvedPath { did, .. }
                         | clean::BorrowedRef {
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index 696bdae94fc..a0f4502f750 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -60,12 +60,12 @@ impl Serialize for ItemType {
 
 impl<'a> From<&'a clean::Item> for ItemType {
     fn from(item: &'a clean::Item) -> ItemType {
-        let inner = match item.inner {
+        let kind = match item.kind {
             clean::StrippedItem(box ref item) => item,
-            ref inner => inner,
+            ref kind => kind,
         };
 
-        match *inner {
+        match *kind {
             clean::ModuleItem(..) => ItemType::Module,
             clean::ExternCrateItem(..) => ItemType::ExternCrate,
             clean::ImportItem(..) => ItemType::Import,
diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs
index dcb0184c58c..b893d6c64ec 100644
--- a/src/librustdoc/formats/mod.rs
+++ b/src/librustdoc/formats/mod.rs
@@ -32,7 +32,7 @@ pub struct Impl {
 
 impl Impl {
     pub fn inner_impl(&self) -> &clean::Impl {
-        match self.impl_item.inner {
+        match self.impl_item.kind {
             clean::ImplItem(ref impl_) => impl_,
             _ => panic!("non-impl item found in impl"),
         }
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index 90ace4d44c4..273e2819257 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -86,7 +86,7 @@ pub fn run_format<T: FormatRenderer>(
             }
 
             cx.mod_item_in(&item, &name, &cache)?;
-            let module = match item.inner {
+            let module = match item.kind {
                 clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m,
                 _ => unreachable!(),
             };
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index add28de17ed..0541bf118e1 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -165,7 +165,7 @@ pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
 }
 
 crate fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
-    let (all_types, ret_types) = match item.inner {
+    let (all_types, ret_types) = match item.kind {
         clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
         clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
         clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5ac0ffcfbf1..eebb07f0476 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -52,7 +52,6 @@ use rustc_ast_pretty::pprust;
 use rustc_attr::StabilityLevel;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_feature::UnstableFeatures;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::Mutability;
@@ -397,6 +396,7 @@ impl FormatRenderer for Context {
             resource_suffix,
             static_root_path,
             generate_search_filter,
+            unstable_features,
             ..
         } = options;
 
@@ -466,7 +466,7 @@ impl FormatRenderer for Context {
             static_root_path,
             fs: DocFS::new(sender),
             edition,
-            codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
+            codes: ErrorCodes::from(unstable_features.is_nightly_build()),
             playground,
         };
 
@@ -618,7 +618,7 @@ impl FormatRenderer for Context {
 
         // Render sidebar-items.js used throughout this module.
         if !self.render_redirect_pages {
-            let module = match item.inner {
+            let module = match item.kind {
                 clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m,
                 _ => unreachable!(),
             };
@@ -1717,7 +1717,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache)
 
     write!(buf, "</span>"); // out-of-band
     write!(buf, "<span class=\"in-band\">");
-    let name = match item.inner {
+    let name = match item.kind {
         clean::ModuleItem(ref m) => {
             if m.is_crate {
                 "Crate "
@@ -1766,7 +1766,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache)
 
     write!(buf, "</span></h1>"); // in-band
 
-    match item.inner {
+    match item.kind {
         clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
         clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => {
             item_function(buf, cx, item, f)
@@ -2133,7 +2133,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
             );
         }
 
-        match myitem.inner {
+        match myitem.kind {
             clean::ExternCrateItem(ref name, ref src) => {
                 use crate::html::format::anchor;
 
@@ -2169,7 +2169,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
                     continue;
                 }
 
-                let unsafety_flag = match myitem.inner {
+                let unsafety_flag = match myitem.kind {
                     clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func)
                         if func.header.unsafety == hir::Unsafety::Unsafe =>
                     {
@@ -2582,7 +2582,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait,
             }
             for (pos, m) in provided.iter().enumerate() {
                 render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait);
-                match m.inner {
+                match m.kind {
                     clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => {
                         write!(w, ",\n    {{ ... }}\n");
                     }
@@ -2958,7 +2958,7 @@ fn render_assoc_item(
             where_clause = WhereClause { gens: g, indent, end_newline }
         )
     }
-    match item.inner {
+    match item.kind {
         clean::StrippedItem(..) => {}
         clean::TyMethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent),
         clean::MethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent),
@@ -2994,7 +2994,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct
     let mut fields = s
         .fields
         .iter()
-        .filter_map(|f| match f.inner {
+        .filter_map(|f| match f.kind {
             clean::StructFieldItem(ref ty) => Some((f, ty)),
             _ => None,
         })
@@ -3044,7 +3044,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union,
     let mut fields = s
         .fields
         .iter()
-        .filter_map(|f| match f.inner {
+        .filter_map(|f| match f.kind {
             clean::StructFieldItem(ref ty) => Some((f, ty)),
             _ => None,
         })
@@ -3097,7 +3097,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
             for v in &e.variants {
                 write!(w, "    ");
                 let name = v.name.as_ref().unwrap();
-                match v.inner {
+                match v.kind {
                     clean::VariantItem(ref var) => match var.kind {
                         clean::VariantKind::CLike => write!(w, "{}", name),
                         clean::VariantKind::Tuple(ref tys) => {
@@ -3147,7 +3147,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
                 id = id,
                 name = variant.name.as_ref().unwrap()
             );
-            if let clean::VariantItem(ref var) = variant.inner {
+            if let clean::VariantItem(ref var) = variant.kind {
                 if let clean::VariantKind::Tuple(ref tys) = var.kind {
                     write!(w, "(");
                     for (i, ty) in tys.iter().enumerate() {
@@ -3164,8 +3164,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
             document_non_exhaustive(w, variant);
 
             use crate::clean::{Variant, VariantKind};
-            if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.inner
-            {
+            if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.kind {
                 let variant_id = cx.derive_id(format!(
                     "{}.{}.fields",
                     ItemType::Variant,
@@ -3179,7 +3178,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
                 );
                 for field in &s.fields {
                     use crate::clean::StructFieldItem;
-                    if let StructFieldItem(ref ty) = field.inner {
+                    if let StructFieldItem(ref ty) = field.kind {
                         let id = cx.derive_id(format!(
                             "variant.{}.field.{}",
                             variant.name.as_ref().unwrap(),
@@ -3275,7 +3274,7 @@ fn render_struct(
             let mut has_visible_fields = false;
             write!(w, " {{");
             for field in fields {
-                if let clean::StructFieldItem(ref ty) = field.inner {
+                if let clean::StructFieldItem(ref ty) = field.kind {
                     write!(
                         w,
                         "\n{}    {}{}: {},",
@@ -3306,7 +3305,7 @@ fn render_struct(
                 if i > 0 {
                     write!(w, ", ");
                 }
-                match field.inner {
+                match field.kind {
                     clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
                     clean::StructFieldItem(ref ty) => {
                         write!(w, "{}{}", field.visibility.print_with_space(), ty.print())
@@ -3352,7 +3351,7 @@ fn render_union(
 
     write!(w, " {{\n{}", tab);
     for field in fields {
-        if let clean::StructFieldItem(ref ty) = field.inner {
+        if let clean::StructFieldItem(ref ty) = field.kind {
             write!(
                 w,
                 "    {}{}: {},\n{}",
@@ -3516,7 +3515,7 @@ fn render_deref_methods(
         .inner_impl()
         .items
         .iter()
-        .find_map(|item| match item.inner {
+        .find_map(|item| match item.kind {
             clean::TypedefItem(ref t, true) => Some(match *t {
                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
                 _ => (&t.type_, &t.type_),
@@ -3538,7 +3537,7 @@ fn render_deref_methods(
 }
 
 fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
-    let self_type_opt = match item.inner {
+    let self_type_opt = match item.kind {
         clean::MethodItem(ref method) => method.decl.self_type(),
         clean::TyMethodItem(ref method) => method.decl.self_type(),
         _ => None,
@@ -3589,7 +3588,7 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String {
                     ));
                     let t_did = impl_.trait_.def_id().unwrap();
                     for it in &impl_.items {
-                        if let clean::TypedefItem(ref tydef, _) = it.inner {
+                        if let clean::TypedefItem(ref tydef, _) = it.kind {
                             out.push_str("<span class=\"where fmt-newline\">    ");
                             assoc_type(
                                 &mut out,
@@ -3657,7 +3656,7 @@ fn render_impl(
             fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute);
             if show_def_docs {
                 for it in &i.inner_impl().items {
-                    if let clean::TypedefItem(ref tydef, _) = it.inner {
+                    if let clean::TypedefItem(ref tydef, _) = it.kind {
                         write!(w, "<span class=\"where fmt-newline\">  ");
                         assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), "");
                         write!(w, ";</span>");
@@ -3728,14 +3727,14 @@ fn render_impl(
         };
 
         let (is_hidden, extra_class) =
-            if (trait_.is_none() || item.doc_value().is_some() || item.inner.is_type_alias())
+            if (trait_.is_none() || item.doc_value().is_some() || item.kind.is_type_alias())
                 && !is_default_item
             {
                 (false, "")
             } else {
                 (true, " hidden")
             };
-        match item.inner {
+        match item.kind {
             clean::MethodItem(clean::Method { .. })
             | clean::TyMethodItem(clean::TyMethod { .. }) => {
                 // Only render when the method is not static or we allow static methods
@@ -4000,7 +3999,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca
         write!(
             buffer,
             "<p class=\"location\">{}{}</p>",
-            match it.inner {
+            match it.kind {
                 clean::StructItem(..) => "Struct ",
                 clean::TraitItem(..) => "Trait ",
                 clean::PrimitiveItem(..) => "Primitive Type ",
@@ -4040,7 +4039,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca
             it.name.as_ref().expect("crates always have a name")
         );
     }
-    match it.inner {
+    match it.kind {
         clean::StructItem(ref s) => sidebar_struct(buffer, it, s),
         clean::TraitItem(ref t) => sidebar_trait(buffer, it, t),
         clean::PrimitiveItem(_) => sidebar_primitive(buffer, it),
@@ -4180,7 +4179,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
                 .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did)
             {
                 if let Some((target, real_target)) =
-                    impl_.inner_impl().items.iter().find_map(|item| match item.inner {
+                    impl_.inner_impl().items.iter().find_map(|item| match item.kind {
                         clean::TypedefItem(ref t, true) => Some(match *t {
                             clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
                             _ => (&t.type_, &t.type_),
@@ -4319,8 +4318,8 @@ fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) ->
 }
 
 fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> {
-    match item.inner {
-        clean::ItemEnum::ImplItem(ref i) => {
+    match item.kind {
+        clean::ItemKind::ImplItem(ref i) => {
             if let Some(ref trait_) = i.trait_ {
                 Some((
                     format!("{:#}", i.for_.print()),
@@ -4470,7 +4469,7 @@ fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) {
 fn get_struct_fields_name(fields: &[clean::Item]) -> String {
     let mut fields = fields
         .iter()
-        .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false })
+        .filter(|f| if let clean::StructFieldItem(..) = f.kind { true } else { false })
         .filter_map(|f| match f.name {
             Some(ref name) => {
                 Some(format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 3a87e1c46a6..33bd57223b8 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -2,7 +2,6 @@ use std::fs::{create_dir_all, read_to_string, File};
 use std::io::prelude::*;
 use std::path::Path;
 
-use rustc_feature::UnstableFeatures;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::DUMMY_SP;
 
@@ -66,7 +65,7 @@ pub fn render<P: AsRef<Path>>(
     let title = metadata[0];
 
     let mut ids = IdMap::new();
-    let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+    let error_codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
     let text = if !options.markdown_no_toc {
         MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).into_string()
     } else {
@@ -131,7 +130,7 @@ pub fn test(mut options: Options) -> Result<(), String> {
         options.enable_per_target_ignores,
     );
     collector.set_position(DUMMY_SP);
-    let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+    let codes = ErrorCodes::from(options.render_options.unstable_features.is_nightly_build());
 
     find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
 
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index ced26fcf5b0..ef68bae1078 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -187,7 +187,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
 
 impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
     fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
-        match i.inner {
+        match i.kind {
             _ if !i.def_id.is_local() => {
                 // non-local items are skipped because they can be out of the users control,
                 // especially in the case of trait impls, which rustdoc eagerly inlines
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index e0cb5bf1a4e..fd0dd339abd 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -671,7 +671,7 @@ fn resolve_associated_trait_item(
     let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
     let mut candidates: Vec<_> = implicit_impls
         .flat_map(|impl_outer| {
-            match impl_outer.inner {
+            match impl_outer.kind {
                 clean::ImplItem(impl_) => {
                     debug!("considering auto or blanket impl for trait {:?}", impl_.trait_);
                     // Give precedence to methods that were overridden
@@ -681,14 +681,14 @@ fn resolve_associated_trait_item(
                                 return None;
                             }
                             let kind = assoc
-                                .inner
+                                .kind
                                 .as_assoc_kind()
                                 .expect("inner items for a trait should be associated items");
                             if kind.namespace() != ns {
                                 return None;
                             }
 
-                            trace!("considering associated item {:?}", assoc.inner);
+                            trace!("considering associated item {:?}", assoc.kind);
                             // We have a slight issue: normal methods come from `clean` types,
                             // but provided methods come directly from `tcx`.
                             // Fortunately, we don't need the whole method, we just need to know
@@ -832,7 +832,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
         }
 
-        let current_item = match item.inner {
+        let current_item = match item.kind {
             clean::ModuleItem(..) => {
                 if item.attrs.inner_docs {
                     if item.def_id.is_top_level_module() { item.name.clone() } else { None }
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 5eb3f98b123..81de0730247 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -55,11 +55,11 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
 
     // scan through included items ahead of time to splice in Deref targets to the "valid" sets
     for it in &new_items {
-        if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.inner {
+        if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.kind {
             if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() {
                 let target = items
                     .iter()
-                    .find_map(|item| match item.inner {
+                    .find_map(|item| match item.kind {
                         TypedefItem(ref t, true) => Some(&t.type_),
                         _ => None,
                     })
@@ -75,7 +75,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     }
 
     new_items.retain(|it| {
-        if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.inner {
+        if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.kind {
             cleaner.keep_item(for_)
                 || trait_.as_ref().map_or(false, |t| cleaner.keep_item(t))
                 || blanket_impl.is_some()
@@ -96,7 +96,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     }
 
     if let Some(ref mut it) = krate.module {
-        if let ModuleItem(Module { ref mut items, .. }) = it.inner {
+        if let ModuleItem(Module { ref mut items, .. }) = it.kind {
             items.extend(synth.impls);
             items.extend(new_items);
         } else {
diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs
index 686ec51fb06..094f85f2ccb 100644
--- a/src/librustdoc/passes/doc_test_lints.rs
+++ b/src/librustdoc/passes/doc_test_lints.rs
@@ -58,7 +58,7 @@ impl crate::doctest::Tester for Tests {
 }
 
 pub fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
-    if matches!(item.inner,
+    if matches!(item.kind,
         clean::StructFieldItem(_)
         | clean::VariantItem(_)
         | clean::AssocConstItem(_, _)
@@ -92,9 +92,7 @@ pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
 
     find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None);
 
-    if tests.found_tests == 0
-        && rustc_feature::UnstableFeatures::from_environment().is_nightly_build()
-    {
+    if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() {
         if should_have_doc_example(cx, &item) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
             let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs
index 1d9be619ec9..26b64b4905e 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/html_tags.rs
@@ -5,7 +5,6 @@ use crate::fold::DocFolder;
 use crate::html::markdown::opts;
 use core::ops::Range;
 use pulldown_cmark::{Event, Parser};
-use rustc_feature::UnstableFeatures;
 use rustc_session::lint;
 use std::iter::Peekable;
 use std::str::CharIndices;
@@ -27,7 +26,7 @@ impl<'a, 'tcx> InvalidHtmlTagsLinter<'a, 'tcx> {
 }
 
 pub fn check_invalid_html_tags(krate: Crate, cx: &DocContext<'_>) -> Crate {
-    if !UnstableFeatures::from_environment().is_nightly_build() {
+    if !cx.tcx.sess.is_nightly_build() {
         krate
     } else {
         let mut coll = InvalidHtmlTagsLinter::new(cx);
diff --git a/src/librustdoc/passes/non_autolinks.rs b/src/librustdoc/passes/non_autolinks.rs
index 4a8fc7fc618..964773dc055 100644
--- a/src/librustdoc/passes/non_autolinks.rs
+++ b/src/librustdoc/passes/non_autolinks.rs
@@ -7,7 +7,6 @@ use core::ops::Range;
 use pulldown_cmark::{Event, LinkType, Parser, Tag};
 use regex::Regex;
 use rustc_errors::Applicability;
-use rustc_feature::UnstableFeatures;
 use rustc_session::lint;
 
 pub const CHECK_NON_AUTOLINKS: Pass = Pass {
@@ -54,7 +53,7 @@ impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> {
 }
 
 pub fn check_non_autolinks(krate: Crate, cx: &DocContext<'_>) -> Crate {
-    if !UnstableFeatures::from_environment().is_nightly_build() {
+    if !cx.tcx.sess.is_nightly_build() {
         krate
     } else {
         let mut coll = NonAutolinksLinter::new(cx);
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index f82e72b488b..4b9e150eb1e 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -41,7 +41,7 @@ impl<'a> DocFolder for Stripper<'a> {
         if i.attrs.lists(sym::doc).has_word(sym::hidden) {
             debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
             // use a dedicated hidden item for given item type if any
-            match i.inner {
+            match i.kind {
                 clean::StructFieldItem(..) | clean::ModuleItem(..) => {
                     // We need to recurse into stripped modules to
                     // strip things like impl methods but when doing so
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 9b4f62235f5..4250c2b48fc 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -13,7 +13,7 @@ pub struct Stripper<'a> {
 
 impl<'a> DocFolder for Stripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        match i.inner {
+        match i.kind {
             clean::StrippedItem(..) => {
                 // We need to recurse into stripped modules to strip things
                 // like impl methods but when doing so we must not add any
@@ -86,7 +86,7 @@ impl<'a> DocFolder for Stripper<'a> {
             clean::KeywordItem(..) => {}
         }
 
-        let fastreturn = match i.inner {
+        let fastreturn = match i.kind {
             // nothing left to do for traits (don't want to filter their
             // methods out, visibility controlled by the trait)
             clean::TraitItem(..) => true,
@@ -123,7 +123,7 @@ pub struct ImplStripper<'a> {
 
 impl<'a> DocFolder for ImplStripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        if let clean::ImplItem(ref imp) = i.inner {
+        if let clean::ImplItem(ref imp) = i.kind {
             // emptied none trait impls can be stripped
             if imp.trait_.is_none() && imp.items.is_empty() {
                 return None;
@@ -162,7 +162,7 @@ pub struct ImportStripper;
 
 impl DocFolder for ImportStripper {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        match i.inner {
+        match i.kind {
             clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
                 None
             }
diff --git a/src/test/ui/cross/cross-file-errors/main.rs b/src/test/ui/cross/cross-file-errors/main.rs
index 74e9461803c..1902ab94d4c 100644
--- a/src/test/ui/cross/cross-file-errors/main.rs
+++ b/src/test/ui/cross/cross-file-errors/main.rs
@@ -3,5 +3,6 @@ mod underscore;
 
 fn main() {
     underscore!();
-    //~^ ERROR expected expression, found reserved identifier `_`
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/cross/cross-file-errors/main.stderr b/src/test/ui/cross/cross-file-errors/main.stderr
index f9101d8a583..b8658745060 100644
--- a/src/test/ui/cross/cross-file-errors/main.stderr
+++ b/src/test/ui/cross/cross-file-errors/main.stderr
@@ -1,15 +1,31 @@
-error: expected expression, found reserved identifier `_`
+error[E0658]: destructuring assignments are unstable
   --> $DIR/underscore.rs:8:9
    |
 LL |         _
-   |         ^ expected expression
+   |         ^
    | 
   ::: $DIR/main.rs:5:5
    |
 LL |     underscore!();
    |     -------------- in this macro invocation
    |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to previous error
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/underscore.rs:8:9
+   |
+LL |         _
+   |         ^ `_` not allowed here
+   | 
+  ::: $DIR/main.rs:5:5
+   |
+LL |     underscore!();
+   |     -------------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs
index 393dfc16c0a..0d45ff7da72 100644
--- a/src/test/ui/destructuring-assignment/nested_destructure.rs
+++ b/src/test/ui/destructuring-assignment/nested_destructure.rs
@@ -14,4 +14,7 @@ fn main() {
     Struct { a: TupleStruct((a, b), c), b: [d] } =
         Struct { a: TupleStruct((0, 1), 2), b: [3] };
     assert_eq!((a, b, c, d), (0, 1, 2, 3));
+
+    // unnested underscore: just discard
+    _ = 1;
 }
diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs
index 3dd10aff19c..76cdc1260fc 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure.rs
+++ b/src/test/ui/destructuring-assignment/slice_destructure.rs
@@ -9,6 +9,8 @@ fn main() {
   let mut c;
   [a, .., b, c] = [1, 2, 3, 4, 5];
   assert_eq!((a, b, c), (1, 4, 5));
+  [_, a, _] = [1, 2, 3];
+  assert_eq!((a, b), (2, 4));
   [..] = [1, 2, 3];
   [c, ..] = [5, 6, 6];
   assert_eq!(c, 5);
diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
index f636ea3511c..90d93892f7f 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
@@ -4,4 +4,5 @@ fn main() {
   let (mut a, mut b);
   [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
   [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2
+  [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2
 }
diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
index 728687deb8b..cc412c72df5 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
@@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2
 LL |   [a, a, b] = [1, 2];
    |   ^^^^^^^^^ expected 2 elements
 
-error: aborting due to 2 previous errors
+error[E0527]: pattern requires 1 element but array has 2
+  --> $DIR/slice_destructure_fail.rs:7:3
+   |
+LL |   [_] = [1, 2];
+   |   ^^^ expected 2 elements
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0527`.
diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs
index b3a96ee1573..2bcbd9d0d74 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure.rs
+++ b/src/test/ui/destructuring-assignment/struct_destructure.rs
@@ -12,8 +12,10 @@ fn main() {
     assert_eq!((a, b), (0, 1));
     Struct { a: b, b: a }  = Struct { a: 1, b: 2 };
     assert_eq!((a,b), (2, 1));
+    Struct { a: _, b } = Struct { a: 1, b: 2 };
+    assert_eq!((a, b), (2, 2));
     Struct { a, .. } = Struct { a: 1, b: 3 };
-    assert_eq!((a, b), (1, 1));
+    assert_eq!((a, b), (1, 2));
     Struct { .. } = Struct { a: 1, b: 4 };
-    assert_eq!((a, b), (1, 1));
+    assert_eq!((a, b), (1, 2));
 }
diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
index c22695ed388..4aa327b61f4 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
@@ -9,6 +9,8 @@ fn main() {
     let mut c;
     let d = Struct { a: 0, b: 1 };
     Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c`
+    Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b`
+    //~| ERROR expected identifier, found reserved identifier `_`
     Struct { a, ..d } = Struct { a: 1, b: 2 };
     //~^ ERROR functional record updates are not allowed in destructuring assignments
     Struct { a, .. }; //~ ERROR base expression required after `..`
diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
index 4da4698804f..81661a357e7 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
@@ -1,11 +1,19 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/struct_destructure_fail.rs:12:17
+   |
+LL |     Struct { a, _ } = Struct { a: 1, b: 2 };
+   |     ------      ^ expected identifier, found reserved identifier
+   |     |
+   |     while parsing this struct
+
 error: functional record updates are not allowed in destructuring assignments
-  --> $DIR/struct_destructure_fail.rs:12:19
+  --> $DIR/struct_destructure_fail.rs:14:19
    |
 LL |     Struct { a, ..d } = Struct { a: 1, b: 2 };
    |                   ^ help: consider removing the trailing pattern
 
 error: base expression required after `..`
-  --> $DIR/struct_destructure_fail.rs:14:19
+  --> $DIR/struct_destructure_fail.rs:16:19
    |
 LL |     Struct { a, .. };
    |                   ^ add a base expression here
@@ -16,6 +24,22 @@ error[E0026]: struct `Struct` does not have a field named `c`
 LL |     Struct { a, b, c } = Struct { a: 0, b: 1 };
    |                    ^ struct `Struct` does not have this field
 
-error: aborting due to 3 previous errors
+error[E0027]: pattern does not mention field `b`
+  --> $DIR/struct_destructure_fail.rs:12:5
+   |
+LL |     Struct { a, _ } = Struct { a: 1, b: 2 };
+   |     ^^^^^^^^^^^^^^^ missing field `b`
+   |
+help: include the missing field in the pattern
+   |
+LL |     Struct { a, b, _ } = Struct { a: 1, b: 2 };
+   |               ^^^
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     Struct { a, .., _ } = Struct { a: 1, b: 2 };
+   |               ^^^^
+
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0026`.
+Some errors have detailed explanations: E0026, E0027.
+For more information about an error, try `rustc --explain E0026`.
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs
index 16aafc4693f..2096182d421 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure.rs
+++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs
@@ -16,6 +16,8 @@ fn main() {
     assert_eq!((a, b), (2, 2));
     (b, ..) = (5, 6, 7);
     assert_eq!(b, 5);
+    (a, _) = (8, 9);
+    assert_eq!(a, 8);
 
     // Test for a non-Copy type (String):
     let (mut c, mut d);
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
index b76f4968e62..5524e91dc40 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
@@ -7,4 +7,5 @@ fn main() {
     (a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
     (a, a, b) = (1, 2); //~ ERROR mismatched types
     (C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
+    (_,) = (1, 2); //~ ERROR mismatched types
 }
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
index a60e1cb1eec..1146b88278d 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
@@ -25,7 +25,18 @@ LL |     (C, ..) = (0,1);
    |      |
    |      cannot assign to this expression
 
-error: aborting due to 3 previous errors
+error[E0308]: mismatched types
+  --> $DIR/tuple_destructure_fail.rs:10:5
+   |
+LL |     (_,) = (1, 2);
+   |     ^^^^   ------ this expression has type `({integer}, {integer})`
+   |     |
+   |     expected a tuple with 2 elements, found one with 1 element
+   |
+   = note: expected type `({integer}, {integer})`
+             found tuple `(_,)`
+
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0070, E0308.
 For more information about an error, try `rustc --explain E0070`.
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
index 106a9b16db4..7b5c5ad2bae 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
@@ -23,8 +23,10 @@ fn main() {
     assert_eq!((a, b), (0, 1));
     TupleStruct(a, .., b) = TupleStruct(1, 2);
     assert_eq!((a, b), (1, 2));
+    TupleStruct(_, a) = TupleStruct(2, 2);
+    assert_eq!((a, b), (2, 2));
     TupleStruct(..) = TupleStruct(3, 4);
-    assert_eq!((a, b), (1, 2));
+    assert_eq!((a, b), (2, 2));
     TupleStruct(5,6).assign(&mut a, &mut b);
     assert_eq!((a, b), (5, 6));
     Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8);
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
index 61ae42a5175..c39db061177 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
@@ -29,8 +29,12 @@ fn main() {
 
     TupleStruct(a, a, b) = TupleStruct(1, 2);
     //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields
+    TupleStruct(_) = TupleStruct(1, 2);
+    //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
     //~^ ERROR this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+    Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
+    //~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
 
     // Check if `test` is recognized as not a tuple struct but a function call:
     test() = TupleStruct(0, 0);
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
index 863eedecf76..0e7174e5b19 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
@@ -23,17 +23,35 @@ LL | struct TupleStruct<S, T>(S, T);
 LL |     TupleStruct(a, a, b) = TupleStruct(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
-error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
   --> $DIR/tuple_struct_destructure_fail.rs:32:5
    |
+LL | struct TupleStruct<S, T>(S, T);
+   | ------------------------------- tuple struct defined here
+...
+LL |     TupleStruct(_) = TupleStruct(1, 2);
+   |     ^^^^^^^^^^^^^^ expected 2 fields, found 1
+
+error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:34:5
+   |
 LL |     SingleVariant(S, T)
    |     ------------------- tuple variant defined here
 ...
 LL |     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:36:5
+   |
+LL |     SingleVariant(S, T)
+   |     ------------------- tuple variant defined here
+...
+LL |     Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
+   |     ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1
+
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:36:12
+  --> $DIR/tuple_struct_destructure_fail.rs:40:12
    |
 LL |     test() = TupleStruct(0, 0);
    |     ------ ^
@@ -41,7 +59,7 @@ LL |     test() = TupleStruct(0, 0);
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:38:14
+  --> $DIR/tuple_struct_destructure_fail.rs:42:14
    |
 LL |     (test)() = TupleStruct(0, 0);
    |     -------- ^
@@ -49,14 +67,14 @@ LL |     (test)() = TupleStruct(0, 0);
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:40:38
+  --> $DIR/tuple_struct_destructure_fail.rs:44:38
    |
 LL |     <Alias::<isize> as Test>::test() = TupleStruct(0, 0);
    |     -------------------------------- ^
    |     |
    |     cannot assign to this expression
 
-error: aborting due to 7 previous errors
+error: aborting due to 9 previous errors
 
 Some errors have detailed explanations: E0023, E0070.
 For more information about an error, try `rustc --explain E0023`.
diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
index b41f2f52a3d..4ed4f56702c 100644
--- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
@@ -4,5 +4,7 @@ struct S { x : u32 }
 
 #[cfg(FALSE)]
 fn foo() {
+    _; //~ ERROR destructuring assignments are unstable
+
     S { x: 5, .. }; //~ ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
index 442e36cd306..a5ed761a01c 100644
--- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
@@ -1,5 +1,14 @@
 error[E0658]: destructuring assignments are unstable
-  --> $DIR/underscore-range-expr-gating.rs:7:15
+  --> $DIR/underscore-range-expr-gating.rs:7:5
+   |
+LL |     _;
+   |     ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/underscore-range-expr-gating.rs:9:15
    |
 LL |     S { x: 5, .. };
    |               ^^
@@ -7,6 +16,6 @@ LL |     S { x: 5, .. };
    = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
    = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
index a8ea3faefe8..00638e04f5d 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
@@ -8,12 +8,18 @@ trait T {
 
 fn main() {
     let _: usize = foo(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
     let _: S = S(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
     let _: usize = T::baz(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
index a6d1c4b859f..248fa6b9c9c 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
@@ -1,38 +1,93 @@
-error: expected expression, found reserved identifier `_`
+error[E0658]: destructuring assignments are unstable
   --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24
    |
 LL |     let _: usize = foo(_, _);
-   |                        ^ expected expression
+   |                        ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27
+   |
+LL |     let _: usize = foo(_, _);
+   |                           ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18
+   |
+LL |     let _: S = S(_, _);
+   |                  ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21
+   |
+LL |     let _: S = S(_, _);
+   |                     ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27
+   |
+LL |     let _: usize = T::baz(_, _);
+   |                           ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30
+   |
+LL |     let _: usize = T::baz(_, _);
+   |                              ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24
+   |
+LL |     let _: usize = foo(_, _);
+   |                        ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
+error: in expressions, `_` can only be used on the left-hand side of an assignment
   --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27
    |
 LL |     let _: usize = foo(_, _);
-   |                           ^ expected expression
+   |                           ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:18
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18
    |
 LL |     let _: S = S(_, _);
-   |                  ^ expected expression
+   |                  ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:21
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21
    |
 LL |     let _: S = S(_, _);
-   |                     ^ expected expression
+   |                     ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:27
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27
    |
 LL |     let _: usize = T::baz(_, _);
-   |                           ^ expected expression
+   |                           ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:30
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30
    |
 LL |     let _: usize = T::baz(_, _);
-   |                              ^ expected expression
+   |                              ^ `_` not allowed here
 
-error: aborting due to 6 previous errors
+error: aborting due to 12 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
index 625120b880e..1fcd41e4dbf 100644
--- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
@@ -170,6 +170,7 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::MacCall(..)
             | ast::ExprKind::MethodCall(..)
             | ast::ExprKind::Paren(..)
+            | ast::ExprKind::Underscore
             | ast::ExprKind::Path(..)
             | ast::ExprKind::Repeat(..)
             | ast::ExprKind::Ret(..)
diff --git a/src/tools/rustfmt b/src/tools/rustfmt
-Subproject 0f29ff6da0c5ff622e739beb8fc3bbe77119b3c
+Subproject 293d7d01118c9fb5479649399e1dae60322b8e0