From e9161db5b55ee5398e10a6806cb62d67e725278c Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 4 Nov 2024 14:28:35 +0100
Subject: [PATCH 1/3] Fix invalid coverage computation when
 `--output-format=json` is enabled

---
 src/librustdoc/clean/mod.rs                     | 4 ++--
 src/librustdoc/core.rs                          | 7 +++++++
 src/librustdoc/passes/calculate_doc_coverage.rs | 1 +
 src/librustdoc/passes/strip_hidden.rs           | 2 +-
 src/librustdoc/passes/strip_priv_imports.rs     | 2 +-
 src/librustdoc/passes/strip_private.rs          | 2 +-
 src/librustdoc/visit_ast.rs                     | 2 +-
 7 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c367eed53e07b..bd4704e8b28ed 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2907,7 +2907,7 @@ fn clean_extern_crate<'tcx>(
                     None => false,
                 }
         })
-        && !cx.output_format.is_json();
+        && !cx.is_json();
 
     let krate_owner_def_id = krate.owner_id.def_id;
     if please_inline {
@@ -3000,7 +3000,7 @@ fn clean_use_statement_inner<'tcx>(
     // forcefully don't inline if this is not public or if the
     // #[doc(no_inline)] attribute is present.
     // Don't inline doc(hidden) imports so they can be stripped at a later stage.
-    let mut denied = cx.output_format.is_json()
+    let mut denied = cx.is_json()
         || !(visibility.is_public()
             || (cx.render_options.document_private && is_visible_from_parent_mod))
         || pub_underscore
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 0f7d4d3e8f318..274b61ae1d3c0 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -121,6 +121,13 @@ impl<'tcx> DocContext<'tcx> {
             _ => None,
         }
     }
