Skip to content

Commit

Permalink
Add support to generate documentation for tests
Browse files Browse the repository at this point in the history
The new option --document-tests is unstable and documented as such.
In order to use it is needed to add `--cfg test` and in case the tests
are not marked public to add `--document-private-items`.
The implementation hide the auto generate main test function and
constants.
  • Loading branch information
ifxfrancois committed Oct 7, 2024
1 parent 7caad69 commit fa3e84f
Show file tree
Hide file tree
Showing 18 changed files with 145 additions and 19 deletions.
31 changes: 31 additions & 0 deletions src/doc/rustdoc/src/unstable-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,34 @@ add the `--scrape-tests` flag.

This flag enables the generation of links in the source code pages which allow the reader
to jump to a type definition.

### `--document-tests`: show test items

Using this flag looks like this:

```bash
$ rustdoc src/lib.rs -Z unstable-options --cfg test --document-private-items --document-tests
```

By default, `rustdoc` does not document test items.

```rust
/// by default this test function would not be documented
#[test]
fn test_in_module() {
assert_eq!(2, 1 + 1);
}
/// by default this test module would not be documented
#[cfg(test)]
mod tests {
/// by default this test function would not be documented
#[test]
fn test_in_a_test_module() {
assert_eq!(2, 1 + 1);
}
}
```

