Auto merge of #43348 - kennytm:fix-24658-doc-every-platform, r=alexcrichton

Expose all OS-specific modules in libstd doc.

1. Uses the special `--cfg dox` configuration passed by rustbuild when running `rustdoc`. Changes the `#[cfg(platform)]` into `#[cfg(any(dox, platform))]` so that platform-specific API are visible to rustdoc.

2. Since platform-specific implementations often won't compile correctly on other platforms, `rustdoc` is changed to apply `everybody_loops` to the functions during documentation and doc-test harness.

3. Since platform-specific code are documented on all platforms now, it could confuse users who found a useful API but is non-portable. Also, their examples will be doc-tested, so must be excluded when not testing on the native platform. An undocumented attribute `#[doc(cfg(...))]` is introduced to serve the above purposed.

Fixes #24658 (Does _not_ fully implement #1998).
This commit is contained in:
bors 2017-08-13 03:00:20 +00:00
commit 0ed03e5490
23 changed files with 1264 additions and 78 deletions

View File

@ -0,0 +1,42 @@
# `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

View File

@ -44,6 +44,7 @@ use std::io::{self, Write};
use std::option;
use std::path::Path;
use std::str::FromStr;
use std::mem;
use rustc::hir::map as hir_map;
use rustc::hir::map::blocks;
@ -618,52 +619,53 @@ impl UserIdentifiedItem {
}
}
struct ReplaceBodyWithLoop {
// Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere.
pub struct ReplaceBodyWithLoop {
within_static_or_const: bool,
}
impl ReplaceBodyWithLoop {
fn new() -> ReplaceBodyWithLoop {
pub fn new() -> ReplaceBodyWithLoop {
ReplaceBodyWithLoop { within_static_or_const: false }
}
fn run<R, F: FnOnce(&mut Self) -> R>(&mut self, is_const: bool, action: F) -> R {
let old_const = mem::replace(&mut self.within_static_or_const, is_const);
let ret = action(self);
self.within_static_or_const = old_const;
ret
}
}
impl fold::Folder for ReplaceBodyWithLoop {
fn fold_item_kind(&mut self, i: ast::ItemKind) -> ast::ItemKind {
match i {
ast::ItemKind::Static(..) |
ast::ItemKind::Const(..) => {
self.within_static_or_const = true;
let ret = fold::noop_fold_item_kind(i, self);
self.within_static_or_const = false;
return ret;
}
_ => fold::noop_fold_item_kind(i, self),
}
let is_const = match i {
ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true,
ast::ItemKind::Fn(_, _, ref constness, _, _, _) =>
constness.node == ast::Constness::Const,
_ => false,
};
self.run(is_const, |s| fold::noop_fold_item_kind(i, s))
}
fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
match i.node {
ast::TraitItemKind::Const(..) => {
self.within_static_or_const = true;
let ret = fold::noop_fold_trait_item(i, self);
self.within_static_or_const = false;
return ret;
}
_ => fold::noop_fold_trait_item(i, self),
}
let is_const = match i.node {
ast::TraitItemKind::Const(..) => true,
ast::TraitItemKind::Method(ast::MethodSig { ref constness, .. }, _) =>
constness.node == ast::Constness::Const,
_ => false,
};
self.run(is_const, |s| fold::noop_fold_trait_item(i, s))
}
fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
match i.node {
ast::ImplItemKind::Const(..) => {
self.within_static_or_const = true;
let ret = fold::noop_fold_impl_item(i, self);
self.within_static_or_const = false;
return ret;
}
_ => fold::noop_fold_impl_item(i, self),
}
let is_const = match i.node {
ast::ImplItemKind::Const(..) => true,
ast::ImplItemKind::Method(ast::MethodSig { ref constness, .. }, _) =>
constness.node == ast::Constness::Const,
_ => false,
};
self.run(is_const, |s| fold::noop_fold_impl_item(i, s))
}
fn fold_block(&mut self, b: P<ast::Block>) -> P<ast::Block> {

889
src/librustdoc/clean/cfg.rs Normal file
View File

@ -0,0 +1,889 @@
// 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.
//! Representation of a `#[doc(cfg(...))]` attribute.
// FIXME: Once RFC #1868 is implemented, switch to use those structures instead.
use std::mem;
use std::fmt::{self, Write};
use std::ops;
use std::ascii::AsciiExt;
use syntax::symbol::Symbol;
use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, LitKind};
use syntax::parse::ParseSess;
use syntax::feature_gate::Features;
use syntax_pos::Span;
use html::escape::Escape;
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, PartialEq)]
pub enum Cfg {
/// Accepts all configurations.
True,
/// Denies all configurations.
False,
/// A generic configration option, e.g. `test` or `target_os = "linux"`.
Cfg(Symbol, Option<Symbol>),
/// Negate a configuration requirement, i.e. `not(x)`.
Not(Box<Cfg>),
/// Union of a list of configuration requirements, i.e. `any(...)`.
Any(Vec<Cfg>),
/// Intersection of a list of configuration requirements, i.e. `all(...)`.
All(Vec<Cfg>),
}
#[derive(PartialEq, Debug)]
pub struct InvalidCfgError {
pub msg: &'static str,
pub span: Span,
}
impl Cfg {
/// Parses a `NestedMetaItem` into a `Cfg`.
fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
match nested_cfg.node {
NestedMetaItemKind::MetaItem(ref cfg) => Cfg::parse(cfg),
NestedMetaItemKind::Literal(ref lit) => Err(InvalidCfgError {
msg: "unexpected literal",
span: lit.span,
}),
}
}
/// Parses a `MetaItem` into a `Cfg`.
///
/// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g. `unix` or
/// `target_os = "redox"`.
///
/// If the content is not properly formatted, it will return an error indicating what and where
/// the error is.
pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
let name = cfg.name();
match cfg.node {
MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
MetaItemKind::NameValue(ref lit) => match lit.node {
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.as_str() {
"all" => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)),
"any" => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)),
"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,
}),
}
}
}
}
/// Checks whether the given configuration can be matched in the current session.
///
/// Equivalent to `attr::cfg_matches`.
// FIXME: Actually make use of `features`.
pub fn matches(&self, parse_sess: &ParseSess, features: Option<&Features>) -> bool {
match *self {
Cfg::False => false,
Cfg::True => true,
Cfg::Not(ref child) => !child.matches(parse_sess, features),
Cfg::All(ref sub_cfgs) => {
sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(parse_sess, features))
},
Cfg::Any(ref sub_cfgs) => {
sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(parse_sess, features))
},
Cfg::Cfg(name, value) => parse_sess.config.contains(&(name, value)),
}
}
/// Whether the configuration consists of just `Cfg` or `Not`.
fn is_simple(&self) -> bool {
match *self {
Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true,
Cfg::All(..) | Cfg::Any(..) => false,
}
}
/// Whether the configuration consists of just `Cfg`, `Not` or `All`.
fn is_all(&self) -> bool {
match *self {
Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true,
Cfg::Any(..) => false,
}
}
/// Renders the configuration for human display, as a short HTML description.
pub(crate) fn render_short_html(&self) -> String {
let mut msg = Html(self).to_string();
if self.should_capitalize_first_letter() {
if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) {
msg[i .. i+1].make_ascii_uppercase();
}
}
msg
}
/// Renders the configuration for long display, as a long HTML description.
pub(crate) fn render_long_html(&self) -> String {
let mut msg = format!("This is supported on <strong>{}</strong>", Html(self));
if self.should_append_only_to_description() {
msg.push_str(" only");
}
msg.push('.');
msg
}
fn should_capitalize_first_letter(&self) -> bool {
match *self {
Cfg::False | Cfg::True | Cfg::Not(..) => true,
Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => {
sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false)
},
Cfg::Cfg(name, _) => match &*name.as_str() {
"debug_assertions" | "target_endian" => true,
_ => false,
},
}
}
fn should_append_only_to_description(&self) -> bool {
match *self {
Cfg::False | Cfg::True => false,
Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
Cfg::Not(ref child) => match **child {
Cfg::Cfg(..) => true,
_ => false,
}
}
}
}
impl ops::Not for Cfg {
type Output = Cfg;
fn not(self) -> Cfg {
match self {
Cfg::False => Cfg::True,
Cfg::True => Cfg::False,
Cfg::Not(cfg) => *cfg,
s => Cfg::Not(Box::new(s)),
}
}
}
impl ops::BitAndAssign for Cfg {
fn bitand_assign(&mut self, other: Cfg) {
match (self, other) {
(&mut Cfg::False, _) | (_, Cfg::True) => {},
(s, Cfg::False) => *s = Cfg::False,
(s @ &mut Cfg::True, b) => *s = b,
(&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => a.append(b),
(&mut Cfg::All(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
(s, Cfg::All(mut a)) => {
let b = mem::replace(s, Cfg::True);
a.push(b);
*s = Cfg::All(a);
},
(s, b) => {
let a = mem::replace(s, Cfg::True);
*s = Cfg::All(vec![a, b]);
},
}
}
}
impl ops::BitAnd for Cfg {
type Output = Cfg;
fn bitand(mut self, other: Cfg) -> Cfg {
self &= other;
self
}
}
impl ops::BitOrAssign for Cfg {
fn bitor_assign(&mut self, other: Cfg) {
match (self, other) {
(&mut Cfg::True, _) | (_, Cfg::False) => {},
(s, Cfg::True) => *s = Cfg::True,
(s @ &mut Cfg::False, b) => *s = b,
(&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => a.append(b),
(&mut Cfg::Any(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
(s, Cfg::Any(mut a)) => {
let b = mem::replace(s, Cfg::True);
a.push(b);
*s = Cfg::Any(a);
},
(s, b) => {
let a = mem::replace(s, Cfg::True);
*s = Cfg::Any(vec![a, b]);
},
}
}
}
impl ops::BitOr for Cfg {
type Output = Cfg;
fn bitor(mut self, other: Cfg) -> Cfg {
self |= other;
self
}
}
struct Html<'a>(&'a Cfg);
fn write_with_opt_paren<T: fmt::Display>(
fmt: &mut fmt::Formatter,
has_paren: bool,
obj: T,
) -> fmt::Result {
if has_paren {
fmt.write_char('(')?;
}
obj.fmt(fmt)?;
if has_paren {
fmt.write_char(')')?;
}
Ok(())
}
impl<'a> fmt::Display for Html<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self.0 {
Cfg::Not(ref child) => match **child {
Cfg::Any(ref sub_cfgs) => {
let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
" nor "
} else {
", nor "
};
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
fmt.write_str(if i == 0 { "neither " } else { separator })?;
write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?;
}
Ok(())
}
ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple)),
ref c => write!(fmt, "not ({})", Html(c)),
},
Cfg::Any(ref sub_cfgs) => {
let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
" or "
} else {
", or "
};
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
if i != 0 {
fmt.write_str(separator)?;
}
write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?;
}
Ok(())
},
Cfg::All(ref sub_cfgs) => {
for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
if i != 0 {
fmt.write_str(" and ")?;
}
write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg))?;
}
Ok(())
},
Cfg::True => fmt.write_str("everywhere"),
Cfg::False => fmt.write_str("nowhere"),
Cfg::Cfg(name, value) => {
let n = &*name.as_str();
let human_readable = match (n, value) {
("unix", None) => "Unix",
("windows", None) => "Windows",
("debug_assertions", None) => "debug-assertions enabled",
("target_os", Some(os)) => match &*os.as_str() {
"android" => "Android",
"bitrig" => "Bitrig",
"dragonfly" => "DragonFly BSD",
"emscripten" => "Emscripten",
"freebsd" => "FreeBSD",
"fuchsia" => "Fuchsia",
"haiku" => "Haiku",
"ios" => "iOS",
"l4re" => "L4Re",
"linux" => "Linux",
"macos" => "macOS",
"nacl" => "NaCl",
"netbsd" => "NetBSD",
"openbsd" => "OpenBSD",
"redox" => "Redox",
"solaris" => "Solaris",
"windows" => "Windows",
_ => "",
},
("target_arch", Some(arch)) => match &*arch.as_str() {
"aarch64" => "AArch64",
"arm" => "ARM",
"asmjs" => "asm.js",
"mips" => "MIPS",
"mips64" => "MIPS-64",
"msp430" => "MSP430",
"powerpc" => "PowerPC",
"powerpc64" => "PowerPC-64",
"s390x" => "s390x",
"sparc64" => "SPARC64",
"wasm32" => "WebAssembly",
"x86" => "x86",
"x86_64" => "x86-64",
_ => "",
},
("target_vendor", Some(vendor)) => match &*vendor.as_str() {
"apple" => "Apple",
"pc" => "PC",
"rumprun" => "Rumprun",
"sun" => "Sun",
_ => ""
},
("target_env", Some(env)) => match &*env.as_str() {
"gnu" => "GNU",
"msvc" => "MSVC",
"musl" => "musl",
"newlib" => "Newlib",
"uclibc" => "uClibc",
_ => "",
},
("target_endian", Some(endian)) => return write!(fmt, "{}-endian", endian),
("target_pointer_width", Some(bits)) => return write!(fmt, "{}-bit", bits),
_ => "",
};
if !human_readable.is_empty() {
fmt.write_str(human_readable)
} else if let Some(v) = value {
write!(fmt, "<code>{}=\"{}\"</code>", Escape(n), Escape(&*v.as_str()))
} else {
write!(fmt, "<code>{}</code>", Escape(n))
}
}
}
}
}
#[cfg(test)]
mod test {
use super::Cfg;
use syntax::symbol::Symbol;
use syntax::ast::*;
use syntax::codemap::dummy_spanned;
use syntax_pos::DUMMY_SP;
fn word_cfg(s: &str) -> Cfg {
Cfg::Cfg(Symbol::intern(s), None)
}
fn name_value_cfg(name: &str, value: &str) -> Cfg {
Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
}
#[test]
fn test_cfg_not() {
assert_eq!(!Cfg::False, Cfg::True);
assert_eq!(!Cfg::True, Cfg::False);
assert_eq!(!word_cfg("test"), Cfg::Not(Box::new(word_cfg("test"))));
assert_eq!(
!Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
Cfg::Not(Box::new(Cfg::All(vec![word_cfg("a"), word_cfg("b")])))
);
assert_eq!(
!Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
Cfg::Not(Box::new(Cfg::Any(vec![word_cfg("a"), word_cfg("b")])))
);
assert_eq!(!Cfg::Not(Box::new(word_cfg("test"))), word_cfg("test"));
}
#[test]
fn test_cfg_and() {
let mut x = Cfg::False;
x &= Cfg::True;
assert_eq!(x, Cfg::False);
x = word_cfg("test");
x &= Cfg::False;
assert_eq!(x, Cfg::False);
x = word_cfg("test2");
x &= Cfg::True;
assert_eq!(x, word_cfg("test2"));
x = Cfg::True;
x &= word_cfg("test3");
assert_eq!(x, word_cfg("test3"));
x &= word_cfg("test4");
assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
x &= word_cfg("test5");
assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]);
assert_eq!(x, Cfg::All(vec![
word_cfg("test3"),
word_cfg("test4"),
word_cfg("test5"),
word_cfg("test6"),
word_cfg("test7"),
]));
let mut y = Cfg::Any(vec![word_cfg("a"), word_cfg("b")]);
y &= x;
assert_eq!(y, Cfg::All(vec![
word_cfg("test3"),
word_cfg("test4"),
word_cfg("test5"),
word_cfg("test6"),
word_cfg("test7"),
Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
]));
assert_eq!(
word_cfg("a") & word_cfg("b") & word_cfg("c"),
Cfg::All(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
);
}
#[test]
fn test_cfg_or() {
let mut x = Cfg::True;
x |= Cfg::False;
assert_eq!(x, Cfg::True);
x = word_cfg("test");
x |= Cfg::True;
assert_eq!(x, Cfg::True);
x = word_cfg("test2");
x |= Cfg::False;
assert_eq!(x, word_cfg("test2"));
x = Cfg::False;
x |= word_cfg("test3");
assert_eq!(x, word_cfg("test3"));
x |= word_cfg("test4");
assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
x |= word_cfg("test5");
assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]);
assert_eq!(x, Cfg::Any(vec![
word_cfg("test3"),
word_cfg("test4"),
word_cfg("test5"),
word_cfg("test6"),
word_cfg("test7"),
]));
let mut y = Cfg::All(vec![word_cfg("a"), word_cfg("b")]);
y |= x;
assert_eq!(y, Cfg::Any(vec![
word_cfg("test3"),
word_cfg("test4"),
word_cfg("test5"),
word_cfg("test6"),
word_cfg("test7"),
Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
]));
assert_eq!(
word_cfg("a") | word_cfg("b") | word_cfg("c"),
Cfg::Any(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
);
}
#[test]
fn test_parse_ok() {
let mi = MetaItem {
name: Symbol::intern("all"),
node: MetaItemKind::Word,
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
let mi = MetaItem {
name: Symbol::intern("all"),
node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
Symbol::intern("done"),
StrStyle::Cooked,
))),
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
let mi = MetaItem {
name: Symbol::intern("all"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("b"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
let mi = MetaItem {
name: Symbol::intern("any"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("b"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
let mi = MetaItem {
name: Symbol::intern("not"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
let mi = MetaItem {
name: Symbol::intern("not"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("any"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("all"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("b"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("c"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
let mi = MetaItem {
name: Symbol::intern("all"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("b"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("c"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c")));
}
#[test]
fn test_parse_err() {
let mi = MetaItem {
name: Symbol::intern("foo"),
node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
let mi = MetaItem {
name: Symbol::intern("not"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("b"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
let mi = MetaItem {
name: Symbol::intern("not"),
node: MetaItemKind::List(vec![]),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
let mi = MetaItem {
name: Symbol::intern("foo"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
let mi = MetaItem {
name: Symbol::intern("all"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("foo"),
node: MetaItemKind::List(vec![]),
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("b"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
let mi = MetaItem {
name: Symbol::intern("any"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("a"),
node: MetaItemKind::Word,
span: DUMMY_SP,
})),
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("foo"),
node: MetaItemKind::List(vec![]),
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
let mi = MetaItem {
name: Symbol::intern("not"),
node: MetaItemKind::List(vec![
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
name: Symbol::intern("foo"),
node: MetaItemKind::List(vec![]),
span: DUMMY_SP,
})),
]),
span: DUMMY_SP,
};
assert!(Cfg::parse(&mi).is_err());
}
#[test]
fn test_render_short_html() {
assert_eq!(
word_cfg("unix").render_short_html(),
"Unix"
);
assert_eq!(
name_value_cfg("target_os", "macos").render_short_html(),
"macOS"
);
assert_eq!(
name_value_cfg("target_pointer_width", "16").render_short_html(),
"16-bit"
);
assert_eq!(
name_value_cfg("target_endian", "little").render_short_html(),
"Little-endian"
);
assert_eq!(
(!word_cfg("windows")).render_short_html(),
"Non-Windows"
);
assert_eq!(
(word_cfg("unix") & word_cfg("windows")).render_short_html(),
"Unix and Windows"
);
assert_eq!(
(word_cfg("unix") | word_cfg("windows")).render_short_html(),
"Unix or Windows"
);
assert_eq!(
(
word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")
).render_short_html(),
"Unix and Windows and debug-assertions enabled"
);
assert_eq!(
(
word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")
).render_short_html(),
"Unix or Windows or debug-assertions enabled"
);
assert_eq!(
(
!(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))
).render_short_html(),
"Neither Unix nor Windows nor debug-assertions enabled"
);
assert_eq!(
(
(word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) |
(word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))
).render_short_html(),
"Unix and x86-64, or Windows and 64-bit"
);
assert_eq!(
(!(word_cfg("unix") & word_cfg("windows"))).render_short_html(),
"Not (Unix and Windows)"
);
assert_eq!(
(
(word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")
).render_short_html(),
"(Debug-assertions enabled or Windows) and Unix"
);
}
#[test]
fn test_render_long_html() {
assert_eq!(
word_cfg("unix").render_long_html(),
"This is supported on <strong>Unix</strong> only."
);
assert_eq!(
name_value_cfg("target_os", "macos").render_long_html(),
"This is supported on <strong>macOS</strong> only."
);
assert_eq!(
name_value_cfg("target_pointer_width", "16").render_long_html(),
"This is supported on <strong>16-bit</strong> only."
);
assert_eq!(
name_value_cfg("target_endian", "little").render_long_html(),
"This is supported on <strong>little-endian</strong> only."
);
assert_eq!(
(!word_cfg("windows")).render_long_html(),
"This is supported on <strong>non-Windows</strong> only."
);
assert_eq!(
(word_cfg("unix") & word_cfg("windows")).render_long_html(),
"This is supported on <strong>Unix and Windows</strong> only."
);
assert_eq!(
(word_cfg("unix") | word_cfg("windows")).render_long_html(),
"This is supported on <strong>Unix or Windows</strong> only."
);
assert_eq!(
(
word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")
).render_long_html(),
"This is supported on <strong>Unix and Windows and debug-assertions enabled</strong> \
only."
);
assert_eq!(
(
word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")
).render_long_html(),
"This is supported on <strong>Unix or Windows or debug-assertions enabled</strong> \
only."
);
assert_eq!(
(
!(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))
).render_long_html(),
"This is supported on <strong>neither Unix nor Windows nor debug-assertions \
enabled</strong>."
);
assert_eq!(
(
(word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) |
(word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))
).render_long_html(),
"This is supported on <strong>Unix and x86-64, or Windows and 64-bit</strong> only."
);
assert_eq!(
(!(word_cfg("unix") & word_cfg("windows"))).render_long_html(),
"This is supported on <strong>not (Unix and Windows)</strong>."
);
assert_eq!(
(
(word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")
).render_long_html(),
"This is supported on <strong>(debug-assertions enabled or Windows) and Unix</strong> \
only."
);
}
}

View File

@ -52,8 +52,11 @@ use visit_ast;
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 @@ impl<I: IntoIterator<Item=ast::NestedMetaItem>> NestedAttributesExt for I {
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 {
let mut doc_strings = vec![];
let mut sp = None;
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);
/// 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);
}
}
}
return None;
}
}
}
}
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 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;
}
}
}
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 @@ impl AttributesExt for Attributes {
}
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)
}
}

View File

@ -10,6 +10,7 @@
use rustc_lint;
use rustc_driver::{driver, target_features, abort_on_err};
use rustc_driver::pretty::ReplaceBodyWithLoop;
use rustc::dep_graph::DepGraph;
use rustc::session::{self, config};
use rustc::hir::def_id::DefId;
@ -26,6 +27,7 @@ use rustc_metadata::cstore::CStore;
use syntax::{ast, codemap};
use syntax::feature_gate::UnstableFeatures;
use syntax::fold::Folder;
use errors;
use errors::emitter::ColorConfig;
@ -158,6 +160,7 @@ pub fn run_core(search_paths: SearchPaths,
let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(),
&sess,
&input));
let krate = ReplaceBodyWithLoop::new().fold_crate(krate);
let name = link::find_crate_name(Some(&sess), &krate.attrs, &input);

View File

@ -1962,6 +1962,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
}

View File

@ -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;

View File

@ -26,6 +26,7 @@
#![feature(test)]
#![feature(unicode)]
#![feature(vec_remove_item)]
#![feature(ascii_ctype)]
extern crate arena;
extern crate getopts;

View File

@ -33,6 +33,9 @@ pub use self::strip_priv_imports::strip_priv_imports;
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 @@ pub const PASSES: &'static [Pass] = &[
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 @@ pub const DEFAULT_PASSES: &'static [&'static str] = &[
"strip-private",
"collapse-docs",
"unindent-comments",
"propagate-doc-cfg",
];

View File

@ -0,0 +1,47 @@
// 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
}
}

View File

@ -32,6 +32,7 @@ use rustc_back::dynamic_lib::DynamicLibrary;
use rustc_back::tempdir::TempDir;
use rustc_driver::{self, driver, Compilation};
use rustc_driver::driver::phase_2_configure_and_expand;
use rustc_driver::pretty::ReplaceBodyWithLoop;
use rustc_metadata::cstore::CStore;
use rustc_resolve::MakeGlobMap;
use rustc_trans;
@ -39,6 +40,7 @@ use rustc_trans::back::link;
use syntax::ast;
use syntax::codemap::CodeMap;
use syntax::feature_gate::UnstableFeatures;
use syntax::fold::Folder;
use syntax_pos::{BytePos, DUMMY_SP, Pos, Span};
use errors;
use errors::emitter::ColorConfig;
@ -72,6 +74,7 @@ pub fn run(input: &str,
crate_types: vec![config::CrateTypeDylib],
externs: externs.clone(),
unstable_features: UnstableFeatures::from_environment(),
lint_cap: Some(::rustc::lint::Level::Allow),
actually_rustdoc: true,
..config::basic_options().clone()
};
@ -94,6 +97,7 @@ pub fn run(input: &str,
let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(),
&sess,
&input));
let krate = ReplaceBodyWithLoop::new().fold_crate(krate);
let driver::ExpansionResult { defs, mut hir_forest, .. } = {
phase_2_configure_and_expand(
&sess, &cstore, krate, None, "rustdoc-test", None, MakeGlobMap::No, |_| Ok(())
@ -121,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
};
@ -574,6 +579,7 @@ impl Collector {
}
struct HirCollector<'a, 'hir: 'a> {
sess: &'a session::Session,
collector: &'a mut Collector,
map: &'a hir::map::Map<'hir>
}
@ -583,12 +589,18 @@ impl<'a, 'hir> HirCollector<'a, 'hir> {
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() {

View File

@ -315,6 +315,7 @@
#![feature(untagged_unions)]
#![feature(unwind_attributes)]
#![feature(vec_push_all)]
#![feature(doc_cfg)]
#![cfg_attr(test, feature(update_panic_count))]
#![default_lib_allocator]

View File

@ -13,26 +13,36 @@
#![stable(feature = "os", since = "1.0.0")]
#![allow(missing_docs, bad_style, missing_debug_implementations)]
#[cfg(any(target_os = "redox", unix))]
#[cfg(all(not(dox), any(target_os = "redox", unix)))]
#[stable(feature = "rust1", since = "1.0.0")]
pub use sys::ext as unix;
#[cfg(windows)]
#[cfg(all(not(dox), windows))]
#[stable(feature = "rust1", since = "1.0.0")]
pub use sys::ext as windows;
#[cfg(target_os = "android")] pub mod android;
#[cfg(target_os = "bitrig")] pub mod bitrig;
#[cfg(target_os = "dragonfly")] pub mod dragonfly;
#[cfg(target_os = "freebsd")] pub mod freebsd;
#[cfg(target_os = "haiku")] pub mod haiku;
#[cfg(target_os = "ios")] pub mod ios;
#[cfg(target_os = "linux")] pub mod linux;
#[cfg(target_os = "macos")] pub mod macos;
#[cfg(target_os = "nacl")] pub mod nacl;
#[cfg(target_os = "netbsd")] pub mod netbsd;
#[cfg(target_os = "openbsd")] pub mod openbsd;
#[cfg(target_os = "solaris")] pub mod solaris;
#[cfg(target_os = "emscripten")] pub mod emscripten;
#[cfg(target_os = "fuchsia")] pub mod fuchsia;
#[cfg(dox)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use sys::unix_ext as unix;
#[cfg(dox)]
#[stable(feature = "rust1", since = "1.0.0")]
pub use sys::windows_ext as windows;
#[cfg(any(dox, target_os = "linux"))]
#[doc(cfg(target_os = "linux"))]
pub mod linux;
#[cfg(all(not(dox), target_os = "android"))] pub mod android;
#[cfg(all(not(dox), target_os = "bitrig"))] pub mod bitrig;
#[cfg(all(not(dox), target_os = "dragonfly"))] pub mod dragonfly;
#[cfg(all(not(dox), target_os = "freebsd"))] pub mod freebsd;
#[cfg(all(not(dox), target_os = "haiku"))] pub mod haiku;
#[cfg(all(not(dox), target_os = "ios"))] pub mod ios;
#[cfg(all(not(dox), target_os = "macos"))] pub mod macos;
#[cfg(all(not(dox), target_os = "nacl"))] pub mod nacl;
#[cfg(all(not(dox), target_os = "netbsd"))] pub mod netbsd;
#[cfg(all(not(dox), target_os = "openbsd"))] pub mod openbsd;
#[cfg(all(not(dox), target_os = "solaris"))] pub mod solaris;
#[cfg(all(not(dox), target_os = "emscripten"))] pub mod emscripten;
#[cfg(all(not(dox), target_os = "fuchsia"))] pub mod fuchsia;
pub mod raw;

View File

@ -45,3 +45,33 @@ mod imp;
#[cfg(target_os = "redox")]
#[path = "redox/mod.rs"]
mod imp;
// Import essential modules from both platforms when documenting.
#[cfg(all(dox, not(unix)))]
use os::linux as platform;
#[cfg(all(dox, not(any(unix, target_os = "redox"))))]
#[path = "unix/ext/mod.rs"]
pub mod unix_ext;
#[cfg(all(dox, any(unix, target_os = "redox")))]
pub use self::ext as unix_ext;
#[cfg(all(dox, not(windows)))]
#[macro_use]
#[path = "windows/compat.rs"]
mod compat;
#[cfg(all(dox, not(windows)))]
#[path = "windows/c.rs"]
mod c;
#[cfg(all(dox, not(windows)))]
#[path = "windows/ext/mod.rs"]
pub mod windows_ext;
#[cfg(all(dox, windows))]
pub use self::ext as windows_ext;

View File

@ -28,6 +28,7 @@
//! ```
#![stable(feature = "rust1", since = "1.0.0")]
#![doc(cfg(target_os = "redox"))]
pub mod ffi;
pub mod fs;

View File

@ -28,6 +28,7 @@
//! ```
#![stable(feature = "rust1", since = "1.0.0")]
#![doc(cfg(unix))]
pub mod io;
pub mod ffi;

View File

@ -12,8 +12,19 @@
//! Unix-specific networking functionality
#[cfg(unix)]
use libc;
// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
#[cfg(not(unix))]
mod libc {
pub use libc::c_int;
pub type socklen_t = u32;
pub struct sockaddr;
#[derive(Clone)]
pub struct sockaddr_un;
}
use ascii;
use ffi::OsStr;
use fmt;

View File

@ -13,20 +13,21 @@
use io::{self, ErrorKind};
use libc;
#[cfg(target_os = "android")] pub use os::android as platform;
#[cfg(target_os = "bitrig")] pub use os::bitrig as platform;
#[cfg(target_os = "dragonfly")] pub use os::dragonfly as platform;
#[cfg(target_os = "freebsd")] pub use os::freebsd as platform;
#[cfg(target_os = "haiku")] pub use os::haiku as platform;
#[cfg(target_os = "ios")] pub use os::ios as platform;
#[cfg(target_os = "linux")] pub use os::linux as platform;
#[cfg(target_os = "macos")] pub use os::macos as platform;
#[cfg(target_os = "nacl")] pub use os::nacl as platform;
#[cfg(target_os = "netbsd")] pub use os::netbsd as platform;
#[cfg(target_os = "openbsd")] pub use os::openbsd as platform;
#[cfg(target_os = "solaris")] pub use os::solaris as platform;
#[cfg(target_os = "emscripten")] pub use os::emscripten as platform;
#[cfg(target_os = "fuchsia")] pub use os::fuchsia as platform;
#[cfg(any(dox, target_os = "linux"))] pub use os::linux as platform;
#[cfg(all(not(dox), target_os = "android"))] pub use os::android as platform;
#[cfg(all(not(dox), target_os = "bitrig"))] pub use os::bitrig as platform;
#[cfg(all(not(dox), target_os = "dragonfly"))] pub use os::dragonfly as platform;
#[cfg(all(not(dox), target_os = "freebsd"))] pub use os::freebsd as platform;
#[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform;
#[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform;
#[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform;
#[cfg(all(not(dox), target_os = "nacl"))] pub use os::nacl as platform;
#[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform;
#[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform;
#[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform;
#[cfg(all(not(dox), target_os = "emscripten"))] pub use os::emscripten as platform;
#[cfg(all(not(dox), target_os = "fuchsia"))] pub use os::fuchsia as platform;
#[macro_use]
pub mod weak;

View File

@ -301,7 +301,7 @@ pub const PIPE_READMODE_BYTE: DWORD = 0x00000000;
pub const FD_SETSIZE: usize = 64;
#[repr(C)]
#[cfg(target_arch = "x86")]
#[cfg(not(target_pointer_width = "64"))]
pub struct WSADATA {
pub wVersion: WORD,
pub wHighVersion: WORD,
@ -312,7 +312,7 @@ pub struct WSADATA {
pub lpVendorInfo: *mut u8,
}
#[repr(C)]
#[cfg(target_arch = "x86_64")]
#[cfg(target_pointer_width = "64")]
pub struct WSADATA {
pub wVersion: WORD,
pub wHighVersion: WORD,
@ -768,6 +768,14 @@ pub struct FLOATING_SAVE_AREA {
_Dummy: [u8; 512] // FIXME: Fill this out
}
// FIXME(#43348): This structure is used for backtrace only, and a fake
// definition is provided here only to allow rustdoc to pass type-check. This
// will not appear in the final documentation. This should be also defined for
// other architectures supported by Windows such as ARM, and for historical
// interest, maybe MIPS and PowerPC as well.
#[cfg(all(dox, not(any(target_arch = "x86_64", target_arch = "x86"))))]
pub enum CONTEXT {}
#[repr(C)]
pub struct SOCKADDR_STORAGE_LH {
pub ss_family: ADDRESS_FAMILY,

View File

@ -17,6 +17,7 @@
//! platform-agnostic idioms would not normally support.
#![stable(feature = "rust1", since = "1.0.0")]
#![doc(cfg(windows))]
pub mod ffi;
pub mod fs;

View File

@ -372,6 +372,9 @@ declare_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! (
@ -1172,6 +1175,16 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
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
}

View File

@ -0,0 +1,12 @@
// 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() {}

View File

@ -0,0 +1,47 @@
// 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() {}
}
}