about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-29 05:19:10 +0000
committerbors <bors@rust-lang.org>2024-07-29 05:19:10 +0000
commita5ee5cbad1dde83dde61959e9436716094e3408c (patch)
tree12450124ab22020a8af917a09187f373e84824d5
parent2e630267b2bce50af3258ce4817e377fa09c145b (diff)
parent0c6d2fb3dcd987667d2b82c531326cfd401f1afa (diff)
downloadrust-a5ee5cbad1dde83dde61959e9436716094e3408c.tar.gz
rust-a5ee5cbad1dde83dde61959e9436716094e3408c.zip
Auto merge of #128330 - matthiaskrgr:rollup-auairjd, r=matthiaskrgr
Rollup of 5 pull requests

Successful merges:

 - #109174 (Replace `io::Cursor::{remaining_slice, is_empty}`)
 - #127290 (Fully document `rustdoc-json-types`)
 - #128055 (std: unsafe-wrap personality::dwarf::eh)
 - #128269 (improve cargo invocations on bootstrap)
 - #128310 (Add missing periods on `BTreeMap` cursor `peek_next` docs)

Failed merges:

 - #127543 (More unsafe attr verification)
 - #128182 (handle no_std targets on std builds)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--library/alloc/src/collections/btree/map.rs6
-rw-r--r--library/std/src/io/cursor.rs65
-rw-r--r--library/std/src/io/tests.rs4
-rw-r--r--library/std/src/sys/personality/dwarf/eh.rs112
-rw-r--r--library/std/src/sys/personality/dwarf/mod.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs58
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs18
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs8
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs14
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs53
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs10
-rw-r--r--src/bootstrap/src/core/builder.rs99
-rw-r--r--src/rustdoc-json-types/lib.rs585
15 files changed, 785 insertions, 257 deletions
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 8fe4cd7144b..d84654e36d7 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -2921,7 +2921,7 @@ impl<'a, K, V> Cursor<'a, K, V> {
     /// Returns a reference to the key and value of the next element without
     /// moving the cursor.
     ///
-    /// If the cursor is at the end of the map then `None` is returned
+    /// If the cursor is at the end of the map then `None` is returned.
     #[unstable(feature = "btree_cursors", issue = "107540")]
     pub fn peek_next(&self) -> Option<(&'a K, &'a V)> {
         self.clone().next()
@@ -2963,7 +2963,7 @@ impl<'a, K, V, A> CursorMut<'a, K, V, A> {
     /// Returns a reference to the key and value of the next element without
     /// moving the cursor.
     ///
-    /// If the cursor is at the end of the map then `None` is returned
+    /// If the cursor is at the end of the map then `None` is returned.
     #[unstable(feature = "btree_cursors", issue = "107540")]
     pub fn peek_next(&mut self) -> Option<(&K, &mut V)> {
         let (k, v) = self.inner.peek_next()?;
@@ -3061,7 +3061,7 @@ impl<'a, K, V, A> CursorMutKey<'a, K, V, A> {
     /// Returns a reference to the key and value of the next element without
     /// moving the cursor.
     ///
-    /// If the cursor is at the end of the map then `None` is returned
+    /// If the cursor is at the end of the map then `None` is returned.
     #[unstable(feature = "btree_cursors", issue = "107540")]
     pub fn peek_next(&mut self) -> Option<(&mut K, &mut V)> {
         let current = self.current.as_mut()?;
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index dd32d77934a..9f913eae095 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -209,55 +209,60 @@ impl<T> Cursor<T>
 where
     T: AsRef<[u8]>,
 {
-    /// Returns the remaining slice.
+    /// Splits the underlying slice at the cursor position and returns them.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(cursor_remaining)]
+    /// #![feature(cursor_split)]
     /// use std::io::Cursor;
     ///
     /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
     ///
-    /// assert_eq!(buff.remaining_slice(), &[1, 2, 3, 4, 5]);
+    /// assert_eq!(buff.split(), ([].as_slice(), [1, 2, 3, 4, 5].as_slice()));
     ///
     /// buff.set_position(2);
-    /// assert_eq!(buff.remaining_slice(), &[3, 4, 5]);
-    ///
-    /// buff.set_position(4);
-    /// assert_eq!(buff.remaining_slice(), &[5]);
+    /// assert_eq!(buff.split(), ([1, 2].as_slice(), [3, 4, 5].as_slice()));
     ///
     /// buff.set_position(6);
-    /// assert_eq!(buff.remaining_slice(), &[]);
+    /// assert_eq!(buff.split(), ([1, 2, 3, 4, 5].as_slice(), [].as_slice()));
     /// ```
-    #[unstable(feature = "cursor_remaining", issue = "86369")]
-    pub fn remaining_slice(&self) -> &[u8] {
-        let len = self.pos.min(self.inner.as_ref().len() as u64);
-        &self.inner.as_ref()[(len as usize)..]
+    #[unstable(feature = "cursor_split", issue = "86369")]
+    pub fn split(&self) -> (&[u8], &[u8]) {
+        let slice = self.inner.as_ref();
+        let pos = self.pos.min(slice.len() as u64);
+        slice.split_at(pos as usize)
     }
+}
 
-    /// Returns `true` if the remaining slice is empty.
+impl<T> Cursor<T>
+where
+    T: AsMut<[u8]>,
+{
+    /// Splits the underlying slice at the cursor position and returns them
+    /// mutably.
     ///
     /// # Examples
     ///
     /// ```
-    /// #![feature(cursor_remaining)]
+    /// #![feature(cursor_split)]
     /// use std::io::Cursor;
     ///
     /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
     ///
-    /// buff.set_position(2);
-    /// assert!(!buff.is_empty());
+    /// assert_eq!(buff.split_mut(), ([].as_mut_slice(), [1, 2, 3, 4, 5].as_mut_slice()));
     ///
-    /// buff.set_position(5);
-    /// assert!(buff.is_empty());
+    /// buff.set_position(2);
+    /// assert_eq!(buff.split_mut(), ([1, 2].as_mut_slice(), [3, 4, 5].as_mut_slice()));
     ///
-    /// buff.set_position(10);
-    /// assert!(buff.is_empty());
+    /// buff.set_position(6);
+    /// assert_eq!(buff.split_mut(), ([1, 2, 3, 4, 5].as_mut_slice(), [].as_mut_slice()));
     /// ```
-    #[unstable(feature = "cursor_remaining", issue = "86369")]
-    pub fn is_empty(&self) -> bool {
-        self.pos >= self.inner.as_ref().len() as u64
+    #[unstable(feature = "cursor_split", issue = "86369")]
+    pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) {
+        let slice = self.inner.as_mut();
+        let pos = self.pos.min(slice.len() as u64);
+        slice.split_at_mut(pos as usize)
     }
 }
 
@@ -319,7 +324,7 @@ where
     T: AsRef<[u8]>,
 {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        let n = Read::read(&mut self.remaining_slice(), buf)?;
+        let n = Read::read(&mut Cursor::split(self).1, buf)?;
         self.pos += n as u64;
         Ok(n)
     }
@@ -327,7 +332,7 @@ where
     fn read_buf(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
         let prev_written = cursor.written();
 
-        Read::read_buf(&mut self.remaining_slice(), cursor.reborrow())?;
+        Read::read_buf(&mut Cursor::split(self).1, cursor.reborrow())?;
 
         self.pos += (cursor.written() - prev_written) as u64;
 
@@ -351,7 +356,7 @@ where
     }
 
     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
-        let result = Read::read_exact(&mut self.remaining_slice(), buf);
+        let result = Read::read_exact(&mut Cursor::split(self).1, buf);
 
         match result {
             Ok(_) => self.pos += buf.len() as u64,
@@ -365,14 +370,14 @@ where
     fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
         let prev_written = cursor.written();
 
-        let result = Read::read_buf_exact(&mut self.remaining_slice(), cursor.reborrow());
+        let result = Read::read_buf_exact(&mut Cursor::split(self).1, cursor.reborrow());
         self.pos += (cursor.written() - prev_written) as u64;
 
         result
     }
 
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
-        let content = self.remaining_slice();
+        let content = Cursor::split(self).1;
         let len = content.len();
         buf.try_reserve(len)?;
         buf.extend_from_slice(content);
@@ -383,7 +388,7 @@ where
 
     fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
         let content =
-            crate::str::from_utf8(self.remaining_slice()).map_err(|_| io::Error::INVALID_UTF8)?;
+            crate::str::from_utf8(Cursor::split(self).1).map_err(|_| io::Error::INVALID_UTF8)?;
         let len = content.len();
         buf.try_reserve(len)?;
         buf.push_str(content);
@@ -399,7 +404,7 @@ where
     T: AsRef<[u8]>,
 {
     fn fill_buf(&mut self) -> io::Result<&[u8]> {
-        Ok(self.remaining_slice())
+        Ok(Cursor::split(self).1)
     }
     fn consume(&mut self, amt: usize) {
         self.pos += amt as u64;
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index 9a1cbb2627f..bb6a53bb290 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -676,13 +676,13 @@ fn cursor_read_exact_eof() {
 
     let mut r = slice.clone();
     assert!(r.read_exact(&mut [0; 10]).is_err());
-    assert!(r.is_empty());
+    assert!(Cursor::split(&r).1.is_empty());
 
     let mut r = slice;
     let buf = &mut [0; 10];
     let mut buf = BorrowedBuf::from(buf.as_mut_slice());
     assert!(r.read_buf_exact(buf.unfilled()).is_err());
-    assert!(r.is_empty());
+    assert!(Cursor::split(&r).1.is_empty());
     assert_eq!(buf.filled(), b"123456");
 }
 
diff --git a/library/std/src/sys/personality/dwarf/eh.rs b/library/std/src/sys/personality/dwarf/eh.rs
index 89172ecf16b..c37c3e442ae 100644
--- a/library/std/src/sys/personality/dwarf/eh.rs
+++ b/library/std/src/sys/personality/dwarf/eh.rs
@@ -70,45 +70,51 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
 
     let func_start = context.func_start;
     let mut reader = DwarfReader::new(lsda);
-
-    let start_encoding = reader.read::<u8>();
-    // base address for landing pad offsets
-    let lpad_base = if start_encoding != DW_EH_PE_omit {
-        read_encoded_pointer(&mut reader, context, start_encoding)?
-    } else {
-        func_start
+    let lpad_base = unsafe {
+        let start_encoding = reader.read::<u8>();
+        // base address for landing pad offsets
+        if start_encoding != DW_EH_PE_omit {
+            read_encoded_pointer(&mut reader, context, start_encoding)?
+        } else {
+            func_start
+        }
     };
+    let call_site_encoding = unsafe {
+        let ttype_encoding = reader.read::<u8>();
+        if ttype_encoding != DW_EH_PE_omit {
+            // Rust doesn't analyze exception types, so we don't care about the type table
+            reader.read_uleb128();
+        }
 
-    let ttype_encoding = reader.read::<u8>();
-    if ttype_encoding != DW_EH_PE_omit {
-        // Rust doesn't analyze exception types, so we don't care about the type table
-        reader.read_uleb128();
-    }
-
-    let call_site_encoding = reader.read::<u8>();
-    let call_site_table_length = reader.read_uleb128();
-    let action_table = reader.ptr.add(call_site_table_length as usize);
+        reader.read::<u8>()
+    };
+    let action_table = unsafe {
+        let call_site_table_length = reader.read_uleb128();
+        reader.ptr.add(call_site_table_length as usize)
+    };
     let ip = context.ip;
 
     if !USING_SJLJ_EXCEPTIONS {
         // read the callsite table
         while reader.ptr < action_table {
-            // these are offsets rather than pointers;
-            let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?;
-            let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?;
-            let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?;
-            let cs_action_entry = reader.read_uleb128();
-            // Callsite table is sorted by cs_start, so if we've passed the ip, we
-            // may stop searching.
-            if ip < func_start.wrapping_add(cs_start) {
-                break;
-            }
-            if ip < func_start.wrapping_add(cs_start + cs_len) {
-                if cs_lpad == 0 {
-                    return Ok(EHAction::None);
-                } else {
-                    let lpad = lpad_base.wrapping_add(cs_lpad);
-                    return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
+            unsafe {
+                // these are offsets rather than pointers;
+                let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?;
+                let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?;
+                let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?;
+                let cs_action_entry = reader.read_uleb128();
+                // Callsite table is sorted by cs_start, so if we've passed the ip, we
+                // may stop searching.
+                if ip < func_start.wrapping_add(cs_start) {
+                    break;
+                }
+                if ip < func_start.wrapping_add(cs_start + cs_len) {
+                    if cs_lpad == 0 {
+                        return Ok(EHAction::None);
+                    } else {
+                        let lpad = lpad_base.wrapping_add(cs_lpad);
+                        return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
+                    }
                 }
             }
         }
@@ -125,15 +131,15 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
         }
         let mut idx = ip.addr();
         loop {
-            let cs_lpad = reader.read_uleb128();
-            let cs_action_entry = reader.read_uleb128();
+            let cs_lpad = unsafe { reader.read_uleb128() };
+            let cs_action_entry = unsafe { reader.read_uleb128() };
             idx -= 1;
             if idx == 0 {
                 // Can never have null landing pad for sjlj -- that would have
                 // been indicated by a -1 call site index.
                 // FIXME(strict provenance)
                 let lpad = ptr::with_exposed_provenance((cs_lpad + 1) as usize);
-                return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
+                return Ok(unsafe { interpret_cs_action(action_table, cs_action_entry, lpad) });
             }
         }
     }
@@ -151,9 +157,9 @@ unsafe fn interpret_cs_action(
     } else {
         // If lpad != 0 and cs_action_entry != 0, we have to check ttype_index.
         // If ttype_index == 0 under the condition, we take cleanup action.
-        let action_record = action_table.offset(cs_action_entry as isize - 1);
+        let action_record = unsafe { action_table.offset(cs_action_entry as isize - 1) };
         let mut action_reader = DwarfReader::new(action_record);
-        let ttype_index = action_reader.read_sleb128();
+        let ttype_index = unsafe { action_reader.read_sleb128() };
         if ttype_index == 0 {
             EHAction::Cleanup(lpad)
         } else if ttype_index > 0 {
@@ -186,18 +192,20 @@ unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result<
     if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 {
         return Err(());
     }
-    let result = match encoding & 0x0F {
-        // despite the name, LLVM also uses absptr for offsets instead of pointers
-        DW_EH_PE_absptr => reader.read::<usize>(),
-        DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
-        DW_EH_PE_udata2 => reader.read::<u16>() as usize,
-        DW_EH_PE_udata4 => reader.read::<u32>() as usize,
-        DW_EH_PE_udata8 => reader.read::<u64>() as usize,
-        DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
-        DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
-        DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
-        DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
-        _ => return Err(()),
+    let result = unsafe {
+        match encoding & 0x0F {
+            // despite the name, LLVM also uses absptr for offsets instead of pointers
+            DW_EH_PE_absptr => reader.read::<usize>(),
+            DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
+            DW_EH_PE_udata2 => reader.read::<u16>() as usize,
+            DW_EH_PE_udata4 => reader.read::<u32>() as usize,
+            DW_EH_PE_udata8 => reader.read::<u64>() as usize,
+            DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
+            DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
+            DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
+            DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
+            _ => return Err(()),
+        }
     };
     Ok(result)
 }
@@ -250,14 +258,14 @@ unsafe fn read_encoded_pointer(
         if encoding & 0x0F != DW_EH_PE_absptr {
             return Err(());
         }
-        reader.read::<*const u8>()
+        unsafe { reader.read::<*const u8>() }
     } else {
-        let offset = read_encoded_offset(reader, encoding & 0x0F)?;
+        let offset = unsafe { read_encoded_offset(reader, encoding & 0x0F)? };
         base_ptr.wrapping_add(offset)
     };
 
     if encoding & DW_EH_PE_indirect != 0 {
-        ptr = *(ptr.cast::<*const u8>());
+        ptr = unsafe { *(ptr.cast::<*const u8>()) };
     }
 
     Ok(ptr)
diff --git a/library/std/src/sys/personality/dwarf/mod.rs b/library/std/src/sys/personality/dwarf/mod.rs
index 89f7f133e21..5c52d96c4ca 100644
--- a/library/std/src/sys/personality/dwarf/mod.rs
+++ b/library/std/src/sys/personality/dwarf/mod.rs
@@ -5,6 +5,7 @@
 // This module is used only by x86_64-pc-windows-gnu for now, but we
 // are compiling it everywhere to avoid regressions.
 #![allow(unused)]
+#![forbid(unsafe_op_in_unsafe_fn)]
 
 #[cfg(test)]
 mod tests;
@@ -17,7 +18,6 @@ pub struct DwarfReader {
     pub ptr: *const u8,
 }
 
-#[forbid(unsafe_op_in_unsafe_fn)]
 impl DwarfReader {
     pub fn new(ptr: *const u8) -> DwarfReader {
         DwarfReader { ptr }
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 1a9aa0c51b6..11ddae2aa24 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -12,16 +12,6 @@ use crate::core::builder::{
 use crate::core::config::TargetSelection;
 use crate::{Compiler, Mode, Subcommand};
 
-pub fn cargo_subcommand(kind: Kind) -> &'static str {
-    match kind {
-        Kind::Check
-        // We ensure check steps for both std and rustc from build_steps/clippy, so handle `Kind::Clippy` as well.
-        | Kind::Clippy => "check",
-        Kind::Fix => "fix",
-        _ => unreachable!(),
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Std {
     pub target: TargetSelection,
@@ -31,11 +21,22 @@ pub struct Std {
     ///
     /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
     crates: Vec<String>,
+    /// Override `Builder::kind` on cargo invocations.
+    ///
+    /// By default, `Builder::kind` is propagated as the subcommand to the cargo invocations.
+    /// However, there are cases when this is not desirable. For example, when running `x clippy $tool_name`,
+    /// passing `Builder::kind` to cargo invocations would run clippy on the entire compiler and library,
+    /// which is not useful if we only want to lint a few crates with specific rules.
+    override_build_kind: Option<Kind>,
 }
 
 impl Std {
     pub fn new(target: TargetSelection) -> Self {
-        Self { target, crates: vec![] }
+        Self::new_with_build_kind(target, None)
+    }
+
+    pub fn new_with_build_kind(target: TargetSelection, kind: Option<Kind>) -> Self {
+        Self { target, crates: vec![], override_build_kind: kind }
     }
 }
 
@@ -49,7 +50,7 @@ impl Step for Std {
 
     fn make_run(run: RunConfig<'_>) {
         let crates = run.make_run_crates(Alias::Library);
-        run.builder.ensure(Std { target: run.target, crates });
+        run.builder.ensure(Std { target: run.target, crates, override_build_kind: None });
     }
 
     fn run(self, builder: &Builder<'_>) {
@@ -64,7 +65,7 @@ impl Step for Std {
             Mode::Std,
             SourceType::InTree,
             target,
-            cargo_subcommand(builder.kind),
+            self.override_build_kind.unwrap_or(builder.kind),
         );
 
         std_cargo(builder, target, compiler.stage, &mut cargo);
@@ -118,7 +119,7 @@ impl Step for Std {
             Mode::Std,
             SourceType::InTree,
             target,
-            cargo_subcommand(builder.kind),
+            self.override_build_kind.unwrap_or(builder.kind),
         );
 
         // If we're not in stage 0, tests and examples will fail to compile
@@ -159,16 +160,31 @@ pub struct Rustc {
     ///
     /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
     crates: Vec<String>,
+    /// Override `Builder::kind` on cargo invocations.
+    ///
+    /// By default, `Builder::kind` is propagated as the subcommand to the cargo invocations.
+    /// However, there are cases when this is not desirable. For example, when running `x clippy $tool_name`,
+    /// passing `Builder::kind` to cargo invocations would run clippy on the entire compiler and library,
+    /// which is not useful if we only want to lint a few crates with specific rules.
+    override_build_kind: Option<Kind>,
 }
 
 impl Rustc {
     pub fn new(target: TargetSelection, builder: &Builder<'_>) -> Self {
+        Self::new_with_build_kind(target, builder, None)
+    }
+
+    pub fn new_with_build_kind(
+        target: TargetSelection,
+        builder: &Builder<'_>,
+        kind: Option<Kind>,
+    ) -> Self {
         let crates = builder
             .in_tree_crates("rustc-main", Some(target))
             .into_iter()
             .map(|krate| krate.name.to_string())
             .collect();
-        Self { target, crates }
+        Self { target, crates, override_build_kind: kind }
     }
 }
 
@@ -183,7 +199,7 @@ impl Step for Rustc {
 
     fn make_run(run: RunConfig<'_>) {
         let crates = run.make_run_crates(Alias::Compiler);
-        run.builder.ensure(Rustc { target: run.target, crates });
+        run.builder.ensure(Rustc { target: run.target, crates, override_build_kind: None });
     }
 
     /// Builds the compiler.
@@ -204,7 +220,7 @@ impl Step for Rustc {
             builder.ensure(crate::core::build_steps::compile::Std::new(compiler, compiler.host));
             builder.ensure(crate::core::build_steps::compile::Std::new(compiler, target));
         } else {
-            builder.ensure(Std::new(target));
+            builder.ensure(Std::new_with_build_kind(target, self.override_build_kind));
         }
 
         let mut cargo = builder::Cargo::new(
@@ -213,7 +229,7 @@ impl Step for Rustc {
             Mode::Rustc,
             SourceType::InTree,
             target,
-            cargo_subcommand(builder.kind),
+            self.override_build_kind.unwrap_or(builder.kind),
         );
 
         rustc_cargo(builder, &mut cargo, target, &compiler);
@@ -291,7 +307,7 @@ impl Step for CodegenBackend {
             Mode::Codegen,
             SourceType::InTree,
             target,
-            cargo_subcommand(builder.kind),
+            builder.kind,
         );
 
         cargo
@@ -349,7 +365,7 @@ impl Step for RustAnalyzer {
             compiler,
             Mode::ToolRustc,
             target,
-            cargo_subcommand(builder.kind),
+            builder.kind,
             "src/tools/rust-analyzer",
             SourceType::InTree,
             &["in-rust-tree".to_owned()],
@@ -417,7 +433,7 @@ macro_rules! tool_check_step {
                     compiler,
                     Mode::ToolRustc,
                     target,
-                    cargo_subcommand(builder.kind),
+                    builder.kind,
                     $path,
                     $source_type,
                     &[],
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index a4be6bc56c5..49310367182 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -11,7 +11,7 @@ use std::path::Path;
 
 use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
 use crate::utils::helpers::t;
-use crate::{Build, Compiler, Mode, Subcommand};
+use crate::{Build, Compiler, Kind, Mode, Subcommand};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CleanAll {}
@@ -66,7 +66,7 @@ macro_rules! clean_crate_tree {
             fn run(self, builder: &Builder<'_>) -> Self::Output {
                 let compiler = self.compiler;
                 let target = compiler.host;
-                let mut cargo = builder.bare_cargo(compiler, $mode, target, "clean");
+                let mut cargo = builder.bare_cargo(compiler, $mode, target, Kind::Clean);
 
                 // Since https://github.com/rust-lang/rust/pull/111076 enables
                 // unstable cargo feature (`public-dependency`), we need to ensure
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index c24840dc23c..e45b44b82de 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -116,8 +116,14 @@ impl Step for Std {
         let target = self.target;
         let compiler = builder.compiler(builder.top_stage, builder.config.build);
 
-        let mut cargo =
-            builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, "clippy");
+        let mut cargo = builder::Cargo::new(
+            builder,
+            compiler,
+            Mode::Std,
+            SourceType::InTree,
+            target,
+            Kind::Clippy,
+        );
 
         std_cargo(builder, target, compiler.stage, &mut cargo);
 
@@ -178,7 +184,7 @@ impl Step for Rustc {
             builder.ensure(compile::Std::new(compiler, compiler.host));
             builder.ensure(compile::Std::new(compiler, target));
         } else {
-            builder.ensure(check::Std::new(target));
+            builder.ensure(check::Std::new_with_build_kind(target, Some(Kind::Check)));
         }
 
         let mut cargo = builder::Cargo::new(
@@ -187,7 +193,7 @@ impl Step for Rustc {
             Mode::Rustc,
             SourceType::InTree,
             target,
-            "clippy",
+            Kind::Clippy,
         );
 
         rustc_cargo(builder, &mut cargo, target, &compiler);
@@ -245,14 +251,14 @@ macro_rules! lint_any {
                 let compiler = builder.compiler(builder.top_stage, builder.config.build);
                 let target = self.target;
 
-                builder.ensure(check::Rustc::new(target, builder));
+                builder.ensure(check::Rustc::new_with_build_kind(target, builder, Some(Kind::Check)));
 
                 let cargo = prepare_tool_cargo(
                     builder,
                     compiler,
                     Mode::ToolRustc,
                     target,
-                    "clippy",
+                    Kind::Clippy,
                     $path,
                     SourceType::InTree,
                     &[],
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index ca0364b8ed5..ce456a72002 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -247,7 +247,7 @@ impl Step for Std {
                 Mode::Std,
                 SourceType::InTree,
                 target,
-                "check",
+                Kind::Check,
             );
             cargo.rustflag("-Zalways-encode-mir");
             cargo.arg("--manifest-path").arg(builder.src.join("library/sysroot/Cargo.toml"));
@@ -259,7 +259,7 @@ impl Step for Std {
                 Mode::Std,
                 SourceType::InTree,
                 target,
-                "build",
+                Kind::Build,
             );
             std_cargo(builder, target, compiler.stage, &mut cargo);
             for krate in &*self.crates {
@@ -919,7 +919,7 @@ impl Step for Rustc {
             Mode::Rustc,
             SourceType::InTree,
             target,
-            "build",
+            Kind::Build,
         );
 
         rustc_cargo(builder, &mut cargo, target, &compiler);
@@ -1359,7 +1359,7 @@ impl Step for CodegenBackend {
             Mode::Codegen,
             SourceType::InTree,
             target,
-            "build",
+            Kind::Build,
         );
         cargo
             .arg("--manifest-path")
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index f83dfcad9ec..0b780dae9c2 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -714,7 +714,7 @@ fn doc_std(
     let out_dir = target_dir.join(target.triple).join("doc");
 
     let mut cargo =
-        builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, "doc");
+        builder::Cargo::new(builder, compiler, Mode::Std, SourceType::InTree, target, Kind::Doc);
 
     compile::std_cargo(builder, target, compiler.stage, &mut cargo);
     cargo
@@ -816,8 +816,14 @@ impl Step for Rustc {
         );
 
         // Build cargo command.
-        let mut cargo =
-            builder::Cargo::new(builder, compiler, Mode::Rustc, SourceType::InTree, target, "doc");
+        let mut cargo = builder::Cargo::new(
+            builder,
+            compiler,
+            Mode::Rustc,
+            SourceType::InTree,
+            target,
+            Kind::Doc,
+        );
 
         cargo.rustdocflag("--document-private-items");
         // Since we always pass --document-private-items, there's no need to warn about linking to private items.
@@ -964,7 +970,7 @@ macro_rules! tool_doc {
                     compiler,
                     Mode::ToolRustc,
                     target,
-                    "doc",
+                    Kind::Doc,
                     $path,
                     source_type,
                     &[],
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index b3ddb028015..fde1693646a 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -8,7 +8,7 @@ use std::path::PathBuf;
 use crate::core::build_steps::dist::distdir;
 use crate::core::build_steps::test;
 use crate::core::build_steps::tool::{self, SourceType, Tool};
-use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::flags::get_completion;
 use crate::core::config::TargetSelection;
 use crate::utils::exec::command;
@@ -142,7 +142,7 @@ impl Step for Miri {
             host_compiler,
             Mode::ToolRustc,
             host,
-            "run",
+            Kind::Run,
             "src/tools/miri",
             SourceType::InTree,
             &[],
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 16425ba9f7c..79b7dd78810 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -68,7 +68,7 @@ impl Step for CrateBootstrap {
             compiler,
             Mode::ToolBootstrap,
             bootstrap_host,
-            "test",
+            Kind::Test,
             path,
             SourceType::InTree,
             &[],
@@ -119,7 +119,7 @@ You can skip linkcheck with --skip src/tools/linkchecker"
             compiler,
             Mode::ToolBootstrap,
             bootstrap_host,
-            "test",
+            Kind::Test,
             "src/tools/linkchecker",
             SourceType::InTree,
             &[],
@@ -284,7 +284,7 @@ impl Step for Cargo {
             compiler,
             Mode::ToolRustc,
             self.host,
-            "test",
+            Kind::Test,
             "src/tools/cargo",
             SourceType::Submodule,
             &[],
@@ -355,7 +355,7 @@ impl Step for RustAnalyzer {
             compiler,
             Mode::ToolRustc,
             host,
-            "test",
+            Kind::Test,
             crate_path,
             SourceType::InTree,
             &["in-rust-tree".to_owned()],
@@ -407,7 +407,7 @@ impl Step for Rustfmt {
             compiler,
             Mode::ToolRustc,
             host,
-            "test",
+            Kind::Test,
             "src/tools/rustfmt",
             SourceType::InTree,
             &[],
@@ -442,7 +442,7 @@ impl Miri {
             Mode::Std,
             SourceType::Submodule,
             target,
-            "miri-setup",
+            Kind::MiriSetup,
         );
 
         // Tell `cargo miri setup` where to find the sources.
@@ -527,7 +527,7 @@ impl Step for Miri {
             host_compiler,
             Mode::ToolRustc,
             host,
-            "test",
+            Kind::Test,
             "src/tools/miri",
             SourceType::InTree,
             &[],
@@ -617,7 +617,7 @@ impl Step for CargoMiri {
             compiler,
             Mode::ToolStd, // it's unclear what to use here, we're not building anything just doing a smoke test!
             target,
-            "miri-test",
+            Kind::MiriTest,
             "src/tools/miri/test-cargo-miri",
             SourceType::Submodule,
             &[],
@@ -677,7 +677,7 @@ impl Step for CompiletestTest {
             // when std sources change.
             Mode::ToolStd,
             host,
-            "test",
+            Kind::Test,
             "src/tools/compiletest",
             SourceType::InTree,
             &[],
@@ -727,7 +727,7 @@ impl Step for Clippy {
             compiler,
             Mode::ToolRustc,
             host,
-            "test",
+            Kind::Test,
             "src/tools/clippy",
             SourceType::InTree,
             &[],
@@ -1277,7 +1277,7 @@ impl Step for RunMakeSupport {
             self.compiler,
             Mode::ToolStd,
             self.target,
-            "build",
+            Kind::Build,
             "src/tools/run-make-support",
             SourceType::InTree,
             &[],
@@ -1321,7 +1321,7 @@ impl Step for CrateRunMakeSupport {
             compiler,
             Mode::ToolBootstrap,
             host,
-            "test",
+            Kind::Test,
             "src/tools/run-make-support",
             SourceType::InTree,
             &[],
@@ -1367,7 +1367,7 @@ impl Step for CrateBuildHelper {
             compiler,
             Mode::ToolBootstrap,
             host,
-            "test",
+            Kind::Test,
             "src/tools/build_helper",
             SourceType::InTree,
             &[],
@@ -2631,7 +2631,7 @@ impl Step for Crate {
                 mode,
                 SourceType::InTree,
                 target,
-                "miri-test",
+                Kind::MiriTest,
             );
             // This hack helps bootstrap run standard library tests in Miri. The issue is as
             // follows: when running `cargo miri test` on libcore, cargo builds a local copy of core
@@ -2654,14 +2654,7 @@ impl Step for Crate {
             }
 
             // Build `cargo test` command
-            builder::Cargo::new(
-                builder,
-                compiler,
-                mode,
-                SourceType::InTree,
-                target,
-                builder.kind.as_str(),
-            )
+            builder::Cargo::new(builder, compiler, mode, SourceType::InTree, target, builder.kind)
         };
 
         match mode {
@@ -2753,7 +2746,7 @@ impl Step for CrateRustdoc {
             compiler,
             Mode::ToolRustc,
             target,
-            builder.kind.as_str(),
+            builder.kind,
             "src/tools/rustdoc",
             SourceType::InTree,
             &[],
@@ -2845,7 +2838,7 @@ impl Step for CrateRustdocJsonTypes {
             compiler,
             Mode::ToolRustc,
             target,
-            builder.kind.as_str(),
+            builder.kind,
             "src/rustdoc-json-types",
             SourceType::InTree,
             &[],
@@ -3079,7 +3072,7 @@ impl Step for TierCheck {
             self.compiler,
             Mode::ToolStd,
             self.compiler.host,
-            "run",
+            Kind::Run,
             "src/tools/tier-check",
             SourceType::InTree,
             &[],
@@ -3151,7 +3144,7 @@ impl Step for RustInstaller {
             compiler,
             Mode::ToolBootstrap,
             bootstrap_host,
-            "test",
+            Kind::Test,
             "src/tools/rust-installer",
             SourceType::InTree,
             &[],
@@ -3321,7 +3314,7 @@ impl Step for CodegenCranelift {
                 Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
                 SourceType::InTree,
                 target,
-                "run",
+                Kind::Run,
             );
 
             cargo.current_dir(&builder.src.join("compiler/rustc_codegen_cranelift"));
@@ -3453,7 +3446,7 @@ impl Step for CodegenGCC {
                 Mode::Codegen, // Must be codegen to ensure dlopen on compiled dylibs works
                 SourceType::InTree,
                 target,
-                "run",
+                Kind::Run,
             );
 
             cargo.current_dir(&builder.src.join("compiler/rustc_codegen_gcc"));
@@ -3541,7 +3534,7 @@ impl Step for TestFloatParse {
             compiler,
             Mode::ToolStd,
             bootstrap_host,
-            "test",
+            Kind::Test,
             path,
             SourceType::InTree,
             &[],
@@ -3564,7 +3557,7 @@ impl Step for TestFloatParse {
             compiler,
             Mode::ToolStd,
             bootstrap_host,
-            "run",
+            Kind::Run,
             path,
             SourceType::InTree,
             &[],
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 147193061c8..4d573b107b5 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -90,7 +90,7 @@ impl Step for ToolBuild {
             compiler,
             self.mode,
             target,
-            "build",
+            Kind::Build,
             path,
             self.source_type,
             &self.extra_features,
@@ -136,12 +136,12 @@ pub fn prepare_tool_cargo(
     compiler: Compiler,
     mode: Mode,
     target: TargetSelection,
-    command: &'static str,
+    cmd_kind: Kind,
     path: &str,
     source_type: SourceType,
     extra_features: &[String],
 ) -> CargoCommand {
-    let mut cargo = builder::Cargo::new(builder, compiler, mode, source_type, target, command);
+    let mut cargo = builder::Cargo::new(builder, compiler, mode, source_type, target, cmd_kind);
 
     let dir = builder.src.join(path);
     cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
@@ -646,7 +646,7 @@ impl Step for Rustdoc {
             build_compiler,
             Mode::ToolRustc,
             target,
-            "build",
+            Kind::Build,
             "src/tools/rustdoc",
             SourceType::InTree,
             features.as_slice(),
@@ -905,7 +905,7 @@ impl Step for LlvmBitcodeLinker {
             self.compiler,
             Mode::ToolRustc,
             self.target,
-            "build",
+            Kind::Build,
             "src/tools/llvm-bitcode-linker",
             SourceType::InTree,
             &self.extra_features,
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 99b5c891403..9eb4ca033a8 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -689,7 +689,7 @@ impl<'a> ShouldRun<'a> {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord, ValueEnum)]
 pub enum Kind {
     #[value(alias = "b")]
     Build,
@@ -701,6 +701,8 @@ pub enum Kind {
     #[value(alias = "t")]
     Test,
     Miri,
+    MiriSetup,
+    MiriTest,
     Bench,
     #[value(alias = "d")]
     Doc,
@@ -725,6 +727,8 @@ impl Kind {
             Kind::Format => "fmt",
             Kind::Test => "test",
             Kind::Miri => "miri",
+            Kind::MiriSetup => panic!("`as_str` is not supported for `Kind::MiriSetup`."),
+            Kind::MiriTest => panic!("`as_str` is not supported for `Kind::MiriTest`."),
             Kind::Bench => "bench",
             Kind::Doc => "doc",
             Kind::Clean => "clean",
@@ -1000,6 +1004,7 @@ impl<'a> Builder<'a> {
             Kind::Vendor => describe!(vendor::Vendor),
             // special-cased in Build::build()
             Kind::Format | Kind::Suggest | Kind::Perf => vec![],
+            Kind::MiriTest | Kind::MiriSetup => unreachable!(),
         }
     }
 
@@ -1386,23 +1391,30 @@ impl<'a> Builder<'a> {
         compiler: Compiler,
         mode: Mode,
         target: TargetSelection,
-        cmd: &str, // FIXME make this properly typed
+        cmd_kind: Kind,
     ) -> BootstrapCommand {
-        let mut cargo;
-        if cmd == "clippy" {
-            cargo = self.cargo_clippy_cmd(compiler);
-            cargo.arg(cmd);
-        } else if let Some(subcmd) = cmd.strip_prefix("miri") {
-            // Command must be "miri-X".
-            let subcmd = subcmd
-                .strip_prefix('-')
-                .unwrap_or_else(|| panic!("expected `miri-$subcommand`, but got {}", cmd));
-            cargo = self.cargo_miri_cmd(compiler);
-            cargo.arg("miri").arg(subcmd);
-        } else {
-            cargo = command(&self.initial_cargo);
-            cargo.arg(cmd);
-        }
+        let mut cargo = match cmd_kind {
+            Kind::Clippy => {
+                let mut cargo = self.cargo_clippy_cmd(compiler);
+                cargo.arg(cmd_kind.as_str());
+                cargo
+            }
+            Kind::MiriSetup => {
+                let mut cargo = self.cargo_miri_cmd(compiler);
+                cargo.arg("miri").arg("setup");
+                cargo
+            }
+            Kind::MiriTest => {
+                let mut cargo = self.cargo_miri_cmd(compiler);
+                cargo.arg("miri").arg("test");
+                cargo
+            }
+            _ => {
+                let mut cargo = command(&self.initial_cargo);
+                cargo.arg(cmd_kind.as_str());
+                cargo
+            }
+        };
 
         // Run cargo from the source root so it can find .cargo/config.
         // This matters when using vendoring and the working directory is outside the repository.
@@ -1431,7 +1443,7 @@ impl<'a> Builder<'a> {
             Color::Auto => {} // nothing to do
         }
 
-        if cmd != "install" {
+        if cmd_kind != Kind::Install {
             cargo.arg("--target").arg(target.rustc_target_arg());
         } else {
             assert_eq!(target, compiler.host);
@@ -1440,8 +1452,11 @@ impl<'a> Builder<'a> {
         if self.config.rust_optimize.is_release() {
             // FIXME: cargo bench/install do not accept `--release`
             // and miri doesn't want it
-            if cmd != "bench" && cmd != "install" && !cmd.starts_with("miri-") {
-                cargo.arg("--release");
+            match cmd_kind {
+                Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest => {}
+                _ => {
+                    cargo.arg("--release");
+                }
             }
         }
 
@@ -1464,9 +1479,9 @@ impl<'a> Builder<'a> {
         mode: Mode,
         source_type: SourceType,
         target: TargetSelection,
-        cmd: &str, // FIXME make this properly typed
+        cmd_kind: Kind,
     ) -> Cargo {
-        let mut cargo = self.bare_cargo(compiler, mode, target, cmd);
+        let mut cargo = self.bare_cargo(compiler, mode, target, cmd_kind);
         let out_dir = self.stage_out(compiler, mode);
 
         let mut hostflags = HostFlags::default();
@@ -1477,7 +1492,7 @@ impl<'a> Builder<'a> {
             self.clear_if_dirty(&out_dir, &backend);
         }
 
-        if cmd == "doc" || cmd == "rustdoc" {
+        if cmd_kind == Kind::Doc {
             let my_out = match mode {
                 // This is the intended out directory for compiler documentation.
                 Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target),
@@ -1508,7 +1523,7 @@ impl<'a> Builder<'a> {
 
         // Set a flag for `check`/`clippy`/`fix`, so that certain build
         // scripts can do less work (i.e. not building/requiring LLVM).
-        if cmd == "check" || cmd == "clippy" || cmd == "fix" {
+        if matches!(cmd_kind, Kind::Check | Kind::Clippy | Kind::Fix) {
             // If we've not yet built LLVM, or it's stale, then bust
             // the rustc_llvm cache. That will always work, even though it
             // may mean that on the next non-check build we'll need to rebuild
@@ -1558,7 +1573,7 @@ impl<'a> Builder<'a> {
             rustflags.arg("--cfg=bootstrap");
         }
 
-        if cmd == "clippy" {
+        if cmd_kind == Kind::Clippy {
             // clippy overwrites sysroot if we pass it to cargo.
             // Pass it directly to clippy instead.
             // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`,
@@ -1654,7 +1669,7 @@ impl<'a> Builder<'a> {
             Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
             Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
                 // Build proc macros both for the host and the target
-                if target != compiler.host && cmd != "check" {
+                if target != compiler.host && cmd_kind != Kind::Check {
                     cargo.arg("-Zdual-proc-macros");
                     rustflags.arg("-Zdual-proc-macros");
                 }
@@ -1739,7 +1754,7 @@ impl<'a> Builder<'a> {
         }
         cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata);
 
-        if cmd == "clippy" {
+        if cmd_kind == Kind::Clippy {
             rustflags.arg("-Zforce-unstable-if-unmarked");
         }
 
@@ -1755,10 +1770,15 @@ impl<'a> Builder<'a> {
         //
         // Only clear out the directory if we're compiling std; otherwise, we
         // should let Cargo take care of things for us (via depdep info)
-        if !self.config.dry_run() && mode == Mode::Std && cmd == "build" {
+        if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build {
             self.clear_if_dirty(&out_dir, &self.rustc(compiler));
         }
 
+        let rustdoc_path = match cmd_kind {
+            Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc(compiler),
+            _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"),
+        };
+
         // Customize the compiler we're running. Specify the compiler to cargo
         // as our shim and then pass it some various options used to configure
         // how the actual compiler itself is called.
@@ -1772,15 +1792,7 @@ impl<'a> Builder<'a> {
             .env("RUSTC_SYSROOT", sysroot)
             .env("RUSTC_LIBDIR", libdir)
             .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
-            .env(
-                "RUSTDOC_REAL",
-                // Make sure to handle both `test` and `miri-test` commands.
-                if cmd == "doc" || cmd == "rustdoc" || (cmd.ends_with("test") && want_rustdoc) {
-                    self.rustdoc(compiler)
-                } else {
-                    PathBuf::from("/path/to/nowhere/rustdoc/not/required")
-                },
-            )
+            .env("RUSTDOC_REAL", rustdoc_path)
             .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir())
             .env("RUSTC_BREAK_ON_ICE", "1");
 
@@ -1799,7 +1811,7 @@ impl<'a> Builder<'a> {
         }
 
         // If this is for `miri-test`, prepare the sysroots.
-        if cmd == "miri-test" {
+        if cmd_kind == Kind::MiriTest {
             self.ensure(compile::Std::new(compiler, compiler.host));
             let host_sysroot = self.sysroot(compiler);
             let miri_sysroot = test::Miri::build_miri_sysroot(self, compiler, target);
@@ -1813,7 +1825,8 @@ impl<'a> Builder<'a> {
             rustflags.arg(&format!("-Zstack-protector={stack_protector}"));
         }
 
-        if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
+        if !matches!(cmd_kind, Kind::Build | Kind::Check | Kind::Clippy | Kind::Fix) && want_rustdoc
+        {
             cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
         }
 
@@ -2430,9 +2443,9 @@ impl Cargo {
         mode: Mode,
         source_type: SourceType,
         target: TargetSelection,
-        cmd: &str, // FIXME make this properly typed
+        cmd_kind: Kind,
     ) -> Cargo {
-        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd);
+        let mut cargo = builder.cargo(compiler, mode, source_type, target, cmd_kind);
         cargo.configure_linker(builder);
         cargo
     }
@@ -2448,9 +2461,9 @@ impl Cargo {
         mode: Mode,
         source_type: SourceType,
         target: TargetSelection,
-        cmd: &str, // FIXME make this properly typed
+        cmd_kind: Kind,
     ) -> Cargo {
-        builder.cargo(compiler, mode, source_type, target, cmd)
+        builder.cargo(compiler, mode, source_type, target, cmd_kind)
     }
 
     pub fn rustdocflag(&mut self, arg: &str) -> &mut Cargo {
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index f67fdaac042..841a87fc48a 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -8,10 +8,16 @@ use std::path::PathBuf;
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 
-/// rustdoc format-version.
+/// The version of JSON output that this crate represents.
+///
+/// This integer is incremented with every breaking change to the API,
+/// and is returned along with the JSON blob as [`Crate::format_version`].
+/// Consuming code should assert that this value matches the format version(s) that it supports.
 pub const FORMAT_VERSION: u32 = 32;
 
-/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
+/// The root of the emitted JSON blob.
+///
+/// It contains all type/documentation information
 /// about the language items in the local crate, as well as info about external items to allow
 /// tools to find or link to them.
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -34,13 +40,18 @@ pub struct Crate {
     pub format_version: u32,
 }
 
+/// Metadata of a crate, either the same crate on which `rustdoc` was invoked, or its dependency.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct ExternalCrate {
+    /// The name of the crate.
     pub name: String,
+    /// The root URL at which the crate's documentation lives.
     pub html_root_url: Option<String>,
 }
 
-/// For external (not defined in the local crate) items, you don't get the same level of
+/// Information about an external (not defined in the local crate) [`Item`].
+///
+/// For external items, you don't get the same level of
 /// information. This struct should contain enough to generate a link/reference to the item in
 /// question, or can be used by a tool that takes the json output of multiple crates to find
 /// the actual item definition with all the relevant info.
@@ -61,6 +72,10 @@ pub struct ItemSummary {
     pub kind: ItemKind,
 }
 
+/// Anything that can hold documentation - modules, structs, enums, functions, traits, etc.
+///
+/// The `Item` data type holds fields that can apply to any of these,
+/// and leaves kind-specific details (like function args or enum variants) to the `inner` field.
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 pub struct Item {
     /// The unique identifier of this item. Can be used to find this item in various mappings.
@@ -83,10 +98,13 @@ pub struct Item {
     pub links: FxHashMap<String, Id>,
     /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
     pub attrs: Vec<String>,
+    /// Information about the item’s deprecation, if present.
     pub deprecation: Option<Deprecation>,
+    /// The type-specific fields describing this item.
     pub inner: ItemEnum,
 }
 
+/// A range of source code.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Span {
     /// The path to the source file for this span relative to the path `rustdoc` was invoked with.
@@ -97,28 +115,39 @@ pub struct Span {
     pub end: (usize, usize),
 }
 
+/// Information about the deprecation of an [`Item`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Deprecation {
+    /// Usually a version number when this [`Item`] first became deprecated.
     pub since: Option<String>,
+    /// The reason for deprecation and/or what alternatives to use.
     pub note: Option<String>,
 }
 
+/// Visibility of an [`Item`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum Visibility {
+    /// Explicitly public visibility set with `pub`.
     Public,
     /// For the most part items are private by default. The exceptions are associated items of
     /// public traits and variants of public enums.
     Default,
+    /// Explicitly crate-wide visibility set with `pub(crate)`
     Crate,
-    /// For `pub(in path)` visibility. `parent` is the module it's restricted to and `path` is how
-    /// that module was referenced (like `"super::super"` or `"crate::foo::bar"`).
+    /// For `pub(in path)` visibility.
     Restricted {
+        /// ID of the module to which this visibility restricts items.
         parent: Id,
+        /// The path with which [`parent`] was referenced
+        /// (like `super::super` or `crate::foo::bar`).
+        ///
+        /// [`parent`]: Visibility::Restricted::parent
         path: String,
     },
 }
 
+/// Dynamic trait object type (`dyn Trait`).
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct DynTrait {
     /// All the traits implemented. One of them is the vtable, and the rest must be auto traits.
@@ -133,64 +162,133 @@ pub struct DynTrait {
     pub lifetime: Option<String>,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// A trait and potential HRTBs
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct PolyTrait {
+    /// The path to the trait.
     #[serde(rename = "trait")]
     pub trait_: Path,
     /// Used for Higher-Rank Trait Bounds (HRTBs)
     /// ```text
     /// dyn for<'a> Fn() -> &'a i32"
     ///     ^^^^^^^
-    ///       |
-    ///       this part
     /// ```
     pub generic_params: Vec<GenericParamDef>,
 }
 
+/// A set of generic arguments provided to a path segment, e.g.
+///
+/// ```text
+/// std::option::Option::<u32>::None
+///                      ^^^^^
+/// ```
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum GenericArgs {
-    /// <'a, 32, B: Copy, C = u32>
-    AngleBracketed { args: Vec<GenericArg>, bindings: Vec<TypeBinding> },
-    /// Fn(A, B) -> C
-    Parenthesized { inputs: Vec<Type>, output: Option<Type> },
+    /// `<'a, 32, B: Copy, C = u32>`
+    AngleBracketed {
+        /// The list of each argument on this type.
+        /// ```text
+        /// <'a, 32, B: Copy, C = u32>
+        ///  ^^^^^^
+        /// ```
+        args: Vec<GenericArg>,
+        /// Associated type or constant bindings (e.g. `Item=i32` or `Item: Clone`) for this type.
+        bindings: Vec<TypeBinding>,
+    },
+    /// `Fn(A, B) -> C`
+    Parenthesized {
+        /// The input types, enclosed in parentheses.
+        inputs: Vec<Type>,
+        /// The output type provided after the `->`, if present.
+        output: Option<Type>,
+    },
 }
 
+/// One argument in a list of generic arguments to a path segment.
+///
+/// Part of [`GenericArgs`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum GenericArg {
+    /// A lifetime argument.
+    /// ```text
+    /// std::borrow::Cow<'static, str>
+    ///                  ^^^^^^^
+    /// ```
     Lifetime(String),
+    /// A type argument.
+    /// ```text
+    /// std::borrow::Cow<'static, str>
+    ///                           ^^^
+    /// ```
     Type(Type),
+    /// A constant as a generic argument.
+    /// ```text
+    /// core::array::IntoIter<u32, { 640 * 1024 }>
+    ///                            ^^^^^^^^^^^^^^
+    /// ```
     Const(Constant),
+    /// A generic argument that's explicitly set to be inferred.
+    /// ```text
+    /// std::vec::Vec::<_>::new()
+    ///                 ^
+    /// ```
     Infer,
 }
 
+/// A constant.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Constant {
+    /// The stringified expression of this constant. Note that its mapping to the original
+    /// source code is unstable and it's not guaranteed that it'll match the source code.
     pub expr: String,
+    /// The value of the evaluated expression for this constant, which is only computed for numeric
+    /// types.
     pub value: Option<String>,
+    /// Whether this constant is a bool, numeric, string, or char literal.
     pub is_literal: bool,
 }
 
+/// Describes a bound applied to an associated type/constant.
+///
+/// Example:
+/// ```text
+/// IntoIterator<Item = u32, IntoIter: Clone>
+///              ^^^^^^^^^^  ^^^^^^^^^^^^^^^
+/// ```
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct TypeBinding {
+    /// The name of the associated type/constant.
     pub name: String,
+    /// Arguments provided to the associated type/constant.
     pub args: GenericArgs,
+    /// The kind of bound applied to the associated type/constant.
     pub binding: TypeBindingKind,
 }
 
+/// The way in which an associate type/constant is bound.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum TypeBindingKind {
+    /// The required value/type is specified exactly. e.g.
+    /// ```text
+    /// Iterator<Item = u32, IntoIter: DoubleEndedIterator>
+    ///          ^^^^^^^^^^
+    /// ```
     Equality(Term),
+    /// The type is required to satisfy a set of bounds.
+    /// ```text
+    /// Iterator<Item = u32, IntoIter: DoubleEndedIterator>
+    ///                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    /// ```
     Constraint(Vec<GenericBound>),
 }
 
 /// An opaque identifier for an item.
 ///
-/// It can be used to lookup in [Crate::index] or [Crate::paths] to resolve it
-/// to an [Item].
+/// It can be used to lookup in [`Crate::index`] or [`Crate::paths`] to resolve it
+/// to an [`Item`].
 ///
 /// Id's are only valid within a single JSON blob. They cannot be used to
 /// resolve references between the JSON output's for different crates.
@@ -202,115 +300,230 @@ pub enum TypeBindingKind {
 // FIXME(aDotInTheVoid): Consider making this non-public in rustdoc-types.
 pub struct Id(pub String);
 
+/// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any aditional info.
+///
+/// Part of [`ItemSummary`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum ItemKind {
+    /// A module declaration, e.g. `mod foo;` or `mod foo {}`
     Module,
+    /// A crate imported via the `extern crate` syntax.
     ExternCrate,
+    /// An import of 1 or more items into scope, using the `use` keyword.
     Import,
+    /// A `struct` declaration.
     Struct,
+    /// A field of a struct.
     StructField,
+    /// A `union` declaration.
     Union,
+    /// An `enum` declaration.
     Enum,
+    /// A variant of a enum.
     Variant,
+    /// A function declaration, e.g. `fn f() {}`
     Function,
+    /// A type alias declaration, e.g. `type Pig = std::borrow::Cow<'static, str>;`
     TypeAlias,
     OpaqueTy,
+    /// The declaration of a constant, e.g. `const GREETING: &str = "Hi :3";`
     Constant,
+    /// A `trait` declaration.
     Trait,
+    /// A trait alias declaration, e.g. `trait Int = Add + Sub + Mul + Div;`
+    ///
+    /// See [the tracking issue](https://github.com/rust-lang/rust/issues/41517)
     TraitAlias,
+    /// An `impl` block.
     Impl,
+    /// A `static` declaration.
     Static,
+    /// `type`s from an `extern` block.
+    ///
+    /// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
     ForeignType,
+    /// A macro declaration.
+    ///
+    /// Corresponds to either `ItemEnum::Macro(_)`
+    /// or `ItemEnum::ProcMacro(ProcMacro { kind: MacroKind::Bang })`
     Macro,
+    /// A procedural macro attribute.
+    ///
+    /// Corresponds to `ItemEnum::ProcMacro(ProcMacro { kind: MacroKind::Attr })`
     ProcAttribute,
+    /// A procedural macro usable in the `#[derive()]` attribute.
+    ///
+    /// Corresponds to `ItemEnum::ProcMacro(ProcMacro { kind: MacroKind::Derive })`
     ProcDerive,
+    /// An associated constant of a trait or a type.
     AssocConst,
+    /// An associated type of a trait or a type.
     AssocType,
+    /// A primitive type, e.g. `u32`.
+    ///
+    /// [`Item`]s of this kind only come from the core library.
     Primitive,
+    /// A keyword declaration.
+    ///
+    /// [`Item`]s of this kind only come from the come library and exist solely
+    /// to carry documentation for the respective keywords.
     Keyword,
 }
 
+/// Specific fields of an item.
+///
+/// Part of [`Item`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum ItemEnum {
+    /// A module declaration, e.g. `mod foo;` or `mod foo {}`
     Module(Module),
+    /// A crate imported via the `extern crate` syntax.
     ExternCrate {
+        /// The name of the imported crate.
         name: String,
+        /// If the crate is renamed, this is its name in the crate.
         rename: Option<String>,
     },
+    /// An import of 1 or more items into scope, using the `use` keyword.
     Import(Import),
 
+    /// A `union` declaration.
     Union(Union),
+    /// A `struct` declaration.
     Struct(Struct),
+    /// A field of a struct.
     StructField(Type),
+    /// An `enum` declaration.
     Enum(Enum),
+    /// A variant of a enum.
     Variant(Variant),
 
+    /// A function declaration (including methods and other associated functions)
     Function(Function),
 
+    /// A `trait` declaration.
     Trait(Trait),
+    /// A trait alias declaration, e.g. `trait Int = Add + Sub + Mul + Div;`
+    ///
+    /// See [the tracking issue](https://github.com/rust-lang/rust/issues/41517)
     TraitAlias(TraitAlias),
+    /// An `impl` block.
     Impl(Impl),
 
+    /// A type alias declaration, e.g. `type Pig = std::borrow::Cow<'static, str>;`
     TypeAlias(TypeAlias),
     OpaqueTy(OpaqueTy),
+    /// The declaration of a constant, e.g. `const GREETING: &str = "Hi :3";`
     Constant {
+        /// The type of the constant.
         #[serde(rename = "type")]
         type_: Type,
+        /// The declared constant itself.
         #[serde(rename = "const")]
         const_: Constant,
     },
 
+    /// A declaration of a `static`.
     Static(Static),
 
-    /// `type`s from an extern block
+    /// `type`s from an `extern` block.
+    ///
+    /// See [the tracking issue](https://github.com/rust-lang/rust/issues/43467)
     ForeignType,
 
-    /// Declarative macro_rules! macro
+    /// A macro_rules! declarative macro. Contains a single string with the source
+    /// representation of the macro with the patterns stripped.
     Macro(String),
+    /// A procedural macro.
     ProcMacro(ProcMacro),
 
+    /// A primitive type, e.g. `u32`.
+    ///
+    /// [`Item`]s of this kind only come from the core library.
     Primitive(Primitive),
 
+    /// An associated constant of a trait or a type.
     AssocConst {
+        /// The type of the constant.
         #[serde(rename = "type")]
         type_: Type,
-        /// e.g. `const X: usize = 5;`
+        /// The stringified expression for the default value, if provided, e.g.
+        /// ```rust
+        /// const X: usize = 640 * 1024;
+        /// //               ^^^^^^^^^^
+        /// ```
         default: Option<String>,
     },
+    /// An associated type of a trait or a type.
     AssocType {
+        /// The generic parameters and where clauses on ahis associated type.
         generics: Generics,
+        /// The bounds for this associated type. e.g.
+        /// ```rust
+        /// trait IntoIterator {
+        ///     type Item;
+        ///     type IntoIter: Iterator<Item = Self::Item>;
+        /// //                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        /// }
+        /// ```
         bounds: Vec<GenericBound>,
-        /// e.g. `type X = usize;`
+        /// The default for this type, if provided, e.g.
+        /// ```rust
+        /// type X = usize;
+        /// //       ^^^^^
+        /// ```
         default: Option<Type>,
     },
 }
 
+/// A module declaration, e.g. `mod foo;` or `mod foo {}`.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Module {
+    /// Whether this is the root item of a crate.
+    ///
+    /// This item doesn't correspond to any construction in the source code and is generated by the
+    /// compiler.
     pub is_crate: bool,
+    /// [`Item`]s declared inside this module.
     pub items: Vec<Id>,
     /// If `true`, this module is not part of the public API, but it contains
     /// items that are re-exported as public API.
     pub is_stripped: bool,
 }
 
+/// A `union`.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Union {
+    /// The generic parameters and where clauses on this union.
     pub generics: Generics,
+    /// Whether any fields have been removed from the result, due to being private or hidden.
     pub fields_stripped: bool,
+    /// The list of fields in the union.
+    ///
+    /// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
     pub fields: Vec<Id>,
+    /// All impls (both of traits and inherent) for this union.
+    ///
+    /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Impl`].
     pub impls: Vec<Id>,
 }
 
+/// A `struct`.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Struct {
+    /// The kind of the struct (e.g. unit, tuple-like or struct-like) and the data specific to it,
+    /// i.e. fields.
     pub kind: StructKind,
+    /// The generic parameters and where clauses on this struct.
     pub generics: Generics,
+    /// All impls (both of traits and inherent) for this struct.
+    /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Impl`].
     pub impls: Vec<Id>,
 }
 
+/// The kind of a [`Struct`] and the data specific to it, i.e. fields.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum StructKind {
@@ -322,13 +535,14 @@ pub enum StructKind {
     Unit,
     /// A struct with unnamed fields.
     ///
+    /// All [`Id`]'s will point to [`ItemEnum::StructField`].
+    /// Unlike most of JSON, private and `#[doc(hidden)]` fields will be given as `None`
+    /// instead of being omitted, because order matters.
+    ///
     /// ```rust
     /// pub struct TupleStruct(i32);
     /// pub struct EmptyTupleStruct();
     /// ```
-    ///
-    /// All [`Id`]'s will point to [`ItemEnum::StructField`]. Private and
-    /// `#[doc(hidden)]` fields will be given as `None`
     Tuple(Vec<Option<Id>>),
     /// A struct with named fields.
     ///
@@ -336,17 +550,32 @@ pub enum StructKind {
     /// pub struct PlainStruct { x: i32 }
     /// pub struct EmptyPlainStruct {}
     /// ```
-    Plain { fields: Vec<Id>, fields_stripped: bool },
+    Plain {
+        /// The list of fields in the struct.
+        ///
+        /// All of the corresponding [`Item`]s are of kind [`ItemEnum::StructField`].
+        fields: Vec<Id>,
+        /// Whether any fields have been removed from the result, due to being private or hidden.
+        fields_stripped: bool,
+    },
 }
 
+/// An `enum`.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Enum {
+    /// Information about the type parameters and `where` clauses of the enum.
     pub generics: Generics,
+    /// Whether any variants have been removed from the result, due to being private or hidden.
     pub variants_stripped: bool,
+    /// The list of variants in the enum.
+    ///
+    /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`]
     pub variants: Vec<Id>,
+    /// `impl`s for the enum.
     pub impls: Vec<Id>,
 }
 
+/// A variant of an enum.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Variant {
     /// Whether the variant is plain, a tuple-like, or struct-like. Contains the fields.
@@ -355,6 +584,7 @@ pub struct Variant {
     pub discriminant: Option<Discriminant>,
 }
 
+/// The kind of an [`Enum`] [`Variant`] and the data specific to it, i.e. fields.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum VariantKind {
@@ -369,7 +599,8 @@ pub enum VariantKind {
     Plain,
     /// A variant with unnamed fields.
     ///
-    /// Unlike most of json, `#[doc(hidden)]` fields will be given as `None`
+    /// All [`Id`]'s will point to [`ItemEnum::StructField`].
+    /// Unlike most of JSON, `#[doc(hidden)]` fields will be given as `None`
     /// instead of being omitted, because order matters.
     ///
     /// ```rust
@@ -387,9 +618,16 @@ pub enum VariantKind {
     ///     EmptyStructVariant {},
     /// }
     /// ```
-    Struct { fields: Vec<Id>, fields_stripped: bool },
+    Struct {
+        /// The list of variants in the enum.
+        /// All of the corresponding [`Item`]s are of kind [`ItemEnum::Variant`].
+        fields: Vec<Id>,
+        /// Whether any variants have been removed from the result, due to being private or hidden.
+        fields_stripped: bool,
+    },
 }
 
+/// The value that distinguishes a variant in an [`Enum`] from other variants.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Discriminant {
     /// The expression that produced the discriminant.
@@ -407,62 +645,123 @@ pub struct Discriminant {
     pub value: String,
 }
 
+/// A set of fundamental properties of a function.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Header {
+    /// Is this function marked as `const`?
     #[serde(rename = "const")]
     pub const_: bool,
+    /// Is this function unsafe?
     #[serde(rename = "unsafe")]
     pub unsafe_: bool,
+    /// Is this function async?
     #[serde(rename = "async")]
     pub async_: bool,
+    /// The ABI used by the function.
     pub abi: Abi,
 }
 
+/// The ABI (Application Binary Interface) used by a function.
+///
+/// If a variant has an `unwind` field, this means the ABI that it represents can be specified in 2
+/// ways: `extern "_"` and `extern "_-unwind"`, and a value of `true` for that field signifies the
+/// latter variant.
+///
+/// See the [Rustonomicon section](https://doc.rust-lang.org/nightly/nomicon/ffi.html#ffi-and-unwinding)
+/// on unwinding for more info.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum Abi {
     // We only have a concrete listing here for stable ABI's because their are so many
     // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list
+    /// The default ABI, but that can also be written explicitly with `extern "Rust"`.
     Rust,
+    /// Can be specified as `extern "C"` or, as a shorthand, just `extern`.
     C { unwind: bool },
+    /// Can be specified as `extern "cdecl"`.
     Cdecl { unwind: bool },
+    /// Can be specified as `extern "stdcall"`.
     Stdcall { unwind: bool },
+    /// Can be specified as `extern "fastcall"`.
     Fastcall { unwind: bool },
+    /// Can be specified as `extern "aapcs"`.
     Aapcs { unwind: bool },
+    /// Can be specified as `extern "win64"`.
     Win64 { unwind: bool },
+    /// Can be specifed as `extern "sysv64"`.
     SysV64 { unwind: bool },
+    /// Can be specified as `extern "system"`.
     System { unwind: bool },
+    /// Any other ABI, including unstable ones.
     Other(String),
 }
 
-/// Represents a function (including methods and other associated functions)
+/// A function declaration (including methods and other associated functions).
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Function {
+    /// Information about the function signature, or declaration.
     pub decl: FnDecl,
+    /// Information about the function’s type parameters and `where` clauses.
     pub generics: Generics,
+    /// Information about core properties of the function, e.g. whether it's `const`, its ABI, etc.
     pub header: Header,
+    /// Whether the function has a body, i.e. an implementation.
     pub has_body: bool,
 }
 
+/// Generic parameters accepted by an item and `where` clauses imposed on it and the parameters.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Generics {
+    /// A list of generic parameter definitions (e.g. `<T: Clone + Hash, U: Copy>`).
     pub params: Vec<GenericParamDef>,
+    /// A list of where predicates (e.g. `where T: Iterator, T::Item: Copy`).
     pub where_predicates: Vec<WherePredicate>,
 }
 
+/// One generic parameter accepted by an item.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct GenericParamDef {
+    /// Name of the parameter.
+    /// ```rust
+    /// fn f<'resource, Resource>(x: &'resource Resource) {}
+    /// //    ^^^^^^^^  ^^^^^^^^
+    /// ```
     pub name: String,
+    /// The kind of the parameter and data specific to a particular parameter kind, e.g. type
+    /// bounds.
     pub kind: GenericParamDefKind,
 }
 
+/// The kind of a [`GenericParamDef`].
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum GenericParamDefKind {
+    /// Denotes a lifetime parameter.
     Lifetime {
+        /// Lifetimes that this lifetime parameter is required to outlive.
+        ///
+        /// ```rust
+        /// fn f<'a, 'b, 'resource: 'a + 'b>(a: &'a str, b: &'b str, res: &'resource str) {}
+        /// //                      ^^^^^^^
+        /// ```
         outlives: Vec<String>,
     },
+
+    /// Denotes a type parameter.
     Type {
+        /// Bounds applied directly to the type. Note that the bounds from `where` clauses
+        /// that constrain this parameter won't appear here.
+        ///
+        /// ```rust
+        /// fn default2<T: Default>() -> [T; 2] where T: Clone { todo!() }
+        /// //             ^^^^^^^
+        /// ```
         bounds: Vec<GenericBound>,
+        /// The default type for this parameter, if provided, e.g.
+        ///
+        /// ```rust
+        /// trait PartialEq<Rhs = Self> {}
+        /// //                    ^^^^
+        /// ```
         default: Option<Type>,
         /// This is normally `false`, which means that this generic parameter is
         /// declared in the Rust source text.
@@ -489,43 +788,75 @@ pub enum GenericParamDefKind {
         /// the Rust source text.
         synthetic: bool,
     },
+
+    /// Denotes a constant parameter.
     Const {
+        /// The type of the constant as declared.
         #[serde(rename = "type")]
         type_: Type,
+        /// The stringified expression for the default value, if provided. It's not guaranteed that
+        /// it'll match the actual source code for the default value.
         default: Option<String>,
     },
 }
 
+/// One `where` clause.
+/// ```rust
+/// fn default<T>() -> T where T: Default { T::default() }
+/// //                         ^^^^^^^^^^
+/// ```
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum WherePredicate {
+    /// A type is expected to comply with a set of bounds
     BoundPredicate {
+        /// The type that's being constrained.
+        ///
+        /// ```rust
+        /// fn f<T>(x: T) where for<'a> &'a T: Iterator {}
+        /// //                              ^
+        /// ```
         #[serde(rename = "type")]
         type_: Type,
+        /// The set of bounds that constrain the type.
+        ///
+        /// ```rust
+        /// fn f<T>(x: T) where for<'a> &'a T: Iterator {}
+        /// //                                 ^^^^^^^^
+        /// ```
         bounds: Vec<GenericBound>,
         /// Used for Higher-Rank Trait Bounds (HRTBs)
-        /// ```text
-        /// where for<'a> &'a T: Iterator,"
-        ///       ^^^^^^^
-        ///       |
-        ///       this part
+        /// ```rust
+        /// fn f<T>(x: T) where for<'a> &'a T: Iterator {}
+        /// //                  ^^^^^^^
         /// ```
         generic_params: Vec<GenericParamDef>,
     },
+
+    /// A lifetime is expected to outlive other lifetimes.
     LifetimePredicate {
+        /// The name of the lifetime.
         lifetime: String,
+        /// The lifetimes that must be encompassed by the lifetime.
         outlives: Vec<String>,
     },
+
+    /// A type must exactly equal another type.
     EqPredicate {
+        /// The left side of the equation.
         lhs: Type,
+        /// The right side of the equation.
         rhs: Term,
     },
 }
 
+/// Either a trait bound or a lifetime bound.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum GenericBound {
+    /// A trait bound.
     TraitBound {
+        /// The full path to the trait.
         #[serde(rename = "trait")]
         trait_: Path,
         /// Used for Higher-Rank Trait Bounds (HRTBs)
@@ -536,79 +867,142 @@ pub enum GenericBound {
         ///          this part
         /// ```
         generic_params: Vec<GenericParamDef>,
+        /// The context for which a trait is supposed to be used, e.g. `const
         modifier: TraitBoundModifier,
     },
+    /// A lifetime bound, e.g.
+    /// ```rust
+    /// fn f<'a, T>(x: &'a str, y: &T) where T: 'a {}
+    /// //                                     ^^^
+    /// ```
     Outlives(String),
     /// `use<'a, T>` precise-capturing bound syntax
     Use(Vec<String>),
 }
 
+/// A set of modifiers applied to a trait.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum TraitBoundModifier {
+    /// Marks the absence of a modifier.
     None,
+    /// Indicates that the trait bound relaxes a trait bound applied to a parameter by default,
+    /// e.g. `T: Sized?`, the `Sized` trait is required for all generic type parameters by default
+    /// unless specified otherwise with this modifier.
     Maybe,
+    /// Indicates that the trait bound must be applicable in both a run-time and a compile-time
+    /// context.
     MaybeConst,
 }
 
+/// Either a type or a constant, usually stored as the right-hand side of an equation in places like
+/// [`TypeBinding`]
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum Term {
+    /// A type.
+    ///
+    /// ```rust
+    /// fn f(x: impl IntoIterator<Item = u32>) {}
+    /// //                               ^^^
+    /// ```
     Type(Type),
+    /// A constant.
+    ///
+    /// ```ignore (incomplete feature in the snippet)
+    /// trait Foo {
+    ///     const BAR: usize;
+    /// }
+    ///
+    /// fn f(x: impl Foo<BAR = 42>) {}
+    /// //                     ^^
+    /// ```
     Constant(Constant),
 }
 
+/// A type.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum Type {
-    /// Structs, enums, and unions
+    /// Structs, enums, unions and type aliases, e.g. `std::option::Option<u32>`
     ResolvedPath(Path),
+    /// Dynamic trait object type (`dyn Trait`).
     DynTrait(DynTrait),
-    /// Parameterized types
+    /// Parameterized types. The contained string is the name of the parameter.
     Generic(String),
-    /// Built in numeric (i*, u*, f*) types, bool, and char
+    /// Built-in numeric types (e.g. `u32`, `f32`), `bool`, `char`.
     Primitive(String),
-    /// `extern "ABI" fn`
+    /// A function pointer type, e.g. `fn(u32) -> u32`, `extern "C" fn() -> *const u8`
     FunctionPointer(Box<FunctionPointer>),
-    /// `(String, u32, Box<usize>)`
+    /// A tuple type, e.g. `(String, u32, Box<usize>)`
     Tuple(Vec<Type>),
-    /// `[u32]`
+    /// An unsized slice type, e.g. `[u32]`.
     Slice(Box<Type>),
-    /// [u32; 15]
+    /// An array type, e.g. `[u32; 15]`
     Array {
+        /// The type of the contained element.
         #[serde(rename = "type")]
         type_: Box<Type>,
+        /// The stringified expression that is the length of the array.
+        ///
+        /// Keep in mind that it's not guaranteed to match the actual source code of the expression.
         len: String,
     },
-    /// `u32 is 1..`
+    /// A pattern type, e.g. `u32 is 1..`
+    ///
+    /// See [the tracking issue](https://github.com/rust-lang/rust/issues/123646)
     Pat {
+        /// The base type, e.g. the `u32` in `u32 is 1..`
         #[serde(rename = "type")]
         type_: Box<Type>,
         #[doc(hidden)]
         __pat_unstable_do_not_use: String,
     },
-    /// `impl TraitA + TraitB + ...`
+    /// An opaque type that satisfies a set of bounds, `impl TraitA + TraitB + ...`
     ImplTrait(Vec<GenericBound>),
-    /// `_`
+    /// A type that's left to be inferred, `_`
     Infer,
-    /// `*mut u32`, `*u8`, etc.
+    /// A raw pointer type, e.g. `*mut u32`, `*const u8`, etc.
     RawPointer {
+        /// This is `true` for `*mut _` and `false` for `*const _`.
         mutable: bool,
+        /// The type of the pointee.
         #[serde(rename = "type")]
         type_: Box<Type>,
     },
     /// `&'a mut String`, `&str`, etc.
     BorrowedRef {
+        /// The name of the lifetime of the reference, if provided.
         lifetime: Option<String>,
+        /// This is `true` for `&mut i32` and `false` for `&i32`
         mutable: bool,
+        /// The type of the pointee, e.g. the `i32` in `&'a mut i32`
         #[serde(rename = "type")]
         type_: Box<Type>,
     },
     /// Associated types like `<Type as Trait>::Name` and `T::Item` where
     /// `T: Iterator` or inherent associated types like `Struct::Name`.
     QualifiedPath {
+        /// The name of the associated type in the parent type.
+        ///
+        /// ```ignore (incomplete expresssion)
+        /// <core::array::IntoIter<u32, 42> as Iterator>::Item
+        /// //                                            ^^^^
+        /// ```
         name: String,
+        /// The generic arguments provided to the associated type.
+        ///
+        /// ```ignore (incomplete expression)
+        /// <core::slice::IterMut<'static, u32> as BetterIterator>::Item<'static>
+        /// //                                                          ^^^^^^^^^
+        /// ```
         args: Box<GenericArgs>,
+        /// The type with which this type is associated.
+        ///
+        /// ```ignore (incomplete expression)
+        /// <core::array::IntoIter<u32, 42> as Iterator>::Item
+        /// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        /// ```
         self_type: Box<Type>,
         /// `None` iff this is an *inherent* associated type.
         #[serde(rename = "trait")]
@@ -616,34 +1010,47 @@ pub enum Type {
     },
 }
 
+/// A type that has a simple path to it. This is the kind of type of structs, unions, enums, etc.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Path {
+    /// The name of the type as declared, e.g. in
+    ///
+    /// ```rust
+    /// mod foo {
+    ///     struct Bar;
+    /// }
+    /// ```
+    ///
+    /// for `foo::Bar`, this field will be `Bar`.
     pub name: String,
+    /// The ID of the type.
     pub id: Id,
-    /// Generic arguments to the type
-    /// ```test
+    /// Generic arguments to the type.
+    ///
+    /// ```ignore (incomplete expression)
     /// std::borrow::Cow<'static, str>
-    ///                 ^^^^^^^^^^^^^^
-    ///                 |
-    ///                 this part
+    /// //              ^^^^^^^^^^^^^^
     /// ```
     pub args: Option<Box<GenericArgs>>,
 }
 
+/// A type that is a function pointer.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct FunctionPointer {
+    /// The signature of the function.
     pub decl: FnDecl,
     /// Used for Higher-Rank Trait Bounds (HRTBs)
-    /// ```text
-    /// for<'c> fn(val: &'c i32) -> i32
-    /// ^^^^^^^
-    ///       |
-    ///       this part
+    ///
+    /// ```ignore (incomplete expression)
+    ///    for<'c> fn(val: &'c i32) -> i32
+    /// // ^^^^^^^
     /// ```
     pub generic_params: Vec<GenericParamDef>,
+    /// The core properties of the function, such as the ABI it conforms to, whether it's unsafe, etc.
     pub header: Header,
 }
 
+/// The signature of a function.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct FnDecl {
     /// List of argument names and their type.
@@ -651,42 +1058,86 @@ pub struct FnDecl {
     /// Note that not all names will be valid identifiers, as some of
     /// them may be patterns.
     pub inputs: Vec<(String, Type)>,
+    /// The output type, if specified.
     pub output: Option<Type>,
+    /// Whether the function accepts an arbitrary amount of trailing arguments the C way.
+    ///
+    /// ```ignore (incomplete code)
+    /// fn printf(fmt: &str, ...);
+    /// ```
     pub c_variadic: bool,
 }
 
+/// A `trait` declaration.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Trait {
+    /// Whether the trait is marked `auto` and is thus implemented automatically
+    /// for all aplicable types.
     pub is_auto: bool,
+    /// Whether the trait is marked as `unsafe`.
     pub is_unsafe: bool,
+    /// Whether the trait is [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety).
     pub is_object_safe: bool,
+    /// Associated [`Item`]s that can/must be implemented by the `impl` blocks.
     pub items: Vec<Id>,
+    /// Information about the type parameters and `where` clauses of the trait.
     pub generics: Generics,
+    /// Constraints that must be met by the implementor of the trait.
     pub bounds: Vec<GenericBound>,
+    /// The implementations of the trait.
     pub implementations: Vec<Id>,
 }
 
+/// A trait alias declaration, e.g. `trait Int = Add + Sub + Mul + Div;`
+///
+/// See [the tracking issue](https://github.com/rust-lang/rust/issues/41517)
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct TraitAlias {
+    /// Information about the type parameters and `where` clauses of the alias.
     pub generics: Generics,
+    /// The bounds that are associated with the alias.
     pub params: Vec<GenericBound>,
 }
 
+/// An `impl` block.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Impl {
+    /// Whether this impl is for an unsafe trait.
     pub is_unsafe: bool,
+    /// Information about the impl’s type parameters and `where` clauses.
     pub generics: Generics,
+    /// The list of the names of all the trait methods that weren't mentioned in this impl but
+    /// were provided by the trait itself.
+    ///
+    /// For example, for this impl of the [`PartialEq`] trait:
+    /// ```rust
+    /// struct Foo;
+    ///
+    /// impl PartialEq for Foo {
+    ///     fn eq(&self, other: &Self) -> bool { todo!() }
+    /// }
+    /// ```
+    /// This field will be `["ne"]`, as it has a default implementation defined for it.
     pub provided_trait_methods: Vec<String>,
+    /// The trait being implemented or `None` if the impl is inherent, which means
+    /// `impl Struct {}` as opposed to `impl Trait for Struct {}`.
     #[serde(rename = "trait")]
     pub trait_: Option<Path>,
+    /// The type that the impl block is for.
     #[serde(rename = "for")]
     pub for_: Type,
+    /// The list of associated items contained in this impl block.
     pub items: Vec<Id>,
+    /// Whether this is a negative impl (e.g. `!Sized` or `!Send`).
     pub negative: bool,
+    /// Whether this is an impl that’s implied by the compiler
+    /// (for autotraits, e.g. `Send` or `Sync`).
     pub synthetic: bool,
+    // FIXME: document this
     pub blanket_impl: Option<Type>,
 }
 
+/// A `use` statement.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub struct Import {
@@ -700,16 +1151,34 @@ pub struct Import {
     /// pub use i32 as my_i32;
     /// ```
     pub id: Option<Id>,
-    /// Whether this import uses a glob: `use source::*;`
+    /// Whether this statement is a wildcard `use`, e.g. `use source::*;`
     pub glob: bool,
 }
 
+/// A procedural macro.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct ProcMacro {
+    /// How this macro is supposed to be called: `foo!()`, `#[foo]` or `#[derive(foo)]`
     pub kind: MacroKind,
+    /// Helper attributes defined by a macro to be used inside it.
+    ///
+    /// Defined only for attribute & derive macros.
+    ///
+    /// E.g. the [`Default`] derive macro defines a `#[default]` helper attribute so that one can
+    /// do:
+    ///
+    /// ```rust
+    /// #[derive(Default)]
+    /// enum Option<T> {
+    ///     #[default]
+    ///     None,
+    ///     Some(T),
+    /// }
+    /// ```
     pub helpers: Vec<String>,
 }
 
+/// The way a [`ProcMacro`] is declared to be used.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum MacroKind {
@@ -721,10 +1190,13 @@ pub enum MacroKind {
     Derive,
 }
 
+/// A type alias declaration, e.g. `type Pig = std::borrow::Cow<'static, str>;`
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct TypeAlias {
+    /// The type referred to by this alias.
     #[serde(rename = "type")]
     pub type_: Type,
+    /// Information about the type parameters and `where` clauses of the alias.
     pub generics: Generics,
 }
 
@@ -734,17 +1206,26 @@ pub struct OpaqueTy {
     pub generics: Generics,
 }
 
+/// A `static` declaration.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Static {
+    /// The type of the static.
     #[serde(rename = "type")]
     pub type_: Type,
+    /// This is `true` for mutable statics, declared as `static mut X: T = f();`
     pub mutable: bool,
+    /// The stringified expression for the initial value.
+    ///
+    /// It's not guaranteed that it'll match the actual source code for the initial value.
     pub expr: String,
 }
 
+/// A primitive type declaration. Declarations of this kind can only come from the core library.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Primitive {
+    /// The name of the type.
     pub name: String,
+    /// The implementations, inherent and of traits, on the primitive type.
     pub impls: Vec<Id>,
 }