mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 11:48:30 +00:00
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.
This commit is contained in:
parent
8f935fbb5b
commit
a2b888675a
42
src/doc/unstable-book/src/language-features/doc-cfg.md
Normal file
42
src/doc/unstable-book/src/language-features/doc-cfg.md
Normal 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
|
889
src/librustdoc/clean/cfg.rs
Normal file
889
src/librustdoc/clean/cfg.rs
Normal 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."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -52,8 +52,11 @@ use visit_ast;
|
|||||||
use html::item_type::ItemType;
|
use html::item_type::ItemType;
|
||||||
|
|
||||||
pub mod inline;
|
pub mod inline;
|
||||||
|
pub mod cfg;
|
||||||
mod simplify;
|
mod simplify;
|
||||||
|
|
||||||
|
use self::cfg::Cfg;
|
||||||
|
|
||||||
// extract the stability index for a node from tcx, if possible
|
// extract the stability index for a node from tcx, if possible
|
||||||
fn get_stability(cx: &DocContext, def_id: DefId) -> Option<Stability> {
|
fn get_stability(cx: &DocContext, def_id: DefId) -> Option<Stability> {
|
||||||
cx.tcx.lookup_stability(def_id).clean(cx)
|
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 struct Attributes {
|
||||||
pub doc_strings: Vec<String>,
|
pub doc_strings: Vec<String>,
|
||||||
pub other_attrs: Vec<ast::Attribute>,
|
pub other_attrs: Vec<ast::Attribute>,
|
||||||
|
pub cfg: Option<Rc<Cfg>>,
|
||||||
pub span: Option<syntax_pos::Span>,
|
pub span: Option<syntax_pos::Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attributes {
|
impl Attributes {
|
||||||
pub fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
|
/// Extracts the content from an attribute `#[doc(cfg(content))]`.
|
||||||
let mut doc_strings = vec![];
|
fn extract_cfg(mi: &ast::MetaItem) -> Option<&ast::MetaItem> {
|
||||||
let mut sp = None;
|
use syntax::ast::NestedMetaItemKind::MetaItem;
|
||||||
let other_attrs = attrs.iter().filter_map(|attr| {
|
|
||||||
attr.with_desugared_doc(|attr| {
|
if let ast::MetaItemKind::List(ref nmis) = mi.node {
|
||||||
if let Some(value) = attr.value_str() {
|
if nmis.len() == 1 {
|
||||||
if attr.check_name("doc") {
|
if let MetaItem(ref cfg_mi) = nmis[0].node {
|
||||||
doc_strings.push(value.to_string());
|
if cfg_mi.check_name("cfg") {
|
||||||
if sp.is_none() {
|
if let ast::MetaItemKind::List(ref cfg_nmis) = cfg_mi.node {
|
||||||
sp = Some(attr.span);
|
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())
|
Some(attr.clone())
|
||||||
})
|
})
|
||||||
}).collect();
|
}).collect();
|
||||||
Attributes {
|
Attributes {
|
||||||
doc_strings: doc_strings,
|
doc_strings,
|
||||||
other_attrs: other_attrs,
|
other_attrs,
|
||||||
|
cfg: if cfg == Cfg::True { None } else { Some(Rc::new(cfg)) },
|
||||||
span: sp,
|
span: sp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,8 +618,8 @@ impl AttributesExt for Attributes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Clean<Attributes> for [ast::Attribute] {
|
impl Clean<Attributes> for [ast::Attribute] {
|
||||||
fn clean(&self, _cx: &DocContext) -> Attributes {
|
fn clean(&self, cx: &DocContext) -> Attributes {
|
||||||
Attributes::from_ast(self)
|
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))
|
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
|
stability
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +152,7 @@ a.test-arrow {
|
|||||||
|
|
||||||
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
|
||||||
.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
|
.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
|
||||||
|
.stab.portability { background: #C4ECFF; border-color: #7BA5DB; }
|
||||||
|
|
||||||
#help > div {
|
#help > div {
|
||||||
background: #e9e9e9;
|
background: #e9e9e9;
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(unicode)]
|
#![feature(unicode)]
|
||||||
#![feature(vec_remove_item)]
|
#![feature(vec_remove_item)]
|
||||||
|
#![feature(ascii_ctype)]
|
||||||
|
|
||||||
extern crate arena;
|
extern crate arena;
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
|
@ -33,6 +33,9 @@ pub use self::strip_priv_imports::strip_priv_imports;
|
|||||||
mod unindent_comments;
|
mod unindent_comments;
|
||||||
pub use self::unindent_comments::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
|
type Pass = (&'static str, // name
|
||||||
fn(clean::Crate) -> plugins::PluginResult, // fn
|
fn(clean::Crate) -> plugins::PluginResult, // fn
|
||||||
&'static str); // description
|
&'static str); // description
|
||||||
@ -49,6 +52,8 @@ pub const PASSES: &'static [Pass] = &[
|
|||||||
implies strip-priv-imports"),
|
implies strip-priv-imports"),
|
||||||
("strip-priv-imports", strip_priv_imports,
|
("strip-priv-imports", strip_priv_imports,
|
||||||
"strips all private import statements (`use`, `extern crate`) from a crate"),
|
"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] = &[
|
pub const DEFAULT_PASSES: &'static [&'static str] = &[
|
||||||
@ -56,6 +61,7 @@ pub const DEFAULT_PASSES: &'static [&'static str] = &[
|
|||||||
"strip-private",
|
"strip-private",
|
||||||
"collapse-docs",
|
"collapse-docs",
|
||||||
"unindent-comments",
|
"unindent-comments",
|
||||||
|
"propagate-doc-cfg",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
47
src/librustdoc/passes/propagate_doc_cfg.rs
Normal file
47
src/librustdoc/passes/propagate_doc_cfg.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -125,6 +125,7 @@ pub fn run(input: &str,
|
|||||||
let map = hir::map::map_crate(&mut hir_forest, defs);
|
let map = hir::map::map_crate(&mut hir_forest, defs);
|
||||||
let krate = map.krate();
|
let krate = map.krate();
|
||||||
let mut hir_collector = HirCollector {
|
let mut hir_collector = HirCollector {
|
||||||
|
sess: &sess,
|
||||||
collector: &mut collector,
|
collector: &mut collector,
|
||||||
map: &map
|
map: &map
|
||||||
};
|
};
|
||||||
@ -578,6 +579,7 @@ impl Collector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct HirCollector<'a, 'hir: 'a> {
|
struct HirCollector<'a, 'hir: 'a> {
|
||||||
|
sess: &'a session::Session,
|
||||||
collector: &'a mut Collector,
|
collector: &'a mut Collector,
|
||||||
map: &'a hir::map::Map<'hir>
|
map: &'a hir::map::Map<'hir>
|
||||||
}
|
}
|
||||||
@ -587,12 +589,18 @@ impl<'a, 'hir> HirCollector<'a, 'hir> {
|
|||||||
name: String,
|
name: String,
|
||||||
attrs: &[ast::Attribute],
|
attrs: &[ast::Attribute],
|
||||||
nested: F) {
|
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();
|
let has_name = !name.is_empty();
|
||||||
if has_name {
|
if has_name {
|
||||||
self.collector.names.push(name);
|
self.collector.names.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut attrs = Attributes::from_ast(attrs);
|
|
||||||
attrs.collapse_doc_comments();
|
attrs.collapse_doc_comments();
|
||||||
attrs.unindent_doc_comments();
|
attrs.unindent_doc_comments();
|
||||||
if let Some(doc) = attrs.doc_value() {
|
if let Some(doc) = attrs.doc_value() {
|
||||||
|
@ -364,6 +364,9 @@ declare_features! (
|
|||||||
// global allocators and their internals
|
// global allocators and their internals
|
||||||
(active, global_allocator, "1.20.0", None),
|
(active, global_allocator, "1.20.0", None),
|
||||||
(active, allocator_internals, "1.20.0", None),
|
(active, allocator_internals, "1.20.0", None),
|
||||||
|
|
||||||
|
// #[doc(cfg(...))]
|
||||||
|
(active, doc_cfg, "1.21.0", Some(43781)),
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_features! (
|
declare_features! (
|
||||||
@ -1157,6 +1160,16 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||||||
self.context.check_attribute(attr, false);
|
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) {
|
if self.context.features.proc_macro && attr::is_known(attr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
12
src/test/compile-fail/feature-gate-doc_cfg.rs
Normal file
12
src/test/compile-fail/feature-gate-doc_cfg.rs
Normal 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() {}
|
47
src/test/rustdoc/doc-cfg.rs
Normal file
47
src/test/rustdoc/doc-cfg.rs
Normal 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() {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user