未验证 提交 a2b88867 编写于 作者: K kennytm

Implemented #[doc(cfg(...))].

This attribute has two effects:

1. Items with this attribute and their children will have the "This is
   supported on **** only" message attached in the documentation.

2. The items' doc tests will be skipped if the configuration does not
   match.
上级 8f935fbb
# `doc_cfg`
The tracking issue for this feature is: [#43781]
------
The `doc_cfg` feature allows an API be documented as only available in some specific platforms.
This attribute has two effects:
1. In the annotated item's documentation, there will be a message saying "This is supported on
(platform) only".
2. The item's doc-tests will only run on the specific platform.
This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the
standard library be documented.
```rust
#![feature(doc_cfg)]
#[cfg(any(windows, feature = "documentation"))]
#[doc(cfg(windows))]
/// The application's icon in the notification area (a.k.a. system tray).
///
/// # Examples
///
/// ```no_run
/// extern crate my_awesome_ui_library;
/// use my_awesome_ui_library::current_app;
/// use my_awesome_ui_library::windows::notification;
///
/// let icon = current_app().get::<notification::Icon>();
/// icon.show();
/// icon.show_message("Hello");
/// ```
pub struct Icon {
// ...
}
```
[#43781]: https://github.com/rust-lang/rust/issues/43781
[#43348]: https://github.com/rust-lang/rust/issues/43348
\ No newline at end of file
此差异已折叠。
......@@ -52,8 +52,11 @@
use html::item_type::ItemType;
pub mod inline;
pub mod cfg;
mod simplify;
use self::cfg::Cfg;
// extract the stability index for a node from tcx, if possible
fn get_stability(cx: &DocContext, def_id: DefId) -> Option<Stability> {
cx.tcx.lookup_stability(def_id).clean(cx)
......@@ -536,31 +539,67 @@ fn has_word(self, word: &str) -> bool {
pub struct Attributes {
pub doc_strings: Vec<String>,
pub other_attrs: Vec<ast::Attribute>,
pub cfg: Option<Rc<Cfg>>,
pub span: Option<syntax_pos::Span>,
}
impl Attributes {
pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
/// Extracts the content from an attribute `#[doc(cfg(content))]`.
fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
use syntax::ast::NestedMetaItemKind::MetaItem;
if let ast::MetaItemKind::List(ref nmis) = mi.node {
if nmis.len() == 1 {
if let MetaItem(ref cfg_mi) = nmis[0].node {
if cfg_mi.check_name("cfg") {
if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.node {
if cfg_nmis.len() == 1 {
if let MetaItem(ref content_mi) = cfg_nmis[0].node {
return Some(content_mi);
}
}
}
}
}
}
}
None
}
pub fn from_ast(diagnostic: &::errors::Handler, attrs: &[ast::Attribute]) -> Attributes {
let mut doc_strings = vec![];
let mut sp = None;
let mut cfg = Cfg::True;
let other_attrs = attrs.iter().filter_map(|attr| {
attr.with_desugared_doc(|attr| {
if let Some(value) = attr.value_str() {
if attr.check_name("doc") {
doc_strings.push(value.to_string());
if sp.is_none() {
sp = Some(attr.span);
if attr.check_name("doc") {
if let Some(mi) = attr.meta() {
if let Some(value) = mi.value_str() {
// Extracted #[doc = "..."]
doc_strings.push(value.to_string());
if sp.is_none() {
sp = Some(attr.span);
}
return None;
} else if let Some(cfg_mi) = Attributes::extract_cfg(&mi) {
// Extracted #[doc(cfg(...))]
match Cfg::parse(cfg_mi) {
Ok(new_cfg) => cfg &= new_cfg,
Err(e) => diagnostic.span_err(e.span, e.msg),
}
return None;
}
return None;
}
}
Some(attr.clone())
})
}).collect();
Attributes {
doc_strings: doc_strings,
other_attrs: other_attrs,
doc_strings,
other_attrs,
cfg: if cfg == Cfg::True { None } else { Some(Rc::new(cfg)) },
span: sp,
}
}
......@@ -579,8 +618,8 @@ fn lists<'a>(&'a self, name: &'a str) -> ListAttributesIter<'a> {
}
impl Clean<Attributes> for [ast::Attribute] {
fn clean(&self, _cx: &DocContext) -> Attributes {
Attributes::from_ast(self)
fn clean(&self, cx: &DocContext) -> Attributes {
Attributes::from_ast(cx.sess().diagnostic(), self)
}
}
......
......@@ -1950,6 +1950,14 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<S
stability.push(format!("<div class='stab deprecated'>{}</div>", text))
}
if let Some(ref cfg) = item.attrs.cfg {
stability.push(format!("<div class='stab portability'>{}</div>", if show_reason {
cfg.render_long_html()
} else {
cfg.render_short_html()
}));
}
stability
}
......
......@@ -152,6 +152,7 @@ a.test-arrow {
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
.stab.portability { background: #C4ECFF; border-color: #7BA5DB; }
#help > div {
background: #e9e9e9;
......
......@@ -26,6 +26,7 @@
#![feature(test)]
#![feature(unicode)]
#![feature(vec_remove_item)]
#![feature(ascii_ctype)]
extern crate arena;
extern crate getopts;
......
......@@ -33,6 +33,9 @@
mod unindent_comments;
pub use self::unindent_comments::unindent_comments;
mod propagate_doc_cfg;
pub use self::propagate_doc_cfg::propagate_doc_cfg;
type Pass = (&'static str, // name
fn(clean::Crate) -> plugins::PluginResult, // fn
&'static str); // description
......@@ -49,6 +52,8 @@
implies strip-priv-imports"),
("strip-priv-imports", strip_priv_imports,
"strips all private import statements (`use`, `extern crate`) from a crate"),
("propagate-doc-cfg", propagate_doc_cfg,
"propagates `#[doc(cfg(...))]` to child items"),
];
pub const DEFAULT_PASSES: &'static [&'static str] = &[
......@@ -56,6 +61,7 @@
"strip-private",
"collapse-docs",
"unindent-comments",
"propagate-doc-cfg",
];
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::rc::Rc;
use clean::{Crate, Item};
use clean::cfg::Cfg;
use fold::DocFolder;
use plugins::PluginResult;
pub fn propagate_doc_cfg(cr: Crate) -> PluginResult {
CfgPropagator { parent_cfg: None }.fold_crate(cr)
}
struct CfgPropagator {
parent_cfg: Option<Rc<Cfg>>,
}
impl DocFolder for CfgPropagator {
fn fold_item(&mut self, mut item: Item) -> Option<Item> {
let old_parent_cfg = self.parent_cfg.clone();
let new_cfg = match (self.parent_cfg.take(), item.attrs.cfg.take()) {
(None, None) => None,
(Some(rc), None) | (None, Some(rc)) => Some(rc),
(Some(mut a), Some(b)) => {
let b = Rc::try_unwrap(b).unwrap_or_else(|rc| Cfg::clone(&rc));
*Rc::make_mut(&mut a) &= b;
Some(a)
}
};
self.parent_cfg = new_cfg.clone();
item.attrs.cfg = new_cfg;
let result = self.fold_item_recur(item);
self.parent_cfg = old_parent_cfg;
result
}
}
......@@ -125,6 +125,7 @@ pub fn run(input: &str,
let map = hir::map::map_crate(&mut hir_forest, defs);
let krate = map.krate();
let mut hir_collector = HirCollector {
sess: &sess,
collector: &mut collector,
map: &map
};
......@@ -578,6 +579,7 @@ pub fn register_header(&mut self, name: &str, level: u32) {
}
struct HirCollector<'a, 'hir: 'a> {
sess: &'a session::Session,
collector: &'a mut Collector,
map: &'a hir::map::Map<'hir>
}
......@@ -587,12 +589,18 @@ fn visit_testable<F: FnOnce(&mut Self)>(&mut self,
name: String,
attrs: &[ast::Attribute],
nested: F) {
let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs);
if let Some(ref cfg) = attrs.cfg {
if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features.borrow())) {
return;
}
}
let has_name = !name.is_empty();
if has_name {
self.collector.names.push(name);
}
let mut attrs = Attributes::from_ast(attrs);
attrs.collapse_doc_comments();
attrs.unindent_doc_comments();
if let Some(doc) = attrs.doc_value() {
......
......@@ -364,6 +364,9 @@ pub fn new() -> Features {
// global allocators and their internals
(active, global_allocator, "1.20.0", None),
(active, allocator_internals, "1.20.0", None),
// #[doc(cfg(...))]
(active, doc_cfg, "1.21.0", Some(43781)),
);
declare_features! (
......@@ -1157,6 +1160,16 @@ fn visit_attribute(&mut self, attr: &ast::Attribute) {
self.context.check_attribute(attr, false);
}
if attr.check_name("doc") {
if let Some(content) = attr.meta_item_list() {
if content.len() == 1 && content[0].check_name("cfg") {
gate_feature_post!(&self, doc_cfg, attr.span,
"#[doc(cfg(...))] is experimental"
);
}
}
}
if self.context.features.proc_macro && attr::is_known(attr) {
return
}
......
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[doc(cfg(unix))] //~ ERROR: #[doc(cfg(...))] is experimental
fn main() {}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(doc_cfg)]
// @has doc_cfg/struct.Portable.html
// @!has - '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' ''
// @has - '//*[@id="method.unix_and_arm_only_function"]' 'fn unix_and_arm_only_function()'
// @has - '//*[@class="stab portability"]' 'This is supported on Unix and ARM only.'
pub struct Portable;
// @has doc_cfg/unix_only/index.html \
// '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \
// 'This is supported on Unix only.'
// @matches - '//*[@class=" module-item"]//*[@class="stab portability"]' '\AUnix\Z'
// @matches - '//*[@class=" module-item"]//*[@class="stab portability"]' '\AUnix and ARM\Z'
// @count - '//*[@class="stab portability"]' 3
#[doc(cfg(unix))]
pub mod unix_only {
// @has doc_cfg/unix_only/fn.unix_only_function.html \
// '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \
// 'This is supported on Unix only.'
// @count - '//*[@class="stab portability"]' 1
pub fn unix_only_function() {
content::should::be::irrelevant();
}
// @has doc_cfg/unix_only/trait.ArmOnly.html \
// '//*[@id="main"]/*[@class="stability"]/*[@class="stab portability"]' \
// 'This is supported on Unix and ARM only.'
// @count - '//*[@class="stab portability"]' 2
#[doc(cfg(target_arch = "arm"))]
pub trait ArmOnly {
fn unix_and_arm_only_function();
}
impl ArmOnly for super::Portable {
fn unix_and_arm_only_function() {}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册