Note:
* `--cfg test` must be set because tests are guarded by #[cfg(test)].
* `--document-private-items` is typically required because it is standard practice to keep test items private. By enabling this option, you ensure that private items, including tests, are documented as needed while maintaining their non-public status.
6 changes: 5 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,11 @@ fn clean_fn_or_proc_macro<'tcx>(
None => {
let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id));
clean_fn_decl_legacy_const_generics(&mut func, attrs);
FunctionItem(func)
if cx.cache.document_tests && cx.cache.tests.contains(&item.owner_id.to_def_id()) {
TestItem(func)
} else {
FunctionItem(func)
}
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,10 @@ impl Item {
asyncness: hir::IsAsync::NotAsync,
}
}
ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::TyMethodItem(_) => {
ItemKind::FunctionItem(_)
| ItemKind::MethodItem(_, _)
| ItemKind::TyMethodItem(_)
| ItemKind::TestItem(_) => {
let def_id = self.def_id().unwrap();
build_fn_header(def_id, tcx, tcx.asyncness(def_id))
}
Expand Down Expand Up @@ -841,6 +844,7 @@ pub(crate) enum ItemKind {
UnionItem(Union),
EnumItem(Enum),
FunctionItem(Box<Function>),
TestItem(Box<Function>),
ModuleItem(Module),
TypeAliasItem(Box<TypeAlias>),
StaticItem(Static),
Expand Down Expand Up @@ -899,6 +903,7 @@ impl ItemKind {
ExternCrateItem { .. }
| ImportItem(_)
| FunctionItem(_)
| TestItem(_)
| TypeAliasItem(_)
| StaticItem(_)
| ConstantItem(_)
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/clean/types/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ fn should_not_trim() {
fn is_same_generic() {
use crate::clean::types::{PrimitiveType, Type};
use crate::formats::cache::Cache;
let cache = Cache::new(false, false);
let cache = Cache::new(false, false, false);
let generic = Type::Generic(rustc_span::symbol::sym::Any);
let unit = Type::Primitive(PrimitiveType::Unit);
assert!(!generic.is_doc_subtype_of(&unit, &cache));
Expand Down
4 changes: 4 additions & 0 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ pub(crate) struct RenderOptions {
pub(crate) document_private: bool,
/// Document items that have `doc(hidden)`.
pub(crate) document_hidden: bool,
/// Document tests.
pub(crate) document_tests: bool,
/// If `true`, generate a JSON file in the crate folder instead of HTML redirection files.
pub(crate) generate_redirect_map: bool,
/// Show the memory layout of types in the docs.
Expand Down Expand Up @@ -772,6 +774,7 @@ impl Options {
}

let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
let document_tests = matches.opt_present("document-tests");
let with_examples = matches.opt_strs("with-examples");
let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);

Expand Down Expand Up @@ -840,6 +843,7 @@ impl Options {
markdown_playground_url,
document_private,
document_hidden,
document_tests,
generate_redirect_map,
show_type_layout,
unstable_features,
Expand Down
11 changes: 8 additions & 3 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ pub(crate) fn create_config(
remap_path_prefix,
..
}: RustdocOptions,
RenderOptions { document_private, .. }: &RenderOptions,
RenderOptions { document_private, document_tests, .. }: &RenderOptions,
using_internal_features: Arc<AtomicBool>,
) -> rustc_interface::Config {
// Add the doc cfg into the doc build.
Expand Down Expand Up @@ -227,7 +227,8 @@ pub(crate) fn create_config(
if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
let resolve_doc_links =
if *document_private { ResolveDocLinks::All } else { ResolveDocLinks::Exported };
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false)
|| (cfgs.iter().any(|cfg| cfg == "test") && *document_tests);
// plays with error output here!
let sessopts = config::Options {
maybe_sysroot,
Expand Down Expand Up @@ -341,7 +342,11 @@ pub(crate) fn run_global_ctxt(
impl_trait_bounds: Default::default(),
generated_synthetics: Default::default(),
auto_traits,
cache: Cache::new(render_options.document_private, render_options.document_hidden),
cache: Cache::new(
render_options.document_private,
render_options.document_hidden,
render_options.document_tests,
),
inlined: FxHashSet::default(),
output_format,
render_options,
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub(crate) trait DocFolder: Sized {
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
| TestItem(_)
| StaticItem(_)
| ConstantItem(..)
| TraitAliasItem(_)
Expand Down
10 changes: 8 additions & 2 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ pub(crate) struct Cache {
/// Whether to document hidden items.
/// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
pub(crate) document_hidden: bool,
/// Whether to document tests.
/// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
pub(crate) document_tests: bool,
/// DefIds of all functions which are tests.
pub(crate) tests: FxHashSet<DefId>,

/// Crates marked with [`#[doc(masked)]`][doc_masked].
///
Expand Down Expand Up @@ -142,8 +147,8 @@ struct CacheBuilder<'a, 'tcx> {
}

impl Cache {
pub(crate) fn new(document_private: bool, document_hidden: bool) -> Self {
Cache { document_private, document_hidden, ..Cache::default() }
pub(crate) fn new(document_private: bool, document_hidden: bool, document_tests: bool) -> Self {
Cache { document_private, document_hidden, document_tests, ..Cache::default() }
}

/// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was
Expand Down Expand Up @@ -295,6 +300,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
| clean::TraitItem(..)
| clean::TraitAliasItem(..)
| clean::FunctionItem(..)
| clean::TestItem(..)
| clean::ModuleItem(..)
| clean::ForeignFunctionItem(..)
| clean::ForeignStaticItem(..)
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/formats/item_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub(crate) enum ItemType {
TraitAlias = 25,
// This number is reserved for use in JavaScript
// Generic = 26,
Test = 27,
}

impl Serialize for ItemType {
Expand All @@ -83,6 +84,7 @@ impl<'a> From<&'a clean::Item> for ItemType {
clean::UnionItem(..) => ItemType::Union,
clean::EnumItem(..) => ItemType::Enum,
clean::FunctionItem(..) => ItemType::Function,
clean::TestItem(..) => ItemType::Test,
clean::TypeAliasItem(..) => ItemType::TypeAlias,
clean::StaticItem(..) => ItemType::Static,
clean::ConstantItem(..) => ItemType::Constant,
Expand Down Expand Up @@ -176,6 +178,7 @@ impl ItemType {
ItemType::Union => "union",
ItemType::Enum => "enum",
ItemType::Function => "fn",
ItemType::Test => "test",
ItemType::TypeAlias => "type",
ItemType::Static => "static",
ItemType::Trait => "trait",
Expand Down
15 changes: 15 additions & 0 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ struct AllTypes {
attribute_macros: FxIndexSet<ItemEntry>,
derive_macros: FxIndexSet<ItemEntry>,
trait_aliases: FxIndexSet<ItemEntry>,
tests: FxIndexSet<ItemEntry>,
}

impl AllTypes {
Expand All @@ -360,6 +361,7 @@ impl AllTypes {
attribute_macros: new_set(100),
derive_macros: new_set(100),
trait_aliases: new_set(100),
tests: new_set(100),
}
}

Expand All @@ -385,6 +387,7 @@ impl AllTypes {
}
ItemType::ProcDerive => self.derive_macros.insert(ItemEntry::new(new_url, name)),
ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
ItemType::Test => self.tests.insert(ItemEntry::new(new_url, name)),
_ => true,
};
}
Expand Down Expand Up @@ -414,6 +417,9 @@ impl AllTypes {
if !self.functions.is_empty() {
sections.insert(ItemSection::Functions);
}
if !self.tests.is_empty() {
sections.insert(ItemSection::Tests);
}
if !self.type_aliases.is_empty() {
sections.insert(ItemSection::TypeAliases);
}
Expand All @@ -432,6 +438,9 @@ impl AllTypes {
if !self.trait_aliases.is_empty() {
sections.insert(ItemSection::TraitAliases);
}
if !self.tests.is_empty() {
sections.insert(ItemSection::Tests);
}

sections
}
Expand Down Expand Up @@ -468,6 +477,7 @@ impl AllTypes {
print_entries(f, &self.attribute_macros, ItemSection::AttributeMacros);
print_entries(f, &self.derive_macros, ItemSection::DeriveMacros);
print_entries(f, &self.functions, ItemSection::Functions);
print_entries(f, &self.tests, ItemSection::Tests);
print_entries(f, &self.type_aliases, ItemSection::TypeAliases);
print_entries(f, &self.trait_aliases, ItemSection::TraitAliases);
print_entries(f, &self.statics, ItemSection::Statics);
Expand Down Expand Up @@ -2227,6 +2237,7 @@ pub(crate) enum ItemSection {
Statics,
Traits,
Functions,
Tests,
TypeAliases,
Unions,
Implementations,
Expand Down Expand Up @@ -2259,6 +2270,7 @@ impl ItemSection {
Statics,
Traits,
Functions,
Tests,
TypeAliases,
Unions,
Implementations,
Expand All @@ -2284,6 +2296,7 @@ impl ItemSection {
Self::Unions => "unions",
Self::Enums => "enums",
Self::Functions => "functions",
Self::Tests => "tests",
Self::TypeAliases => "types",
Self::Statics => "statics",
Self::Constants => "constants",
Expand Down Expand Up @@ -2313,6 +2326,7 @@ impl ItemSection {
Self::Unions => "Unions",
Self::Enums => "Enums",
Self::Functions => "Functions",
Self::Tests => "Tests",
Self::TypeAliases => "Type Aliases",
Self::Statics => "Statics",
Self::Constants => "Constants",
Expand Down Expand Up @@ -2343,6 +2357,7 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
ItemType::Union => ItemSection::Unions,
ItemType::Enum => ItemSection::Enums,
ItemType::Function => ItemSection::Functions,
ItemType::Test => ItemSection::Tests,
ItemType::TypeAlias => ItemSection::TypeAliases,
ItemType::Static => ItemSection::Statics,
ItemType::Constant => ItemSection::Constants,
Expand Down
8 changes: 5 additions & 3 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf
}
}
clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ",
clean::TestItem(..) => "Test ",
clean::TraitItem(..) => "Trait ",
clean::StructItem(..) => "Struct ",
clean::UnionItem(..) => "Union ",
Expand Down Expand Up @@ -254,9 +255,9 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf

match &item.kind {
clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f, _) => {
item_function(buf, cx, item, f)
}
clean::FunctionItem(ref f)
| clean::ForeignFunctionItem(ref f, _)
| clean::TestItem(ref f) => item_function(buf, cx, item, f),
clean::TraitItem(ref t) => item_trait(buf, cx, item, t),
clean::StructItem(ref s) => item_struct(buf, cx, item, s),
clean::UnionItem(ref s) => item_union(buf, cx, item, s),
Expand Down Expand Up @@ -332,6 +333,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
ItemType::Static => 8,
ItemType::Trait => 9,
ItemType::Function => 10,
ItemType::Test => 11,
ItemType::TypeAlias => 12,
ItemType::Union => 13,
_ => 14 + ty as u8,
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/html/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ function preLoadCss(cssUrl) {
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
block("test", "tests", "Tests");
block("type", "types", "Type Aliases");
block("union", "unions", "Unions");
// No point, because these items don't appear in modules
Expand Down
6 changes: 4 additions & 2 deletions src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)),
EnumItem(e) => ItemEnum::Enum(e.into_tcx(tcx)),
VariantItem(v) => ItemEnum::Variant(v.into_tcx(tcx)),
FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)),
FunctionItem(f) | TestItem(f) => {
ItemEnum::Function(from_function(f, true, header.unwrap(), tcx))
}
ForeignFunctionItem(f, _) => {
ItemEnum::Function(from_function(f, false, header.unwrap(), tcx))
}
Expand Down Expand Up @@ -858,7 +860,7 @@ impl FromWithTcx<ItemType> for ItemKind {
Struct => ItemKind::Struct,
Union => ItemKind::Union,
Enum => ItemKind::Enum,
Function | TyMethod | Method => ItemKind::Function,
Function | Test | TyMethod | Method => ItemKind::Function,
TypeAlias => ItemKind::TypeAlias,
Static => ItemKind::Static,
Constant => ItemKind::Constant,
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@ fn opts() -> Vec<RustcOptGroup> {
unstable("scrape-tests", |o| {
o.optflag("", "scrape-tests", "Include test code when scraping examples")
}),
unstable("document-tests", |o| {
o.optflagmulti("", "document-tests", "Generate documentation for tests")
}),
unstable("with-examples", |o| {
o.optmulti(
"",
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/passes/stripper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
| clean::EnumItem(..)
| clean::TraitItem(..)
| clean::FunctionItem(..)
| clean::TestItem(..)
| clean::VariantItem(..)
| clean::ForeignFunctionItem(..)
| clean::ForeignStaticItem(..)
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub(crate) trait DocVisitor<'a>: Sized {
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
| TestItem(_)
| TypeAliasItem(_)
| StaticItem(_)
| ConstantItem(..)
Expand Down
Loading

0 comments on commit fa3e84f

Please sign in to comment.