Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Calculates information used for the --show-coverage flag.

use std::collections::BTreeMap;
use std::fs::{File, create_dir_all};
use std::io::{self, BufWriter, Write, stdout};
use std::ops;

use rustc_hir as hir;
Expand All @@ -10,27 +12,39 @@ use rustc_span::{FileName, RemapPathScopeComponents};
use serde::Serialize;
use tracing::debug;

use crate::clean;
use crate::config::OutputFormat;
use crate::config::{OutputFormat, RenderOptions};
use crate::core::DocContext;
use crate::docfs::PathError;
use crate::error::Error;
use crate::html::markdown::{ErrorCodes, find_testable_code};
use crate::passes::Pass;
use crate::passes::check_doc_test_visibility::{Tests, should_have_doc_example};
use crate::passes::{Tests, should_have_doc_example};
use crate::visit::DocVisitor;

pub(crate) const CALCULATE_DOC_COVERAGE: Pass = Pass {
name: "calculate-doc-coverage",
run: Some(calculate_doc_coverage),
description: "counts the number of items with and without documentation",
};

fn calculate_doc_coverage(krate: clean::Crate, ctx: &mut DocContext<'_>) -> clean::Crate {
use crate::{clean, try_err};

pub(crate) fn run(
krate: &clean::Crate,
ctx: &mut DocContext<'_>,
options: &RenderOptions,
) -> Result<(), Error> {
let is_json = ctx.output_format == OutputFormat::CoverageJson;
let tcx = ctx.tcx;
let mut calc = CoverageCalculator { items: Default::default(), ctx };
calc.visit_crate(&krate);

calc.print_results();

krate
if options.output_to_stdout {
calc.print_results(BufWriter::new(stdout().lock()))
.map_err(|error| Error::new(error, "<stdout>"))
} else {
let out_dir = &options.output;
try_err!(create_dir_all(out_dir), out_dir);
let name = krate.name(tcx);
let mut out_file = out_dir.join(name.as_str());
out_file.set_extension(if is_json { "json" } else { "txt" });
let buf = try_err!(File::create_buffered(&out_file), out_file);
calc.print_results(buf).map_err(|error| Error::new(error, &out_file))?;
println!("Generated output into {out_file:?}");
Ok(())
}
}

#[derive(Default, Copy, Clone, Serialize, Debug)]
Expand Down Expand Up @@ -130,62 +144,66 @@ impl CoverageCalculator<'_, '_> {
.expect("failed to convert JSON data to string")
}

fn print_results(&self) {
fn print_results(&self, mut buf: impl Write) -> io::Result<()> {
let output_format = self.ctx.output_format;
if output_format == OutputFormat::CoverageJson {
println!("{}", self.to_json());
return;
return writeln!(buf, "{}", self.to_json());
}
let mut total = ItemCount::default();

fn print_table_line() {
println!("+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", "");
fn print_table_line(buf: &mut impl Write) -> io::Result<()> {
writeln!(buf, "+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", "")
}

fn print_table_record(
buf: &mut impl Write,
name: &str,
count: ItemCount,
percentage: f64,
examples_percentage: f64,
) {
println!(
) -> io::Result<()> {
writeln!(
buf,
"| {name:<35} | {with_docs:>10} | {percentage:>9.1}% | {with_examples:>10} | \
{examples_percentage:>9.1}% |",
{examples_percentage:>9.1}% |",
with_docs = count.with_docs,
with_examples = count.with_examples,
);
)
}

print_table_line();
println!(
print_table_line(&mut buf)?;
writeln!(
buf,
"| {:<35} | {:>10} | {:>10} | {:>10} | {:>10} |",
"File", "Documented", "Percentage", "Examples", "Percentage",
);
print_table_line();
)?;
print_table_line(&mut buf)?;

for (file, &count) in &self.items {
if let Some(percentage) = count.percentage() {
print_table_record(
&mut buf,
&limit_filename_len(
file.display(RemapPathScopeComponents::COVERAGE).to_string(),
),
count,
percentage,
count.examples_percentage().unwrap_or(0.),
);
)?;

total += count;
}
}

print_table_line();
print_table_line(&mut buf)?;
print_table_record(
&mut buf,
"Total",
total,
total.percentage().unwrap_or(0.0),
total.examples_percentage().unwrap_or(0.0),
);
print_table_line();
)?;
print_table_line(&mut buf)
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,14 @@ impl Options {
output_to_stdout = out_dir == "-";
PathBuf::from(out_dir)
}
(None, None) => PathBuf::from("doc"),
(None, None) => {
if show_coverage {
// If no `-o` option is given and we're in the `--show-coverage` mode, by
// default we print on the stdout.
output_to_stdout = true;
}
PathBuf::from("doc")
}
};

let cfgs = matches.opt_strs("cfg");
Expand Down
7 changes: 7 additions & 0 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,13 @@ pub(crate) fn run_global_ctxt(
}
}

