about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-08-02 02:33:16 +0000
committerbors <bors@rust-lang.org>2021-08-02 02:33:16 +0000
commiteffea9a2a0d501db5722d507690a1a66236933bf (patch)
treee504e13021242bcf417a32729de4f8101471d6bf
parent24bbf7ac2fd6f5e71f5a4873baa4456e45bd648d (diff)
parent0851841970f3bcc7616ff7f9c837e19d52c5e0dd (diff)
downloadrust-effea9a2a0d501db5722d507690a1a66236933bf.tar.gz
rust-effea9a2a0d501db5722d507690a1a66236933bf.zip
Auto merge of #87689 - JohnTitor:rollup-ns38b56, r=JohnTitor
Rollup of 13 pull requests

Successful merges:

 - #86183 (Change environment variable getters to error recoverably)
 - #86439 (Remove `Ipv4Addr::is_ietf_protocol_assignment`)
 - #86509 (Move `os_str_bytes` to `sys::unix`)
 - #86593 (Partially stabilize `const_slice_first_last`)
 - #86936 (Add documentation for `Ipv6MulticastScope`)
 - #87282 (Ensure `./x.py dist` adheres to `build.tools`)
 - #87468 (Update rustfmt)
 - #87504 (Update mdbook.)
 - #87608 (Remove unused field `Session.system_library_path`)
 - #87629 (Consistent spelling of "adapter" in the standard library)
 - #87633 (Update compiler_builtins to fix i128 shift/mul on thumbv6m)
 - #87644 (Recommend `swap_remove` in `Vec::remove` docs)
 - #87653 (mark a UB doctest as no_run)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock12
