diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 06e9d9ed329..bce5cd8174d 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -279,6 +279,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_doc!( cfg => doc_cfg + cfg_hide => doc_cfg_hide masked => doc_masked notable_trait => doc_notable_trait keyword => doc_keyword diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index eae7f7854ec..e5c57d0ee30 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -380,6 +380,9 @@ declare_features! ( /// Allows `#[doc(cfg(...))]`. (active, doc_cfg, "1.21.0", Some(43781), None), + /// Allows `#[doc(cfg_hide(...))]`. + (active, doc_cfg_hide, "1.49.0", Some(43781), None), + /// Allows `#[doc(masked)]`. (active, doc_masked, "1.21.0", Some(44027), None), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 77baf7d7381..382dbc377d6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -399,6 +399,7 @@ symbols! { cfg_attr_multi, cfg_doctest, cfg_eval, + cfg_hide, cfg_panic, cfg_sanitize, cfg_target_abi, @@ -547,6 +548,7 @@ symbols! { doc, doc_alias, doc_cfg, + doc_cfg_hide, doc_keyword, doc_masked, doc_notable_trait, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 39493f6edf8..b463c1dc714 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -318,10 +318,10 @@ fn merge_attrs( } else { Attributes::from_ast(&both, None) }, - both.cfg(cx.tcx), + both.cfg(cx.tcx, &cx.cache.hidden_cfg), ) } else { - (old_attrs.clean(cx), old_attrs.cfg(cx.tcx)) + (old_attrs.clean(cx), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg)) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 41bc30fd398..969d15dd6a1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1973,7 +1973,7 @@ fn clean_extern_crate( def_id: crate_def_id.into(), visibility: krate.vis.clean(cx), kind: box ExternCrateItem { src: orig_name }, - cfg: attrs.cfg(cx.tcx), + cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), }] } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 288644ff296..ebbf280d96d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -421,7 +421,7 @@ impl Item { kind, box ast_attrs.clean(cx), cx, - ast_attrs.cfg(cx.tcx), + ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), ) } @@ -747,7 +747,7 @@ crate trait AttributesExt { fn other_attrs(&self) -> Vec; - fn cfg(&self, tcx: TyCtxt<'_>) -> Option>; + fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option>; } impl AttributesExt for [ast::Attribute] { @@ -772,7 +772,7 @@ impl AttributesExt for [ast::Attribute] { self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect() } - fn cfg(&self, tcx: TyCtxt<'_>) -> Option> { + fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option> { let sess = tcx.sess; let doc_cfg_active = tcx.features().doc_cfg; @@ -813,6 +813,7 @@ impl AttributesExt for [ast::Attribute] { .filter_map(|attr| { Cfg::parse(&attr).map_err(|e| sess.diagnostic().span_err(e.span, e.msg)).ok() }) + .filter(|cfg| !hidden_cfg.contains(cfg)) .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) } } else { @@ -844,6 +845,8 @@ impl AttributesExt for [ast::Attribute] { } } + // treat #[target_feature(enable = "feat")] attributes as if they were + // #[doc(cfg(target_feature = "feat"))] attributes as well for attr in self.lists(sym::target_feature) { if attr.has_name(sym::enable) { if let Some(feat) = attr.value_str() { diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8cf7b50fbd9..9e64d200b43 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1123,7 +1123,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { let ast_attrs = self.tcx.hir().attrs(hir_id); let mut attrs = Attributes::from_ast(ast_attrs, None); - if let Some(ref cfg) = ast_attrs.cfg(self.tcx) { + if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { return; } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index bcfcc3d7039..cc9e081add1 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -119,6 +119,8 @@ crate struct Cache { /// /// Links are indexed by the DefId of the item they document. crate intra_doc_links: FxHashMap>, + /// Cfg that have been hidden via #![doc(cfg_hide(...))] + crate hidden_cfg: FxHashSet, } /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`. diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index e68e1223171..e3ffc1e3f21 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -323,7 +323,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl let import_item = clean::Item { def_id: import_def_id.into(), attrs: import_attrs, - cfg: ast_attrs.cfg(cx.tcx()), + cfg: ast_attrs.cfg(cx.tcx(), &cx.cache().hidden_cfg), ..myitem.clone() }; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 4f5495a176d..cd657fa01d4 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -3,6 +3,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; +use rustc_hir::CRATE_HIR_ID; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::Node; @@ -15,7 +16,7 @@ use rustc_span::symbol::{kw, sym, Symbol}; use std::mem; -use crate::clean::{self, AttributesExt, NestedAttributesExt}; +use crate::clean::{self, cfg::Cfg, AttributesExt, NestedAttributesExt}; use crate::core; use crate::doctree::*; @@ -97,6 +98,27 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } } } + + self.cx.cache.hidden_cfg = self.cx.tcx.hir().attrs(CRATE_HIR_ID) + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().into_iter().flatten()) + .filter(|attr| attr.has_name(sym::cfg_hide)) + .flat_map(|attr| { + attr.meta_item_list() + .unwrap_or(&[]) + .iter() + .filter_map(|attr| { + Some( + Cfg::parse(attr.meta_item()?) + .map_err(|e| self.cx.sess().diagnostic().span_err(e.span, e.msg)) + .ok()?, + ) + }) + .collect::>() + }) + .collect(); + self.cx.cache.exact_paths = self.exact_paths; top_level_module } diff --git a/src/test/rustdoc/doc-cfg-hide.rs b/src/test/rustdoc/doc-cfg-hide.rs new file mode 100644 index 00000000000..b9d0d323137 --- /dev/null +++ b/src/test/rustdoc/doc-cfg-hide.rs @@ -0,0 +1,32 @@ +#![crate_name = "oud"] +#![feature(doc_cfg, doc_cfg_hide)] + +#![doc(cfg_hide(feature = "solecism"))] + +// @has 'oud/struct.Solecism.html' +// @count - '//*[@class="stab portability"]' 0 +// compile-flags:--cfg feature="solecism" +#[cfg(feature = "solecism")] +pub struct Solecism; + +// @has 'oud/struct.Scribacious.html' +// @count - '//*[@class="stab portability"]' 1 +// @matches - '//*[@class="stab portability"]' 'crate feature solecism' +#[cfg(feature = "solecism")] +#[doc(cfg(feature = "solecism"))] +pub struct Scribacious; + +// @has 'oud/struct.Hyperdulia.html' +// @count - '//*[@class="stab portability"]' 1 +// @matches - '//*[@class="stab portability"]' 'crate feature hyperdulia' +// compile-flags:--cfg feature="hyperdulia" +#[cfg(feature = "solecism")] +#[cfg(feature = "hyperdulia")] +pub struct Hyperdulia; + +// @has 'oud/struct.Oystercatcher.html' +// @count - '//*[@class="stab portability"]' 1 +// @matches - '//*[@class="stab portability"]' 'crate features solecism and oystercatcher' +// compile-flags:--cfg feature="oystercatcher" +#[cfg(all(feature = "solecism", feature = "oystercatcher"))] +pub struct Oystercatcher;