]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/cfg.rs
expose #[target_feature] attributes in rustdoc
[rust.git] / src / librustdoc / clean / cfg.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Representation of a `#[doc(cfg(...))]` attribute.
12
13 // FIXME: Once RFC #1868 is implemented, switch to use those structures instead.
14
15 use std::mem;
16 use std::fmt::{self, Write};
17 use std::ops;
18
19 use syntax::symbol::Symbol;
20 use syntax::ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind, LitKind};
21 use syntax::parse::ParseSess;
22 use syntax::feature_gate::Features;
23
24 use syntax_pos::Span;
25
26 use html::escape::Escape;
27
28 #[derive(Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq, Hash)]
29 pub enum Cfg {
30     /// Accepts all configurations.
31     True,
32     /// Denies all configurations.
33     False,
34     /// A generic configration option, e.g. `test` or `target_os = "linux"`.
35     Cfg(Symbol, Option<Symbol>),
36     /// Negate a configuration requirement, i.e. `not(x)`.
37     Not(Box<Cfg>),
38     /// Union of a list of configuration requirements, i.e. `any(...)`.
39     Any(Vec<Cfg>),
40     /// Intersection of a list of configuration requirements, i.e. `all(...)`.
41     All(Vec<Cfg>),
42 }
43
44 #[derive(PartialEq, Debug)]
45 pub struct InvalidCfgError {
46     pub msg: &'static str,
47     pub span: Span,
48 }
49
50 impl Cfg {
51     /// Parses a `NestedMetaItem` into a `Cfg`.
52     fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
53         match nested_cfg.node {
54             NestedMetaItemKind::MetaItem(ref cfg) => Cfg::parse(cfg),
55             NestedMetaItemKind::Literal(ref lit) => Err(InvalidCfgError {
56                 msg: "unexpected literal",
57                 span: lit.span,
58             }),
59         }
60     }
61
62     /// Parses a `MetaItem` into a `Cfg`.
63     ///
64     /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g. `unix` or
65     /// `target_os = "redox"`.
66     ///
67     /// If the content is not properly formatted, it will return an error indicating what and where
68     /// the error is.
69     pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
70         let name = cfg.name();
71         match cfg.node {
72             MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
73             MetaItemKind::NameValue(ref lit) => match lit.node {
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",
79                     span: lit.span,
80                 }),
81             },
82             MetaItemKind::List(ref items) => {
83                 let mut sub_cfgs = items.iter().map(Cfg::parse_nested);
84                 match &*name.as_str() {
85                     "all" => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)),
86                     "any" => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)),
87                     "not" => if sub_cfgs.len() == 1 {
88                         Ok(!sub_cfgs.next().unwrap()?)
89                     } else {
90                         Err(InvalidCfgError {
91                             msg: "expected 1 cfg-pattern",
92                             span: cfg.span,
93                         })
94                     },
95                     _ => Err(InvalidCfgError {
96                         msg: "invalid predicate",
97                         span: cfg.span,
98                     }),
99                 }
100             }
101         }
102     }
103
104     /// Checks whether the given configuration can be matched in the current session.
105     ///
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 {
109         match *self {
110             Cfg::False => false,
111             Cfg::True => true,
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))
115             },
116             Cfg::Any(ref sub_cfgs) => {
117                 sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(parse_sess, features))
118             },
119             Cfg::Cfg(name, value) => parse_sess.config.contains(&(name, value)),
120         }
121     }
122
123     /// Whether the configuration consists of just `Cfg` or `Not`.
124     fn is_simple(&self) -> bool {
125         match *self {
126             Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true,
127             Cfg::All(..) | Cfg::Any(..) => false,
128         }
129     }
130
131     /// Whether the configuration consists of just `Cfg`, `Not` or `All`.
132     fn is_all(&self) -> bool {
133         match *self {
134             Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true,
135             Cfg::Any(..) => false,
136         }
137     }
138
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 = ShortHtml(self).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();
145             }
146         }
147         msg
148     }
149
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() {
153             "with"
154         } else {
155             "on"
156         };
157
158         let mut msg = format!("This is supported {} <strong>{}</strong>", on, Html(self));
159         if self.should_append_only_to_description() {
160             msg.push_str(" only");
161         }
162         msg.push('.');
163         msg
164     }
165
166     fn should_capitalize_first_letter(&self) -> bool {
167         match *self {
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)
171             },
172             Cfg::Cfg(name, _) => match &*name.as_str() {
173                 "debug_assertions" | "target_endian" => true,
174                 _ => false,
175             },
176         }
177     }
178
179     fn should_append_only_to_description(&self) -> bool {
180         match *self {
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,
185                 _ => false,
186             }
187         }
188     }
189
190     fn should_use_with_in_description(&self) -> bool {
191         match *self {
192             Cfg::Cfg(ref name, _) if name == &"target_feature" => true,
193             _ => false,
194         }
195     }
196 }
197
198 impl ops::Not for Cfg {
199     type Output = Cfg;
200     fn not(self) -> Cfg {
201         match self {
202             Cfg::False => Cfg::True,
203             Cfg::True => Cfg::False,
204             Cfg::Not(cfg) => *cfg,
205             s => Cfg::Not(Box::new(s)),
206         }
207     }
208 }
209
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);
220                 a.push(b);
221                 *s = Cfg::All(a);
222             },
223             (s, b) => {
224                 let a = mem::replace(s, Cfg::True);
225                 *s = Cfg::All(vec![a, b]);
226             },
227         }
228     }
229 }
230
231 impl ops::BitAnd for Cfg {
232     type Output = Cfg;
233     fn bitand(mut self, other: Cfg) -> Cfg {
234         self &= other;
235         self
236     }
237 }
238
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);
249                 a.push(b);
250                 *s = Cfg::Any(a);
251             },
252             (s, b) => {
253                 let a = mem::replace(s, Cfg::True);
254                 *s = Cfg::Any(vec![a, b]);
255             },
256         }
257     }
258 }
259
260 impl ops::BitOr for Cfg {
261     type Output = Cfg;
262     fn bitor(mut self, other: Cfg) -> Cfg {
263         self |= other;
264         self
265     }
266 }
267
268 struct Html<'a>(&'a Cfg);
269
270 fn write_with_opt_paren<T: fmt::Display>(
271     fmt: &mut fmt::Formatter,
272     has_paren: bool,
273     obj: T,
274 ) -> fmt::Result {
275     if has_paren {
276         fmt.write_char('(')?;
277     }
278     obj.fmt(fmt)?;
279     if has_paren {
280         fmt.write_char(')')?;
281     }
282     Ok(())
283 }
284
285
286 impl<'a> fmt::Display for Html<'a> {
287     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
288         match *self.0 {
289             Cfg::Not(ref child) => match **child {
290                 Cfg::Any(ref sub_cfgs) => {
291                     let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
292                         " nor "
293                     } else {
294                         ", nor "
295                     };
296                     for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
297                         fmt.write_str(if i == 0 { "neither " } else { separator })?;
298                         write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?;
299                     }
300                     Ok(())
301                 }
302                 ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple)),
303                 ref c => write!(fmt, "not ({})", Html(c)),
304             },
305
306             Cfg::Any(ref sub_cfgs) => {
307                 let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
308                     " or "
309                 } else {
310                     ", or "
311                 };
312                 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
313                     if i != 0 {
314                         fmt.write_str(separator)?;
315                     }
316                     write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?;
317                 }
318                 Ok(())
319             },
320
321             Cfg::All(ref sub_cfgs) => {
322                 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
323                     if i != 0 {
324                         fmt.write_str(" and ")?;
325                     }
326                     write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg))?;
327                 }
328                 Ok(())
329             },
330
331             Cfg::True => fmt.write_str("everywhere"),
332             Cfg::False => fmt.write_str("nowhere"),
333
334             Cfg::Cfg(name, value) => {
335                 let n = &*name.as_str();
336                 let human_readable = match (n, value) {
337                     ("unix", None) => "Unix",
338                     ("windows", None) => "Windows",
339                     ("debug_assertions", None) => "debug-assertions enabled",
340                     ("target_os", Some(os)) => match &*os.as_str() {
341                         "android" => "Android",
342                         "bitrig" => "Bitrig",
343                         "dragonfly" => "DragonFly BSD",
344                         "emscripten" => "Emscripten",
345                         "freebsd" => "FreeBSD",
346                         "fuchsia" => "Fuchsia",
347                         "haiku" => "Haiku",
348                         "ios" => "iOS",
349                         "l4re" => "L4Re",
350                         "linux" => "Linux",
351                         "macos" => "macOS",
352                         "netbsd" => "NetBSD",
353                         "openbsd" => "OpenBSD",
354                         "redox" => "Redox",
355                         "solaris" => "Solaris",
356                         "windows" => "Windows",
357                         _ => "",
358                     },
359                     ("target_arch", Some(arch)) => match &*arch.as_str() {
360                         "aarch64" => "AArch64",
361                         "arm" => "ARM",
362                         "asmjs" => "asm.js",
363                         "mips" => "MIPS",
364                         "mips64" => "MIPS-64",
365                         "msp430" => "MSP430",
366                         "powerpc" => "PowerPC",
367                         "powerpc64" => "PowerPC-64",
368                         "s390x" => "s390x",
369                         "sparc64" => "SPARC64",
370                         "wasm32" => "WebAssembly",
371                         "x86" => "x86",
372                         "x86_64" => "x86-64",
373                         _ => "",
374                     },
375                     ("target_vendor", Some(vendor)) => match &*vendor.as_str() {
376                         "apple" => "Apple",
377                         "pc" => "PC",
378                         "rumprun" => "Rumprun",
379                         "sun" => "Sun",
380                         _ => ""
381                     },
382                     ("target_env", Some(env)) => match &*env.as_str() {
383                         "gnu" => "GNU",
384                         "msvc" => "MSVC",
385                         "musl" => "musl",
386                         "newlib" => "Newlib",
387                         "uclibc" => "uClibc",
388                         _ => "",
389                     },
390                     ("target_endian", Some(endian)) => return write!(fmt, "{}-endian", endian),
391                     ("target_pointer_width", Some(bits)) => return write!(fmt, "{}-bit", bits),
392                     ("target_feature", Some(feat)) =>
393                         return write!(fmt, "target feature <code>{}</code>", feat),
394                     _ => "",
395                 };
396                 if !human_readable.is_empty() {
397                     fmt.write_str(human_readable)
398                 } else if let Some(v) = value {
399                     write!(fmt, "<code>{}=\"{}\"</code>", Escape(n), Escape(&*v.as_str()))
400                 } else {
401                     write!(fmt, "<code>{}</code>", Escape(n))
402                 }
403             }
404         }
405     }
406 }
407
408 struct ShortHtml<'a>(&'a Cfg);
409
410 impl<'a> fmt::Display for ShortHtml<'a> {
411     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
412         match *self.0 {
413             Cfg::Cfg(ref name, Some(ref vendor)) if name == &"target_feature" => {
414                 write!(fmt, "<code>{}</code>", vendor)
415             },
416             ref cfg => write!(fmt, "{}", Html(cfg)),
417         }
418     }
419 }
420
421 #[cfg(test)]
422 mod test {
423     use super::Cfg;
424
425     use syntax::symbol::Symbol;
426     use syntax::ast::*;
427     use syntax::codemap::dummy_spanned;
428     use syntax_pos::DUMMY_SP;
429     use syntax::with_globals;
430
431     fn word_cfg(s: &str) -> Cfg {
432         Cfg::Cfg(Symbol::intern(s), None)
433     }
434
435     fn name_value_cfg(name: &str, value: &str) -> Cfg {
436         Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
437     }
438
439     #[test]
440     fn test_cfg_not() {
441         with_globals(|| {
442             assert_eq!(!Cfg::False, Cfg::True);
443             assert_eq!(!Cfg::True, Cfg::False);
444             assert_eq!(!word_cfg("test"), Cfg::Not(Box::new(word_cfg("test"))));
445             assert_eq!(
446                 !Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
447                 Cfg::Not(Box::new(Cfg::All(vec![word_cfg("a"), word_cfg("b")])))
448             );
449             assert_eq!(
450                 !Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
451                 Cfg::Not(Box::new(Cfg::Any(vec![word_cfg("a"), word_cfg("b")])))
452             );
453             assert_eq!(!Cfg::Not(Box::new(word_cfg("test"))), word_cfg("test"));
454         })
455     }
456
457     #[test]
458     fn test_cfg_and() {
459         with_globals(|| {
460             let mut x = Cfg::False;
461             x &= Cfg::True;
462             assert_eq!(x, Cfg::False);
463
464             x = word_cfg("test");
465             x &= Cfg::False;
466             assert_eq!(x, Cfg::False);
467
468             x = word_cfg("test2");
469             x &= Cfg::True;
470             assert_eq!(x, word_cfg("test2"));
471
472             x = Cfg::True;
473             x &= word_cfg("test3");
474             assert_eq!(x, word_cfg("test3"));
475
476             x &= word_cfg("test4");
477             assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
478
479             x &= word_cfg("test5");
480             assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
481
482             x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]);
483             assert_eq!(x, Cfg::All(vec![
484                 word_cfg("test3"),
485                 word_cfg("test4"),
486                 word_cfg("test5"),
487                 word_cfg("test6"),
488                 word_cfg("test7"),
489             ]));
490
491             let mut y = Cfg::Any(vec![word_cfg("a"), word_cfg("b")]);
492             y &= x;
493             assert_eq!(y, Cfg::All(vec![
494                 word_cfg("test3"),
495                 word_cfg("test4"),
496                 word_cfg("test5"),
497                 word_cfg("test6"),
498                 word_cfg("test7"),
499                 Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
500             ]));
501
502             assert_eq!(
503                 word_cfg("a") & word_cfg("b") & word_cfg("c"),
504                 Cfg::All(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
505             );
506         })
507     }
508
509     #[test]
510     fn test_cfg_or() {
511         with_globals(|| {
512             let mut x = Cfg::True;
513             x |= Cfg::False;
514             assert_eq!(x, Cfg::True);
515
516             x = word_cfg("test");
517             x |= Cfg::True;
518             assert_eq!(x, Cfg::True);
519
520             x = word_cfg("test2");
521             x |= Cfg::False;
522             assert_eq!(x, word_cfg("test2"));
523
524             x = Cfg::False;
525             x |= word_cfg("test3");
526             assert_eq!(x, word_cfg("test3"));
527
528             x |= word_cfg("test4");
529             assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
530
531             x |= word_cfg("test5");
532             assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
533
534             x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]);
535             assert_eq!(x, Cfg::Any(vec![
536                 word_cfg("test3"),
537                 word_cfg("test4"),
538                 word_cfg("test5"),
539                 word_cfg("test6"),
540                 word_cfg("test7"),
541             ]));
542
543             let mut y = Cfg::All(vec![word_cfg("a"), word_cfg("b")]);
544             y |= x;
545             assert_eq!(y, Cfg::Any(vec![
546                 word_cfg("test3"),
547                 word_cfg("test4"),
548                 word_cfg("test5"),
549                 word_cfg("test6"),
550                 word_cfg("test7"),
551                 Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
552             ]));
553
554             assert_eq!(
555                 word_cfg("a") | word_cfg("b") | word_cfg("c"),
556                 Cfg::Any(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
557             );
558         })
559     }
560
561     #[test]
562     fn test_parse_ok() {
563         with_globals(|| {
564             let mi = MetaItem {
565                 name: Symbol::intern("all"),
566                 node: MetaItemKind::Word,
567                 span: DUMMY_SP,
568             };
569             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
570
571             let mi = MetaItem {
572                 name: Symbol::intern("all"),
573                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
574                     Symbol::intern("done"),
575                     StrStyle::Cooked,
576                 ))),
577                 span: DUMMY_SP,
578             };
579             assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
580
581             let mi = MetaItem {
582                 name: Symbol::intern("all"),
583                 node: MetaItemKind::List(vec![
584                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
585                         name: Symbol::intern("a"),
586                         node: MetaItemKind::Word,
587                         span: DUMMY_SP,
588                     })),
589                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
590                         name: Symbol::intern("b"),
591                         node: MetaItemKind::Word,
592                         span: DUMMY_SP,
593                     })),
594                 ]),
595                 span: DUMMY_SP,
596             };
597             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
598
599             let mi = MetaItem {
600                 name: Symbol::intern("any"),
601                 node: MetaItemKind::List(vec![
602                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
603                         name: Symbol::intern("a"),
604                         node: MetaItemKind::Word,
605                         span: DUMMY_SP,
606                     })),
607                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
608                         name: Symbol::intern("b"),
609                         node: MetaItemKind::Word,
610                         span: DUMMY_SP,
611                     })),
612                 ]),
613                 span: DUMMY_SP,
614             };
615             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
616
617             let mi = MetaItem {
618                 name: Symbol::intern("not"),
619                 node: MetaItemKind::List(vec![
620                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
621                         name: Symbol::intern("a"),
622                         node: MetaItemKind::Word,
623                         span: DUMMY_SP,
624                     })),
625                 ]),
626                 span: DUMMY_SP,
627             };
628             assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
629
630             let mi = MetaItem {
631                 name: Symbol::intern("not"),
632                 node: MetaItemKind::List(vec![
633                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
634                         name: Symbol::intern("any"),
635                         node: MetaItemKind::List(vec![
636                             dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
637                                 name: Symbol::intern("a"),
638                                 node: MetaItemKind::Word,
639                                 span: DUMMY_SP,
640                             })),
641                             dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
642                                 name: Symbol::intern("all"),
643                                 node: MetaItemKind::List(vec![
644                                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
645                                         name: Symbol::intern("b"),
646                                         node: MetaItemKind::Word,
647                                         span: DUMMY_SP,
648                                     })),
649                                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
650                                         name: Symbol::intern("c"),
651                                         node: MetaItemKind::Word,
652                                         span: DUMMY_SP,
653                                     })),
654                                 ]),
655                                 span: DUMMY_SP,
656                             })),
657                         ]),
658                         span: DUMMY_SP,
659                     })),
660                 ]),
661                 span: DUMMY_SP,
662             };
663             assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
664
665             let mi = MetaItem {
666                 name: Symbol::intern("all"),
667                 node: MetaItemKind::List(vec![
668                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
669                         name: Symbol::intern("a"),
670                         node: MetaItemKind::Word,
671                         span: DUMMY_SP,
672                     })),
673                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
674                         name: Symbol::intern("b"),
675                         node: MetaItemKind::Word,
676                         span: DUMMY_SP,
677                     })),
678                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
679                         name: Symbol::intern("c"),
680                         node: MetaItemKind::Word,
681                         span: DUMMY_SP,
682                     })),
683                 ]),
684                 span: DUMMY_SP,
685             };
686             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c")));
687         })
688     }
689
690     #[test]
691     fn test_parse_err() {
692         with_globals(|| {
693             let mi = MetaItem {
694                 name: Symbol::intern("foo"),
695                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
696                 span: DUMMY_SP,
697             };
698             assert!(Cfg::parse(&mi).is_err());
699
700             let mi = MetaItem {
701                 name: Symbol::intern("not"),
702                 node: MetaItemKind::List(vec![
703                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
704                         name: Symbol::intern("a"),
705                         node: MetaItemKind::Word,
706                         span: DUMMY_SP,
707                     })),
708                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
709                         name: Symbol::intern("b"),
710                         node: MetaItemKind::Word,
711                         span: DUMMY_SP,
712                     })),
713                 ]),
714                 span: DUMMY_SP,
715             };
716             assert!(Cfg::parse(&mi).is_err());
717
718             let mi = MetaItem {
719                 name: Symbol::intern("not"),
720                 node: MetaItemKind::List(vec![]),
721                 span: DUMMY_SP,
722             };
723             assert!(Cfg::parse(&mi).is_err());
724
725             let mi = MetaItem {
726                 name: Symbol::intern("foo"),
727                 node: MetaItemKind::List(vec![
728                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
729                         name: Symbol::intern("a"),
730                         node: MetaItemKind::Word,
731                         span: DUMMY_SP,
732                     })),
733                 ]),
734                 span: DUMMY_SP,
735             };
736             assert!(Cfg::parse(&mi).is_err());
737
738             let mi = MetaItem {
739                 name: Symbol::intern("all"),
740                 node: MetaItemKind::List(vec![
741                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
742                         name: Symbol::intern("foo"),
743                         node: MetaItemKind::List(vec![]),
744                         span: DUMMY_SP,
745                     })),
746                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
747                         name: Symbol::intern("b"),
748                         node: MetaItemKind::Word,
749                         span: DUMMY_SP,
750                     })),
751                 ]),
752                 span: DUMMY_SP,
753             };
754             assert!(Cfg::parse(&mi).is_err());
755
756             let mi = MetaItem {
757                 name: Symbol::intern("any"),
758                 node: MetaItemKind::List(vec![
759                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
760                         name: Symbol::intern("a"),
761                         node: MetaItemKind::Word,
762                         span: DUMMY_SP,
763                     })),
764                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
765                         name: Symbol::intern("foo"),
766                         node: MetaItemKind::List(vec![]),
767                         span: DUMMY_SP,
768                     })),
769                 ]),
770                 span: DUMMY_SP,
771             };
772             assert!(Cfg::parse(&mi).is_err());
773
774             let mi = MetaItem {
775                 name: Symbol::intern("not"),
776                 node: MetaItemKind::List(vec![
777                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
778                         name: Symbol::intern("foo"),
779                         node: MetaItemKind::List(vec![]),
780                         span: DUMMY_SP,
781                     })),
782                 ]),
783                 span: DUMMY_SP,
784             };
785             assert!(Cfg::parse(&mi).is_err());
786         })
787     }
788
789     #[test]
790     fn test_render_short_html() {
791         with_globals(|| {
792             assert_eq!(
793                 word_cfg("unix").render_short_html(),
794                 "Unix"
795             );
796             assert_eq!(
797                 name_value_cfg("target_os", "macos").render_short_html(),
798                 "macOS"
799             );
800             assert_eq!(
801                 name_value_cfg("target_pointer_width", "16").render_short_html(),
802                 "16-bit"
803             );
804             assert_eq!(
805                 name_value_cfg("target_endian", "little").render_short_html(),
806                 "Little-endian"
807             );
808             assert_eq!(
809                 (!word_cfg("windows")).render_short_html(),
810                 "Non-Windows"
811             );
812             assert_eq!(
813                 (word_cfg("unix") & word_cfg("windows")).render_short_html(),
814                 "Unix and Windows"
815             );
816             assert_eq!(
817                 (word_cfg("unix") | word_cfg("windows")).render_short_html(),
818                 "Unix or Windows"
819             );
820             assert_eq!(
821                 (
822                     word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")
823                 ).render_short_html(),
824                 "Unix and Windows and debug-assertions enabled"
825             );
826             assert_eq!(
827                 (
828                     word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")
829                 ).render_short_html(),
830                 "Unix or Windows or debug-assertions enabled"
831             );
832             assert_eq!(
833                 (
834                     !(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))
835                 ).render_short_html(),
836                 "Neither Unix nor Windows nor debug-assertions enabled"
837             );
838             assert_eq!(
839                 (
840                     (word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) |
841                     (word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))
842                 ).render_short_html(),
843                 "Unix and x86-64, or Windows and 64-bit"
844             );
845             assert_eq!(
846                 (!(word_cfg("unix") & word_cfg("windows"))).render_short_html(),
847                 "Not (Unix and Windows)"
848             );
849             assert_eq!(
850                 (
851                     (word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")
852                 ).render_short_html(),
853                 "(Debug-assertions enabled or Windows) and Unix"
854             );
855             assert_eq!(
856                 name_value_cfg("target_feature", "sse2").render_short_html(),
857                 "<code>sse2</code>"
858             );
859         })
860     }
861
862     #[test]
863     fn test_render_long_html() {
864         with_globals(|| {
865             assert_eq!(
866                 word_cfg("unix").render_long_html(),
867                 "This is supported on <strong>Unix</strong> only."
868             );
869             assert_eq!(
870                 name_value_cfg("target_os", "macos").render_long_html(),
871                 "This is supported on <strong>macOS</strong> only."
872             );
873             assert_eq!(
874                 name_value_cfg("target_pointer_width", "16").render_long_html(),
875                 "This is supported on <strong>16-bit</strong> only."
876             );
877             assert_eq!(
878                 name_value_cfg("target_endian", "little").render_long_html(),
879                 "This is supported on <strong>little-endian</strong> only."
880             );
881             assert_eq!(
882                 (!word_cfg("windows")).render_long_html(),
883                 "This is supported on <strong>non-Windows</strong> only."
884             );
885             assert_eq!(
886                 (word_cfg("unix") & word_cfg("windows")).render_long_html(),
887                 "This is supported on <strong>Unix and Windows</strong> only."
888             );
889             assert_eq!(
890                 (word_cfg("unix") | word_cfg("windows")).render_long_html(),
891                 "This is supported on <strong>Unix or Windows</strong> only."
892             );
893             assert_eq!(
894                 (
895                     word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")
896                 ).render_long_html(),
897                 "This is supported on <strong>Unix and Windows and debug-assertions enabled\
898                  </strong> only."
899             );
900             assert_eq!(
901                 (
902                     word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")
903                 ).render_long_html(),
904                 "This is supported on <strong>Unix or Windows or debug-assertions enabled\
905                  </strong> only."
906             );
907             assert_eq!(
908                 (
909                     !(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))
910                 ).render_long_html(),
911                 "This is supported on <strong>neither Unix nor Windows nor debug-assertions \
912                     enabled</strong>."
913             );
914             assert_eq!(
915                 (
916                     (word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) |
917                     (word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))
918                 ).render_long_html(),
919                 "This is supported on <strong>Unix and x86-64, or Windows and 64-bit</strong> \
920                  only."
921             );
922             assert_eq!(
923                 (!(word_cfg("unix") & word_cfg("windows"))).render_long_html(),
924                 "This is supported on <strong>not (Unix and Windows)</strong>."
925             );
926             assert_eq!(
927                 (
928                     (word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")
929                 ).render_long_html(),
930                 "This is supported on <strong>(debug-assertions enabled or Windows) and Unix\
931                 </strong> only."
932             );
933             assert_eq!(
934                 name_value_cfg("target_feature", "sse2").render_long_html(),
935                 "This is supported with <strong>target feature <code>sse2</code></strong> only."
936             );
937         })
938     }
939 }