-rw-r--r--compiler/rustc_session/src/session.rs5
-rw-r--r--library/alloc/src/vec/mod.rs6
-rw-r--r--library/core/src/alloc/mod.rs4
-rw-r--r--library/core/src/iter/traits/iterator.rs6
-rw-r--r--library/core/src/ptr/non_null.rs8
-rw-r--r--library/core/src/slice/mod.rs8
-rw-r--r--library/core/tests/iter/adapters/mod.rs2
-rw-r--r--library/std/src/env.rs26
-rw-r--r--library/std/src/io/mod.rs24
-rw-r--r--library/std/src/net/ip.rs84
-rw-r--r--library/std/src/net/ip/tests.rs17
-rw-r--r--library/std/src/sys/hermit/mod.rs3
-rw-r--r--library/std/src/sys/hermit/os.rs9
-rw-r--r--library/std/src/sys/sgx/mod.rs4
-rw-r--r--library/std/src/sys/sgx/os.rs4
-rw-r--r--library/std/src/sys/unix/mod.rs3
-rw-r--r--library/std/src/sys/unix/os.rs9
-rw-r--r--library/std/src/sys/unix/os_str.rs (renamed from library/std/src/sys_common/os_str_bytes.rs)1
-rw-r--r--library/std/src/sys/unix/os_str/tests.rs (renamed from library/std/src/sys_common/os_str_bytes/tests.rs)0
-rw-r--r--library/std/src/sys/unsupported/common.rs2
-rw-r--r--library/std/src/sys/unsupported/mod.rs2
-rw-r--r--library/std/src/sys/unsupported/os.rs4
-rw-r--r--library/std/src/sys/wasi/mod.rs3
-rw-r--r--library/std/src/sys/wasi/os.rs9
-rw-r--r--library/std/src/sys/wasm/mod.rs4
-rw-r--r--library/std/src/sys/windows/os.rs19
-rw-r--r--library/std/src/sys_common/mod.rs4
-rw-r--r--src/bootstrap/builder.rs44
-rw-r--r--src/bootstrap/dist.rs229
-rw-r--r--src/bootstrap/install.rs16
-rw-r--r--src/test/ui/macros/issue-86082-option-env-invalid-char.rs10
-rw-r--r--src/tools/rustbook/Cargo.toml2
-rw-r--r--src/tools/rustfmt/Cargo.lock24
-rw-r--r--src/tools/rustfmt/Configurations.md24
-rw-r--r--src/tools/rustfmt/Contributing.md6
-rw-r--r--src/tools/rustfmt/README.md4
-rw-r--r--src/tools/rustfmt/appveyor.yml110
-rw-r--r--src/tools/rustfmt/rust-toolchain2
-rw-r--r--src/tools/rustfmt/src/attr.rs2
-rw-r--r--src/tools/rustfmt/src/bin/main.rs18
-rw-r--r--src/tools/rustfmt/src/cargo-fmt/main.rs5
-rw-r--r--src/tools/rustfmt/src/chains.rs5
-rw-r--r--src/tools/rustfmt/src/closures.rs15
-rw-r--r--src/tools/rustfmt/src/comment.rs21
-rw-r--r--src/tools/rustfmt/src/config/config_type.rs2
-rw-r--r--src/tools/rustfmt/src/config/file_lines.rs4
-rw-r--r--src/tools/rustfmt/src/config/license.rs1
-rw-r--r--src/tools/rustfmt/src/emitter/diff.rs2
-rw-r--r--src/tools/rustfmt/src/expr.rs30
-rw-r--r--src/tools/rustfmt/src/formatting/newline_style.rs2
-rw-r--r--src/tools/rustfmt/src/imports.rs30
-rw-r--r--src/tools/rustfmt/src/issues.rs14
-rw-r--r--src/tools/rustfmt/src/items.rs26
-rw-r--r--src/tools/rustfmt/src/lib.rs7
-rw-r--r--src/tools/rustfmt/src/lists.rs11
-rw-r--r--src/tools/rustfmt/src/macros.rs39
-rw-r--r--src/tools/rustfmt/src/matches.rs7
-rw-r--r--src/tools/rustfmt/src/missed_spans.rs8
-rw-r--r--src/tools/rustfmt/src/overflow.rs28
-rw-r--r--src/tools/rustfmt/src/patterns.rs25
-rw-r--r--src/tools/rustfmt/src/rustfmt_diff.rs7
-rw-r--r--src/tools/rustfmt/src/skip.rs6
-rw-r--r--src/tools/rustfmt/src/source_file.rs2
-rw-r--r--src/tools/rustfmt/src/string.rs4
-rw-r--r--src/tools/rustfmt/src/syntux/parser.rs5
-rw-r--r--src/tools/rustfmt/src/types.rs15
-rw-r--r--src/tools/rustfmt/src/utils.rs5
-rw-r--r--src/tools/rustfmt/src/visitor.rs18
-rw-r--r--src/tools/rustfmt/tests/source/configs/match_arm_leading_pipes/preserve.rs8
-rw-r--r--src/tools/rustfmt/tests/target/configs/match_arm_leading_pipes/preserve.rs8
-rw-r--r--src/tools/rustfmt/tests/target/issue_4868.rs17
72 files changed, 571 insertions, 579 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b7c180012e0..ac2615a6979 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -646,9 +646,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.47"
+version = "0.1.49"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd4ed89e0a5c3e50b15c0045fbe1ff8567b703bc07544faf935ddff0aaa7b65f"
+checksum = "20b1438ef42c655665a8ab2c1c6d605a305f031d38d9be689ddfef41a20f3aa2"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
@@ -1495,9 +1495,9 @@ dependencies = [
 
 [[package]]
 name = "handlebars"
-version = "3.4.0"
+version = "4.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5deefd4816fb852b1ff3cb48f6c41da67be2d0e1d20b26a7a3b076da11f064b1"
+checksum = "72a0ffab8c36d0436114310c7e10b59b3307e650ddfabf6d006028e29a70c6e6"
 dependencies = [
  "log",
  "pest",
@@ -2100,9 +2100,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.7"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28f6a882f3880ec68e96f60d6b543c34941e2f307ad10e2992e4db9acfe96529"
+checksum = "4ee73932975c44c485e541416d7c30abb31a053af7e49682f6e856f1e4d6ab2a"
 dependencies = [
  "ammonia",
  "anyhow",
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 369af437c43..9ab6dbb1ea9 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -198,10 +198,6 @@ pub struct Session {
     /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
     pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>,
 
-    /// Path for libraries that will take preference over libraries shipped by Rust.
-    /// Used by windows-gnu targets to priortize system mingw-w64 libraries.
-    pub system_library_path: OneThread<RefCell<Option<Option<PathBuf>>>>,
-
     /// Tracks the current behavior of the CTFE engine when an error occurs.
     /// Options range from returning the error without a backtrace to returning an error
     /// and immediately printing the backtrace to stderr.
@@ -1375,7 +1371,6 @@ pub fn build_session(
         driver_lint_caps,
         trait_methods_not_found: Lock::new(Default::default()),
         confused_type_with_std_module: Lock::new(Default::default()),
-        system_library_path: OneThread::new(RefCell::new(Default::default())),
         ctfe_backtrace,
         miri_unleashed_features: Lock::new(Default::default()),
         asm_arch,
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index ba47da4821b..0db06812a4c 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1360,6 +1360,12 @@ impl<T, A: Allocator> Vec<T, A> {
     /// Removes and returns the element at position `index` within the vector,
     /// shifting all elements after it to the left.
     ///
+    /// Note: Because this shifts over the remaining elements, it has a
+    /// worst-case performance of O(n). If you don't need the order of elements
+    /// to be preserved, use [`swap_remove`] instead.
+    ///
+    /// [`swap_remove`]: Vec::swap_remove
+    ///
     /// # Panics
     ///
     /// Panics if `index` is out of bounds.
diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs
index 06a761531b6..1f1033b0437 100644
--- a/library/core/src/alloc/mod.rs
+++ b/library/core/src/alloc/mod.rs
@@ -338,9 +338,9 @@ pub unsafe trait Allocator {
         Ok(new_ptr)
     }
 
-    /// Creates a "by reference" adaptor for this instance of `Allocator`.
+    /// Creates a "by reference" adapter for this instance of `Allocator`.
     ///
-    /// The returned adaptor also implements `Allocator` and will simply borrow this.
+    /// The returned adapter also implements `Allocator` and will simply borrow this.
     #[inline(always)]
     fn by_ref(&self) -> &Self
     where
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index f34adb878c4..537e42f66de 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -694,7 +694,7 @@ pub trait Iterator {
     /// more idiomatic to use a `for` loop, but `for_each` may be more legible
     /// when processing items at the end of longer iterator chains. In some
     /// cases `for_each` may also be faster than a loop, because it will use
-    /// internal iteration on adaptors like `Chain`.
+    /// internal iteration on adapters like `Chain`.
     ///
     /// [`for`]: ../../book/ch03-05-control-flow.html#looping-through-a-collection-with-for
     ///
@@ -1293,7 +1293,7 @@ pub trait Iterator {
         Take::new(self, n)
     }
 
-    /// An iterator adaptor similar to [`fold`] that holds internal state and
+    /// An iterator adapter similar to [`fold`] that holds internal state and
     /// produces a new iterator.
     ///
     /// [`fold`]: Iterator::fold
@@ -1604,7 +1604,7 @@ pub trait Iterator {
 
     /// Borrows an iterator, rather than consuming it.
     ///
-    /// This is useful to allow applying iterator adaptors while still
+    /// This is useful to allow applying iterator adapters while still
     /// retaining ownership of the original iterator.
     ///
     /// # Examples
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 032df7f5a80..87c8674af0d 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -173,8 +173,14 @@ impl<T: ?Sized> NonNull<T> {
     ///
     /// let mut x = 0u32;
     /// let ptr = unsafe { NonNull::new_unchecked(&mut x as *mut _) };
+    /// ```
+    ///
+    /// *Incorrect* usage of this function:
+    ///
+    /// ```rust,no_run
+    /// use std::ptr::NonNull;
     ///
-    /// // NEVER DO THAT!!!
+    /// // NEVER DO THAT!!! This is undefined behavior. ⚠️
     /// let ptr = unsafe { NonNull::<u32>::new_unchecked(std::ptr::null_mut()) };
     /// ```
     #[stable(feature = "nonnull", since = "1.25.0")]
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 261b9a3feb7..67eecab99d8 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -139,7 +139,7 @@ impl<T> [T] {
     /// assert_eq!(None, w.first());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")]
+    #[rustc_const_stable(feature = "const_slice_first_last_not_mut", since = "1.56.0")]
     #[inline]
     pub const fn first(&self) -> Option<&T> {
         if let [first, ..] = self { Some(first) } else { None }
@@ -177,7 +177,7 @@ impl<T> [T] {
     /// }
     /// ```
     #[stable(feature = "slice_splits", since = "1.5.0")]
-    #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")]
+    #[rustc_const_stable(feature = "const_slice_first_last_not_mut", since = "1.56.0")]
     #[inline]
     pub const fn split_first(&self) -> Option<(&T, &[T])> {
         if let [first, tail @ ..] = self { Some((first, tail)) } else { None }
@@ -217,7 +217,7 @@ impl<T> [T] {
     /// }
     /// ```
     #[stable(feature = "slice_splits", since = "1.5.0")]
-    #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")]
+    #[rustc_const_stable(feature = "const_slice_first_last_not_mut", since = "1.56.0")]
     #[inline]
     pub const fn split_last(&self) -> Option<(&T, &[T])> {
         if let [init @ .., last] = self { Some((last, init)) } else { None }
@@ -256,7 +256,7 @@ impl<T> [T] {
     /// assert_eq!(None, w.last());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    #[rustc_const_unstable(feature = "const_slice_first_last", issue = "83570")]
+    #[rustc_const_stable(feature = "const_slice_first_last_not_mut", since = "1.56.0")]
     #[inline]
     pub const fn last(&self) -> Option<&T> {
         if let [.., last] = self { Some(last) } else { None }
diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs
index 96a53be1eaa..567d9fe49ca 100644
--- a/library/core/tests/iter/adapters/mod.rs
+++ b/library/core/tests/iter/adapters/mod.rs
@@ -24,7 +24,7 @@ use core::cell::Cell;
 
 /// An iterator that panics whenever `next` or next_back` is called
 /// after `None` has already been returned. This does not violate
-/// `Iterator`'s contract. Used to test that iterator adaptors don't
+/// `Iterator`'s contract. Used to test that iterator adapters don't
 /// poll their inner iterators after exhausting them.
 pub struct NonFused<I> {
     iter: I,
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 79b7e2e57a3..f9f14d0dc63 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -185,15 +185,9 @@ impl fmt::Debug for VarsOs {
 ///
 /// # Errors
 ///
-/// Errors if the environment variable is not present.
-/// Errors if the environment variable is not valid Unicode. If this is not desired, consider using
-/// [`var_os`].
-///
-/// # Panics
-///
-/// This function may panic if `key` is empty, contains an ASCII equals sign
-/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
-/// character.
+/// Returns `[None]` if the environment variable isn't set.
+/// Returns `[None]` if the environment variable is not valid Unicode. If this is not
+/// desired, consider using [`var_os`].
 ///
 /// # Examples
 ///
@@ -219,18 +213,17 @@ fn _var(key: &OsStr) -> Result<String, VarError> {
 }
 
 /// Fetches the environment variable `key` from the current process, returning
-/// [`None`] if the variable isn't set.
-///
-/// # Panics
-///
-/// This function may panic if `key` is empty, contains an ASCII equals sign
-/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
-/// character.
+/// [`None`] if the variable isn't set or there's another error.
 ///
 /// Note that the method will not check if the environment variable
 /// is valid Unicode. If you want to have an error on invalid UTF-8,
 /// use the [`var`] function instead.
 ///
+/// # Errors
+///
+/// Returns `[None]` if the variable isn't set.
+/// May return `[None]` if the variable value contains the NUL character.
+///
 /// # Examples
 ///
 /// ```
@@ -249,7 +242,6 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
 
 fn _var_os(key: &OsStr) -> Option<OsString> {
     os_imp::getenv(key)
-        .unwrap_or_else(|e| panic!("failed to get environment variable `{:?}`: {}", key, e))
 }
 
 /// The error type for operations interacting with environment variables.
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index b336b65ad7d..fa073d080c6 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -810,9 +810,9 @@ pub trait Read {
         default_read_exact(self, buf)
     }
 
-    /// Creates a "by reference" adaptor for this instance of `Read`.
+    /// Creates a "by reference" adapter for this instance of `Read`.
     ///
-    /// The returned adaptor also implements `Read` and will simply borrow this
+    /// The returned adapter also implements `Read` and will simply borrow this
     /// current reader.
     ///
     /// # Examples
@@ -889,7 +889,7 @@ pub trait Read {
         Bytes { inner: self }
     }
 
-    /// Creates an adaptor which will chain this stream with another.
+    /// Creates an adapter which will chain this stream with another.
     ///
     /// The returned `Read` instance will first read all bytes from this object
     /// until EOF is encountered. Afterwards the output is equivalent to the
@@ -927,7 +927,7 @@ pub trait Read {
         Chain { first: self, second: next, done_first: false }
     }
 
-    /// Creates an adaptor which will read at most `limit` bytes from it.
+    /// Creates an adapter which will read at most `limit` bytes from it.
     ///
     /// This function returns a new instance of `Read` which will read at most
     /// `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
@@ -1326,7 +1326,7 @@ impl Initializer {
 /// * The [`write`] method will attempt to write some data into the object,
 ///   returning how many bytes were successfully written.
 ///
-/// * The [`flush`] method is useful for adaptors and explicit buffers
+/// * The [`flush`] method is useful for adapters and explicit buffers
 ///   themselves for ensuring that all buffered data has been pushed out to the
 ///   'true sink'.
 ///
@@ -1646,12 +1646,12 @@ pub trait Write {
     fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> {
         // Create a shim which translates a Write to a fmt::Write and saves
         // off I/O errors. instead of discarding them
-        struct Adaptor<'a, T: ?Sized + 'a> {
+        struct Adapter<'a, T: ?Sized + 'a> {
             inner: &'a mut T,
             error: Result<()>,
         }
 
-        impl<T: Write + ?Sized> fmt::Write for Adaptor<'_, T> {
+        impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
             fn write_str(&mut self, s: &str) -> fmt::Result {
                 match self.inner.write_all(s.as_bytes()) {
                     Ok(()) => Ok(()),
@@ -1663,7 +1663,7 @@ pub trait Write {
             }
         }
 
-        let mut output = Adaptor { inner: self, error: Ok(()) };
+        let mut output = Adapter { inner: self, error: Ok(()) };
         match fmt::write(&mut output, fmt) {
             Ok(()) => Ok(()),
             Err(..) => {
@@ -1677,9 +1677,9 @@ pub trait Write {
         }
     }
 
-    /// Creates a "by reference" adaptor for this instance of `Write`.
+    /// Creates a "by reference" adapter for this instance of `Write`.
     ///
-    /// The returned adaptor also implements `Write` and will simply borrow this
+    /// The returned adapter also implements `Write` and will simply borrow this
     /// current writer.
     ///
     /// # Examples
@@ -2263,7 +2263,7 @@ pub trait BufRead: Read {
     }
 }
 
-/// Adaptor to chain together two readers.
+/// Adapter to chain together two readers.
 ///
 /// This struct is generally created by calling [`chain`] on a reader.
 /// Please see the documentation of [`chain`] for more details.
@@ -2414,7 +2414,7 @@ impl<T, U> SizeHint for Chain<T, U> {
     }
 }
 
-/// Reader adaptor which limits the bytes read from an underlying reader.
+/// Reader adapter which limits the bytes read from an underlying reader.
 ///
 /// This struct is generally created by calling [`take`] on a reader.
 /// Please see the documentation of [`take`] for more details.
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
index 88309875978..a23e29c5c76 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip.rs
@@ -116,16 +116,58 @@ pub struct Ipv6Addr {
     inner: c::in6_addr,
 }
 
-#[allow(missing_docs)]
+/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2].
+///
+/// # Stability Guarantees
+///
+/// Not all possible values for a multicast scope have been assigned.
+/// Future RFCs may introduce new scopes, which will be added as variants to this enum;
+/// because of this the enum is marked as `#[non_exhaustive]`.
+///
+/// # Examples
+/// ```
+/// #![feature(ip)]
+///
+/// use std::net::Ipv6Addr;
+/// use std::net::Ipv6MulticastScope::*;
+///
+/// // An IPv6 multicast address with global scope (`ff0e::`).
+/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0);
+///
+/// // Will print "Global scope".
+/// match address.multicast_scope() {
+///     Some(InterfaceLocal) => println!("Interface-Local scope"),
+///     Some(LinkLocal) => println!("Link-Local scope"),
+///     Some(RealmLocal) => println!("Realm-Local scope"),
+///     Some(AdminLocal) => println!("Admin-Local scope"),
+///     Some(SiteLocal) => println!("Site-Local scope"),
+///     Some(OrganizationLocal) => println!("Organization-Local scope"),
+///     Some(Global) => println!("Global scope"),
+///     Some(_) => println!("Unknown scope"),
+///     None => println!("Not a multicast address!")
+/// }
+///
+/// ```
+///
+/// [IPv6 multicast address]: Ipv6Addr
+/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2
 #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
 #[unstable(feature = "ip", issue = "27709")]
+#[non_exhaustive]
 pub enum Ipv6MulticastScope {
+    /// Interface-Local scope.
     InterfaceLocal,
+    /// Link-Local scope.
     LinkLocal,
+    /// Realm-Local scope.
     RealmLocal,
+    /// Admin-Local scope.
     AdminLocal,
+    /// Site-Local scope.
     SiteLocal,
+    /// Organization-Local scope.
     OrganizationLocal,
+    /// Global scope.
     Global,
 }
 
@@ -486,8 +528,7 @@ impl Ipv4Addr {
     /// - addresses used for documentation (see [`Ipv4Addr::is_documentation()`])
     /// - the unspecified address (see [`Ipv4Addr::is_unspecified()`]), and the whole
     ///   `0.0.0.0/8` block
-    /// - addresses reserved for future protocols (see
-    /// [`Ipv4Addr::is_ietf_protocol_assignment()`], except
+    /// - addresses reserved for future protocols, except
     /// `192.0.0.9/32` and `192.0.0.10/32` which are globally routable
     /// - addresses reserved for future use (see [`Ipv4Addr::is_reserved()`]
     /// - addresses reserved for networking devices benchmarking (see
@@ -560,7 +601,8 @@ impl Ipv4Addr {
             && !self.is_broadcast()
             && !self.is_documentation()
             && !self.is_shared()
-            && !self.is_ietf_protocol_assignment()
+            // addresses reserved for future protocols (`192.0.0.0/24`)
+            && !(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0)
             && !self.is_reserved()
             && !self.is_benchmarking()
             // Make sure the address is not in 0.0.0.0/8
@@ -589,40 +631,6 @@ impl Ipv4Addr {
         self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
     }
 
-    /// Returns [`true`] if this address is part of `192.0.0.0/24`, which is reserved to
-    /// IANA for IETF protocol assignments, as documented in [IETF RFC 6890].
-    ///
-    /// Note that parts of this block are in use:
-    ///
-    /// - `192.0.0.8/32` is the "IPv4 dummy address" (see [IETF RFC 7600])
-    /// - `192.0.0.9/32` is the "Port Control Protocol Anycast" (see [IETF RFC 7723])
-    /// - `192.0.0.10/32` is used for NAT traversal (see [IETF RFC 8155])
-    ///
-    /// [IETF RFC 6890]: https://tools.ietf.org/html/rfc6890
-    /// [IETF RFC 7600]: https://tools.ietf.org/html/rfc7600
-    /// [IETF RFC 7723]: https://tools.ietf.org/html/rfc7723
-    /// [IETF RFC 8155]: https://tools.ietf.org/html/rfc8155
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(ip)]
-    /// use std::net::Ipv4Addr;
-    ///
-    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 0).is_ietf_protocol_assignment(), true);
-    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 8).is_ietf_protocol_assignment(), true);
-    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 9).is_ietf_protocol_assignment(), true);
-    /// assert_eq!(Ipv4Addr::new(192, 0, 0, 255).is_ietf_protocol_assignment(), true);
-    /// assert_eq!(Ipv4Addr::new(192, 0, 1, 0).is_ietf_protocol_assignment(), false);
-    /// assert_eq!(Ipv4Addr::new(191, 255, 255, 255).is_ietf_protocol_assignment(), false);
-    /// ```
-    #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")]
-    #[unstable(feature = "ip", issue = "27709")]
-    #[inline]
-    pub const fn is_ietf_protocol_assignment(&self) -> bool {
-        self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
-    }
-
     /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for
     /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0`
     /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`.
diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs
index 2109980ad05..dbfab9dde40 100644
--- a/library/std/src/net/ip/tests.rs
+++ b/library/std/src/net/ip/tests.rs
@@ -339,7 +339,6 @@ fn ipv4_properties() {
             let broadcast: u16 = 1 << 6;
             let documentation: u16 = 1 << 7;
             let benchmarking: u16 = 1 << 8;
-            let ietf_protocol_assignment: u16 = 1 << 9;
             let reserved: u16 = 1 << 10;
             let shared: u16 = 1 << 11;
 
@@ -397,12 +396,6 @@ fn ipv4_properties() {
                 assert!(!ip!($s).is_benchmarking());
             }
 
-            if ($mask & ietf_protocol_assignment) == ietf_protocol_assignment {
-                assert!(ip!($s).is_ietf_protocol_assignment());
-            } else {
-                assert!(!ip!($s).is_ietf_protocol_assignment());
-            }
-
             if ($mask & reserved) == reserved {
                 assert!(ip!($s).is_reserved());
             } else {
@@ -426,7 +419,6 @@ fn ipv4_properties() {
     let broadcast: u16 = 1 << 6;
     let documentation: u16 = 1 << 7;
     let benchmarking: u16 = 1 << 8;
-    let ietf_protocol_assignment: u16 = 1 << 9;
     let reserved: u16 = 1 << 10;
     let shared: u16 = 1 << 11;
 
@@ -449,9 +441,9 @@ fn ipv4_properties() {
     check!("198.18.0.0", benchmarking);
     check!("198.18.54.2", benchmarking);
     check!("198.19.255.255", benchmarking);
-    check!("192.0.0.0", ietf_protocol_assignment);
-    check!("192.0.0.255", ietf_protocol_assignment);
-    check!("192.0.0.100", ietf_protocol_assignment);
+    check!("192.0.0.0");
+    check!("192.0.0.255");
+    check!("192.0.0.100");
     check!("240.0.0.0", reserved);
     check!("251.54.1.76", reserved);
     check!("254.255.255.255", reserved);
@@ -823,9 +815,6 @@ fn ipv4_const() {
     const IS_SHARED: bool = IP_ADDRESS.is_shared();
     assert!(!IS_SHARED);
 
-    const IS_IETF_PROTOCOL_ASSIGNMENT: bool = IP_ADDRESS.is_ietf_protocol_assignment();
-    assert!(!IS_IETF_PROTOCOL_ASSIGNMENT);
-
     const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking();
     assert!(!IS_BENCHMARKING);
 
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index 10c19424953..185b68c0a78 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -32,6 +32,8 @@ pub mod memchr;
 pub mod mutex;
 pub mod net;
 pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
 #[path = "../unix/path.rs"]
 pub mod path;
 #[path = "../unsupported/pipe.rs"]
@@ -47,7 +49,6 @@ pub mod thread_local_key;
 pub mod time;
 
 use crate::io::ErrorKind;
-pub use crate::sys_common::os_str_bytes as os_str;
 
 #[allow(unused_extern_crates)]
 pub extern crate hermit_abi as abi;
diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs
index eeb30a578c0..8f927df85be 100644
--- a/library/std/src/sys/hermit/os.rs
+++ b/library/std/src/sys/hermit/os.rs
@@ -140,13 +140,8 @@ pub fn env() -> Env {
     }
 }
 
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
-    unsafe {
-        match ENV.as_ref().unwrap().lock().unwrap().get_mut(k) {
-            Some(value) => Ok(Some(value.clone())),
-            None => Ok(None),
-        }
-    }
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
 }
 
 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
index fce6b420732..a2a763c2b4e 100644
--- a/library/std/src/sys/sgx/mod.rs
+++ b/library/std/src/sys/sgx/mod.rs
@@ -26,6 +26,8 @@ pub mod memchr;
 pub mod mutex;
 pub mod net;
 pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
 pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
@@ -37,8 +39,6 @@ pub mod thread;
 pub mod thread_local_key;
 pub mod time;
 
-pub use crate::sys_common::os_str_bytes as os_str;
-
 // SAFETY: must be called only once during runtime initialization.
 // NOTE: this is not guaranteed to run, for example when Rust code is called externally.
 pub unsafe fn init(argc: isize, argv: *const *const u8) {
diff --git a/library/std/src/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs
index 144248d60c9..5f8b8def7c6 100644
--- a/library/std/src/sys/sgx/os.rs
+++ b/library/std/src/sys/sgx/os.rs
@@ -106,8 +106,8 @@ pub fn env() -> Env {
     get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default().into_iter()
 }
 
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
-    Ok(get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()))
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
 }
 
 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 9e553ec7682..f827a4bca53 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -30,6 +30,7 @@ pub mod net;
 #[cfg(target_os = "l4re")]
 pub use self::l4re::net;
 pub mod os;
+pub mod os_str;
 pub mod path;
 pub mod pipe;
 pub mod process;
@@ -42,8 +43,6 @@ pub mod thread_local_dtor;
 pub mod thread_local_key;
 pub mod time;
 
-pub use crate::sys_common::os_str_bytes as os_str;
-
 // SAFETY: must be called only once during runtime initialization.
 // NOTE: this is not guaranteed to run, for example when Rust code is called externally.
 pub unsafe fn init(argc: isize, argv: *const *const u8) {
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index cc0802ed709..dfbd93c90e6 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -532,19 +532,18 @@ pub fn env() -> Env {
     }
 }
 
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+pub fn getenv(k: &OsStr) -> Option<OsString> {
     // environment variables with a nul byte can't be set, so their value is
     // always None as well
-    let k = CString::new(k.as_bytes())?;
+    let k = CString::new(k.as_bytes()).ok()?;
     unsafe {
         let _guard = env_read_lock();
         let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
-        let ret = if s.is_null() {
+        if s.is_null() {
             None
         } else {
             Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
-        };
-        Ok(ret)
+        }
     }
 }
 
diff --git a/library/std/src/sys_common/os_str_bytes.rs b/library/std/src/sys/unix/os_str.rs
index 569600470db..ae96d6b4df4 100644
--- a/library/std/src/sys_common/os_str_bytes.rs
+++ b/library/std/src/sys/unix/os_str.rs
@@ -13,6 +13,7 @@ use crate::sys_common::{AsInner, IntoInner};
 use core::str::lossy::{Utf8Lossy, Utf8LossyChunk};
 
 #[cfg(test)]
+#[path = "../unix/os_str/tests.rs"]
 mod tests;
 
 #[derive(Hash)]
diff --git a/library/std/src/sys_common/os_str_bytes/tests.rs b/library/std/src/sys/unix/os_str/tests.rs
index 37967378155..37967378155 100644
--- a/library/std/src/sys_common/os_str_bytes/tests.rs
+++ b/library/std/src/sys/unix/os_str/tests.rs
diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs
index 4e6c301d29f..a06b44e96a9 100644
--- a/library/std/src/sys/unsupported/common.rs
+++ b/library/std/src/sys/unsupported/common.rs
@@ -4,8 +4,6 @@ pub mod memchr {
     pub use core::slice::memchr::{memchr, memrchr};
 }
 
-pub use crate::sys_common::os_str_bytes as os_str;
-
 // This is not necessarily correct. May want to consider making it part of the
 // spec definition?
 use crate::os::raw::c_char;
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
index 3ef4c6b8a8f..a1276193bda 100644
--- a/library/std/src/sys/unsupported/mod.rs
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -11,6 +11,8 @@ pub mod io;
 pub mod mutex;
 pub mod net;
 pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
 #[path = "../unix/path.rs"]
 pub mod path;
 pub mod pipe;
diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs
index e30395a0b1d..2886ec1180e 100644
--- a/library/std/src/sys/unsupported/os.rs
+++ b/library/std/src/sys/unsupported/os.rs
@@ -76,8 +76,8 @@ pub fn env() -> Env {
     panic!("not supported on this platform")
 }
 
-pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
-    Ok(None)
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+    None
 }
 
 pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index 4af99bfa464..8d62335aae5 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -32,7 +32,8 @@ pub mod io;
 pub mod mutex;
 pub mod net;
 pub mod os;
-pub use crate::sys_common::os_str_bytes as os_str;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
 #[path = "../unix/path.rs"]
 pub mod path;
 #[path = "../unsupported/pipe.rs"]
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
index f129ee55a83..c5229a18834 100644
--- a/library/std/src/sys/wasi/os.rs
+++ b/library/std/src/sys/wasi/os.rs
@@ -175,17 +175,16 @@ pub fn env() -> Env {
     }
 }
 
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
-    let k = CString::new(k.as_bytes())?;
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    let k = CString::new(k.as_bytes()).ok()?;
     unsafe {
         let _guard = env_lock();
         let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
-        let ret = if s.is_null() {
+        if s.is_null() {
             None
         } else {
             Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
-        };
-        Ok(ret)
+        }
     }
 }
 
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index cd701a333f8..c81d653a5e3 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -30,6 +30,8 @@ pub mod io;
 pub mod net;
 #[path = "../unsupported/os.rs"]
 pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
 #[path = "../unix/path.rs"]
 pub mod path;
 #[path = "../unsupported/pipe.rs"]
@@ -45,8 +47,6 @@ pub mod thread_local_key;
 #[path = "../unsupported/time.rs"]
 pub mod time;
 
-pub use crate::sys_common::os_str_bytes as os_str;
-
 cfg_if::cfg_if! {
     if #[cfg(target_feature = "atomics")] {
         #[path = "atomics/condvar.rs"]
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index 77c378a66af..8db97ba50a8 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -253,22 +253,13 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
     cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
 }
 
-pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
-    let k = to_u16s(k)?;
-    let res = super::fill_utf16_buf(
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+    let k = to_u16s(k).ok()?;
+    super::fill_utf16_buf(
         |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
         |buf| OsStringExt::from_wide(buf),
-    );
-    match res {
-        Ok(value) => Ok(Some(value)),
-        Err(e) => {
-            if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
-                Ok(None)
-            } else {
-                Err(e)
-            }
-        }
-    }
+    )
+    .ok()
 }
 
 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index db83bad60d8..894440564b7 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -26,10 +26,6 @@ pub mod fs;
 pub mod io;
 pub mod memchr;
 pub mod mutex;
-// `doc` is required because `sys/mod.rs` imports `unix/ext/mod.rs` on Windows
-// when generating documentation.
-#[cfg(any(doc, not(windows)))]
-pub mod os_str_bytes;
 pub mod process;
 pub mod remutex;
 #[macro_use]
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 56ecc6e68a9..f2d841cb335 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -163,14 +163,8 @@ impl StepDescription {
     }
 
     fn maybe_run(&self, builder: &Builder<'_>, pathset: &PathSet) {
-        if builder.config.exclude.iter().any(|e| pathset.has(e)) {
-            eprintln!("Skipping {:?} because it is excluded", pathset);
+        if self.is_excluded(builder, pathset) {
             return;
-        } else if !builder.config.exclude.is_empty() {
-            eprintln!(
-                "{:?} not skipped for {:?} -- not in {:?}",
-                pathset, self.name, builder.config.exclude
-            );
         }
 
         // Determine the targets participating in this rule.
@@ -182,6 +176,21 @@ impl StepDescription {
         }
     }
 
+    fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool {
+        if builder.config.exclude.iter().any(|e| pathset.has(e)) {
+            eprintln!("Skipping {:?} because it is excluded", pathset);
+            return true;
+        }
+
+        if !builder.config.exclude.is_empty() {
+            eprintln!(
+                "{:?} not skipped for {:?} -- not in {:?}",
+                pathset, self.name, builder.config.exclude
+            );
+        }
+        false
+    }
+
     fn run(v: &[StepDescription], builder: &Builder<'_>, paths: &[PathBuf]) {
         let should_runs =
             v.iter().map(|desc| (desc.should_run)(ShouldRun::new(builder))).collect::<Vec<_>>();
@@ -1579,6 +1588,27 @@ impl<'a> Builder<'a> {
         self.cache.put(step, out.clone());
         out
     }
+
+    /// Ensure that a given step is built *only if it's supposed to be built by default*, returning
+    /// its output. This will cache the step, so it's safe (and good!) to call this as often as
+    /// needed to ensure that all dependencies are build.
+    pub(crate) fn ensure_if_default<T, S: Step<Output = Option<T>>>(
+        &'a self,
+        step: S,
+    ) -> S::Output {
+        let desc = StepDescription::from::<S>();
+        let should_run = (desc.should_run)(ShouldRun::new(self));
+
+        // Avoid running steps contained in --exclude
+        for pathset in &should_run.paths {
+            if desc.is_excluded(self, pathset) {
+                return None;
+            }
+        }
+
+        // Only execute if it's supposed to run as default
+        if desc.default && should_run.is_really_default() { self.ensure(step) } else { None }
+    }
 }
 
 #[cfg(test)]
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index c37763243c0..64075e18366 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -8,6 +8,7 @@
 //! out to `rust-installer` still. This may one day be replaced with bits and
 //! pieces of `rustup.rs`!
 
+use std::collections::HashSet;
 use std::env;
 use std::fs;
 use std::path::{Path, PathBuf};
@@ -45,6 +46,13 @@ fn missing_tool(tool_name: &str, skip: bool) {
     }
 }
 
+fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool {
+    if !builder.config.extended {
+        return false;
+    }
+    builder.config.tools.as_ref().map_or(true, |tools| tools.contains(tool))
+}
+
 #[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct Docs {
     pub host: TargetSelection,
@@ -55,7 +63,8 @@ impl Step for Docs {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("src/doc")
+        let default = run.builder.config.docs;
+        run.path("src/doc").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -65,9 +74,6 @@ impl Step for Docs {
     /// Builds the `rust-docs` installer component.
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let host = self.host;
-        if !builder.config.docs {
-            return None;
-        }
         builder.default_doc(&[]);
 
         let dest = "share/doc/rust/html";
@@ -676,8 +682,8 @@ impl Step for Analysis {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        let builder = run.builder;
-        run.path("analysis").default_condition(builder.config.extended)
+        let default = should_build_extended_tool(&run.builder, "analysis");
+        run.path("analysis").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -698,7 +704,6 @@ impl Step for Analysis {
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let compiler = self.compiler;
         let target = self.target;
-        assert!(builder.config.extended);
         if compiler.host != builder.config.build {
             return None;
         }
@@ -953,11 +958,13 @@ pub struct Cargo {
 }
 
 impl Step for Cargo {
-    type Output = GeneratedTarball;
+    type Output = Option<GeneratedTarball>;
+    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("cargo")
+        let default = should_build_extended_tool(&run.builder, "cargo");
+        run.path("cargo").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -971,7 +978,7 @@ impl Step for Cargo {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
+    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let compiler = self.compiler;
         let target = self.target;
 
@@ -996,7 +1003,7 @@ impl Step for Cargo {
             }
         }
 
-        tarball.generate()
+        Some(tarball.generate())
     }
 }
 
@@ -1009,9 +1016,11 @@ pub struct Rls {
 impl Step for Rls {
     type Output = Option<GeneratedTarball>;
     const ONLY_HOSTS: bool = true;
+    const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("rls")
+        let default = should_build_extended_tool(&run.builder, "rls");
+        run.path("rls").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1028,7 +1037,6 @@ impl Step for Rls {
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let compiler = self.compiler;
         let target = self.target;
-        assert!(builder.config.extended);
 
         let rls = builder
             .ensure(tool::Rls { compiler, target, extra_features: Vec::new() })
@@ -1054,10 +1062,12 @@ pub struct RustAnalyzer {
 
 impl Step for RustAnalyzer {
     type Output = Option<GeneratedTarball>;
+    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("rust-analyzer")
+        let default = should_build_extended_tool(&run.builder, "rust-analyzer");
+        run.path("rust-analyzer").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1080,7 +1090,6 @@ impl Step for RustAnalyzer {
         }
         let compiler = self.compiler;
         let target = self.target;
-        assert!(builder.config.extended);
 
         if target.contains("riscv64") {
             // riscv64 currently has an LLVM bug that makes rust-analyzer unable
@@ -1108,11 +1117,13 @@ pub struct Clippy {
 }
 
 impl Step for Clippy {
-    type Output = GeneratedTarball;
+    type Output = Option<GeneratedTarball>;
+    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("clippy")
+        let default = should_build_extended_tool(&run.builder, "clippy");
+        run.path("clippy").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1126,10 +1137,9 @@ impl Step for Clippy {
         });
     }
 
-    fn run(self, builder: &Builder<'_>) -> GeneratedTarball {
+    fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let compiler = self.compiler;
         let target = self.target;
-        assert!(builder.config.extended);
 
         // Prepare the image directory
         // We expect clippy to build, because we've exited this step above if tool
@@ -1147,7 +1157,7 @@ impl Step for Clippy {
         tarball.add_file(clippy, "bin", 0o755);
         tarball.add_file(cargoclippy, "bin", 0o755);
         tarball.add_legal_and_readme_to("share/doc/clippy");
-        tarball.generate()
+        Some(tarball.generate())
     }
 }
 
@@ -1159,10 +1169,12 @@ pub struct Miri {
 
 impl Step for Miri {
     type Output = Option<GeneratedTarball>;
+    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("miri")
+        let default = should_build_extended_tool(&run.builder, "miri");
+        run.path("miri").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1185,7 +1197,6 @@ impl Step for Miri {
         }
         let compiler = self.compiler;
         let target = self.target;
-        assert!(builder.config.extended);
 
         let miri = builder
             .ensure(tool::Miri { compiler, target, extra_features: Vec::new() })
@@ -1218,10 +1229,12 @@ pub struct Rustfmt {
 
 impl Step for Rustfmt {
     type Output = Option<GeneratedTarball>;
+    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("rustfmt")
+        let default = should_build_extended_tool(&run.builder, "rustfmt");
+        run.path("rustfmt").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1270,10 +1283,17 @@ pub struct RustDemangler {
 
 impl Step for RustDemangler {
     type Output = Option<GeneratedTarball>;
+    const DEFAULT: bool = true;
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("rust-demangler")
+        // While other tools use `should_build_extended_tool` to decide whether to be run by
+        // default or not, `rust-demangler` must be build when *either* it's enabled as a tool like
+        // the other ones or if `profiler = true`. Because we don't know the target at this stage
+        // we run the step by default when only `extended = true`, and decide whether to actually
+        // run it or not later.
+        let default = run.builder.config.extended;
+        run.path("rust-demangler").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -1290,11 +1310,11 @@ impl Step for RustDemangler {
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let compiler = self.compiler;
         let target = self.target;
-        assert!(builder.config.extended);
 
         // Only build this extended tool if explicitly included in `tools`, or if `profiler = true`
-        let profiler = builder.config.profiler_enabled(target);
-        if !builder.config.tools.as_ref().map_or(profiler, |t| t.contains("rust-demangler")) {
+        let condition = should_build_extended_tool(builder, "rust-demangler")
+            || builder.config.profiler_enabled(target);
+        if builder.config.extended && !condition {
             return None;
         }
 
@@ -1345,51 +1365,44 @@ impl Step for Extended {
 
         builder.info(&format!("Dist extended stage{} ({})", compiler.stage, target));
 
-        let rustc_installer = builder.ensure(Rustc { compiler: builder.compiler(stage, target) });
-        let cargo_installer = builder.ensure(Cargo { compiler, target });
-        let rustfmt_installer = builder.ensure(Rustfmt { compiler, target });
-        let rust_demangler_installer = builder.ensure(RustDemangler { compiler, target });
-        let rls_installer = builder.ensure(Rls { compiler, target });
-        let rust_analyzer_installer = builder.ensure(RustAnalyzer { compiler, target });
-        let llvm_tools_installer = builder.ensure(LlvmTools { target });
-        let clippy_installer = builder.ensure(Clippy { compiler, target });
-        let miri_installer = builder.ensure(Miri { compiler, target });
-        let mingw_installer = builder.ensure(Mingw { host: target });
-        let analysis_installer = builder.ensure(Analysis { compiler, target });
-
-        let docs_installer = builder.ensure(Docs { host: target });
-        let std_installer = builder.ensure(Std { compiler, target });
-
-        let etc = builder.src.join("src/etc/installer");
-
-        // Avoid producing tarballs during a dry run.
-        if builder.config.dry_run {
-            return;
+        let mut tarballs = Vec::new();
+        let mut built_tools = HashSet::new();
+        macro_rules! add_component {
+            ($name:expr => $step:expr) => {
+                if let Some(tarball) = builder.ensure_if_default($step) {
+                    tarballs.push(tarball);
+                    built_tools.insert($name);
+                }
+            };
         }
 
         // When rust-std package split from rustc, we needed to ensure that during
         // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
         // the std files during uninstall. To do this ensure that rustc comes
         // before rust-std in the list below.
-        let mut tarballs = Vec::new();
-        tarballs.push(rustc_installer);
-        tarballs.push(cargo_installer);
-        tarballs.push(clippy_installer);
-        tarballs.extend(rust_demangler_installer.clone());
-        tarballs.extend(rls_installer.clone());
-        tarballs.extend(rust_analyzer_installer.clone());
-        tarballs.extend(miri_installer.clone());
-        tarballs.extend(rustfmt_installer.clone());
-        tarballs.extend(llvm_tools_installer);
-        if let Some(analysis_installer) = analysis_installer {
-            tarballs.push(analysis_installer);
-        }
-        tarballs.push(std_installer.expect("missing std"));
-        if let Some(docs_installer) = docs_installer {
-            tarballs.push(docs_installer);
+        tarballs.push(builder.ensure(Rustc { compiler: builder.compiler(stage, target) }));
+        tarballs.push(builder.ensure(Std { compiler, target }).expect("missing std"));
+
+        if target.contains("windows-gnu") {
+            tarballs.push(builder.ensure(Mingw { host: target }).expect("missing mingw"));
         }
-        if target.contains("pc-windows-gnu") {
-            tarballs.push(mingw_installer.unwrap());
+
+        add_component!("rust-docs" => Docs { host: target });
+        add_component!("rust-demangler"=> RustDemangler { compiler, target });
+        add_component!("cargo" => Cargo { compiler, target });
+        add_component!("rustfmt" => Rustfmt { compiler, target });
+        add_component!("rls" => Rls { compiler, target });
+        add_component!("rust-analyzer" => RustAnalyzer { compiler, target });
+        add_component!("llvm-components" => LlvmTools { target });
+        add_component!("clippy" => Clippy { compiler, target });
+        add_component!("miri" => Miri { compiler, target });
+        add_component!("analysis" => Analysis { compiler, target });
+
+        let etc = builder.src.join("src/etc/installer");
+
+        // Avoid producing tarballs during a dry run.
+        if builder.config.dry_run {
+            return;
         }
 
         let tarball = Tarball::new(builder, "rust", &target.triple);
@@ -1434,20 +1447,10 @@ impl Step for Extended {
 
         let xform = |p: &Path| {
             let mut contents = t!(fs::read_to_string(p));
-            if rust_demangler_installer.is_none() {
-                contents = filter(&contents, "rust-demangler");
-            }
-            if rls_installer.is_none() {
-                contents = filter(&contents, "rls");
-            }
-            if rust_analyzer_installer.is_none() {
-                contents = filter(&contents, "rust-analyzer");
-            }
-            if miri_installer.is_none() {
-                contents = filter(&contents, "miri");
-            }
-            if rustfmt_installer.is_none() {
-                contents = filter(&contents, "rustfmt");
+            for tool in &["rust-demangler", "rls", "rust-analyzer", "miri", "rustfmt"] {
+                if !built_tools.contains(tool) {
+                    contents = filter(&contents, tool);
+                }
             }
             let ret = tmp.join(p.file_name().unwrap());
             t!(fs::write(&ret, &contents));
@@ -1485,19 +1488,11 @@ impl Step for Extended {
             prepare("rust-std");
             prepare("rust-analysis");
             prepare("clippy");
-            if rust_demangler_installer.is_some() {
-                prepare("rust-demangler");
-            }
-            if rls_installer.is_some() {
-                prepare("rls");
-            }
-            if rust_analyzer_installer.is_some() {
-                prepare("rust-analyzer");
-            }
-            if miri_installer.is_some() {
-                prepare("miri");
+            for tool in &["rust-demangler", "rls", "rust-analyzer", "miri"] {
+                if built_tools.contains(tool) {
+                    prepare(tool);
+                }
             }
-
             // create an 'uninstall' package
             builder.install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755);
             pkgbuild("uninstall");
@@ -1554,17 +1549,10 @@ impl Step for Extended {
             prepare("rust-docs");
             prepare("rust-std");
             prepare("clippy");
-            if rust_demangler_installer.is_some() {
-                prepare("rust-demangler");
-            }
-            if rls_installer.is_some() {
-                prepare("rls");
-            }
-            if rust_analyzer_installer.is_some() {
-                prepare("rust-analyzer");
-            }
-            if miri_installer.is_some() {
-                prepare("miri");
+            for tool in &["rust-demangler", "rls", "rust-analyzer", "miri"] {
+                if built_tools.contains(tool) {
+                    prepare(tool);
+                }
             }
             if target.contains("windows-gnu") {
                 prepare("rust-mingw");
@@ -1643,7 +1631,7 @@ impl Step for Extended {
                     .arg("-out")
                     .arg(exe.join("StdGroup.wxs")),
             );
-            if rls_installer.is_some() {
+            if built_tools.contains("rls") {
                 builder.run(
                     Command::new(&heat)
                         .current_dir(&exe)
@@ -1662,7 +1650,7 @@ impl Step for Extended {
                         .arg(etc.join("msi/remove-duplicates.xsl")),
                 );
             }
-            if rust_analyzer_installer.is_some() {
+            if built_tools.contains("rust-analyzer") {
                 builder.run(
                     Command::new(&heat)
                         .current_dir(&exe)
@@ -1698,7 +1686,7 @@ impl Step for Extended {
                     .arg("-t")
                     .arg(etc.join("msi/remove-duplicates.xsl")),
             );
-            if rust_demangler_installer.is_some() {
+            if built_tools.contains("rust-demangler") {
                 builder.run(
                     Command::new(&heat)
                         .current_dir(&exe)
@@ -1717,7 +1705,7 @@ impl Step for Extended {
                         .arg(etc.join("msi/remove-duplicates.xsl")),
                 );
             }
-            if miri_installer.is_some() {
+            if built_tools.contains("miri") {
                 builder.run(
                     Command::new(&heat)
                         .current_dir(&exe)
@@ -1790,16 +1778,16 @@ impl Step for Extended {
                     .arg(&input);
                 add_env(builder, &mut cmd, target);
 
-                if rust_demangler_installer.is_some() {
+                if built_tools.contains("rust-demangler") {
                     cmd.arg("-dRustDemanglerDir=rust-demangler");
                 }
-                if rls_installer.is_some() {
+                if built_tools.contains("rls") {
                     cmd.arg("-dRlsDir=rls");
                 }
-                if rust_analyzer_installer.is_some() {
+                if built_tools.contains("rust-analyzer") {
                     cmd.arg("-dRustAnalyzerDir=rust-analyzer");
                 }
-                if miri_installer.is_some() {
+                if built_tools.contains("miri") {
                     cmd.arg("-dMiriDir=miri");
                 }
                 if target.contains("windows-gnu") {
@@ -1815,16 +1803,16 @@ impl Step for Extended {
             candle("CargoGroup.wxs".as_ref());
             candle("StdGroup.wxs".as_ref());
             candle("ClippyGroup.wxs".as_ref());
-            if rust_demangler_installer.is_some() {
+            if built_tools.contains("rust-demangler") {
                 candle("RustDemanglerGroup.wxs".as_ref());
             }
-            if rls_installer.is_some() {
+            if built_tools.contains("rls") {
                 candle("RlsGroup.wxs".as_ref());
             }
-            if rust_analyzer_installer.is_some() {
+            if built_tools.contains("rust-analyzer") {
                 candle("RustAnalyzerGroup.wxs".as_ref());
             }
-            if miri_installer.is_some() {
+            if built_tools.contains("miri") {
                 candle("MiriGroup.wxs".as_ref());
             }
             candle("AnalysisGroup.wxs".as_ref());
@@ -1858,16 +1846,16 @@ impl Step for Extended {
                 .arg("ClippyGroup.wixobj")
                 .current_dir(&exe);
 
-            if rls_installer.is_some() {
+            if built_tools.contains("rls") {
                 cmd.arg("RlsGroup.wixobj");
             }
-            if rust_analyzer_installer.is_some() {
+            if built_tools.contains("rust-analyzer") {
                 cmd.arg("RustAnalyzerGroup.wixobj");
             }
-            if rust_demangler_installer.is_some() {
+            if built_tools.contains("rust-demangler") {
                 cmd.arg("RustDemanglerGroup.wixobj");
             }
-            if miri_installer.is_some() {
+            if built_tools.contains("miri") {
                 cmd.arg("MiriGroup.wixobj");
             }
 
@@ -2003,9 +1991,11 @@ pub struct LlvmTools {
 impl Step for LlvmTools {
     type Output = Option<GeneratedTarball>;
     const ONLY_HOSTS: bool = true;
+    const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path("llvm-tools")
+        let default = should_build_extended_tool(&run.builder, "llvm-tools");
+        run.path("llvm-tools").default_condition(default)
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -2014,7 +2004,6 @@ impl Step for LlvmTools {
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
         let target = self.target;
-        assert!(builder.config.extended);
 
         /* run only if llvm-config isn't used */
         if let Some(config) = builder.config.target_config.get(&target) {
diff --git a/src/bootstrap/install.rs b/src/bootstrap/install.rs
index 8a1b6df0daf..06acf1a9a00 100644
--- a/src/bootstrap/install.rs
+++ b/src/bootstrap/install.rs
@@ -139,12 +139,8 @@ macro_rules! install {
 
 install!((self, builder, _config),
     Docs, "src/doc", _config.docs, only_hosts: false, {
-        if let Some(tarball) = builder.ensure(dist::Docs { host: self.target }) {
-            install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
-        } else {
-            panic!("docs are not available to install, \
-                check that `build.docs` is true in `config.toml`");
-        }
+        let tarball = builder.ensure(dist::Docs { host: self.target }).expect("missing docs");
+        install_sh(builder, "docs", self.compiler.stage, Some(self.target), &tarball);
     };
     Std, "library/std", true, only_hosts: false, {
         for target in &builder.targets {
@@ -158,7 +154,9 @@ install!((self, builder, _config),
         }
     };
     Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
-        let tarball = builder.ensure(dist::Cargo { compiler: self.compiler, target: self.target });
+        let tarball = builder
+            .ensure(dist::Cargo { compiler: self.compiler, target: self.target })
+            .expect("missing cargo");
         install_sh(builder, "cargo", self.compiler.stage, Some(self.target), &tarball);
     };
     Rls, "rls", Self::should_build(_config), only_hosts: true, {
@@ -182,7 +180,9 @@ install!((self, builder, _config),
         }
     };
     Clippy, "clippy", Self::should_build(_config), only_hosts: true, {
-        let tarball = builder.ensure(dist::Clippy { compiler: self.compiler, target: self.target });
+        let tarball = builder
+            .ensure(dist::Clippy { compiler: self.compiler, target: self.target })
+            .expect("missing clippy");
         install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
     };
     Miri, "miri", Self::should_build(_config), only_hosts: true, {
diff --git a/src/test/ui/macros/issue-86082-option-env-invalid-char.rs b/src/test/ui/macros/issue-86082-option-env-invalid-char.rs
new file mode 100644
index 00000000000..b556b24d6aa
--- /dev/null
+++ b/src/test/ui/macros/issue-86082-option-env-invalid-char.rs
@@ -0,0 +1,10 @@
+// check-pass
+//
+// Regression test for issue #86082
+//
+// Checks that option_env! does not panic on receiving an invalid
+// environment variable name.
+
+fn main() {
+    option_env!("\0=");
+}
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index 26a66fb021e..d03d1190d34 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -10,6 +10,6 @@ clap = "2.25.0"
 env_logger = "0.7.1"
 
 [dependencies.mdbook]
-version = "0.4.6"
+version = "0.4.11"
 default-features = false
 features = ["search"]
diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock
index 0e12e81904c..03bb5598007 100644
--- a/src/tools/rustfmt/Cargo.lock
+++ b/src/tools/rustfmt/Cargo.lock
@@ -73,7 +73,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea"
 dependencies = [
  "backtrace-sys",
- "cfg-if",
+ "cfg-if 0.1.10",
  "libc",
  "rustc-demangle",
 ]
@@ -163,6 +163,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
 [[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
 name = "clap"
 version = "2.33.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -207,7 +213,7 @@ version = "0.6.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
  "lazy_static",
 ]
 
@@ -218,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
 dependencies = [
  "autocfg",
- "cfg-if",
+ "cfg-if 0.1.10",
  "lazy_static",
 ]
 
@@ -245,7 +251,7 @@ version = "2.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
  "dirs-sys",
 ]
 
@@ -255,7 +261,7 @@ version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
  "libc",
  "redox_users",
  "winapi",
@@ -401,11 +407,11 @@ checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
 
 [[package]]
 name = "log"
-version = "0.4.8"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 dependencies = [
- "cfg-if",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
@@ -426,7 +432,7 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
 ]
 
 [[package]]
diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md
index 9daa7065379..d2e5613eba9 100644
--- a/src/tools/rustfmt/Configurations.md
+++ b/src/tools/rustfmt/Configurations.md
@@ -1475,7 +1475,9 @@ Copyright 2018 The Rust Project Developers.`, etc.:
 
 ## `match_arm_blocks`
 
-Wrap the body of arms in blocks when it does not fit on the same line with the pattern of arms
+Controls whether arm bodies are wrapped in cases where the first line of the body cannot fit on the same line as the `=>` operator.
+
+The Style Guide requires that bodies are block wrapped by default if a line break is required after the `=>`, but this option can be used to disable that behavior to prevent wrapping arm bodies in that event, so long as the body does not contain multiple statements nor line comments.
 
 - **Default value**: `true`
 - **Possible values**: `true`, `false`
@@ -1486,10 +1488,16 @@ Wrap the body of arms in blocks when it does not fit on the same line with the p
 ```rust
 fn main() {
     match lorem {
-        true => {
+        ipsum => {
             foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
         }
-        false => println!("{}", sit),
+        dolor => println!("{}", sit),
+        sit => foo(
+            "foooooooooooooooooooooooo",
+            "baaaaaaaaaaaaaaaaaaaaaaaarr",
+            "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz",
+            "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx",
+        ),
     }
 }
 ```
@@ -1499,9 +1507,15 @@ fn main() {
 ```rust
 fn main() {
     match lorem {
-        true =>
+        lorem =>
             foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x),
-        false => println!("{}", sit),
+        ipsum => println!("{}", sit),
+        sit => foo(
+            "foooooooooooooooooooooooo",
+            "baaaaaaaaaaaaaaaaaaaaaaaarr",
+            "baaaaaaaaaaaaaaaaaaaazzzzzzzzzzzzz",
+            "qqqqqqqqquuuuuuuuuuuuuuuuuuuuuuuuuuxxx",
+        ),
     }
 }
 ```
diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md
index 1b77dad11f0..e6dc6a22037 100644
--- a/src/tools/rustfmt/Contributing.md
+++ b/src/tools/rustfmt/Contributing.md
@@ -65,7 +65,7 @@ and get a better grasp on the execution flow.
 
 ## Hack!
 
-Here are some [good starting issues](https://github.com/rust-lang/rustfmt/issues?q=is%3Aopen+is%3Aissue+label%3Agood-first-issue).
+Here are some [good starting issues](https://github.com/rust-lang/rustfmt/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
 
 If you've found areas which need polish and don't have issues, please submit a
 PR, don't feel there needs to be an issue.
@@ -138,8 +138,8 @@ format.
 
 There are different nodes for every kind of item and expression in Rust. For
 more details see the source code in the compiler -
-[ast.rs](https://dxr.mozilla.org/rust/source/src/libsyntax/ast.rs) - and/or the
-[docs](https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/index.html).
+[ast.rs](https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast/src/ast.rs) - and/or the
+[docs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html).
 
 Many nodes in the AST (but not all, annoyingly) have a `Span`. A `Span` is a
 range in the source code, it can easily be converted to a snippet of source
diff --git a/src/tools/rustfmt/README.md b/src/tools/rustfmt/README.md
index 500a9f9a37c..9c7a1c4bc34 100644
--- a/src/tools/rustfmt/README.md
+++ b/src/tools/rustfmt/README.md
@@ -230,5 +230,5 @@ Apache License (Version 2.0).
 See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details.
 
 [rust]: https://github.com/rust-lang/rust
-[fmt rfcs]: https://github.com/rust-lang-nursery/fmt-rfcs
-[style guide]: https://github.com/rust-lang-nursery/fmt-rfcs/blob/master/guide/guide.md
+[fmt rfcs]: https://github.com/rust-dev-tools/fmt-rfcs
+[style guide]: https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md
diff --git a/src/tools/rustfmt/appveyor.yml b/src/tools/rustfmt/appveyor.yml
index 7bfe696009f..5ac99fd71f8 100644
--- a/src/tools/rustfmt/appveyor.yml
+++ b/src/tools/rustfmt/appveyor.yml
@@ -1,55 +1,55 @@
-# This is based on https://github.com/japaric/rust-everywhere/blob/master/appveyor.yml

-# and modified (mainly removal of deployment) to suit rustfmt.

-

-environment:

-  global:

-    PROJECT_NAME: rustfmt

-  matrix:

-    # Stable channel

-    # - TARGET: i686-pc-windows-gnu

-    #   CHANNEL: stable

-    # - TARGET: i686-pc-windows-msvc

-    #   CHANNEL: stable

-    # - TARGET: x86_64-pc-windows-gnu

-    #   CHANNEL: stable

-    # - TARGET: x86_64-pc-windows-msvc

-    #   CHANNEL: stable

-    # Beta channel

-    # - TARGET: i686-pc-windows-gnu

-    #   CHANNEL: beta

-    # - TARGET: i686-pc-windows-msvc

-    #   CHANNEL: beta

-    # - TARGET: x86_64-pc-windows-gnu

-    #   CHANNEL: beta

-    # - TARGET: x86_64-pc-windows-msvc

-    #   CHANNEL: beta

-    # Nightly channel

-    - TARGET: i686-pc-windows-gnu

-      CHANNEL: nightly

-    - TARGET: i686-pc-windows-msvc

-      CHANNEL: nightly

-    - TARGET: x86_64-pc-windows-gnu

-      CHANNEL: nightly

-    - TARGET: x86_64-pc-windows-msvc

-      CHANNEL: nightly

-

-# Install Rust and Cargo

-# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)

-install:

-  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe

-  - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin

-  - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin

-  - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin

-  - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y

-  - rustc -Vv

-  - cargo -V

-

-# ???

-build: false

-

-test_script:

-  - set CFG_RELEASE_CHANNEL=nightly

-  - set CFG_RELEASE=nightly

-  - cargo build --verbose

-  - cargo test

-  - cargo test -- --ignored

+# This is based on https://github.com/japaric/rust-everywhere/blob/master/appveyor.yml
+# and modified (mainly removal of deployment) to suit rustfmt.
+
+environment:
+  global:
+    PROJECT_NAME: rustfmt
+  matrix:
+    # Stable channel
+    # - TARGET: i686-pc-windows-gnu
+    #   CHANNEL: stable
+    # - TARGET: i686-pc-windows-msvc
+    #   CHANNEL: stable
+    # - TARGET: x86_64-pc-windows-gnu
+    #   CHANNEL: stable
+    # - TARGET: x86_64-pc-windows-msvc
+    #   CHANNEL: stable
+    # Beta channel
+    # - TARGET: i686-pc-windows-gnu
+    #   CHANNEL: beta
+    # - TARGET: i686-pc-windows-msvc
+    #   CHANNEL: beta
+    # - TARGET: x86_64-pc-windows-gnu
+    #   CHANNEL: beta
+    # - TARGET: x86_64-pc-windows-msvc
+    #   CHANNEL: beta
+    # Nightly channel
+    - TARGET: i686-pc-windows-gnu
+      CHANNEL: nightly
+    - TARGET: i686-pc-windows-msvc
+      CHANNEL: nightly
+    - TARGET: x86_64-pc-windows-gnu
+      CHANNEL: nightly
+    - TARGET: x86_64-pc-windows-msvc
+      CHANNEL: nightly
+
+# Install Rust and Cargo
+# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)
+install:
+  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
+  - if "%TARGET%" == "i686-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw32\bin
+  - if "%TARGET%" == "x86_64-pc-windows-gnu" set PATH=%PATH%;C:\msys64\mingw64\bin
+  - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
+  - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y
+  - rustc -Vv
+  - cargo -V
+
+# ???
+build: false
+
+test_script:
+  - set CFG_RELEASE_CHANNEL=nightly
+  - set CFG_RELEASE=nightly
+  - cargo build --verbose
+  - cargo test
+  - cargo test -- --ignored
diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain
index 7c9d02d933d..b0cd4464df8 100644
--- a/src/tools/rustfmt/rust-toolchain
+++ b/src/tools/rustfmt/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-05-13"
+channel = "nightly-2021-07-23"
 components = ["rustc-dev"]
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index c5ffb074ba5..315eb10a9db 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -183,7 +183,7 @@ fn format_derive(
     } else if let SeparatorTactic::Always = context.config.trailing_comma() {
         // Retain the trailing comma.
         result.push_str(&item_str);
-    } else if item_str.ends_with(",") {
+    } else if item_str.ends_with(',') {
         // Remove the trailing comma.
         result.push_str(&item_str[..item_str.len() - 1]);
     } else {
diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs
index 56b07222212..4b4aa42d935 100644
--- a/src/tools/rustfmt/src/bin/main.rs
+++ b/src/tools/rustfmt/src/bin/main.rs
@@ -178,12 +178,15 @@ fn make_opts() -> Options {
     opts.optflag("v", "verbose", "Print verbose output");
     opts.optflag("q", "quiet", "Print less output");
     opts.optflag("V", "version", "Show version information");
-    opts.optflagopt(
-        "h",
-        "help",
-        "Show this message or help about a specific topic: `config` or `file-lines`",
-        "=TOPIC",
-    );
+    let help_topics = if is_nightly {
+        "`config` or `file-lines`"
+    } else {
+        "`config`"
+    };
+    let mut help_topic_msg = "Show this message or help about a specific topic: ".to_owned();
+    help_topic_msg.push_str(help_topics);
+
+    opts.optflagopt("h", "help", &help_topic_msg, "=TOPIC");
 
     opts
 }
@@ -437,7 +440,7 @@ fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> {
             return Ok(Operation::Help(HelpOp::None));
         } else if topic == Some("config".to_owned()) {
             return Ok(Operation::Help(HelpOp::Config));
-        } else if topic == Some("file-lines".to_owned()) {
+        } else if topic == Some("file-lines".to_owned()) && is_nightly() {
             return Ok(Operation::Help(HelpOp::FileLines));
         } else {
             return Err(OperationError::UnknownHelpTopic(topic.unwrap()));
@@ -689,6 +692,7 @@ fn edition_from_edition_str(edition_str: &str) -> Result<Edition> {
     match edition_str {
         "2015" => Ok(Edition::Edition2015),
         "2018" => Ok(Edition::Edition2018),
+        "2021" => Ok(Edition::Edition2021),
         _ => Err(format_err!("Invalid value for `--edition`")),
     }
 }
diff --git a/src/tools/rustfmt/src/cargo-fmt/main.rs b/src/tools/rustfmt/src/cargo-fmt/main.rs
index 9062a2952ec..90ffad927e2 100644
--- a/src/tools/rustfmt/src/cargo-fmt/main.rs
+++ b/src/tools/rustfmt/src/cargo-fmt/main.rs
@@ -1,6 +1,7 @@
 // Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/).
 
 #![deny(warnings)]
+#![allow(clippy::match_like_matches_macro)]
 
 use std::cmp::Ordering;
 use std::collections::{BTreeMap, BTreeSet};
@@ -405,8 +406,8 @@ fn get_targets_recursive(
                 .packages
                 .iter()
                 .find(|p| p.name == dependency.name && p.source.is_none());
-            let manifest_path = if dependency_package.is_some() {
-                PathBuf::from(&dependency_package.unwrap().manifest_path)
+            let manifest_path = if let Some(dep_pkg) = dependency_package {
+                PathBuf::from(&dep_pkg.manifest_path)
             } else {
                 let mut package_manifest_path = PathBuf::from(&package.manifest_path);
                 package_manifest_path.pop();
diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs
index 8053f0e8fec..614638ea2ab 100644
--- a/src/tools/rustfmt/src/chains.rs
+++ b/src/tools/rustfmt/src/chains.rs
@@ -231,10 +231,7 @@ impl ChainItem {
     }
 
     fn is_comment(&self) -> bool {
-        match self.kind {
-            ChainItemKind::Comment(..) => true,
-            _ => false,
-        }
+        matches!(self.kind, ChainItemKind::Comment(..))
     }
 
     fn rewrite_method_call(
diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs
index 3d65077ddc2..c9d46aef294 100644
--- a/src/tools/rustfmt/src/closures.rs
+++ b/src/tools/rustfmt/src/closures.rs
@@ -336,7 +336,7 @@ pub(crate) fn rewrite_last_closure(
 
         // We force to use block for the body of the closure for certain kinds of expressions.
         if is_block_closure_forced(context, body) {
-            return rewrite_closure_with_block(body, &prefix, context, body_shape).and_then(
+            return rewrite_closure_with_block(body, &prefix, context, body_shape).map(
                 |body_str| {
                     match fn_decl.output {
                         ast::FnRetTy::Default(..) if body_str.lines().count() <= 7 => {
@@ -344,15 +344,15 @@ pub(crate) fn rewrite_last_closure(
                             // closure.  However, if the closure has a return type, then we must
                             // keep the blocks.
                             match rewrite_closure_expr(body, &prefix, context, shape) {
-                                Some(ref single_line_body_str)
+                                Some(single_line_body_str)
                                     if !single_line_body_str.contains('\n') =>
                                 {
-                                    Some(single_line_body_str.clone())
+                                    single_line_body_str
                                 }
-                                _ => Some(body_str),
+                                _ => body_str,
                             }
                         }
-                        _ => Some(body_str),
+                        _ => body_str,
                     }
                 },
             );
@@ -377,10 +377,7 @@ pub(crate) fn rewrite_last_closure(
 pub(crate) fn args_have_many_closure(args: &[OverflowableItem<'_>]) -> bool {
     args.iter()
         .filter_map(OverflowableItem::to_expr)
-        .filter(|expr| match expr.kind {
-            ast::ExprKind::Closure(..) => true,
-            _ => false,
-        })
+        .filter(|expr| matches!(expr.kind, ast::ExprKind::Closure(..)))
         .count()
         > 1
 }
diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs
index c71302fdd18..0f8118a408e 100644
--- a/src/tools/rustfmt/src/comment.rs
+++ b/src/tools/rustfmt/src/comment.rs
@@ -67,10 +67,7 @@ impl<'a> CommentStyle<'a> {
 
     /// Returns `true` if the commenting style is for documentation.
     pub(crate) fn is_doc_comment(&self) -> bool {
-        match *self {
-            CommentStyle::TripleSlash | CommentStyle::Doc => true,
-            _ => false,
-        }
+        matches!(*self, CommentStyle::TripleSlash | CommentStyle::Doc)
     }
 
     pub(crate) fn opener(&self) -> &'a str {
@@ -689,8 +686,8 @@ impl<'a> CommentRewrite<'a> {
 
         self.code_block_attr = None;
         self.item_block = None;
-        if line.starts_with("```") {
-            self.code_block_attr = Some(CodeBlockAttribute::new(&line[3..]))
+        if let Some(stripped) = line.strip_prefix("```") {
+            self.code_block_attr = Some(CodeBlockAttribute::new(stripped))
         } else if self.fmt.config.wrap_comments() && ItemizedBlock::is_itemized_line(&line) {
             let ib = ItemizedBlock::new(&line);
             self.item_block = Some(ib);
@@ -948,8 +945,8 @@ fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a s
     {
         (&line[4..], true)
     } else if let CommentStyle::Custom(opener) = *style {
-        if line.starts_with(opener) {
-            (&line[opener.len()..], true)
+        if let Some(ref stripped) = line.strip_prefix(opener) {
+            (stripped, true)
         } else {
             (&line[opener.trim_end().len()..], false)
         }
@@ -968,8 +965,8 @@ fn left_trim_comment_line<'a>(line: &'a str, style: &CommentStyle<'_>) -> (&'a s
         || line.starts_with("**")
     {
         (&line[2..], line.chars().nth(1).unwrap() == ' ')
-    } else if line.starts_with('*') {
-        (&line[1..], false)
+    } else if let Some(stripped) = line.strip_prefix('*') {
+        (stripped, false)
     } else {
         (line, line.starts_with(' '))
     }
@@ -1682,8 +1679,8 @@ impl<'a> Iterator for CommentReducer<'a> {
 fn remove_comment_header(comment: &str) -> &str {
     if comment.starts_with("///") || comment.starts_with("//!") {
         &comment[3..]
-    } else if comment.starts_with("//") {
-        &comment[2..]
+    } else if let Some(ref stripped) = comment.strip_prefix("//") {
+        stripped
     } else if (comment.starts_with("/**") && !comment.starts_with("/**/"))
         || comment.starts_with("/*!")
     {
diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs
index 2f567b25521..7fc4486ddcd 100644
--- a/src/tools/rustfmt/src/config/config_type.rs
+++ b/src/tools/rustfmt/src/config/config_type.rs
@@ -409,7 +409,7 @@ macro_rules! create_config {
                 if self.was_set().merge_imports() {
                     eprintln!(
                         "Warning: the `merge_imports` option is deprecated. \
-                        Use `imports_granularity=Crate` instead"
+                        Use `imports_granularity=\"Crate\"` instead"
                     );
                     if !self.was_set().imports_granularity() {
                         self.imports_granularity.2 = if self.merge_imports() {
diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs
index 22dd091cb51..4b799780d85 100644
--- a/src/tools/rustfmt/src/config/file_lines.rs
+++ b/src/tools/rustfmt/src/config/file_lines.rs
@@ -305,7 +305,7 @@ impl str::FromStr for FileLines {
         let mut m = HashMap::new();
         for js in v {
             let (s, r) = JsonSpan::into_tuple(js)?;
-            m.entry(s).or_insert_with(|| vec![]).push(r);
+            m.entry(s).or_insert_with(Vec::new).push(r);
         }
         Ok(FileLines::from_ranges(m))
     }
@@ -322,7 +322,7 @@ impl JsonSpan {
     fn into_tuple(self) -> Result<(FileName, Range), FileLinesError> {
         let (lo, hi) = self.range;
         let canonical = canonicalize_path_string(&self.file)
-            .ok_or_else(|| FileLinesError::CannotCanonicalize(self.file))?;
+            .ok_or(FileLinesError::CannotCanonicalize(self.file))?;
         Ok((canonical, Range::new(lo, hi)))
     }
 }
diff --git a/src/tools/rustfmt/src/config/license.rs b/src/tools/rustfmt/src/config/license.rs
index 121a1b1c151..c7feb502ea9 100644
--- a/src/tools/rustfmt/src/config/license.rs
+++ b/src/tools/rustfmt/src/config/license.rs
@@ -3,7 +3,6 @@ use std::fs::File;
 use std::io;
 use std::io::Read;
 
-use regex;
 use regex::Regex;
 
 #[derive(Debug)]
diff --git a/src/tools/rustfmt/src/emitter/diff.rs b/src/tools/rustfmt/src/emitter/diff.rs
index 9be4fb28f99..2fbbfedb566 100644
--- a/src/tools/rustfmt/src/emitter/diff.rs
+++ b/src/tools/rustfmt/src/emitter/diff.rs
@@ -45,7 +45,7 @@ impl Emitter for DiffEmitter {
             return Ok(EmitterResult { has_diff: true });
         }
 
-        return Ok(EmitterResult { has_diff });
+        Ok(EmitterResult { has_diff })
     }
 }
 
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index bca9f77f959..6cfeb9977a9 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -263,15 +263,12 @@ pub(crate) fn format_expr(
             }
 
             fn needs_space_after_range(rhs: &ast::Expr) -> bool {
-                match rhs.kind {
-                    // Don't format `.. ..` into `....`, which is invalid.
-                    //
-                    // This check is unnecessary for `lhs`, because a range
-                    // starting from another range needs parentheses as `(x ..) ..`
-                    // (`x .. ..` is a range from `x` to `..`).
-                    ast::ExprKind::Range(None, _, _) => true,
-                    _ => false,
-                }
+                // Don't format `.. ..` into `....`, which is invalid.
+                //
+                // This check is unnecessary for `lhs`, because a range
+                // starting from another range needs parentheses as `(x ..) ..`
+                // (`x .. ..` is a range from `x` to `..`).
+                matches!(rhs.kind, ast::ExprKind::Range(None, _, _))
             }
 
             let default_sp_delim = |lhs: Option<&ast::Expr>, rhs: Option<&ast::Expr>| {
@@ -531,7 +528,7 @@ pub(crate) fn rewrite_block_with_visitor(
 
     let inner_attrs = attrs.map(inner_attributes);
     let label_str = rewrite_label(label);
-    visitor.visit_block(block, inner_attrs.as_ref().map(|a| &**a), has_braces);
+    visitor.visit_block(block, inner_attrs.as_deref(), has_braces);
     let visitor_context = visitor.get_context();
     context
         .skipped_range
@@ -595,7 +592,7 @@ pub(crate) fn rewrite_cond(
                 String::from("\n") + &shape.indent.block_only().to_string(context.config);
             control_flow
                 .rewrite_cond(context, shape, &alt_block_sep)
-                .and_then(|rw| Some(rw.0))
+                .map(|rw| rw.0)
         }),
     }
 }
@@ -1157,18 +1154,11 @@ pub(crate) fn is_empty_block(
 }
 
 pub(crate) fn stmt_is_expr(stmt: &ast::Stmt) -> bool {
-    match stmt.kind {
-        ast::StmtKind::Expr(..) => true,
-        _ => false,
-    }
+    matches!(stmt.kind, ast::StmtKind::Expr(..))
 }
 
 pub(crate) fn is_unsafe_block(block: &ast::Block) -> bool {
-    if let ast::BlockCheckMode::Unsafe(..) = block.rules {
-        true
-    } else {
-        false
-    }
+    matches!(block.rules, ast::BlockCheckMode::Unsafe(..))
 }
 
 pub(crate) fn rewrite_literal(
diff --git a/src/tools/rustfmt/src/formatting/newline_style.rs b/src/tools/rustfmt/src/formatting/newline_style.rs
index ac620094900..97c4fc16d6f 100644
--- a/src/tools/rustfmt/src/formatting/newline_style.rs
+++ b/src/tools/rustfmt/src/formatting/newline_style.rs
@@ -77,7 +77,7 @@ fn convert_to_windows_newlines(formatted_text: &String) -> String {
     transformed
 }
 
-fn convert_to_unix_newlines(formatted_text: &String) -> String {
+fn convert_to_unix_newlines(formatted_text: &str) -> String {
     formatted_text.replace(WINDOWS_NEWLINE, UNIX_NEWLINE)
 }
 
diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs
index 0f635fe1ccb..64d78605f0c 100644
--- a/src/tools/rustfmt/src/imports.rs
+++ b/src/tools/rustfmt/src/imports.rs
@@ -374,7 +374,7 @@ impl UseTree {
             UseTreeKind::Nested(ref list) => {
                 // Extract comments between nested use items.
                 // This needs to be done before sorting use items.
-                let items: Vec<_> = itemize_list(
+                let items = itemize_list(
                     context.snippet_provider,
                     list.iter().map(|(tree, _)| tree),
                     "}",
@@ -385,8 +385,8 @@ impl UseTree {
                     context.snippet_provider.span_after(a.span, "{"),
                     a.span.hi(),
                     false,
-                )
-                .collect();
+                );
+
                 // in case of a global path and the nested list starts at the root,
                 // e.g., "::{foo, bar}"
                 if a.prefix.segments.len() == 1 && leading_modsep {
@@ -394,7 +394,7 @@ impl UseTree {
                 }
                 result.path.push(UseSegment::List(
                     list.iter()
-                        .zip(items.into_iter())
+                        .zip(items)
                         .map(|(t, list_item)| {
                             Self::from_ast(context, &t.0, Some(list_item), None, None, None)
                         })
@@ -466,11 +466,8 @@ impl UseTree {
 
         // Normalise foo::self as bar -> foo as bar.
         if let UseSegment::Slf(_) = last {
-            match self.path.last() {
-                Some(UseSegment::Ident(_, None)) => {
-                    aliased_self = true;
-                }
-                _ => {}
+            if let Some(UseSegment::Ident(_, None)) = self.path.last() {
+                aliased_self = true;
             }
         }
 
@@ -572,9 +569,8 @@ impl UseTree {
         match self.path.clone().last().unwrap() {
             UseSegment::List(list) => {
                 if list.len() == 1 && list[0].path.len() == 1 {
-                    match list[0].path[0] {
-                        UseSegment::Slf(..) => return vec![self],
-                        _ => (),
+                    if let UseSegment::Slf(..) = list[0].path[0] {
+                        return vec![self];
                     };
                 }
                 let prefix = &self.path[..self.path.len() - 1];
@@ -790,13 +786,9 @@ fn rewrite_nested_use_tree(
         }
     }
     let has_nested_list = use_tree_list.iter().any(|use_segment| {
-        use_segment
-            .path
-            .last()
-            .map_or(false, |last_segment| match last_segment {
-                UseSegment::List(..) => true,
-                _ => false,
-            })
+        use_segment.path.last().map_or(false, |last_segment| {
+            matches!(last_segment, UseSegment::List(..))
+        })
     });
 
     let remaining_width = if has_nested_list {
diff --git a/src/tools/rustfmt/src/issues.rs b/src/tools/rustfmt/src/issues.rs
index d369b75541e..33fb5522aea 100644
--- a/src/tools/rustfmt/src/issues.rs
+++ b/src/tools/rustfmt/src/issues.rs
@@ -126,11 +126,7 @@ impl BadIssueSeeker {
                     return Seeking::Number {
                         issue: Issue {
                             issue_type: IssueType::Todo,
-                            missing_number: if let ReportTactic::Unnumbered = self.report_todo {
-                                true
-                            } else {
-                                false
-                            },
+                            missing_number: matches!(self.report_todo, ReportTactic::Unnumbered),
                         },
                         part: NumberPart::OpenParen,
                     };
@@ -144,11 +140,7 @@ impl BadIssueSeeker {
                     return Seeking::Number {
                         issue: Issue {
                             issue_type: IssueType::Fixme,
-                            missing_number: if let ReportTactic::Unnumbered = self.report_fixme {
-                                true
-                            } else {
-                                false
-                            },
+                            missing_number: matches!(self.report_fixme, ReportTactic::Unnumbered),
                         },
                         part: NumberPart::OpenParen,
                     };
@@ -196,7 +188,7 @@ impl BadIssueSeeker {
                 }
             }
             NumberPart::Number => {
-                if c >= '0' && c <= '9' {
+                if ('0'..='9').contains(&c) {
                     part = NumberPart::CloseParen;
                 } else {
                     return IssueClassification::Bad(issue);
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 420484c0ba1..0542358c6e7 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -741,7 +741,7 @@ pub(crate) fn format_impl(
                 // there is only one where-clause predicate
                 // recover the suppressed comma in single line where_clause formatting
                 if generics.where_clause.predicates.len() == 1 {
-                    result.push_str(",");
+                    result.push(',');
                 }
                 result.push_str(&format!("{}{{{}}}", sep, sep));
             } else {
@@ -1207,7 +1207,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> {
 
         let fits_single_line = !generic_bounds_str.contains('\n')
             && !where_str.contains('\n')
-            && generic_bounds_str.len() + where_str.len() + 1 <= shape.width;
+            && generic_bounds_str.len() + where_str.len() < shape.width;
         let space = if generic_bounds_str.is_empty() || where_str.is_empty() {
             Cow::from("")
         } else if fits_single_line {
@@ -1236,8 +1236,8 @@ pub(crate) fn format_trait_alias(
     let lhs = format!("{}trait {} =", vis_str, generics_str);
     // 1 = ";"
     let trait_alias_bounds = TraitAliasBounds {
-        generics,
         generic_bounds,
+        generics,
     };
     rewrite_assign_rhs(context, lhs, &trait_alias_bounds, shape.sub_width(1)?).map(|s| s + ";")
 }
@@ -1993,7 +1993,7 @@ impl Rewrite for ast::Param {
             let num_attrs = self.attrs.len();
             (
                 mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()),
-                param_attrs_result.contains("\n"),
+                param_attrs_result.contains('\n'),
             )
         } else {
             (mk_sp(self.span.lo(), self.span.lo()), false)
@@ -3265,22 +3265,16 @@ pub(crate) fn rewrite_extern_crate(
 
 /// Returns `true` for `mod foo;`, false for `mod foo { .. }`.
 pub(crate) fn is_mod_decl(item: &ast::Item) -> bool {
-    match item.kind {
-        ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _)) => false,
-        _ => true,
-    }
+    !matches!(
+        item.kind,
+        ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _))
+    )
 }
 
 pub(crate) fn is_use_item(item: &ast::Item) -> bool {
-    match item.kind {
-        ast::ItemKind::Use(_) => true,
-        _ => false,
-    }
+    matches!(item.kind, ast::ItemKind::Use(_))
 }
 
 pub(crate) fn is_extern_crate(item: &ast::Item) -> bool {
-    match item.kind {
-        ast::ItemKind::ExternCrate(..) => true,
-        _ => false,
-    }
+    matches!(item.kind, ast::ItemKind::ExternCrate(..))
 }
diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs
index ce8a45eea65..206d2f78290 100644
--- a/src/tools/rustfmt/src/lib.rs
+++ b/src/tools/rustfmt/src/lib.rs
@@ -2,6 +2,7 @@
 #![deny(rust_2018_idioms)]
 #![warn(unreachable_pub)]
 #![recursion_limit = "256"]
+#![allow(clippy::match_like_matches_macro)]
 
 #[macro_use]
 extern crate derive_new;
@@ -30,7 +31,6 @@ use std::panic;
 use std::path::PathBuf;
 use std::rc::Rc;
 
-use ignore;
 use rustc_ast::ast;
 use rustc_span::{symbol, DUMMY_SP};
 use thiserror::Error;
@@ -149,10 +149,7 @@ pub enum ErrorKind {
 
 impl ErrorKind {
     fn is_comment(&self) -> bool {
-        match self {
-            ErrorKind::LostComment => true,
-            _ => false,
-        }
+        matches!(self, ErrorKind::LostComment)
     }
 }
 
diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs
index ccf8f784c04..73e886c5563 100644
--- a/src/tools/rustfmt/src/lists.rs
+++ b/src/tools/rustfmt/src/lists.rs
@@ -194,10 +194,7 @@ impl ListItem {
     // Returns `true` if the item causes something to be written.
     fn is_substantial(&self) -> bool {
         fn empty(s: &Option<String>) -> bool {
-            match *s {
-                Some(ref s) if !s.is_empty() => false,
-                _ => true,
-            }
+            !matches!(*s, Some(ref s) if !s.is_empty())
         }
 
         !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment))
@@ -618,8 +615,8 @@ pub(crate) fn extract_post_comment(
     let post_snippet = post_snippet[..comment_end].trim();
     let post_snippet_trimmed = if post_snippet.starts_with(|c| c == ',' || c == ':') {
         post_snippet[1..].trim_matches(white_space)
-    } else if post_snippet.starts_with(separator) {
-        post_snippet[separator.len()..].trim_matches(white_space)
+    } else if let Some(stripped) = post_snippet.strip_prefix(separator) {
+        stripped.trim_matches(white_space)
     }
     // not comment or over two lines
     else if post_snippet.ends_with(',')
@@ -823,7 +820,7 @@ where
 pub(crate) fn total_item_width(item: &ListItem) -> usize {
     comment_len(item.pre_comment.as_ref().map(|x| &(*x)[..]))
         + comment_len(item.post_comment.as_ref().map(|x| &(*x)[..]))
-        + &item.item.as_ref().map_or(0, |s| unicode_str_width(&s))
+        + item.item.as_ref().map_or(0, |s| unicode_str_width(&s))
 }
 
 fn comment_len(comment: Option<&str>) -> usize {
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index bf4769b34aa..6c5e32716c0 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -179,10 +179,10 @@ fn return_macro_parse_failure_fallback(
         .lines()
         .last()
         .map(|closing_line| {
-            closing_line.trim().chars().all(|ch| match ch {
-                '}' | ')' | ']' => true,
-                _ => false,
-            })
+            closing_line
+                .trim()
+                .chars()
+                .all(|ch| matches!(ch, '}' | ')' | ']'))
         })
         .unwrap_or(false);
     if is_like_block_indent_style {
@@ -690,25 +690,22 @@ fn delim_token_to_str(
 
 impl MacroArgKind {
     fn starts_with_brace(&self) -> bool {
-        match *self {
+        matches!(
+            *self,
             MacroArgKind::Repeat(DelimToken::Brace, _, _, _)
-            | MacroArgKind::Delimited(DelimToken::Brace, _) => true,
-            _ => false,
-        }
+                | MacroArgKind::Delimited(DelimToken::Brace, _)
+        )
     }
 
     fn starts_with_dollar(&self) -> bool {
-        match *self {
-            MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..) => true,
-            _ => false,
-        }
+        matches!(
+            *self,
+            MacroArgKind::Repeat(..) | MacroArgKind::MetaVariable(..)
+        )
     }
 
     fn ends_with_space(&self) -> bool {
-        match *self {
-            MacroArgKind::Separator(..) => true,
-            _ => false,
-        }
+        matches!(*self, MacroArgKind::Separator(..))
     }
 
     fn has_meta_var(&self) -> bool {
@@ -1162,10 +1159,10 @@ fn force_space_before(tok: &TokenKind) -> bool {
 }
 
 fn ident_like(tok: &Token) -> bool {
-    match tok.kind {
-        TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_) => true,
-        _ => false,
-    }
+    matches!(
+        tok.kind,
+        TokenKind::Ident(..) | TokenKind::Literal(..) | TokenKind::Lifetime(_)
+    )
 }
 
 fn next_space(tok: &TokenKind) -> SpaceState {
@@ -1399,7 +1396,7 @@ impl MacroBranch {
         // Undo our replacement of macro variables.
         // FIXME: this could be *much* more efficient.
         for (old, new) in &substs {
-            if old_body.find(new).is_some() {
+            if old_body.contains(new) {
                 debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
                 return None;
             }
diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs
index f33fedce92d..140ec226c02 100644
--- a/src/tools/rustfmt/src/matches.rs
+++ b/src/tools/rustfmt/src/matches.rs
@@ -19,7 +19,7 @@ use crate::source_map::SpanUtils;
 use crate::spanned::Spanned;
 use crate::utils::{
     contains_skip, extra_offset, first_line_width, inner_attributes, last_line_extendable, mk_sp,
-    mk_sp_lo_plus_one, semicolon_for_expr, trimmed_last_line_width, unicode_str_width,
+    semicolon_for_expr, trimmed_last_line_width, unicode_str_width,
 };
 
 /// A simple wrapper type against `ast::Arm`. Used inside `write_list()`.
@@ -167,8 +167,9 @@ fn collect_beginning_verts(
     arms.iter()
         .map(|a| {
             context
-                .snippet_provider
-                .opt_span_before(mk_sp_lo_plus_one(a.pat.span.lo()), "|")
+                .snippet(a.pat.span)
+                .starts_with("|")
+                .then(|| a.pat.span().lo())
         })
         .collect()
 }
diff --git a/src/tools/rustfmt/src/missed_spans.rs b/src/tools/rustfmt/src/missed_spans.rs
index 17b11ed6cf4..263d840785a 100644
--- a/src/tools/rustfmt/src/missed_spans.rs
+++ b/src/tools/rustfmt/src/missed_spans.rs
@@ -230,8 +230,7 @@ impl<'a> FmtVisitor<'a> {
         let last_char = big_snippet
             .chars()
             .rev()
-            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
-            .next();
+            .find(|rev_c| ![' ', '\t'].contains(rev_c));
 
         let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));
         let mut on_same_line = false;
@@ -262,7 +261,7 @@ impl<'a> FmtVisitor<'a> {
         let comment_shape = Shape::legacy(comment_width, comment_indent);
 
         if on_same_line {
-            match subslice.find("\n") {
+            match subslice.find('\n') {
                 None => {
                     self.push_str(subslice);
                 }
@@ -299,8 +298,7 @@ impl<'a> FmtVisitor<'a> {
             match snippet[status.line_start..]
                 .chars()
                 // skip trailing whitespaces
-                .skip_while(|c| *c == ' ' || *c == '\t')
-                .next()
+                .find(|c| !(*c == ' ' || *c == '\t'))
             {
                 Some('\n') | Some('\r') => {
                     if !is_last_comment_block(subslice) {
diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs
index d670b0a41e8..e32213467a5 100644
--- a/src/tools/rustfmt/src/overflow.rs
+++ b/src/tools/rustfmt/src/overflow.rs
@@ -126,21 +126,19 @@ impl<'a> OverflowableItem<'a> {
             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
             OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item {
                 ast::NestedMetaItem::Literal(..) => true,
-                ast::NestedMetaItem::MetaItem(ref meta_item) => match meta_item.kind {
-                    ast::MetaItemKind::Word => true,
-                    _ => false,
-                },
+                ast::NestedMetaItem::MetaItem(ref meta_item) => {
+                    matches!(meta_item.kind, ast::MetaItemKind::Word)
+                }
             },
             _ => false,
         }
     }
 
     pub(crate) fn is_expr(&self) -> bool {
-        match self {
-            OverflowableItem::Expr(..) => true,
-            OverflowableItem::MacroArg(MacroArg::Expr(..)) => true,
-            _ => false,
-        }
+        matches!(
+            self,
+            OverflowableItem::Expr(..) | OverflowableItem::MacroArg(MacroArg::Expr(..))
+        )
     }
 
     pub(crate) fn is_nested_call(&self) -> bool {
@@ -154,10 +152,7 @@ impl<'a> OverflowableItem<'a> {
     pub(crate) fn to_expr(&self) -> Option<&'a ast::Expr> {
         match self {
             OverflowableItem::Expr(expr) => Some(expr),
-            OverflowableItem::MacroArg(macro_arg) => match macro_arg {
-                MacroArg::Expr(ref expr) => Some(expr),
-                _ => None,
-            },
+            OverflowableItem::MacroArg(MacroArg::Expr(ref expr)) => Some(expr),
             _ => None,
         }
     }
@@ -178,10 +173,9 @@ impl<'a> OverflowableItem<'a> {
                     ast::NestedMetaItem::MetaItem(..) => true,
                 }
             }
-            OverflowableItem::SegmentParam(seg) => match seg {
-                SegmentParam::Type(ty) => can_be_overflowed_type(context, ty, len),
-                _ => false,
-            },
+            OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => {
+                can_be_overflowed_type(context, ty, len)
+            }
             OverflowableItem::TuplePatField(pat) => can_be_overflowed_pat(context, pat, len),
             OverflowableItem::Ty(ty) => can_be_overflowed_type(context, ty, len),
             _ => false,
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index fa0ef260991..062e9cef9bb 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -238,7 +238,7 @@ impl Rewrite for Pat {
                         if let Some(rw) = p.rewrite(context, shape) {
                             rw
                         } else {
-                            format!("{}", context.snippet(p.span))
+                            context.snippet(p.span).to_string()
                         }
                     })
                     .collect();
@@ -310,23 +310,22 @@ fn rewrite_struct_pat(
         if fields_str.contains('\n') || fields_str.len() > one_line_width {
             // Add a missing trailing comma.
             if context.config.trailing_comma() == SeparatorTactic::Never {
-                fields_str.push_str(",");
+                fields_str.push(',');
             }
-            fields_str.push_str("\n");
+            fields_str.push('\n');
             fields_str.push_str(&nested_shape.indent.to_string(context.config));
-            fields_str.push_str("..");
         } else {
             if !fields_str.is_empty() {
                 // there are preceding struct fields being matched on
                 if tactic == DefinitiveListTactic::Vertical {
                     // if the tactic is Vertical, write_list already added a trailing ,
-                    fields_str.push_str(" ");
+                    fields_str.push(' ');
                 } else {
                     fields_str.push_str(", ");
                 }
             }
-            fields_str.push_str("..");
         }
+        fields_str.push_str("..");
     }
 
     // ast::Pat doesn't have attrs so use &[]
@@ -411,10 +410,7 @@ impl<'a> Spanned for TuplePatField<'a> {
 impl<'a> TuplePatField<'a> {
     fn is_dotdot(&self) -> bool {
         match self {
-            TuplePatField::Pat(pat) => match pat.kind {
-                ast::PatKind::Rest => true,
-                _ => false,
-            },
+            TuplePatField::Pat(pat) => matches!(pat.kind, ast::PatKind::Rest),
             TuplePatField::Dotdot(_) => true,
         }
     }
@@ -510,10 +506,11 @@ fn count_wildcard_suffix_len(
     )
     .collect();
 
-    for item in items.iter().rev().take_while(|i| match i.item {
-        Some(ref internal_string) if internal_string == "_" => true,
-        _ => false,
-    }) {
+    for item in items
+        .iter()
+        .rev()
+        .take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_"))
+    {
         suffix_len += 1;
 
         if item.has_comment() {
diff --git a/src/tools/rustfmt/src/rustfmt_diff.rs b/src/tools/rustfmt/src/rustfmt_diff.rs
index fc2c7d06e26..a394ce07398 100644
--- a/src/tools/rustfmt/src/rustfmt_diff.rs
+++ b/src/tools/rustfmt/src/rustfmt_diff.rs
@@ -56,10 +56,7 @@ impl From<Vec<Mismatch>> for ModifiedLines {
         let chunks = mismatches.into_iter().map(|mismatch| {
             let lines = mismatch.lines.iter();
             let num_removed = lines
-                .filter(|line| match line {
-                    DiffLine::Resulting(_) => true,
-                    _ => false,
-                })
+                .filter(|line| matches!(line, DiffLine::Resulting(_)))
                 .count();
 
             let new_lines = mismatch.lines.into_iter().filter_map(|line| match line {
@@ -94,7 +91,7 @@ impl fmt::Display for ModifiedLines {
                 "{} {} {}",
                 chunk.line_number_orig,
                 chunk.lines_removed,
-                chunk.lines.iter().count()
+                chunk.lines.len()
             )?;
 
             for line in &chunk.lines {
diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs
index 6c500635a95..0fdc097efc2 100644
--- a/src/tools/rustfmt/src/skip.rs
+++ b/src/tools/rustfmt/src/skip.rs
@@ -32,8 +32,8 @@ impl SkipContext {
     }
 }
 
-static RUSTFMT: &'static str = "rustfmt";
-static SKIP: &'static str = "skip";
+static RUSTFMT: &str = "rustfmt";
+static SKIP: &str = "skip";
 
 /// Say if you're playing with `rustfmt`'s skip attribute
 pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool {
@@ -46,7 +46,7 @@ pub(crate) fn is_skip_attr(segments: &[ast::PathSegment]) -> bool {
             segments[1].ident.to_string() == SKIP
                 && ["macros", "attributes"]
                     .iter()
-                    .any(|&n| n == &pprust::path_segment_to_string(&segments[2]))
+                    .any(|&n| n == pprust::path_segment_to_string(&segments[2]))
         }
         _ => false,
     }
diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs
index 5a9a2cbd80c..853336004d8 100644
--- a/src/tools/rustfmt/src/source_file.rs
+++ b/src/tools/rustfmt/src/source_file.rs
@@ -18,7 +18,7 @@ use rustc_data_structures::sync::Lrc;
 
 // Append a newline to the end of each file.
 pub(crate) fn append_newline(s: &mut String) {
-    s.push_str("\n");
+    s.push('\n');
 }
 
 #[cfg(test)]
diff --git a/src/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs
index 080c4f17788..0cb9d817ca2 100644
--- a/src/tools/rustfmt/src/string.rs
+++ b/src/tools/rustfmt/src/string.rs
@@ -57,7 +57,7 @@ impl<'a> StringFormat<'a> {
     /// This allows to fit more graphemes from the string on a line when
     /// SnippetState::EndWithLineFeed.
     fn max_width_without_indent(&self) -> Option<usize> {
-        Some(self.config.max_width().checked_sub(self.line_end.len())?)
+        self.config.max_width().checked_sub(self.line_end.len())
     }
 }
 
@@ -99,7 +99,7 @@ pub(crate) fn rewrite_string<'a>(
                 if is_new_line(grapheme) {
                     // take care of blank lines
                     result = trim_end_but_line_feed(fmt.trim_end, result);
-                    result.push_str("\n");
+                    result.push('\n');
                     if !is_bareline_ok && cur_start + i + 1 < graphemes.len() {
                         result.push_str(&indent_without_newline);
                         result.push_str(fmt.line_start);
diff --git a/src/tools/rustfmt/src/syntux/parser.rs b/src/tools/rustfmt/src/syntux/parser.rs
index 0b94749f3c6..b5fe4335dd3 100644
--- a/src/tools/rustfmt/src/syntux/parser.rs
+++ b/src/tools/rustfmt/src/syntux/parser.rs
@@ -79,7 +79,7 @@ impl<'a> ParserBuilder<'a> {
                 rustc_span::FileName::Custom("stdin".to_owned()),
                 text,
             )
-            .map_err(|db| Some(db)),
+            .map_err(Some),
         }
     }
 }
@@ -196,8 +196,7 @@ impl<'a> Parser<'a> {
         mac: &'a ast::MacCall,
     ) -> Result<Vec<ast::Item>, &'static str> {
         let token_stream = mac.args.inner_tokens();
-        let mut parser =
-            rustc_parse::stream_to_parser(sess.inner(), token_stream.clone(), Some(""));
+        let mut parser = rustc_parse::stream_to_parser(sess.inner(), token_stream, Some(""));
 
         let mut items = vec![];
         let mut process_if_cfg = true;
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 974c0c5990c..c6f89c31065 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -662,7 +662,7 @@ impl Rewrite for ast::Ty {
                 let mut_str = format_mutability(mt.mutbl);
                 let mut_len = mut_str.len();
                 let mut result = String::with_capacity(128);
-                result.push_str("&");
+                result.push('&');
                 let ref_hi = context.snippet_provider.span_after(self.span(), "&");
                 let mut cmnt_lo = ref_hi;
 
@@ -685,7 +685,7 @@ impl Rewrite for ast::Ty {
                     } else {
                         result.push_str(&lt_str);
                     }
-                    result.push_str(" ");
+                    result.push(' ');
                     cmnt_lo = lifetime.ident.span.hi();
                 }
 
@@ -1048,11 +1048,7 @@ fn join_bounds_inner(
                     true,
                 )
                 .map(|v| (v, trailing_span, extendable)),
-                _ => Some((
-                    String::from(strs) + &trailing_str,
-                    trailing_span,
-                    extendable,
-                )),
+                _ => Some((strs + &trailing_str, trailing_span, extendable)),
             }
         },
     )?;
@@ -1089,10 +1085,7 @@ fn rewrite_lifetime_param(
 ) -> Option<String> {
     let result = generic_params
         .iter()
-        .filter(|p| match p.kind {
-            ast::GenericParamKind::Lifetime => true,
-            _ => false,
-        })
+        .filter(|p| matches!(p.kind, ast::GenericParamKind::Lifetime))
         .map(|lt| lt.rewrite(context, shape))
         .collect::<Option<Vec<_>>>()?
         .join(", ");
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index 614cda5f911..06159a1b26e 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -191,7 +191,7 @@ pub(crate) fn outer_attributes(attrs: &[ast::Attribute]) -> Vec<ast::Attribute>
 
 #[inline]
 pub(crate) fn is_single_line(s: &str) -> bool {
-    s.chars().find(|&c| c == '\n').is_none()
+    !s.chars().any(|c| c == '\n')
 }
 
 #[inline]
@@ -260,8 +260,7 @@ fn is_skip(meta_item: &MetaItem) -> bool {
     match meta_item.kind {
         MetaItemKind::Word => {
             let path_str = pprust::path_to_string(&meta_item.path);
-            path_str == &*skip_annotation().as_str()
-                || path_str == &*depr_skip_annotation().as_str()
+            path_str == *skip_annotation().as_str() || path_str == *depr_skip_annotation().as_str()
         }
         MetaItemKind::List(ref l) => {
             meta_item.has_name(sym::cfg_attr) && l.len() == 2 && is_skip_nested(&l[1])
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index 079568630cf..3f251bf7c16 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/src/visitor.rs
@@ -198,7 +198,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
             let missing_span = self.next_span(hi);
             let snippet = self.snippet(missing_span);
             let len = CommentCodeSlices::new(snippet)
-                .nth(0)
+                .next()
                 .and_then(|(kind, _, s)| {
                     if kind == CodeCharKind::Normal {
                         s.rfind('\n')
@@ -293,7 +293,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                     }
                     let span_in_between = mk_sp(last_hi, span.lo() + BytePos::from_usize(offset));
                     let snippet_in_between = self.snippet(span_in_between);
-                    let mut comment_on_same_line = !snippet_in_between.contains("\n");
+                    let mut comment_on_same_line = !snippet_in_between.contains('\n');
 
                     let mut comment_shape =
                         Shape::indented(self.block_indent, config).comment(config);
@@ -301,7 +301,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                         self.push_str(" ");
                         // put the first line of the comment on the same line as the
                         // block's last line
-                        match sub_slice.find("\n") {
+                        match sub_slice.find('\n') {
                             None => {
                                 self.push_str(&sub_slice);
                             }
@@ -764,7 +764,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                 let hi = self.snippet_provider.span_before(search_span, ";");
                 let target_span = mk_sp(mac.span().lo(), hi + BytePos(1));
                 let rewrite = rewrite.map(|rw| {
-                    if !rw.ends_with(";") {
+                    if !rw.ends_with(';') {
                         format!("{};", rw)
                     } else {
                         rw
@@ -921,7 +921,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         !is_skip_attr(segments)
     }
 
-    fn walk_mod_items(&mut self, items: &Vec<rustc_ast::ptr::P<ast::Item>>) {
+    fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P<ast::Item>]) {
         self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&items));
     }
 
@@ -953,10 +953,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
             // break the Stability Guarantee
             // N.B. This could be updated to utilize the version gates.
             let include_next_empty = if stmts.len() > 1 {
-                match (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind) {
-                    (ast::StmtKind::Item(_), ast::StmtKind::Empty) => true,
-                    _ => false,
-                }
+                matches!(
+                    (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind),
+                    (ast::StmtKind::Item(_), ast::StmtKind::Empty)
+                )
             } else {
                 false
             };
diff --git a/src/tools/rustfmt/tests/source/configs/match_arm_leading_pipes/preserve.rs b/src/tools/rustfmt/tests/source/configs/match_arm_leading_pipes/preserve.rs
index ea303e857de..5486877bde1 100644
--- a/src/tools/rustfmt/tests/source/configs/match_arm_leading_pipes/preserve.rs
+++ b/src/tools/rustfmt/tests/source/configs/match_arm_leading_pipes/preserve.rs
@@ -26,3 +26,11 @@ fn bar() {
         _ => {}
     }
 }
+
+fn f(x: NonAscii) -> bool {
+    match x {
+      // foo
+              |   Éfgh => true,
+        _ => false,
+    }
+}
\ No newline at end of file
diff --git a/src/tools/rustfmt/tests/target/configs/match_arm_leading_pipes/preserve.rs b/src/tools/rustfmt/tests/target/configs/match_arm_leading_pipes/preserve.rs
index 2beb1f5d813..4775575842a 100644
--- a/src/tools/rustfmt/tests/target/configs/match_arm_leading_pipes/preserve.rs
+++ b/src/tools/rustfmt/tests/target/configs/match_arm_leading_pipes/preserve.rs
@@ -25,3 +25,11 @@ fn bar() {
         _ => {}
     }
 }
+
+fn f(x: NonAscii) -> bool {
+    match x {
+        // foo
+        | Éfgh => true,
+        _ => false,
+    }
+}
diff --git a/src/tools/rustfmt/tests/target/issue_4868.rs b/src/tools/rustfmt/tests/target/issue_4868.rs
new file mode 100644
index 00000000000..763a82c3231
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/issue_4868.rs
@@ -0,0 +1,17 @@
+enum NonAscii {
+    Abcd,
+    Éfgh,
+}
+
+use NonAscii::*;
+
+fn f(x: NonAscii) -> bool {
+    match x {
+        Éfgh => true,
+        _ => false,
+    }
+}
+
+fn main() {
+    dbg!(f(Abcd));
+}