From c491e195c467aa11e736457a6bc451e4fc214df6 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Mon, 21 Aug 2017 13:43:45 -0500 Subject: [PATCH 1/4] new attribute #[doc(masked)] to hide internal crates from std docs --- src/librustdoc/clean/mod.rs | 41 +++++++++++++++++++++++++++++++ src/librustdoc/html/render.rs | 46 ++++++++++++++++++++++------------- src/libstd/lib.rs | 7 +++++- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index aab44ddce0e6a..c092c66ad1650 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -117,6 +117,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> { @@ -141,6 +142,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); { @@ -173,6 +186,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, } } } @@ -326,6 +340,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 } @@ -571,6 +588,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; @@ -1651,6 +1682,16 @@ impl GetDefId for Type { fn def_id(&self) -> Option { match *self { ResolvedPath { did, .. } => Some(did), + Primitive(p) => ::html::render::cache().primitive_locations.get(&p).cloned(), + BorrowedRef { type_: box Generic(..), .. } => + Primitive(PrimitiveType::Reference).def_id(), + BorrowedRef { ref type_, .. } => type_.def_id(), + Tuple(..) => Primitive(PrimitiveType::Tuple).def_id(), + BareFunction(..) => Primitive(PrimitiveType::Fn).def_id(), + Slice(..) => Primitive(PrimitiveType::Slice).def_id(), + Array(..) => Primitive(PrimitiveType::Array).def_id(), + RawPointer(..) => Primitive(PrimitiveType::RawPointer).def_id(), + QPath { ref self_type, .. } => self_type.def_id(), _ => None, } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index cc84e340c74f8..75e922d08c804 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -268,6 +268,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, @@ -540,6 +541,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, }; @@ -1104,12 +1106,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(), + }); + } + } } } @@ -1271,18 +1277,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 33bf0d68126d4..6fa4a8738ac7b 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -347,19 +347,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 From bb6de3c9ce1681ff1246c21d2a2ec537f331e258 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Mon, 21 Aug 2017 20:20:21 -0500 Subject: [PATCH 2/4] add feature gate doc_masked and tests --- .../src/language-features/doc-masked.md | 37 +++++++++++++++++++ src/libstd/lib.rs | 1 + src/libsyntax/feature_gate.rs | 6 +++ .../compile-fail/feature-gate-doc_masked.rs | 14 +++++++ src/test/rustdoc/doc-masked.rs | 29 +++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/doc-masked.md create mode 100644 src/test/compile-fail/feature-gate-doc_masked.rs create mode 100644 src/test/rustdoc/doc-masked.rs 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..0d0a46ce9ee54 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/doc-masked.md @@ -0,0 +1,37 @@ +# `doc_masked` + +The tracking issue for this feature is: [TODO](TODO) + +----- + +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 [TODO](TODO) 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. + +```rust +#![feature(doc_masked)] + +// Since std is automatically imported, we need to import it into a separate name to apply the +// attribute. This is used as a simple demonstration, but any extern crate statement will suffice. +#[doc(masked)] +extern crate std as realstd; + +/// A sample marker trait that exists on floating-point numbers, even though this page won't list +/// them! +pub trait MyMarker { } + +impl MyMarker for f32 { } +impl MyMarker for f64 { } +``` diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 6fa4a8738ac7b..41fb5bc684afc 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -313,6 +313,7 @@ #![feature(unwind_attributes)] #![feature(vec_push_all)] #![feature(doc_cfg)] +#![feature(doc_masked)] #![cfg_attr(test, feature(update_panic_count))] #![default_lib_allocator] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 54d41a030fd77..9dad92bb18bbe 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -376,6 +376,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)), @@ -1229,6 +1231,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() {} diff --git a/src/test/rustdoc/doc-masked.rs b/src/test/rustdoc/doc-masked.rs new file mode 100644 index 0000000000000..78d32028e3630 --- /dev/null +++ b/src/test/rustdoc/doc-masked.rs @@ -0,0 +1,29 @@ +// 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. + +#![feature(doc_masked)] + +#![doc(masked)] +extern crate std as realstd; + +// @has doc_masked/struct.LocalStruct.html +// @has - '//*[@class="impl"]//code' 'impl LocalTrait for LocalStruct' +// @!has - '//*[@class="impl"]//code' 'impl Copy for LocalStruct' +#[derive(Copy, Clone)] +pub struct LocalStruct; + +// @has doc_masked/trait.LocalTrait.html +// @has - '//*[@id="implementors-list"]//code' 'impl LocalTrait for LocalStruct' +// @!has - '//*[@id="implementors-list"]//code' 'impl LocalTrait for usize' +pub trait LocalTrait { } + +impl LocalTrait for LocalStruct { } + +impl LocalTrait for usize { } From db09332b39193a75f1de60372bf60b22f06381db Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Mon, 21 Aug 2017 20:48:03 -0500 Subject: [PATCH 3/4] update `doc_masked` docs and tracking issue --- src/doc/unstable-book/src/language-features/doc-masked.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doc/unstable-book/src/language-features/doc-masked.md b/src/doc/unstable-book/src/language-features/doc-masked.md index 0d0a46ce9ee54..5731afc8bc91b 100644 --- a/src/doc/unstable-book/src/language-features/doc-masked.md +++ b/src/doc/unstable-book/src/language-features/doc-masked.md @@ -1,6 +1,6 @@ # `doc_masked` -The tracking issue for this feature is: [TODO](TODO) +The tracking issue for this feature is: [#44027] ----- @@ -16,7 +16,7 @@ of trait implementations. The specifics of the feature are as follows: 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 [TODO](TODO) to ensure that compiler-internal and +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. @@ -35,3 +35,6 @@ pub trait MyMarker { } impl MyMarker for f32 { } impl MyMarker for f64 { } ``` + +[#44026]: https://github.com/rust-lang/rust/pull/44026 +[#44027]: https://github.com/rust-lang/rust/pull/44027 From 64f61113ebdc32ba5f9898fe191ca84fba6eeb94 Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Tue, 5 Sep 2017 13:56:30 -0500 Subject: [PATCH 4/4] remove tests for doc(masked) --- .../src/language-features/doc-masked.md | 16 ---------- src/test/rustdoc/doc-masked.rs | 29 ------------------- 2 files changed, 45 deletions(-) delete mode 100644 src/test/rustdoc/doc-masked.rs diff --git a/src/doc/unstable-book/src/language-features/doc-masked.md b/src/doc/unstable-book/src/language-features/doc-masked.md index 5731afc8bc91b..609939bfc22f6 100644 --- a/src/doc/unstable-book/src/language-features/doc-masked.md +++ b/src/doc/unstable-book/src/language-features/doc-masked.md @@ -20,21 +20,5 @@ 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. -```rust -#![feature(doc_masked)] - -// Since std is automatically imported, we need to import it into a separate name to apply the -// attribute. This is used as a simple demonstration, but any extern crate statement will suffice. -#[doc(masked)] -extern crate std as realstd; - -/// A sample marker trait that exists on floating-point numbers, even though this page won't list -/// them! -pub trait MyMarker { } - -impl MyMarker for f32 { } -impl MyMarker for f64 { } -``` - [#44026]: https://github.com/rust-lang/rust/pull/44026 [#44027]: https://github.com/rust-lang/rust/pull/44027 diff --git a/src/test/rustdoc/doc-masked.rs b/src/test/rustdoc/doc-masked.rs deleted file mode 100644 index 78d32028e3630..0000000000000 --- a/src/test/rustdoc/doc-masked.rs +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -#![feature(doc_masked)] - -#![doc(masked)] -extern crate std as realstd; - -// @has doc_masked/struct.LocalStruct.html -// @has - '//*[@class="impl"]//code' 'impl LocalTrait for LocalStruct' -// @!has - '//*[@class="impl"]//code' 'impl Copy for LocalStruct' -#[derive(Copy, Clone)] -pub struct LocalStruct; - -// @has doc_masked/trait.LocalTrait.html -// @has - '//*[@id="implementors-list"]//code' 'impl LocalTrait for LocalStruct' -// @!has - '//*[@id="implementors-list"]//code' 'impl LocalTrait for usize' -pub trait LocalTrait { } - -impl LocalTrait for LocalStruct { } - -impl LocalTrait for usize { }