From 6f0544abe4b62021ad2849a24db9f89ca3d38ddb Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Tue, 6 Oct 2020 20:48:01 +0200 Subject: [PATCH 1/3] Simplify doc-cfg rendering based on the current context For sub-items on a page don't show cfg that has already been rendered on a parent item. At its simplest this means not showing anything that is shown in the portability message at the top of the page, but also for things like fields of an enum variant if that variant itself is cfg-gated then don't repeat those cfg on each field of the variant. This does not touch trait implementation rendering, as that is more complex and there are existing issues around how it deals with doc-cfg that need to be fixed first. --- src/librustdoc/clean/cfg.rs | 29 ++++ src/librustdoc/html/render/mod.rs | 104 ++++++++---- src/test/rustdoc/doc-cfg-simplification.rs | 182 +++++++++++++++++++++ src/test/rustdoc/doc-cfg.rs | 7 +- src/test/rustdoc/duplicate-cfg.rs | 26 ++- 5 files changed, 296 insertions(+), 52 deletions(-) create mode 100644 src/test/rustdoc/doc-cfg-simplification.rs diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index c039b181178a4..4d8168dd8ef74 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -201,6 +201,35 @@ impl Cfg { _ => false, } } + + /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will + /// return `None` if simplification managed to completely eliminate any requirements from this + /// `Cfg`. + pub(crate) fn simplify_with(&self, assume: &Cfg) -> Option { + if self == assume { + return None; + } + + if let Cfg::All(a) = self { + let mut sub_cfgs: Vec = if let Cfg::All(b) = assume { + a.iter().filter(|a| !b.contains(a)).cloned().collect() + } else { + a.iter().filter(|&a| a != assume).cloned().collect() + }; + let len = sub_cfgs.len(); + return match len { + 0 => None, + 1 => sub_cfgs.pop(), + _ => Some(Cfg::All(sub_cfgs)), + }; + } else if let Cfg::All(b) = assume { + if b.contains(self) { + return None; + } + } + + Some(self.clone()) + } } impl ops::Not for Cfg { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8b5ba7a239c5f..e71df4b45c339 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1762,11 +1762,11 @@ crate fn shorten(s: String) -> String { } } -fn document(w: &mut Buffer, cx: &Context, item: &clean::Item) { +fn document(w: &mut Buffer, cx: &Context, item: &clean::Item, parent: Option<&clean::Item>) { if let Some(ref name) = item.name { info!("Documenting {}", name); } - document_stability(w, cx, item, false); + document_stability(w, cx, item, false, parent); document_full(w, item, cx, "", false); } @@ -1850,8 +1850,14 @@ fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context, prefix: &str, } } -fn document_stability(w: &mut Buffer, cx: &Context, item: &clean::Item, is_hidden: bool) { - let stabilities = short_stability(item, cx); +fn document_stability( + w: &mut Buffer, + cx: &Context, + item: &clean::Item, + is_hidden: bool, + parent: Option<&clean::Item>, +) { + let stabilities = short_stability(item, cx, parent); if !stabilities.is_empty() { write!(w, "
", if is_hidden { " hidden" } else { "" }); for stability in stabilities { @@ -1951,7 +1957,7 @@ pub fn compare_names(mut lhs: &str, mut rhs: &str) -> Ordering { } fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean::Item]) { - document(w, cx, item); + document(w, cx, item, None); let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); @@ -2108,7 +2114,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: {stab_tags}{docs}\ ", name = *myitem.name.as_ref().unwrap(), - stab_tags = stability_tags(myitem), + stab_tags = stability_tags(myitem, item), docs = MarkdownSummaryLine(doc_value, &myitem.links()).into_string(), class = myitem.type_(), add = add, @@ -2132,7 +2138,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: /// Render the stability and deprecation tags that are displayed in the item's summary at the /// module level. -fn stability_tags(item: &clean::Item) -> String { +fn stability_tags(item: &clean::Item, parent: &clean::Item) -> String { let mut tags = String::new(); fn tag_html(class: &str, title: &str, contents: &str) -> String { @@ -2159,7 +2165,13 @@ fn stability_tags(item: &clean::Item) -> String { tags += &tag_html("unstable", "", "Experimental"); } - if let Some(ref cfg) = item.attrs.cfg { + let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) { + (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), + (cfg, _) => cfg.as_deref().cloned(), + }; + + info!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg); + if let Some(ref cfg) = cfg { tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); } @@ -2168,7 +2180,7 @@ fn stability_tags(item: &clean::Item) -> String { /// Render the stability and/or deprecation warning that is displayed at the top of the item's /// documentation. -fn short_stability(item: &clean::Item, cx: &Context) -> Vec { +fn short_stability(item: &clean::Item, cx: &Context, parent: Option<&clean::Item>) -> Vec { let mut stability = vec![]; let error_codes = cx.shared.codes; @@ -2242,7 +2254,18 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { stability.push(format!("
{}
", message)); } - if let Some(ref cfg) = item.attrs.cfg { + let cfg = match (&item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref())) { + (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), + (cfg, _) => cfg.as_deref().cloned(), + }; + + info!( + "Portability {:?} - {:?} = {:?}", + item.attrs.cfg, + parent.and_then(|p| p.attrs.cfg.as_ref()), + cfg + ); + if let Some(cfg) = cfg { stability.push(format!("
{}
", cfg.render_long_html())); } @@ -2281,7 +2304,7 @@ fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Cons } write!(w, ""); - document(w, cx, it) + document(w, cx, it, None) } fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static) { @@ -2295,7 +2318,7 @@ fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static name = it.name.as_ref().unwrap(), typ = s.type_.print() ); - document(w, cx, it) + document(w, cx, it, None) } fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Function) { @@ -2328,7 +2351,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func .print(), spotlight = spotlight_decl(&f.decl), ); - document(w, cx, it) + document(w, cx, it, None) } fn render_implementor( @@ -2353,6 +2376,7 @@ fn render_implementor( w, cx, implementor, + None, AssocItemLink::Anchor(None), RenderMode::Normal, implementor.impl_item.stable_since(), @@ -2382,6 +2406,7 @@ fn render_impls( &mut buffer, cx, i, + Some(containing_item), assoc_link, RenderMode::Normal, containing_item.stable_since(), @@ -2501,7 +2526,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, }); // Trait documentation - document(w, cx, it); + document(w, cx, it, None); fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { write!( @@ -2519,6 +2544,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, fn trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) { let name = m.name.as_ref().unwrap(); + info!("Documenting {} on {}", name, t.name.as_deref().unwrap_or_default()); let item_type = m.type_(); let id = cx.derive_id(format!("{}.{}", item_type, name)); write!(w, "