+
+    /// Returns `true` if the JSON output format is enabled for generating the crate content.
+    ///
+    /// If another option like `--show-coverage` is enabled, it will return false.
+    pub(crate) fn is_json(&self) -> bool {
+        self.output_format.is_json() && !self.show_coverage
+    }
 }
 
 /// Creates a new `DiagCtxt` that can be used to emit warnings and errors.
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index d27e737764dcf..9f9a093da8a38 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -132,6 +132,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
 
     fn print_results(&self) {
         let output_format = self.ctx.output_format;
+        // In this case we want to ensure that the `OutputFormat` is JSON and NOT the `DocContext`.
         if output_format.is_json() {
             println!("{}", self.to_json());
             return;
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index aba04283e59dc..2998289fd273a 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -23,7 +23,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass {
 /// Strip items marked `#[doc(hidden)]`
 pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     let mut retained = ItemIdSet::default();
-    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
+    let is_json_output = cx.is_json();
 
     // strip all #[doc(hidden)] items
     let krate = {
diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs
index 2e9f06bd0a30c..20659d5e8c69c 100644
--- a/src/librustdoc/passes/strip_priv_imports.rs
+++ b/src/librustdoc/passes/strip_priv_imports.rs
@@ -13,7 +13,7 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass {
 };
 
 pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
-    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
+    let is_json_output = cx.is_json();
     ImportStripper {
         tcx: cx.tcx,
         is_json_output,
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index 78f0ad277408b..916c2f0657e47 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -18,7 +18,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass {
 pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     // This stripper collects all *retained* nodes.
     let mut retained = ItemIdSet::default();
-    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
+    let is_json_output = cx.is_json();
 
     // strip all private items
     {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index f789aca73784d..00f5251596b60 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -235,7 +235,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
-        if self.cx.output_format.is_json() {
+        if self.cx.is_json() {
             return false;
         }
 

From 0eff07ee4ec22e7f83462b4b1a42dc04e9d5570b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 4 Nov 2024 14:46:04 +0100
Subject: [PATCH 2/3] Add UI regressions tests for rustdoc `--show-coverage`
 option

---
 tests/rustdoc-ui/show-coverage-json.rs     | 13 +++++++++++++
 tests/rustdoc-ui/show-coverage-json.stdout |  1 +
 tests/rustdoc-ui/show-coverage.rs          | 13 +++++++++++++
 tests/rustdoc-ui/show-coverage.stdout      |  7 +++++++
 4 files changed, 34 insertions(+)
 create mode 100644 tests/rustdoc-ui/show-coverage-json.rs
 create mode 100644 tests/rustdoc-ui/show-coverage-json.stdout
 create mode 100644 tests/rustdoc-ui/show-coverage.rs
 create mode 100644 tests/rustdoc-ui/show-coverage.stdout

diff --git a/tests/rustdoc-ui/show-coverage-json.rs b/tests/rustdoc-ui/show-coverage-json.rs
new file mode 100644
index 0000000000000..3851e34fe3599
--- /dev/null
+++ b/tests/rustdoc-ui/show-coverage-json.rs
@@ -0,0 +1,13 @@
+//@ compile-flags: -Z unstable-options --show-coverage --output-format=json
+//@ check-pass
+
+mod bar {
+    /// a
+    ///
+    /// ```
+    /// let x = 0;
+    /// ```
+    pub struct Foo;
+}
+
+pub use bar::Foo;
diff --git a/tests/rustdoc-ui/show-coverage-json.stdout b/tests/rustdoc-ui/show-coverage-json.stdout
new file mode 100644
index 0000000000000..ed5b5a60212eb
--- /dev/null
+++ b/tests/rustdoc-ui/show-coverage-json.stdout
@@ -0,0 +1 @@
+{"$DIR/show-coverage-json.rs":{"total":2,"with_docs":1,"total_examples":2,"with_examples":1}}
diff --git a/tests/rustdoc-ui/show-coverage.rs b/tests/rustdoc-ui/show-coverage.rs
new file mode 100644
index 0000000000000..00bb1606a82cb
--- /dev/null
+++ b/tests/rustdoc-ui/show-coverage.rs
@@ -0,0 +1,13 @@
+//@ compile-flags: -Z unstable-options --show-coverage
+//@ check-pass
+
+mod bar {
+    /// a
+    ///
+    /// ```
+    /// let x = 0;
+    /// ```
+    pub struct Foo;
+}
+
+pub use bar::Foo;
diff --git a/tests/rustdoc-ui/show-coverage.stdout b/tests/rustdoc-ui/show-coverage.stdout
new file mode 100644
index 0000000000000..b3b7679771f22
--- /dev/null
+++ b/tests/rustdoc-ui/show-coverage.stdout
@@ -0,0 +1,7 @@
++-------------------------------------+------------+------------+------------+------------+
+| File                                | Documented | Percentage |   Examples | Percentage |
++-------------------------------------+------------+------------+------------+------------+
+| ...ests/rustdoc-ui/show-coverage.rs |          1 |      50.0% |          1 |      50.0% |
++-------------------------------------+------------+------------+------------+------------+
+| Total                               |          1 |      50.0% |          1 |      50.0% |
++-------------------------------------+------------+------------+------------+------------+

From 5dfbc0383d6e7ad27f0bdae5542109dafc9cd4f1 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 4 Nov 2024 17:15:28 +0100
Subject: [PATCH 3/3] Rename `DocContext::is_json` into
 `DocContext::is_json_output`

---
 src/librustdoc/clean/mod.rs                 | 4 ++--
 src/librustdoc/core.rs                      | 4 ++--
 src/librustdoc/passes/strip_hidden.rs       | 2 +-
 src/librustdoc/passes/strip_priv_imports.rs | 2 +-
 src/librustdoc/passes/strip_private.rs      | 2 +-
 src/librustdoc/visit_ast.rs                 | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index bd4704e8b28ed..992be651dbe08 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2907,7 +2907,7 @@ fn clean_extern_crate<'tcx>(
                     None => false,
                 }
         })
-        && !cx.is_json();
+        && !cx.is_json_output();
 
     let krate_owner_def_id = krate.owner_id.def_id;
     if please_inline {
@@ -3000,7 +3000,7 @@ fn clean_use_statement_inner<'tcx>(
     // forcefully don't inline if this is not public or if the
     // #[doc(no_inline)] attribute is present.
     // Don't inline doc(hidden) imports so they can be stripped at a later stage.
-    let mut denied = cx.is_json()
+    let mut denied = cx.is_json_output()
         || !(visibility.is_public()
             || (cx.render_options.document_private && is_visible_from_parent_mod))
         || pub_underscore
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 274b61ae1d3c0..7ba3cfb66bd23 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -124,8 +124,8 @@ impl<'tcx> DocContext<'tcx> {
 
     /// Returns `true` if the JSON output format is enabled for generating the crate content.
     ///
-    /// If another option like `--show-coverage` is enabled, it will return false.
-    pub(crate) fn is_json(&self) -> bool {
+    /// If another option like `--show-coverage` is enabled, it will return `false`.
+    pub(crate) fn is_json_output(&self) -> bool {
         self.output_format.is_json() && !self.show_coverage
     }
 }
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 2998289fd273a..4ef5f7f20a917 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -23,7 +23,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass {
 /// Strip items marked `#[doc(hidden)]`
 pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     let mut retained = ItemIdSet::default();
-    let is_json_output = cx.is_json();
+    let is_json_output = cx.is_json_output();
 
     // strip all #[doc(hidden)] items
     let krate = {
diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs
index 20659d5e8c69c..b9b2431f06f2b 100644
--- a/src/librustdoc/passes/strip_priv_imports.rs
+++ b/src/librustdoc/passes/strip_priv_imports.rs
@@ -13,7 +13,7 @@ pub(crate) const STRIP_PRIV_IMPORTS: Pass = Pass {
 };
 
 pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
-    let is_json_output = cx.is_json();
+    let is_json_output = cx.is_json_output();
     ImportStripper {
         tcx: cx.tcx,
         is_json_output,
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index 916c2f0657e47..1bd8a7838ec08 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -18,7 +18,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass {
 pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     // This stripper collects all *retained* nodes.
     let mut retained = ItemIdSet::default();
-    let is_json_output = cx.is_json();
+    let is_json_output = cx.is_json_output();
 
     // strip all private items
     {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 00f5251596b60..31c33fbf49737 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -235,7 +235,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
-        if self.cx.is_json() {
+        if self.cx.is_json_output() {
             return false;
         }