diff --git a/src/doc/unstable-book/src/language-features/doc-masked.md b/src/doc/unstable-book/src/language-features/doc-masked.md new file mode 100644 index 0000000000000..609939bfc22f6 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/doc-masked.md @@ -0,0 +1,24 @@ +# `doc_masked` + +The tracking issue for this feature is: [#44027] + +----- + +The `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists +of trait implementations. The specifics of the feature are as follows: + +1. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute, + it marks the crate as being masked. + +2. When listing traits a given type implements, rustdoc ensures that traits from masked crates are + not emitted into the documentation. + +3. When listing types that implement a given trait, rustdoc ensures that types from masked crates + are not emitted into the documentation. + +This feature was introduced in PR [#44026] to ensure that compiler-internal and +implementation-specific types and traits were not included in the standard library's documentation. +Such types would introduce broken links into the documentation. + +[#44026]: https://github.com/rust-lang/rust/pull/44026 +[#44027]: https://github.com/rust-lang/rust/pull/44027 diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 73ab286f185aa..095eb7797d457 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -120,6 +120,7 @@ pub struct Crate { // These are later on moved into `CACHEKEY`, leaving the map empty. // Only here so that they can be filtered through the rustdoc passes. pub external_traits: FxHashMap, + pub masked_crates: FxHashSet, } impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { @@ -144,6 +145,18 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { // Clean the crate, translating the entire libsyntax AST to one that is // understood by rustdoc. let mut module = self.module.clean(cx); + let mut masked_crates = FxHashSet(); + + match module.inner { + ModuleItem(ref module) => { + for it in &module.items { + if it.is_extern_crate() && it.attrs.has_doc_masked() { + masked_crates.insert(it.def_id.krate); + } + } + } + _ => unreachable!(), + } let ExternalCrate { name, src, primitives, .. } = LOCAL_CRATE.clean(cx); { @@ -176,6 +189,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { primitives, access_levels: Arc::new(mem::replace(&mut access_levels, Default::default())), external_traits: mem::replace(&mut external_traits, Default::default()), + masked_crates, } } } @@ -329,6 +343,9 @@ impl Item { pub fn is_import(&self) -> bool { self.type_() == ItemType::Import } + pub fn is_extern_crate(&self) -> bool { + self.type_() == ItemType::ExternCrate + } pub fn is_stripped(&self) -> bool { match self.inner { StrippedItem(..) => true, _ => false } @@ -574,6 +591,20 @@ impl Attributes { None } + pub fn has_doc_masked(&self) -> bool { + for attr in &self.other_attrs { + if !attr.check_name("doc") { continue; } + + if let Some(items) = attr.meta_item_list() { + if items.iter().filter_map(|i| i.meta_item()).any(|it| it.check_name("masked")) { + return true; + } + } + } + + false + } + pub fn from_ast(diagnostic: &::errors::Handler, attrs: &[ast::Attribute]) -> Attributes { let mut doc_strings = vec![]; let mut sp = None; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index bf0fe5f6e9d98..bac847590e2db 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -266,6 +266,7 @@ pub struct Cache { deref_trait_did: Option, deref_mut_trait_did: Option, owned_box_did: Option, + masked_crates: FxHashSet, // In rare case where a structure is defined in one module but implemented // in another, if the implementing module is parsed before defining module, @@ -538,6 +539,7 @@ pub fn run(mut krate: clean::Crate, deref_trait_did, deref_mut_trait_did, owned_box_did, + masked_crates: mem::replace(&mut krate.masked_crates, FxHashSet()), typarams: external_typarams, }; @@ -1114,12 +1116,16 @@ impl DocFolder for Cache { // Collect all the implementors of traits. if let clean::ImplItem(ref i) = item.inner { - if let Some(did) = i.trait_.def_id() { - self.implementors.entry(did).or_insert(vec![]).push(Implementor { - def_id: item.def_id, - stability: item.stability.clone(), - impl_: i.clone(), - }); + if !self.masked_crates.contains(&item.def_id.krate) { + if let Some(did) = i.trait_.def_id() { + if i.for_.def_id().map_or(true, |d| !self.masked_crates.contains(&d.krate)) { + self.implementors.entry(did).or_insert(vec![]).push(Implementor { + def_id: item.def_id, + stability: item.stability.clone(), + impl_: i.clone(), + }); + } + } } } @@ -1281,18 +1287,24 @@ impl DocFolder for Cache { // primitive rather than always to a struct/enum. // Note: matching twice to restrict the lifetime of the `i` borrow. let did = if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { - match i.for_ { - clean::ResolvedPath { did, .. } | - clean::BorrowedRef { - type_: box clean::ResolvedPath { did, .. }, .. - } => { - Some(did) - } - ref t => { - t.primitive_type().and_then(|t| { - self.primitive_locations.get(&t).cloned() - }) + let masked_trait = i.trait_.def_id().map_or(false, + |d| self.masked_crates.contains(&d.krate)); + if !masked_trait { + match i.for_ { + clean::ResolvedPath { did, .. } | + clean::BorrowedRef { + type_: box clean::ResolvedPath { did, .. }, .. + } => { + Some(did) + } + ref t => { + t.primitive_type().and_then(|t| { + self.primitive_locations.get(&t).cloned() + }) + } } + } else { + None } } else { unreachable!() diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index a3eecd46e9051..9fc7e2c01aa19 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -314,6 +314,7 @@ #![feature(unwind_attributes)] #![feature(vec_push_all)] #![feature(doc_cfg)] +#![feature(doc_masked)] #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(not(stage0), feature(const_max_value))] @@ -359,19 +360,24 @@ use prelude::v1::*; debug_assert_ne, unreachable, unimplemented, write, writeln, try)] extern crate core as __core; -#[allow(deprecated)] extern crate rand as core_rand; +#[doc(masked)] +#[allow(deprecated)] +extern crate rand as core_rand; #[macro_use] #[macro_reexport(vec, format)] extern crate alloc; extern crate alloc_system; extern crate std_unicode; +#[doc(masked)] extern crate libc; // We always need an unwinder currently for backtraces +#[doc(masked)] #[allow(unused_extern_crates)] extern crate unwind; // compiler-rt intrinsics +#[doc(masked)] extern crate compiler_builtins; // During testing, this crate is not actually the "real" std library, but rather diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 38e20b9fe4065..ae0dd872963aa 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -377,6 +377,8 @@ declare_features! ( // #[doc(cfg(...))] (active, doc_cfg, "1.21.0", Some(43781)), + // #[doc(masked)] + (active, doc_masked, "1.21.0", None), // allow `#[must_use]` on functions (RFC 1940) (active, fn_must_use, "1.21.0", Some(43302)), @@ -1237,6 +1239,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, doc_cfg, attr.span, "#[doc(cfg(...))] is experimental" ); + } else if content.iter().any(|c| c.check_name("masked")) { + gate_feature_post!(&self, doc_masked, attr.span, + "#[doc(masked)] is experimental" + ); } } } diff --git a/src/test/compile-fail/feature-gate-doc_masked.rs b/src/test/compile-fail/feature-gate-doc_masked.rs new file mode 100644 index 0000000000000..bb5be9d69710f --- /dev/null +++ b/src/test/compile-fail/feature-gate-doc_masked.rs @@ -0,0 +1,14 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[doc(masked)] //~ ERROR: #[doc(masked)] is experimental +extern crate std as realstd; + +fn main() {}