if show_coverage
&& let Err(error) = crate::calculate_doc_coverage::run(&krate, &mut ctxt, &render_options)
{
eprintln!("{error}");
std::process::exit(1);
}

tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc)));

krate =
Expand Down
5 changes: 3 additions & 2 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ macro_rules! map {
}}
}

mod calculate_doc_coverage;
mod clean;
mod config;
mod core;
Expand Down Expand Up @@ -942,14 +943,14 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate);
}

cache.crate_version = crate_version;

if show_coverage {
// if we ran coverage, bail early, we don't need to also generate docs at this point
// (also we didn't load in any of the useful passes)
return;
}

cache.crate_version = crate_version;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?


rustc_interface::passes::emit_delayed_lints(tcx);

if render_opts.dep_info().is_some() {
Expand Down
9 changes: 3 additions & 6 deletions src/librustdoc/passes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ pub(crate) mod collect_intra_doc_links;
pub(crate) use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS;

mod check_doc_test_visibility;
pub(crate) use self::check_doc_test_visibility::CHECK_DOC_TEST_VISIBILITY;
pub(crate) use self::check_doc_test_visibility::{
CHECK_DOC_TEST_VISIBILITY, Tests, should_have_doc_example,
};

mod collect_trait_impls;
pub(crate) use self::collect_trait_impls::COLLECT_TRAIT_IMPLS;

mod calculate_doc_coverage;
pub(crate) use self::calculate_doc_coverage::CALCULATE_DOC_COVERAGE;

mod lint;
pub(crate) use self::lint::RUN_LINTS;

Expand Down Expand Up @@ -81,7 +80,6 @@ pub(crate) const PASSES: &[Pass] = &[
PROPAGATE_STABILITY,
COLLECT_INTRA_DOC_LINKS,
COLLECT_TRAIT_IMPLS,
CALCULATE_DOC_COVERAGE,
RUN_LINTS,
];

Expand All @@ -103,7 +101,6 @@ pub(crate) const DEFAULT_PASSES: &[ConditionalPass] = &[
pub(crate) const COVERAGE_PASSES: &[ConditionalPass] = &[
ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
ConditionalPass::always(CALCULATE_DOC_COVERAGE),
];

impl ConditionalPass {
Expand Down
5 changes: 5 additions & 0 deletions tests/run-make/rustdoc-show-coverage/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub struct Bar;

impl Bar {
pub fn foo() {}
}
56 changes: 56 additions & 0 deletions tests/run-make/rustdoc-show-coverage/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This test ensures that `-o` option works as expected with `--show-coverage`.
// Regression test for <https://github.com/rust-lang/rust/issues/158929>.

//@ needs-target-std

use run_make_support::rfs::{read_to_string, remove_file};
use run_make_support::{path, rustdoc};

fn run_rustdoc(extra_args: &[&str]) -> String {
rustdoc()
.input("foo.rs")
.arg("-Zunstable-options")
.arg("--show-coverage")
.args(extra_args)
.run()
.stdout_utf8()
}

fn check_print_stdout(extra_args: &[&str], stdout_check: &str) {
let out = run_rustdoc(extra_args);

// By default, it shouldn't have created a `doc` folder.
assert!(!path("doc").exists(), "`doc` folder created with {extra_args:?}");
// It should have display its output on stdout.
assert!(out.starts_with(stdout_check), "{out:?} doesn't start with {stdout_check:?}");
}

fn check_generate_file(ext: &str, extra_args: &[&str], file_check: &str) {
let mut args = extra_args.to_vec();
args.push("-o");
args.push("doc");
let out = run_rustdoc(&args);

// By default, it shouldn't have created a `doc` folder.
assert!(path("doc").exists(), "`doc` folder not created with {args:?}");
let file = format!("doc/foo.{ext}");
assert!(path(&file).exists());

let expected = format!("Generated output into {file:?}\n");
assert_eq!(out, expected, "Expected {expected:?}, got {out:?}");

let content = read_to_string(&file);
assert!(content.starts_with(file_check), "{content:?} doesn't start with {file_check:?}");
remove_file(file);
}

fn main() {
check_print_stdout(&[], "+-");
check_print_stdout(&["-o", "-"], "+-");
check_print_stdout(&["--output-format=json"], "{");
check_print_stdout(&["--output-format=json", "-o", "-"], "{");

// Now we check that it works with "-o something".
check_generate_file("txt", &[], "+-");
check_generate_file("json", &["--output-format=json"], "{");
}
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/allow_missing_docs.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...i/coverage/allow_missing_docs.rs | 5 | 71.4% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 5 | 71.4% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/allow_missing_docs.txt"

@aDotInTheVoid aDotInTheVoid Jul 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes all the coverage tests not actually check that the output numbers are correct.

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely missed it. Gonna add -o - for these tests to ensure they keep the stdout check.

8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/basic.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...sts/rustdoc-ui/coverage/basic.rs | 7 | 50.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 7 | 50.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/basic.txt"
2 changes: 1 addition & 1 deletion tests/rustdoc-ui/coverage/doc-examples-json.stdout
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"$DIR/doc-examples-json.rs":{"total":3,"with_docs":2,"total_examples":1,"with_examples":1}}
Generated output into "$TEST_BUILD_DIR/doc_examples_json.json"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/doc-examples.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...tdoc-ui/coverage/doc-examples.rs | 4 | 100.0% | 1 | 33.3% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 4 | 100.0% | 1 | 33.3% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/doc_examples.txt"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/empty.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...sts/rustdoc-ui/coverage/empty.rs | 0 | 0.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 0 | 0.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/empty.txt"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/enum-tuple-documented.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...overage/enum-tuple-documented.rs | 9 | 100.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 9 | 100.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/enum_tuple_documented.txt"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/enum-tuple.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...ustdoc-ui/coverage/enum-tuple.rs | 6 | 100.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 6 | 100.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/enum_tuple.txt"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/enums.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...sts/rustdoc-ui/coverage/enums.rs | 6 | 75.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 6 | 75.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/enums.txt"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/exotic.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...ts/rustdoc-ui/coverage/exotic.rs | 3 | 100.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 3 | 100.0% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/exotic.txt"
2 changes: 1 addition & 1 deletion tests/rustdoc-ui/coverage/json.stdout
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"$DIR/json.rs":{"total":17,"with_docs":12,"total_examples":13,"with_examples":6}}
Generated output into "$TEST_BUILD_DIR/json.json"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/private.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...s/rustdoc-ui/coverage/private.rs | 4 | 57.1% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 4 | 57.1% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/private.txt"
8 changes: 1 addition & 7 deletions tests/rustdoc-ui/coverage/statics-consts.stdout
Original file line number Diff line number Diff line change
@@ -1,7 +1 @@
+-------------------------------------+------------+------------+------------+------------+
| File | Documented | Percentage | Examples | Percentage |
+-------------------------------------+------------+------------+------------+------------+
| ...oc-ui/coverage/statics-consts.rs | 6 | 85.7% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
| Total | 6 | 85.7% | 0 | 0.0% |
+-------------------------------------+------------+------------+------------+------------+
Generated output into "$TEST_BUILD_DIR/statics_consts.txt"
Loading
Loading