", id = id,); @@ -2526,7 +2552,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, write!(w, ""); render_stability_since(w, m, t); write!(w, "

"); - document(w, cx, m); + document(w, cx, m, Some(t)); } if !types.is_empty() { @@ -2627,6 +2653,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, cx, &implementor, + None, assoc_link, RenderMode::Normal, implementor.impl_item.stable_since(), @@ -2885,7 +2912,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct write!(w, "") }); - document(w, cx, it); + document(w, cx, it, None); let mut fields = s .fields .iter() @@ -2920,7 +2947,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct name = field.name.as_ref().unwrap(), ty = ty.print() ); - document(w, cx, field); + document(w, cx, field, Some(it)); } } } @@ -2935,7 +2962,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, write!(w, "") }); - document(w, cx, it); + document(w, cx, it, None); let mut fields = s .fields .iter() @@ -2967,7 +2994,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, if let Some(stability_class) = field.stability_class() { write!(w, "", stab = stability_class); } - document(w, cx, field); + document(w, cx, field, Some(it)); } } render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) @@ -3022,7 +3049,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca write!(w, "") }); - document(w, cx, it); + document(w, cx, it, None); if !e.variants.is_empty() { write!( w, @@ -3055,7 +3082,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca } } write!(w, "
"); - document(w, cx, variant); + document(w, cx, variant, Some(it)); document_non_exhaustive(w, variant); use crate::clean::{Variant, VariantKind}; @@ -3090,7 +3117,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca f = field.name.as_ref().unwrap(), t = ty.print() ); - document(w, cx, field); + document(w, cx, field, Some(variant)); } } write!(w, ""); @@ -3288,6 +3315,10 @@ fn render_assoc_items( what: AssocItemRender<'_>, cache: &Cache, ) { + info!( + "Documenting associated items of {}", + containing_item.name.as_deref().unwrap_or_default() + ); let v = match cache.impls.get(&it) { Some(v) => v, None => return, @@ -3322,6 +3353,7 @@ fn render_assoc_items( w, cx, i, + Some(containing_item), AssocItemLink::Anchor(None), render_mode, containing_item.stable_since(), @@ -3513,6 +3545,7 @@ fn render_impl( w: &mut Buffer, cx: &Context, i: &Impl, + parent: Option<&clean::Item>, link: AssocItemLink<'_>, render_mode: RenderMode, outer_version: Option<&str>, @@ -3592,6 +3625,7 @@ fn render_impl( w: &mut Buffer, cx: &Context, item: &clean::Item, + parent: Option<&clean::Item>, link: AssocItemLink<'_>, render_mode: RenderMode, is_default_item: bool, @@ -3676,7 +3710,7 @@ fn render_impl( if let Some(it) = t.items.iter().find(|i| i.name == item.name) { // We need the stability of the item from the trait // because impls can't have a stability. - document_stability(w, cx, it, is_hidden); + document_stability(w, cx, it, is_hidden, parent); if item.doc_value().is_some() { document_full(w, item, cx, "", is_hidden); } else if show_def_docs { @@ -3686,13 +3720,13 @@ fn render_impl( } } } else { - document_stability(w, cx, item, is_hidden); + document_stability(w, cx, item, is_hidden, parent); if show_def_docs { document_full(w, item, cx, "", is_hidden); } } } else { - document_stability(w, cx, item, is_hidden); + document_stability(w, cx, item, is_hidden, parent); if show_def_docs { document_short(w, item, link, "", is_hidden); } @@ -3709,6 +3743,7 @@ fn render_impl( w, cx, trait_item, + parent, link, render_mode, false, @@ -3724,6 +3759,7 @@ fn render_impl( cx: &Context, t: &clean::Trait, i: &clean::Impl, + parent: Option<&clean::Item>, render_mode: RenderMode, outer_version: Option<&str>, show_def_docs: bool, @@ -3741,6 +3777,7 @@ fn render_impl( w, cx, trait_item, + parent, assoc_link, render_mode, true, @@ -3763,6 +3800,7 @@ fn render_impl( cx, t, &i.inner_impl(), + parent, render_mode, outer_version, show_def_docs, @@ -3791,7 +3829,7 @@ fn item_opaque_ty( bounds = bounds(&t.bounds, false) ); - document(w, cx, it); + document(w, cx, it, None); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -3818,7 +3856,7 @@ fn item_trait_alias( bounds(&t.bounds, true) ); - document(w, cx, it); + document(w, cx, it, None); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -3839,7 +3877,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typed type_ = t.type_.print() ); - document(w, cx, it); + document(w, cx, it, None); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show @@ -3858,7 +3896,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cac it.name.as_ref().unwrap(), ); - document(w, cx, it); + document(w, cx, it, None); render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) } @@ -4502,7 +4540,7 @@ fn item_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Macro) None, )) }); - document(w, cx, it) + document(w, cx, it, None) } fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::ProcMacro) { @@ -4532,16 +4570,16 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::Pr write!(w, ""); } } - document(w, cx, it) + document(w, cx, it, None) } fn item_primitive(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cache) { - document(w, cx, it); + document(w, cx, it, None); render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) } fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) { - document(w, cx, it) + document(w, cx, it, None) } crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; diff --git a/src/test/rustdoc/doc-cfg-simplification.rs b/src/test/rustdoc/doc-cfg-simplification.rs new file mode 100644 index 0000000000000..633df661be026 --- /dev/null +++ b/src/test/rustdoc/doc-cfg-simplification.rs @@ -0,0 +1,182 @@ +#![crate_name = "globuliferous"] +#![feature(doc_cfg)] + +// @has 'globuliferous/index.html' +// @count - '//*[@class="stab portability"]' 1 +// @matches - '//*[@class="stab portability"]' '^ratel$' + +// @has 'globuliferous/ratel/index.html' +// @count - '//*[@class="stab portability"]' 8 +// @matches - '//*[@class="stab portability"]' 'crate feature ratel' +// @matches - '//*[@class="stab portability"]' '^zoonosology$' +// @matches - '//*[@class="stab portability"]' '^yusho$' +// @matches - '//*[@class="stab portability"]' '^nunciative$' +// @matches - '//*[@class="stab portability"]' '^thionic$' +// @matches - '//*[@class="stab portability"]' '^zincic$' +// @matches - '//*[@class="stab portability"]' '^cosmotellurian$' +// @matches - '//*[@class="stab portability"]' '^aposiopesis$' +#[doc(cfg(feature = "ratel"))] +pub mod ratel { + // @has 'globuliferous/ratel/fn.ovicide.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate feature ratel' + pub fn ovicide() {} + + // @has 'globuliferous/ratel/fn.zoonosology.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and zoonosology' + #[doc(cfg(feature = "zoonosology"))] + pub fn zoonosology() {} + + // @has 'globuliferous/ratel/constant.DIAGRAPHICS.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate feature ratel' + pub const DIAGRAPHICS: () = (); + + // @has 'globuliferous/ratel/constant.YUSHO.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and yusho' + #[doc(cfg(feature = "yusho"))] + pub const YUSHO: () = (); + + // @has 'globuliferous/ratel/static.KEYBUGLE.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate feature ratel' + pub static KEYBUGLE: () = (); + + // @has 'globuliferous/ratel/static.NUNCIATIVE.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and nunciative' + #[doc(cfg(feature = "nunciative"))] + pub static NUNCIATIVE: () = (); + + // @has 'globuliferous/ratel/type.Wrick.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate feature ratel' + pub type Wrick = (); + + // @has 'globuliferous/ratel/type.Thionic.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and thionic' + #[doc(cfg(feature = "thionic"))] + pub type Thionic = (); + + // @has 'globuliferous/ratel/struct.Eventration.html' + // @count - '//*[@class="stab portability"]' 1 + // @matches - '//*[@class="stab portability"]' 'crate feature ratel' + pub struct Eventration; + + // @has 'globuliferous/ratel/struct.Zincic.html' + // @count - '//*[@class="stab portability"]' 2 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and zincic' + // @matches - '//*[@class="stab portability"]' 'crate feature rutherford' + #[doc(cfg(feature = "zincic"))] + pub struct Zincic { + pub rectigrade: (), + + #[doc(cfg(feature = "rutherford"))] + pub rutherford: (), + } + + // @has 'globuliferous/ratel/enum.Cosmotellurian.html' + // @count - '//*[@class="stab portability"]' 10 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and cosmotellurian' + // @matches - '//*[@class="stab portability"]' 'crate feature biotaxy' + // @matches - '//*[@class="stab portability"]' 'crate feature xiphopagus' + // @matches - '//*[@class="stab portability"]' 'crate feature juxtapositive' + // @matches - '//*[@class="stab portability"]' 'crate feature fuero' + // @matches - '//*[@class="stab portability"]' 'crate feature palaeophile' + // @matches - '//*[@class="stab portability"]' 'crate feature broadcloth' + // @matches - '//*[@class="stab portability"]' 'crate features broadcloth and xanthocomic' + // @matches - '//*[@class="stab portability"]' 'crate feature broadcloth' + // @matches - '//*[@class="stab portability"]' 'crate features broadcloth and whosoever' + #[doc(cfg(feature = "cosmotellurian"))] + pub enum Cosmotellurian { + Groundsel { + jagger: (), + + #[doc(cfg(feature = "xiphopagus"))] + xiphopagus: (), + }, + + #[doc(cfg(feature = "biotaxy"))] + Biotaxy { + glossography: (), + + #[doc(cfg(feature = "juxtapositive"))] + juxtapositive: (), + }, + } + + impl Cosmotellurian { + pub fn uxoricide() {} + + #[doc(cfg(feature = "fuero"))] + pub fn fuero() {} + + pub const MAMELLE: () = (); + + #[doc(cfg(feature = "palaeophile"))] + pub const PALAEOPHILE: () = (); + } + + #[doc(cfg(feature = "broadcloth"))] + impl Cosmotellurian { + pub fn trabeculated() {} + + #[doc(cfg(feature = "xanthocomic"))] + pub fn xanthocomic() {} + + pub const BRACHIFEROUS: () = (); + + #[doc(cfg(feature = "whosoever"))] + pub const WHOSOEVER: () = (); + } + + // @has 'globuliferous/ratel/trait.Gnotobiology.html' + // @count - '//*[@class="stab portability"]' 4 + // @matches - '//*[@class="stab portability"]' 'crate feature ratel' + // @matches - '//*[@class="stab portability"]' 'crate feature unzymotic' + // @matches - '//*[@class="stab portability"]' 'crate feature summate' + // @matches - '//*[@class="stab portability"]' 'crate feature unctuous' + pub trait Gnotobiology { + const XYLOTHERAPY: (); + + #[doc(cfg(feature = "unzymotic"))] + const UNZYMOTIC: (); + + type Lepadoid; + + #[doc(cfg(feature = "summate"))] + type Summate; + + fn decalcomania(); + + #[doc(cfg(feature = "unctuous"))] + fn unctuous(); + } + + // @has 'globuliferous/ratel/trait.Aposiopesis.html' + // @count - '//*[@class="stab portability"]' 4 + // @matches - '//*[@class="stab portability"]' 'crate features ratel and aposiopesis' + // @matches - '//*[@class="stab portability"]' 'crate feature umbracious' + // @matches - '//*[@class="stab portability"]' 'crate feature uakari' + // @matches - '//*[@class="stab portability"]' 'crate feature rotograph' + #[doc(cfg(feature = "aposiopesis"))] + pub trait Aposiopesis { + const REDHIBITION: (); + + #[doc(cfg(feature = "umbracious"))] + const UMBRACIOUS: (); + + type Ophthalmoscope; + + #[doc(cfg(feature = "uakari"))] + type Uakari; + + fn meseems(); + + #[doc(cfg(feature = "rotograph"))] + fn rotograph(); + } +} diff --git a/src/test/rustdoc/doc-cfg.rs b/src/test/rustdoc/doc-cfg.rs index aa407b7e92618..d7041ee2f1af8 100644 --- a/src/test/rustdoc/doc-cfg.rs +++ b/src/test/rustdoc/doc-cfg.rs @@ -10,9 +10,8 @@ pub struct Portable; // @has doc_cfg/unix_only/index.html \ // '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \ // 'This is supported on Unix only.' -// @matches - '//*[@class="module-item"]//*[@class="stab portability"]' '\AUnix\Z' -// @matches - '//*[@class="module-item"]//*[@class="stab portability"]' '\AUnix and ARM\Z' -// @count - '//*[@class="stab portability"]' 3 +// @matches - '//*[@class="module-item"]//*[@class="stab portability"]' '\AARM\Z' +// @count - '//*[@class="stab portability"]' 2 #[doc(cfg(unix))] pub mod unix_only { // @has doc_cfg/unix_only/fn.unix_only_function.html \ @@ -26,7 +25,7 @@ pub mod unix_only { // @has doc_cfg/unix_only/trait.ArmOnly.html \ // '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \ // 'This is supported on Unix and ARM only.' - // @count - '//*[@class="stab portability"]' 3 + // @count - '//*[@class="stab portability"]' 2 #[doc(cfg(target_arch = "arm"))] pub trait ArmOnly { fn unix_and_arm_only_function(); diff --git a/src/test/rustdoc/duplicate-cfg.rs b/src/test/rustdoc/duplicate-cfg.rs index 47ba362c97789..7b938af3c7d50 100644 --- a/src/test/rustdoc/duplicate-cfg.rs +++ b/src/test/rustdoc/duplicate-cfg.rs @@ -14,45 +14,41 @@ pub struct Foo; // @has 'foo/bar/index.html' -// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync$' -// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` only' - -// @has 'foo/bar/struct.Bar.html' // @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync only.' #[doc(cfg(feature = "sync"))] pub mod bar { + // @has 'foo/bar/struct.Bar.html' + // @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync only.' #[doc(cfg(feature = "sync"))] pub struct Bar; } // @has 'foo/baz/index.html' -// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync and send$' -// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate features `sync` and `send` only' - -// @has 'foo/baz/struct.Baz.html' // @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.' #[doc(cfg(all(feature = "sync", feature = "send")))] pub mod baz { + // @has 'foo/baz/struct.Baz.html' + // @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.' #[doc(cfg(feature = "sync"))] pub struct Baz; } -// @has 'foo/qux/struct.Qux.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.' +// @has 'foo/qux/index.html' +// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync only.' #[doc(cfg(feature = "sync"))] pub mod qux { + // @has 'foo/qux/struct.Qux.html' + // @has '-' '//*[@class="stab portability"]' 'This is supported on crate features sync and send only.' #[doc(cfg(all(feature = "sync", feature = "send")))] pub struct Qux; } // @has 'foo/quux/index.html' -// @matches '-' '//*[@class="module-item"]//*[@class="stab portability"]' '^sync and send and foo and bar$' -// @has '-' '//*[@class="module-item"]//*[@class="stab portability"]/@title' 'This is supported on crate feature `sync` and crate feature `send` and `foo` and `bar` only' - -// @has 'foo/quux/struct.Quux.html' -// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync and crate feature send and foo and bar only.' +// @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync and crate feature send and foo only.' #[doc(cfg(all(feature = "sync", feature = "send", foo)))] pub mod quux { + // @has 'foo/quux/struct.Quux.html' + // @has '-' '//*[@class="stab portability"]' 'This is supported on crate feature sync and crate feature send and foo and bar only.' #[doc(cfg(all(feature = "send", feature = "sync", bar)))] pub struct Quux; } From 4e263fe999a94f958023b93e8b6f44685a7b15c6 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Tue, 13 Oct 2020 20:18:46 +0200 Subject: [PATCH 2/3] Add test-examples for Cfg::simplify_with --- src/librustdoc/clean/cfg.rs | 2 ++ src/librustdoc/clean/cfg/tests.rs | 36 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 4d8168dd8ef74..b659f3eab4318 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -205,6 +205,8 @@ impl Cfg { /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will /// return `None` if simplification managed to completely eliminate any requirements from this /// `Cfg`. + /// + /// See `tests::test_simplify_with` for examples. pub(crate) fn simplify_with(&self, assume: &Cfg) -> Option { if self == assume { return None; diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 794a7bcaf1cb7..3a78269f19af0 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -433,3 +433,39 @@ fn test_render_long_html() { ); }) } + +#[test] +fn test_simplify_with() { + // This is a tiny subset of things that could be simplified, but it likely covers 90% of + // real world usecases well. + with_default_session_globals(|| { + let foo = word_cfg("foo"); + let bar = word_cfg("bar"); + let baz = word_cfg("baz"); + let quux = word_cfg("quux"); + + let foobar = Cfg::All(vec![foo.clone(), bar.clone()]); + let barbaz = Cfg::All(vec![bar.clone(), baz.clone()]); + let foobarbaz = Cfg::All(vec![foo.clone(), bar.clone(), baz.clone()]); + let bazquux = Cfg::All(vec![baz.clone(), quux.clone()]); + + // Unrelated cfgs don't affect each other + assert_eq!(foo.simplify_with(&bar).as_ref(), Some(&foo)); + assert_eq!(foobar.simplify_with(&bazquux).as_ref(), Some(&foobar)); + + // Identical cfgs are eliminated + assert_eq!(foo.simplify_with(&foo), None); + assert_eq!(foobar.simplify_with(&foobar), None); + + // Multiple cfgs eliminate a single assumed cfg + assert_eq!(foobar.simplify_with(&foo).as_ref(), Some(&bar)); + assert_eq!(foobar.simplify_with(&bar).as_ref(), Some(&foo)); + + // A single cfg is eliminated by multiple assumed cfg containing it + assert_eq!(foo.simplify_with(&foobar), None); + + // Multiple cfgs eliminate the matching subset of multiple assumed cfg + assert_eq!(foobar.simplify_with(&barbaz).as_ref(), Some(&foo)); + assert_eq!(foobar.simplify_with(&foobarbaz), None); + }); +} From 4409cb2e3807c50d2e636ab293f10e6b8a87a7d4 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Tue, 13 Oct 2020 20:21:49 +0200 Subject: [PATCH 3/3] Make portability log at debug level --- src/librustdoc/html/render/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index e71df4b45c339..bd50902102207 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2170,7 +2170,7 @@ fn stability_tags(item: &clean::Item, parent: &clean::Item) -> String { (cfg, _) => cfg.as_deref().cloned(), }; - info!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg); + debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg); if let Some(ref cfg) = cfg { tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); } @@ -2259,7 +2259,7 @@ fn short_stability(item: &clean::Item, cx: &Context, parent: Option<&clean::Item (cfg, _) => cfg.as_deref().cloned(), }; - info!( + debug!( "Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref()),