diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index dfee2b702c1..b72d2624177 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,15 +44,72 @@ 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 }) } } } + crate fn parse_without( + cfg: &MetaItem, + exclude: &FxHashSet, + ) -> Result, InvalidCfgError> { + let name = match cfg.ident() { + Some(ident) => ident.name, + None => { + return Err(InvalidCfgError { + msg: "expected a single identifier", + span: cfg.span, + }); + } + }; + match cfg.kind { + 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, _) => { + 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. + msg: "value of cfg option should be a string literal", + span: lit.span, + }), + }, + MetaItemKind::List(ref items) => { + 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.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 @@ -60,42 +118,7 @@ impl Cfg { /// 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 { - let name = match cfg.ident() { - Some(ident) => ident.name, - None => { - return Err(InvalidCfgError { - msg: "expected a single identifier", - span: cfg.span, - }); - } - }; - match cfg.kind { - MetaItemKind::Word => Ok(Cfg::Cfg(name, None)), - MetaItemKind::NameValue(ref lit) => match lit.kind { - LitKind::Str(value, _) => Ok(Cfg::Cfg(name, Some(value))), - _ => Err(InvalidCfgError { - // FIXME: if the main #[cfg] syntax decided to support non-string literals, - // this should be changed as well. - msg: "value of cfg option should be a string literal", - span: lit.span, - }), - }, - MetaItemKind::List(ref items) => { - let mut sub_cfgs = items.iter().map(Cfg::parse_nested); - 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 => { - if sub_cfgs.len() == 1 { - Ok(!sub_cfgs.next().unwrap()?) - } else { - Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span }) - } - } - _ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }), - } - } - } + Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap()) } /// Checks whether the given configuration can be matched in the current session. diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index fac1a0817e0..7ae7b940f26 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 90cb5d586c2..2cbb3324a5e 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 fcdd8354569..57dd0529535 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 424fa6d6a91..636957fe998 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;