diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index dfee2b702c1c8..b72d262417755 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -8,6 +8,7 @@ use std::mem; use std::ops; use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem}; +use rustc_data_structures::fx::FxHashSet; use rustc_feature::Features; use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; @@ -43,23 +44,22 @@ crate struct InvalidCfgError { impl Cfg { /// Parses a `NestedMetaItem` into a `Cfg`. - fn parse_nested(nested_cfg: &NestedMetaItem) -> Result { + fn parse_nested( + nested_cfg: &NestedMetaItem, + exclude: &FxHashSet, + ) -> Result, InvalidCfgError> { match nested_cfg { - NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg), + NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude), NestedMetaItem::Literal(ref lit) => { Err(InvalidCfgError { msg: "unexpected literal", span: lit.span }) } } } - /// Parses a `MetaItem` into a `Cfg`. - /// - /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or - /// `target_os = "redox"`. - /// - /// If the content is not properly formatted, it will return an error indicating what and where - /// the error is. - crate fn parse(cfg: &MetaItem) -> Result { + crate fn parse_without( + cfg: &MetaItem, + exclude: &FxHashSet, + ) -> Result, InvalidCfgError> { let name = match cfg.ident() { Some(ident) => ident.name, None => { @@ -70,9 +70,15 @@ impl Cfg { } }; match cfg.kind { - MetaItemKind::Word => Ok(Cfg::Cfg(name, None)), + MetaItemKind::Word => { + let cfg = Cfg::Cfg(name, None); + if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) } + } MetaItemKind::NameValue(ref lit) => match lit.kind { - LitKind::Str(value, _) => Ok(Cfg::Cfg(name, Some(value))), + LitKind::Str(value, _) => { + let cfg = Cfg::Cfg(name, Some(value)); + if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) } + } _ => Err(InvalidCfgError { // FIXME: if the main #[cfg] syntax decided to support non-string literals, // this should be changed as well. @@ -81,23 +87,40 @@ impl Cfg { }), }, MetaItemKind::List(ref items) => { - let mut sub_cfgs = items.iter().map(Cfg::parse_nested); - match name { + let sub_cfgs = + items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose()); + let ret = match name { sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)), sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)), sym::not => { + let mut sub_cfgs = sub_cfgs.collect::>(); if sub_cfgs.len() == 1 { - Ok(!sub_cfgs.next().unwrap()?) + Ok(!sub_cfgs.pop().unwrap()?) } else { Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span }) } } _ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }), + }; + match ret { + Ok(c) => Ok(Some(c)), + Err(e) => Err(e), } } } } + /// Parses a `MetaItem` into a `Cfg`. + /// + /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or + /// `target_os = "redox"`. + /// + /// If the content is not properly formatted, it will return an error indicating what and where + /// the error is. + crate fn parse(cfg: &MetaItem) -> Result { + Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) + } + /// Checks whether the given configuration can be matched in the current session. /// /// Equivalent to `attr::cfg_matches`. diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index fac1a0817e033..7ae7b940f26b8 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -831,8 +831,9 @@ impl AttributesExt for [ast::Attribute] { self.iter() .filter(|attr| attr.has_name(sym::cfg)) .filter_map(|attr| single(attr.meta_item_list()?)) - .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok()) - .filter(|cfg| !hidden_cfg.contains(cfg)) + .filter_map(|attr| { + Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten() + }) .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) } else { Cfg::True diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 90cb5d586c211..2cbb3324a5e04 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -141,6 +141,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }) .collect::>() }) + .chain([Cfg::Cfg(sym::test, None)].into_iter()) .collect(); self.cx.cache.exact_paths = self.exact_paths; diff --git a/src/test/rustdoc/doc-auto-cfg.rs b/src/test/rustdoc/doc-auto-cfg.rs index fcdd83545696b..57dd0529535f6 100644 --- a/src/test/rustdoc/doc-auto-cfg.rs +++ b/src/test/rustdoc/doc-auto-cfg.rs @@ -3,6 +3,12 @@ #![crate_name = "foo"] // @has foo/fn.foo.html -// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-test' -#[cfg(not(test))] +// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-doctest' +#[cfg(not(doctest))] pub fn foo() {} + +// @has foo/fn.bar.html +// @has - '//*[@class="item-info"]/*[@class="stab portability"]' 'doc' +// @!has - '//*[@class="item-info"]/*[@class="stab portability"]' 'test' +#[cfg(any(test, doc))] +pub fn bar() {} diff --git a/src/test/rustdoc/doc-cfg-hide.rs b/src/test/rustdoc/doc-cfg-hide.rs index 424fa6d6a911f..636957fe9980d 100644 --- a/src/test/rustdoc/doc-cfg-hide.rs +++ b/src/test/rustdoc/doc-cfg-hide.rs @@ -26,7 +26,7 @@ pub struct Hyperdulia; // @has 'oud/struct.Oystercatcher.html' // @count - '//*[@class="stab portability"]' 1 -// @matches - '//*[@class="stab portability"]' 'crate features solecism and oystercatcher' +// @matches - '//*[@class="stab portability"]' 'crate feature oystercatcher only' // compile-flags:--cfg feature="oystercatcher" #[cfg(all(feature = "solecism", feature = "oystercatcher"))] pub struct Oystercatcher;