1 //! The representation of a `#[doc(cfg(...))]` attribute.
3 // FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
4 // switch to use those structures instead.
7 use std::fmt::{self, Write};
10 use syntax::symbol::{Symbol, sym};
11 use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, LitKind};
12 use syntax::sess::ParseSess;
13 use syntax::feature_gate::Features;
17 use crate::html::escape::Escape;
22 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
24 /// Accepts all configurations.
26 /// Denies all configurations.
28 /// A generic configuration option, e.g., `test` or `target_os = "linux"`.
29 Cfg(Symbol, Option<Symbol>),
30 /// Negates a configuration requirement, i.e., `not(x)`.
32 /// Union of a list of configuration requirements, i.e., `any(...)`.
34 /// Intersection of a list of configuration requirements, i.e., `all(...)`.
38 #[derive(PartialEq, Debug)]
39 pub struct InvalidCfgError {
40 pub msg: &'static str,
45 /// Parses a `NestedMetaItem` into a `Cfg`.
46 fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
48 NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg),
49 NestedMetaItem::Literal(ref lit) => Err(InvalidCfgError {
50 msg: "unexpected literal",
56 /// Parses a `MetaItem` into a `Cfg`.
58 /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or
59 /// `target_os = "redox"`.
61 /// If the content is not properly formatted, it will return an error indicating what and where
63 pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
64 let name = match cfg.ident() {
65 Some(ident) => ident.name,
66 None => return Err(InvalidCfgError {
67 msg: "expected a single identifier",
72 MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
73 MetaItemKind::NameValue(ref lit) => match lit.kind {
74 LitKind::Str(value, _) => Ok(Cfg::Cfg(name, Some(value))),
75 _ => Err(InvalidCfgError {
76 // FIXME: if the main #[cfg] syntax decided to support non-string literals,
77 // this should be changed as well.
78 msg: "value of cfg option should be a string literal",
82 MetaItemKind::List(ref items) => {
83 let mut sub_cfgs = items.iter().map(Cfg::parse_nested);
85 sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)),
86 sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)),
87 sym::not => if sub_cfgs.len() == 1 {
88 Ok(!sub_cfgs.next().unwrap()?)
91 msg: "expected 1 cfg-pattern",
95 _ => Err(InvalidCfgError {
96 msg: "invalid predicate",
104 /// Checks whether the given configuration can be matched in the current session.
106 /// Equivalent to `attr::cfg_matches`.
107 // FIXME: Actually make use of `features`.
108 pub fn matches(&self, parse_sess: &ParseSess, features: Option<&Features>) -> bool {
112 Cfg::Not(ref child) => !child.matches(parse_sess, features),
113 Cfg::All(ref sub_cfgs) => {
114 sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(parse_sess, features))
116 Cfg::Any(ref sub_cfgs) => {
117 sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(parse_sess, features))
119 Cfg::Cfg(name, value) => parse_sess.config.contains(&(name, value)),
123 /// Whether the configuration consists of just `Cfg` or `Not`.
124 fn is_simple(&self) -> bool {
126 Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true,
127 Cfg::All(..) | Cfg::Any(..) => false,
131 /// Whether the configuration consists of just `Cfg`, `Not` or `All`.
132 fn is_all(&self) -> bool {
134 Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true,
135 Cfg::Any(..) => false,
139 /// Renders the configuration for human display, as a short HTML description.
140 pub(crate) fn render_short_html(&self) -> String {
141 let mut msg = Html(self, true).to_string();
142 if self.should_capitalize_first_letter() {
143 if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) {
144 msg[i .. i+1].make_ascii_uppercase();
150 /// Renders the configuration for long display, as a long HTML description.
151 pub(crate) fn render_long_html(&self) -> String {
152 let on = if self.should_use_with_in_description() {
158 let mut msg = format!("This is supported {} <strong>{}</strong>", on, Html(self, false));
159 if self.should_append_only_to_description() {
160 msg.push_str(" only");
166 fn should_capitalize_first_letter(&self) -> bool {
168 Cfg::False | Cfg::True | Cfg::Not(..) => true,
169 Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => {
170 sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false)
172 Cfg::Cfg(name, _) => match &*name.as_str() {
173 "debug_assertions" | "target_endian" => true,
179 fn should_append_only_to_description(&self) -> bool {
181 Cfg::False | Cfg::True => false,
182 Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
183 Cfg::Not(ref child) => match **child {
184 Cfg::Cfg(..) => true,
190 fn should_use_with_in_description(&self) -> bool {
192 Cfg::Cfg(name, _) if name == sym::target_feature => true,
198 impl ops::Not for Cfg {
200 fn not(self) -> Cfg {
202 Cfg::False => Cfg::True,
203 Cfg::True => Cfg::False,
204 Cfg::Not(cfg) => *cfg,
205 s => Cfg::Not(Box::new(s)),
210 impl ops::BitAndAssign for Cfg {
211 fn bitand_assign(&mut self, other: Cfg) {
212 match (self, other) {
213 (&mut Cfg::False, _) | (_, Cfg::True) => {},
214 (s, Cfg::False) => *s = Cfg::False,
215 (s @ &mut Cfg::True, b) => *s = b,
216 (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => a.append(b),
217 (&mut Cfg::All(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
218 (s, Cfg::All(mut a)) => {
219 let b = mem::replace(s, Cfg::True);
224 let a = mem::replace(s, Cfg::True);
225 *s = Cfg::All(vec![a, b]);
231 impl ops::BitAnd for Cfg {
233 fn bitand(mut self, other: Cfg) -> Cfg {
239 impl ops::BitOrAssign for Cfg {
240 fn bitor_assign(&mut self, other: Cfg) {
241 match (self, other) {
242 (&mut Cfg::True, _) | (_, Cfg::False) => {},
243 (s, Cfg::True) => *s = Cfg::True,
244 (s @ &mut Cfg::False, b) => *s = b,
245 (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => a.append(b),
246 (&mut Cfg::Any(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
247 (s, Cfg::Any(mut a)) => {
248 let b = mem::replace(s, Cfg::True);
253 let a = mem::replace(s, Cfg::True);
254 *s = Cfg::Any(vec![a, b]);
260 impl ops::BitOr for Cfg {
262 fn bitor(mut self, other: Cfg) -> Cfg {
268 /// Pretty-print wrapper for a `Cfg`. Also indicates whether the "short-form" rendering should be
270 struct Html<'a>(&'a Cfg, bool);
272 fn write_with_opt_paren<T: fmt::Display>(
273 fmt: &mut fmt::Formatter<'_>,
278 fmt.write_char('(')?;
282 fmt.write_char(')')?;
288 impl<'a> fmt::Display for Html<'a> {
289 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
291 Cfg::Not(ref child) => match **child {
292 Cfg::Any(ref sub_cfgs) => {
293 let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
298 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
299 fmt.write_str(if i == 0 { "neither " } else { separator })?;
300 write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg, self.1))?;
304 ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple, self.1)),
305 ref c => write!(fmt, "not ({})", Html(c, self.1)),
308 Cfg::Any(ref sub_cfgs) => {
309 let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
314 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
316 fmt.write_str(separator)?;
318 write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg, self.1))?;
323 Cfg::All(ref sub_cfgs) => {
324 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
326 fmt.write_str(" and ")?;
328 write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg, self.1))?;
333 Cfg::True => fmt.write_str("everywhere"),
334 Cfg::False => fmt.write_str("nowhere"),
336 Cfg::Cfg(name, value) => {
337 let n = &*name.as_str();
338 let human_readable = match (n, value) {
339 ("unix", None) => "Unix",
340 ("windows", None) => "Windows",
341 ("debug_assertions", None) => "debug-assertions enabled",
342 ("target_os", Some(os)) => match &*os.as_str() {
343 "android" => "Android",
344 "dragonfly" => "DragonFly BSD",
345 "emscripten" => "Emscripten",
346 "freebsd" => "FreeBSD",
347 "fuchsia" => "Fuchsia",
349 "hermit" => "HermitCore",
354 "netbsd" => "NetBSD",
355 "openbsd" => "OpenBSD",
357 "solaris" => "Solaris",
358 "windows" => "Windows",
361 ("target_arch", Some(arch)) => match &*arch.as_str() {
362 "aarch64" => "AArch64",
364 "asmjs" => "JavaScript",
366 "mips64" => "MIPS-64",
367 "msp430" => "MSP430",
368 "powerpc" => "PowerPC",
369 "powerpc64" => "PowerPC-64",
371 "sparc64" => "SPARC64",
372 "wasm32" => "WebAssembly",
374 "x86_64" => "x86-64",
377 ("target_vendor", Some(vendor)) => match &*vendor.as_str() {
380 "rumprun" => "Rumprun",
382 "fortanix" => "Fortanix",
385 ("target_env", Some(env)) => match &*env.as_str() {
389 "newlib" => "Newlib",
390 "uclibc" => "uClibc",
394 ("target_endian", Some(endian)) => return write!(fmt, "{}-endian", endian),
395 ("target_pointer_width", Some(bits)) => return write!(fmt, "{}-bit", bits),
396 ("target_feature", Some(feat)) =>
398 return write!(fmt, "<code>{}</code>", feat);
400 return write!(fmt, "target feature <code>{}</code>", feat);
404 if !human_readable.is_empty() {
405 fmt.write_str(human_readable)
406 } else if let Some(v) = value {
407 write!(fmt, "<code>{}=\"{}\"</code>", Escape(n), Escape(&v.as_str()))
409 write!(fmt, "<code>{}</code>", Escape(n))