From 489f41c9d5857aa1ee4ddd1f5f1e93948acef5b5 Mon Sep 17 00:00:00 2001 From: Luca Gladiator Date: Mon, 17 Jun 2024 15:24:17 +0200 Subject: [PATCH 1/3] Add support to generate documentation for tests --- src/librustdoc/config.rs | 6 ++++++ src/librustdoc/core.rs | 4 +++- src/librustdoc/lib.rs | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 012afada1e5ee..f83af3214d94f 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -160,6 +160,9 @@ pub(crate) struct Options { /// the compiler will scrape examples and not generate documentation. pub(crate) scrape_examples_options: Option, + /// Whether to generate documentation for tests. + pub(crate) document_tests: bool, + /// Note: this field is duplicated in `RenderOptions` because it's useful /// to have it in both places. pub(crate) unstable_features: rustc_feature::UnstableFeatures, @@ -213,6 +216,7 @@ impl fmt::Debug for Options { .field("test_builder_wrappers", &self.test_builder_wrappers) .field("nocapture", &self.nocapture) .field("scrape_examples_options", &self.scrape_examples_options) + .field("document_tests", &self.document_tests) .field("unstable_features", &self.unstable_features) .finish() } @@ -732,6 +736,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); @@ -777,6 +782,7 @@ impl Options { output_format, json_unused_externs, scrape_examples_options, + document_tests, unstable_features, expanded_args: args, }; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index ccd5dadb20a00..aaccabf56bf76 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -195,6 +195,7 @@ pub(crate) fn create_config( describe_lints, lint_cap, scrape_examples_options, + document_tests, expanded_args, .. }: RustdocOptions, @@ -227,7 +228,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) || document_tests; // plays with error output here! let sessopts = config::Options { maybe_sysroot, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c0d2f9cfaf95d..dcd1447eeb62a 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -587,6 +587,9 @@ fn opts() -> Vec { 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( "", From 860f1a78fc49ae1da1f4ea97f6260a4f6a2a85ba Mon Sep 17 00:00:00 2001 From: Luca Gladiator Date: Fri, 21 Jun 2024 16:51:52 +0200 Subject: [PATCH 2/3] Render tests --- src/librustdoc/clean/mod.rs | 6 +++- src/librustdoc/clean/types.rs | 7 ++++- src/librustdoc/config.rs | 8 ++--- src/librustdoc/core.rs | 11 ++++--- src/librustdoc/fold.rs | 1 + src/librustdoc/formats/cache.rs | 10 ++++-- src/librustdoc/formats/item_type.rs | 3 ++ src/librustdoc/html/render/mod.rs | 12 ++++++++ src/librustdoc/html/render/print_item.rs | 4 ++- src/librustdoc/html/static/js/main.js | 1 + src/librustdoc/json/conversions.rs | 6 ++-- src/librustdoc/passes/stripper.rs | 1 + src/librustdoc/visit.rs | 1 + src/librustdoc/visit_ast.rs | 39 ++++++++++++++++++++++-- 14 files changed, 91 insertions(+), 19 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 839bfdf44af19..d7f0cac65618c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1046,7 +1046,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) + } } } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index b387809cc717a..3df7738510cc7 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -659,7 +659,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)) } @@ -826,6 +829,7 @@ pub(crate) enum ItemKind { UnionItem(Union), EnumItem(Enum), FunctionItem(Box), + TestItem(Box), ModuleItem(Module), TypeAliasItem(Box), OpaqueTyItem(OpaqueTy), @@ -885,6 +889,7 @@ impl ItemKind { ExternCrateItem { .. } | ImportItem(_) | FunctionItem(_) + | TestItem(_) | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index f83af3214d94f..a35c7d21bde01 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -160,9 +160,6 @@ pub(crate) struct Options { /// the compiler will scrape examples and not generate documentation. pub(crate) scrape_examples_options: Option, - /// Whether to generate documentation for tests. - pub(crate) document_tests: bool, - /// Note: this field is duplicated in `RenderOptions` because it's useful /// to have it in both places. pub(crate) unstable_features: rustc_feature::UnstableFeatures, @@ -216,7 +213,6 @@ impl fmt::Debug for Options { .field("test_builder_wrappers", &self.test_builder_wrappers) .field("nocapture", &self.nocapture) .field("scrape_examples_options", &self.scrape_examples_options) - .field("document_tests", &self.document_tests) .field("unstable_features", &self.unstable_features) .finish() } @@ -276,6 +272,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. @@ -782,7 +780,6 @@ impl Options { output_format, json_unused_externs, scrape_examples_options, - document_tests, unstable_features, expanded_args: args, }; @@ -806,6 +803,7 @@ impl Options { markdown_playground_url, document_private, document_hidden, + document_tests, generate_redirect_map, show_type_layout, unstable_features, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index aaccabf56bf76..b023d0011917d 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -195,11 +195,10 @@ pub(crate) fn create_config( describe_lints, lint_cap, scrape_examples_options, - document_tests, expanded_args, .. }: RustdocOptions, - RenderOptions { document_private, .. }: &RenderOptions, + RenderOptions { document_private, document_tests, .. }: &RenderOptions, using_internal_features: Arc, ) -> rustc_interface::Config { // Add the doc cfg into the doc build. @@ -229,7 +228,7 @@ pub(crate) fn create_config( 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) || document_tests; + scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false) || *document_tests; // plays with error output here! let sessopts = config::Options { maybe_sysroot, @@ -342,7 +341,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, diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index cf11e2d7899e0..bd379c126887b 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -77,6 +77,7 @@ pub(crate) trait DocFolder: Sized { ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) + | TestItem(_) | OpaqueTyItem(_) | StaticItem(_) | ConstantItem(_) diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 28ddf76a3a695..e5c059669f819 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -89,6 +89,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, /// Crates marked with [`#[doc(masked)]`][doc_masked]. /// @@ -140,8 +145,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 @@ -424,6 +429,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { | clean::TraitItem(..) | clean::TraitAliasItem(..) | clean::FunctionItem(..) + | clean::TestItem(..) | clean::ModuleItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index d5468798bd397..61b9448aba3c3 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -58,6 +58,7 @@ pub(crate) enum ItemType { TraitAlias = 25, // This number is reserved for use in JavaScript // Generic = 26, + Test = 27, } impl Serialize for ItemType { @@ -84,6 +85,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::OpaqueTyItem(..) => ItemType::OpaqueTy, clean::StaticItem(..) => ItemType::Static, @@ -177,6 +179,7 @@ impl ItemType { ItemType::Union => "union", ItemType::Enum => "enum", ItemType::Function => "fn", + ItemType::Test => "test", ItemType::TypeAlias => "type", ItemType::Static => "static", ItemType::Trait => "trait", diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 877a00e206d11..d6cc6d735c740 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -337,6 +337,7 @@ struct AllTypes { traits: FxHashSet, macros: FxHashSet, functions: FxHashSet, + tests: FxHashSet, type_aliases: FxHashSet, opaque_tys: FxHashSet, statics: FxHashSet, @@ -357,6 +358,7 @@ impl AllTypes { traits: new_set(100), macros: new_set(100), functions: new_set(100), + tests: new_set(100), type_aliases: new_set(100), opaque_tys: new_set(100), statics: new_set(100), @@ -381,6 +383,7 @@ impl AllTypes { ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)), ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), + ItemType::Test => self.tests.insert(ItemEntry::new(new_url, name)), ItemType::TypeAlias => self.type_aliases.insert(ItemEntry::new(new_url, name)), ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), @@ -419,6 +422,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); } @@ -476,6 +482,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.opaque_tys, ItemSection::OpaqueTypes); @@ -2149,6 +2156,7 @@ pub(crate) enum ItemSection { Statics, Traits, Functions, + Tests, TypeAliases, Unions, Implementations, @@ -2182,6 +2190,7 @@ impl ItemSection { Statics, Traits, Functions, + Tests, TypeAliases, Unions, Implementations, @@ -2208,6 +2217,7 @@ impl ItemSection { Self::Unions => "unions", Self::Enums => "enums", Self::Functions => "functions", + Self::Tests => "tests", Self::TypeAliases => "types", Self::Statics => "statics", Self::Constants => "constants", @@ -2238,6 +2248,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", @@ -2269,6 +2280,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, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index c5b88c7a951f6..dc9656f75d311 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -186,6 +186,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 ", @@ -254,7 +255,7 @@ 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) => { + 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), @@ -331,6 +332,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, diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 64c356607788c..5f8070c18f842 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -554,6 +554,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 diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7e6a19aa52e0a..aecb0486e3510 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -309,7 +309,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)), TraitItem(t) => ItemEnum::Trait((*t).into_tcx(tcx)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_tcx(tcx)), @@ -844,7 +846,7 @@ impl FromWithTcx 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, OpaqueTy => ItemKind::OpaqueTy, Static => ItemKind::Static, diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 1bea93c784218..3e0f257f97db5 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -55,6 +55,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { | clean::EnumItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) + | clean::TestItem(..) | clean::VariantItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 01e6cb4b93bfc..8e738d91bfa05 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -25,6 +25,7 @@ pub(crate) trait DocVisitor: Sized { ExternCrateItem { src: _ } | ImportItem(_) | FunctionItem(_) + | TestItem(_) | TypeAliasItem(_) | OpaqueTyItem(_) | StaticItem(_) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 886df82e5b69f..d8a0a9fbc719d 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -2,10 +2,10 @@ //! usable for `clean`. use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{walk_body, walk_item, Visitor}; +use rustc_hir::{self as hir, TyKind}; use rustc_hir::{Node, CRATE_HIR_ID}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; @@ -84,6 +84,7 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> { modules: Vec>, is_importable_from_parent: bool, inside_body: bool, + next_is_test: bool, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { @@ -108,6 +109,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { modules: vec![om], is_importable_from_parent: true, inside_body: false, + next_is_test: false, } } @@ -383,6 +385,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { _ => false, } { + if self.cx.cache.document_tests + && let hir::ItemKind::Fn(..) = item.kind + && self.next_is_test + { + self.cx.cache.tests.insert(item.owner_id.to_def_id()); + self.next_is_test = false; + }; self.modules .last_mut() .unwrap() @@ -518,11 +527,35 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }) => { // return-position impl traits are never nameable, and should never be documented. } - hir::ItemKind::Const(..) => { + hir::ItemKind::Const(ty, _, _) => { // Underscore constants do not correspond to a nameable item and // so are never useful in documentation. if name != kw::Underscore { - self.add_to_current_mod(item, renamed, import_id); + if self.cx.cache.document_tests + && let TyKind::Path(rustc_hir::QPath::Resolved(_, path)) = ty.kind + { + let path: String = path + .segments + .iter() + .map(|s| { + if s.ident.name == kw::PathRoot { + "" + } else { + s.ident.name.as_str() + } + }) + .intersperse("::") + .collect(); + if path == "test::TestDescAndFn" { + // Intentionally do not add this, since we want to document the test + // and not the generated constants. + self.next_is_test = true; + } else { + self.add_to_current_mod(item, renamed, import_id); + } + } else { + self.add_to_current_mod(item, renamed, import_id); + } } } hir::ItemKind::Impl(impl_) => { From 799fc5d89fac423d264e44f8bdf6c3a3d471c7cc Mon Sep 17 00:00:00 2001 From: Heike Gilg Date: Mon, 8 Jul 2024 17:15:17 +0200 Subject: [PATCH 3/3] Remove auto created function main from test documentation --- src/librustdoc/visit_ast.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index d8a0a9fbc719d..3bf6f4644a0ac 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -506,8 +506,16 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { hir::ItemKind::Mod(ref m) => { self.enter_mod(item.owner_id.def_id, m, name, renamed, import_id); } - hir::ItemKind::Fn(..) - | hir::ItemKind::ExternCrate(..) + hir::ItemKind::Fn(fn_sig, _, _) => { + // Don't show auto created function "main" that is not in the source code (empty span) when documenting tests. + if !(self.cx.cache.document_tests + && fn_sig.span.is_empty() + && name.as_str() == "main") + { + self.add_to_current_mod(item, renamed, import_id); + } + } + hir::ItemKind::ExternCrate(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..)