]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/cfg.rs
5cac2d1bbe7eeb1e0a5e55b1a26a218b1ed8df00
[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 = Html(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 mut msg = format!("This is supported on <strong>{}</strong>", Html(self));
153         if self.should_append_only_to_description() {
154             msg.push_str(" only");
155         }
156         msg.push('.');
157         msg
158     }
159
160     fn should_capitalize_first_letter(&self) -> bool {
161         match *self {
162             Cfg::False | Cfg::True | Cfg::Not(..) => true,
163             Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => {
164                 sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false)
165             },
166             Cfg::Cfg(name, _) => match &*name.as_str() {
167                 "debug_assertions" | "target_endian" => true,
168                 _ => false,
169             },
170         }
171     }
172
173     fn should_append_only_to_description(&self) -> bool {
174         match *self {
175             Cfg::False | Cfg::True => false,
176             Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
177             Cfg::Not(ref child) => match **child {
178                 Cfg::Cfg(..) => true,
179                 _ => false,
180             }
181         }
182     }
183 }
184
185 impl ops::Not for Cfg {
186     type Output = Cfg;
187     fn not(self) -> Cfg {
188         match self {
189             Cfg::False => Cfg::True,
190             Cfg::True => Cfg::False,
191             Cfg::Not(cfg) => *cfg,
192             s => Cfg::Not(Box::new(s)),
193         }
194     }
195 }
196
197 impl ops::BitAndAssign for Cfg {
198     fn bitand_assign(&mut self, other: Cfg) {
199         match (self, other) {
200             (&mut Cfg::False, _) | (_, Cfg::True) => {},
201             (s, Cfg::False) => *s = Cfg::False,
202             (s @ &mut Cfg::True, b) => *s = b,
203             (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => a.append(b),
204             (&mut Cfg::All(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
205             (s, Cfg::All(mut a)) => {
206                 let b = mem::replace(s, Cfg::True);
207                 a.push(b);
208                 *s = Cfg::All(a);
209             },
210             (s, b) => {
211                 let a = mem::replace(s, Cfg::True);
212                 *s = Cfg::All(vec![a, b]);
213             },
214         }
215     }
216 }
217
218 impl ops::BitAnd for Cfg {
219     type Output = Cfg;
220     fn bitand(mut self, other: Cfg) -> Cfg {
221         self &= other;
222         self
223     }
224 }
225
226 impl ops::BitOrAssign for Cfg {
227     fn bitor_assign(&mut self, other: Cfg) {
228         match (self, other) {
229             (&mut Cfg::True, _) | (_, Cfg::False) => {},
230             (s, Cfg::True) => *s = Cfg::True,
231             (s @ &mut Cfg::False, b) => *s = b,
232             (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => a.append(b),
233             (&mut Cfg::Any(ref mut a), ref mut b) => a.push(mem::replace(b, Cfg::True)),
234             (s, Cfg::Any(mut a)) => {
235                 let b = mem::replace(s, Cfg::True);
236                 a.push(b);
237                 *s = Cfg::Any(a);
238             },
239             (s, b) => {
240                 let a = mem::replace(s, Cfg::True);
241                 *s = Cfg::Any(vec![a, b]);
242             },
243         }
244     }
245 }
246
247 impl ops::BitOr for Cfg {
248     type Output = Cfg;
249     fn bitor(mut self, other: Cfg) -> Cfg {
250         self |= other;
251         self
252     }
253 }
254
255 struct Html<'a>(&'a Cfg);
256
257 fn write_with_opt_paren<T: fmt::Display>(
258     fmt: &mut fmt::Formatter,
259     has_paren: bool,
260     obj: T,
261 ) -> fmt::Result {
262     if has_paren {
263         fmt.write_char('(')?;
264     }
265     obj.fmt(fmt)?;
266     if has_paren {
267         fmt.write_char(')')?;
268     }
269     Ok(())
270 }
271
272
273 impl<'a> fmt::Display for Html<'a> {
274     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
275         match *self.0 {
276             Cfg::Not(ref child) => match **child {
277                 Cfg::Any(ref sub_cfgs) => {
278                     let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
279                         " nor "
280                     } else {
281                         ", nor "
282                     };
283                     for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
284                         fmt.write_str(if i == 0 { "neither " } else { separator })?;
285                         write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?;
286                     }
287                     Ok(())
288                 }
289                 ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Html(simple)),
290                 ref c => write!(fmt, "not ({})", Html(c)),
291             },
292
293             Cfg::Any(ref sub_cfgs) => {
294                 let separator = if sub_cfgs.iter().all(Cfg::is_simple) {
295                     " or "
296                 } else {
297                     ", or "
298                 };
299                 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
300                     if i != 0 {
301                         fmt.write_str(separator)?;
302                     }
303                     write_with_opt_paren(fmt, !sub_cfg.is_all(), Html(sub_cfg))?;
304                 }
305                 Ok(())
306             },
307
308             Cfg::All(ref sub_cfgs) => {
309                 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
310                     if i != 0 {
311                         fmt.write_str(" and ")?;
312                     }
313                     write_with_opt_paren(fmt, !sub_cfg.is_simple(), Html(sub_cfg))?;
314                 }
315                 Ok(())
316             },
317
318             Cfg::True => fmt.write_str("everywhere"),
319             Cfg::False => fmt.write_str("nowhere"),
320
321             Cfg::Cfg(name, value) => {
322                 let n = &*name.as_str();
323                 let human_readable = match (n, value) {
324                     ("unix", None) => "Unix",
325                     ("windows", None) => "Windows",
326                     ("debug_assertions", None) => "debug-assertions enabled",
327                     ("target_os", Some(os)) => match &*os.as_str() {
328                         "android" => "Android",
329                         "bitrig" => "Bitrig",
330                         "dragonfly" => "DragonFly BSD",
331                         "emscripten" => "Emscripten",
332                         "freebsd" => "FreeBSD",
333                         "fuchsia" => "Fuchsia",
334                         "haiku" => "Haiku",
335                         "ios" => "iOS",
336                         "l4re" => "L4Re",
337                         "linux" => "Linux",
338                         "macos" => "macOS",
339                         "netbsd" => "NetBSD",
340                         "openbsd" => "OpenBSD",
341                         "redox" => "Redox",
342                         "solaris" => "Solaris",
343                         "windows" => "Windows",
344                         _ => "",
345                     },
346                     ("target_arch", Some(arch)) => match &*arch.as_str() {
347                         "aarch64" => "AArch64",
348                         "arm" => "ARM",
349                         "asmjs" => "asm.js",
350                         "mips" => "MIPS",
351                         "mips64" => "MIPS-64",
352                         "msp430" => "MSP430",
353                         "powerpc" => "PowerPC",
354                         "powerpc64" => "PowerPC-64",
355                         "s390x" => "s390x",
356                         "sparc64" => "SPARC64",
357                         "wasm32" => "WebAssembly",
358                         "x86" => "x86",
359                         "x86_64" => "x86-64",
360                         _ => "",
361                     },
362                     ("target_vendor", Some(vendor)) => match &*vendor.as_str() {
363                         "apple" => "Apple",
364                         "pc" => "PC",
365                         "rumprun" => "Rumprun",
366                         "sun" => "Sun",
367                         _ => ""
368                     },
369                     ("target_env", Some(env)) => match &*env.as_str() {
370                         "gnu" => "GNU",
371                         "msvc" => "MSVC",
372                         "musl" => "musl",
373                         "newlib" => "Newlib",
374                         "uclibc" => "uClibc",
375                         _ => "",
376                     },
377                     ("target_endian", Some(endian)) => return write!(fmt, "{}-endian", endian),
378                     ("target_pointer_width", Some(bits)) => return write!(fmt, "{}-bit", bits),
379                     _ => "",
380                 };
381                 if !human_readable.is_empty() {
382                     fmt.write_str(human_readable)
383                 } else if let Some(v) = value {
384                     write!(fmt, "<code>{}=\"{}\"</code>", Escape(n), Escape(&*v.as_str()))
385                 } else {
386                     write!(fmt, "<code>{}</code>", Escape(n))
387                 }
388             }
389         }
390     }
391 }
392
393 #[cfg(test)]
394 mod test {
395     use super::Cfg;
396
397     use syntax::symbol::Symbol;
398     use syntax::ast::*;
399     use syntax::codemap::dummy_spanned;
400     use syntax_pos::DUMMY_SP;
401     use syntax::with_globals;
402
403     fn word_cfg(s: &str) -> Cfg {
404         Cfg::Cfg(Symbol::intern(s), None)
405     }
406
407     fn name_value_cfg(name: &str, value: &str) -> Cfg {
408         Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
409     }
410
411     #[test]
412     fn test_cfg_not() {
413         with_globals(|| {
414             assert_eq!(!Cfg::False, Cfg::True);
415             assert_eq!(!Cfg::True, Cfg::False);
416             assert_eq!(!word_cfg("test"), Cfg::Not(Box::new(word_cfg("test"))));
417             assert_eq!(
418                 !Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
419                 Cfg::Not(Box::new(Cfg::All(vec![word_cfg("a"), word_cfg("b")])))
420             );
421             assert_eq!(
422                 !Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
423                 Cfg::Not(Box::new(Cfg::Any(vec![word_cfg("a"), word_cfg("b")])))
424             );
425             assert_eq!(!Cfg::Not(Box::new(word_cfg("test"))), word_cfg("test"));
426         })
427     }
428
429     #[test]
430     fn test_cfg_and() {
431         with_globals(|| {
432             let mut x = Cfg::False;
433             x &= Cfg::True;
434             assert_eq!(x, Cfg::False);
435
436             x = word_cfg("test");
437             x &= Cfg::False;
438             assert_eq!(x, Cfg::False);
439
440             x = word_cfg("test2");
441             x &= Cfg::True;
442             assert_eq!(x, word_cfg("test2"));
443
444             x = Cfg::True;
445             x &= word_cfg("test3");
446             assert_eq!(x, word_cfg("test3"));
447
448             x &= word_cfg("test4");
449             assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4")]));
450
451             x &= word_cfg("test5");
452             assert_eq!(x, Cfg::All(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
453
454             x &= Cfg::All(vec![word_cfg("test6"), word_cfg("test7")]);
455             assert_eq!(x, Cfg::All(vec![
456                 word_cfg("test3"),
457                 word_cfg("test4"),
458                 word_cfg("test5"),
459                 word_cfg("test6"),
460                 word_cfg("test7"),
461             ]));
462
463             let mut y = Cfg::Any(vec![word_cfg("a"), word_cfg("b")]);
464             y &= x;
465             assert_eq!(y, Cfg::All(vec![
466                 word_cfg("test3"),
467                 word_cfg("test4"),
468                 word_cfg("test5"),
469                 word_cfg("test6"),
470                 word_cfg("test7"),
471                 Cfg::Any(vec![word_cfg("a"), word_cfg("b")]),
472             ]));
473
474             assert_eq!(
475                 word_cfg("a") & word_cfg("b") & word_cfg("c"),
476                 Cfg::All(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
477             );
478         })
479     }
480
481     #[test]
482     fn test_cfg_or() {
483         with_globals(|| {
484             let mut x = Cfg::True;
485             x |= Cfg::False;
486             assert_eq!(x, Cfg::True);
487
488             x = word_cfg("test");
489             x |= Cfg::True;
490             assert_eq!(x, Cfg::True);
491
492             x = word_cfg("test2");
493             x |= Cfg::False;
494             assert_eq!(x, word_cfg("test2"));
495
496             x = Cfg::False;
497             x |= word_cfg("test3");
498             assert_eq!(x, word_cfg("test3"));
499
500             x |= word_cfg("test4");
501             assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4")]));
502
503             x |= word_cfg("test5");
504             assert_eq!(x, Cfg::Any(vec![word_cfg("test3"), word_cfg("test4"), word_cfg("test5")]));
505
506             x |= Cfg::Any(vec![word_cfg("test6"), word_cfg("test7")]);
507             assert_eq!(x, Cfg::Any(vec![
508                 word_cfg("test3"),
509                 word_cfg("test4"),
510                 word_cfg("test5"),
511                 word_cfg("test6"),
512                 word_cfg("test7"),
513             ]));
514
515             let mut y = Cfg::All(vec![word_cfg("a"), word_cfg("b")]);
516             y |= x;
517             assert_eq!(y, Cfg::Any(vec![
518                 word_cfg("test3"),
519                 word_cfg("test4"),
520                 word_cfg("test5"),
521                 word_cfg("test6"),
522                 word_cfg("test7"),
523                 Cfg::All(vec![word_cfg("a"), word_cfg("b")]),
524             ]));
525
526             assert_eq!(
527                 word_cfg("a") | word_cfg("b") | word_cfg("c"),
528                 Cfg::Any(vec![word_cfg("a"), word_cfg("b"), word_cfg("c")])
529             );
530         })
531     }
532
533     #[test]
534     fn test_parse_ok() {
535         with_globals(|| {
536             let mi = MetaItem {
537                 name: Symbol::intern("all"),
538                 node: MetaItemKind::Word,
539                 span: DUMMY_SP,
540             };
541             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
542
543             let mi = MetaItem {
544                 name: Symbol::intern("all"),
545                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
546                     Symbol::intern("done"),
547                     StrStyle::Cooked,
548                 ))),
549                 span: DUMMY_SP,
550             };
551             assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
552
553             let mi = MetaItem {
554                 name: Symbol::intern("all"),
555                 node: MetaItemKind::List(vec![
556                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
557                         name: Symbol::intern("a"),
558                         node: MetaItemKind::Word,
559                         span: DUMMY_SP,
560                     })),
561                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
562                         name: Symbol::intern("b"),
563                         node: MetaItemKind::Word,
564                         span: DUMMY_SP,
565                     })),
566                 ]),
567                 span: DUMMY_SP,
568             };
569             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
570
571             let mi = MetaItem {
572                 name: Symbol::intern("any"),
573                 node: MetaItemKind::List(vec![
574                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
575                         name: Symbol::intern("a"),
576                         node: MetaItemKind::Word,
577                         span: DUMMY_SP,
578                     })),
579                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
580                         name: Symbol::intern("b"),
581                         node: MetaItemKind::Word,
582                         span: DUMMY_SP,
583                     })),
584                 ]),
585                 span: DUMMY_SP,
586             };
587             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
588
589             let mi = MetaItem {
590                 name: Symbol::intern("not"),
591                 node: MetaItemKind::List(vec![
592                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
593                         name: Symbol::intern("a"),
594                         node: MetaItemKind::Word,
595                         span: DUMMY_SP,
596                     })),
597                 ]),
598                 span: DUMMY_SP,
599             };
600             assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
601
602             let mi = MetaItem {
603                 name: Symbol::intern("not"),
604                 node: MetaItemKind::List(vec![
605                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
606                         name: Symbol::intern("any"),
607                         node: MetaItemKind::List(vec![
608                             dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
609                                 name: Symbol::intern("a"),
610                                 node: MetaItemKind::Word,
611                                 span: DUMMY_SP,
612                             })),
613                             dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
614                                 name: Symbol::intern("all"),
615                                 node: MetaItemKind::List(vec![
616                                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
617                                         name: Symbol::intern("b"),
618                                         node: MetaItemKind::Word,
619                                         span: DUMMY_SP,
620                                     })),
621                                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
622                                         name: Symbol::intern("c"),
623                                         node: MetaItemKind::Word,
624                                         span: DUMMY_SP,
625                                     })),
626                                 ]),
627                                 span: DUMMY_SP,
628                             })),
629                         ]),
630                         span: DUMMY_SP,
631                     })),
632                 ]),
633                 span: DUMMY_SP,
634             };
635             assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
636
637             let mi = MetaItem {
638                 name: Symbol::intern("all"),
639                 node: MetaItemKind::List(vec![
640                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
641                         name: Symbol::intern("a"),
642                         node: MetaItemKind::Word,
643                         span: DUMMY_SP,
644                     })),
645                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
646                         name: Symbol::intern("b"),
647                         node: MetaItemKind::Word,
648                         span: DUMMY_SP,
649                     })),
650                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
651                         name: Symbol::intern("c"),
652                         node: MetaItemKind::Word,
653                         span: DUMMY_SP,
654                     })),
655                 ]),
656                 span: DUMMY_SP,
657             };
658             assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c")));
659         })
660     }
661
662     #[test]
663     fn test_parse_err() {
664         with_globals(|| {
665             let mi = MetaItem {
666                 name: Symbol::intern("foo"),
667                 node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
668                 span: DUMMY_SP,
669             };
670             assert!(Cfg::parse(&mi).is_err());
671
672             let mi = MetaItem {
673                 name: Symbol::intern("not"),
674                 node: MetaItemKind::List(vec![
675                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
676                         name: Symbol::intern("a"),
677                         node: MetaItemKind::Word,
678                         span: DUMMY_SP,
679                     })),
680                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
681                         name: Symbol::intern("b"),
682                         node: MetaItemKind::Word,
683                         span: DUMMY_SP,
684                     })),
685                 ]),
686                 span: DUMMY_SP,
687             };
688             assert!(Cfg::parse(&mi).is_err());
689
690             let mi = MetaItem {
691                 name: Symbol::intern("not"),
692                 node: MetaItemKind::List(vec![]),
693                 span: DUMMY_SP,
694             };
695             assert!(Cfg::parse(&mi).is_err());
696
697             let mi = MetaItem {
698                 name: Symbol::intern("foo"),
699                 node: MetaItemKind::List(vec![
700                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
701                         name: Symbol::intern("a"),
702                         node: MetaItemKind::Word,
703                         span: DUMMY_SP,
704                     })),
705                 ]),
706                 span: DUMMY_SP,
707             };
708             assert!(Cfg::parse(&mi).is_err());
709
710             let mi = MetaItem {
711                 name: Symbol::intern("all"),
712                 node: MetaItemKind::List(vec![
713                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
714                         name: Symbol::intern("foo"),
715                         node: MetaItemKind::List(vec![]),
716                         span: DUMMY_SP,
717                     })),
718                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
719                         name: Symbol::intern("b"),
720                         node: MetaItemKind::Word,
721                         span: DUMMY_SP,
722                     })),
723                 ]),
724                 span: DUMMY_SP,
725             };
726             assert!(Cfg::parse(&mi).is_err());
727
728             let mi = MetaItem {
729                 name: Symbol::intern("any"),
730                 node: MetaItemKind::List(vec![
731                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
732                         name: Symbol::intern("a"),
733                         node: MetaItemKind::Word,
734                         span: DUMMY_SP,
735                     })),
736                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
737                         name: Symbol::intern("foo"),
738                         node: MetaItemKind::List(vec![]),
739                         span: DUMMY_SP,
740                     })),
741                 ]),
742                 span: DUMMY_SP,
743             };
744             assert!(Cfg::parse(&mi).is_err());
745
746             let mi = MetaItem {
747                 name: Symbol::intern("not"),
748                 node: MetaItemKind::List(vec![
749                     dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
750                         name: Symbol::intern("foo"),
751                         node: MetaItemKind::List(vec![]),
752                         span: DUMMY_SP,
753                     })),
754                 ]),
755                 span: DUMMY_SP,
756             };
757             assert!(Cfg::parse(&mi).is_err());
758         })
759     }
760
761     #[test]
762     fn test_render_short_html() {
763         with_globals(|| {
764             assert_eq!(
765                 word_cfg("unix").render_short_html(),
766                 "Unix"
767             );
768             assert_eq!(
769                 name_value_cfg("target_os", "macos").render_short_html(),
770                 "macOS"
771             );
772             assert_eq!(
773                 name_value_cfg("target_pointer_width", "16").render_short_html(),
774                 "16-bit"
775             );
776             assert_eq!(
777                 name_value_cfg("target_endian", "little").render_short_html(),
778                 "Little-endian"
779             );
780             assert_eq!(
781                 (!word_cfg("windows")).render_short_html(),
782                 "Non-Windows"
783             );
784             assert_eq!(
785                 (word_cfg("unix") & word_cfg("windows")).render_short_html(),
786                 "Unix and Windows"
787             );
788             assert_eq!(
789                 (word_cfg("unix") | word_cfg("windows")).render_short_html(),
790                 "Unix or Windows"
791             );
792             assert_eq!(
793                 (
794                     word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")
795                 ).render_short_html(),
796                 "Unix and Windows and debug-assertions enabled"
797             );
798             assert_eq!(
799                 (
800                     word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")
801                 ).render_short_html(),
802                 "Unix or Windows or debug-assertions enabled"
803             );
804             assert_eq!(
805                 (
806                     !(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))
807                 ).render_short_html(),
808                 "Neither Unix nor Windows nor debug-assertions enabled"
809             );
810             assert_eq!(
811                 (
812                     (word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) |
813                     (word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))
814                 ).render_short_html(),
815                 "Unix and x86-64, or Windows and 64-bit"
816             );
817             assert_eq!(
818                 (!(word_cfg("unix") & word_cfg("windows"))).render_short_html(),
819                 "Not (Unix and Windows)"
820             );
821             assert_eq!(
822                 (
823                     (word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")
824                 ).render_short_html(),
825                 "(Debug-assertions enabled or Windows) and Unix"
826             );
827         })
828     }
829
830     #[test]
831     fn test_render_long_html() {
832         with_globals(|| {
833             assert_eq!(
834                 word_cfg("unix").render_long_html(),
835                 "This is supported on <strong>Unix</strong> only."
836             );
837             assert_eq!(
838                 name_value_cfg("target_os", "macos").render_long_html(),
839                 "This is supported on <strong>macOS</strong> only."
840             );
841             assert_eq!(
842                 name_value_cfg("target_pointer_width", "16").render_long_html(),
843                 "This is supported on <strong>16-bit</strong> only."
844             );
845             assert_eq!(
846                 name_value_cfg("target_endian", "little").render_long_html(),
847                 "This is supported on <strong>little-endian</strong> only."
848             );
849             assert_eq!(
850                 (!word_cfg("windows")).render_long_html(),
851                 "This is supported on <strong>non-Windows</strong> only."
852             );
853             assert_eq!(
854                 (word_cfg("unix") & word_cfg("windows")).render_long_html(),
855                 "This is supported on <strong>Unix and Windows</strong> only."
856             );
857             assert_eq!(
858                 (word_cfg("unix") | word_cfg("windows")).render_long_html(),
859                 "This is supported on <strong>Unix or Windows</strong> only."
860             );
861             assert_eq!(
862                 (
863                     word_cfg("unix") & word_cfg("windows") & word_cfg("debug_assertions")
864                 ).render_long_html(),
865                 "This is supported on <strong>Unix and Windows and debug-assertions enabled\
866                  </strong> only."
867             );
868             assert_eq!(
869                 (
870                     word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions")
871                 ).render_long_html(),
872                 "This is supported on <strong>Unix or Windows or debug-assertions enabled\
873                  </strong> only."
874             );
875             assert_eq!(
876                 (
877                     !(word_cfg("unix") | word_cfg("windows") | word_cfg("debug_assertions"))
878                 ).render_long_html(),
879                 "This is supported on <strong>neither Unix nor Windows nor debug-assertions \
880                     enabled</strong>."
881             );
882             assert_eq!(
883                 (
884                     (word_cfg("unix") & name_value_cfg("target_arch", "x86_64")) |
885                     (word_cfg("windows") & name_value_cfg("target_pointer_width", "64"))
886                 ).render_long_html(),
887                 "This is supported on <strong>Unix and x86-64, or Windows and 64-bit</strong> \
888                  only."
889             );
890             assert_eq!(
891                 (!(word_cfg("unix") & word_cfg("windows"))).render_long_html(),
892                 "This is supported on <strong>not (Unix and Windows)</strong>."
893             );
894             assert_eq!(
895                 (
896                     (word_cfg("debug_assertions") | word_cfg("windows")) & word_cfg("unix")
897                 ).render_long_html(),
898                 "This is supported on <strong>(debug-assertions enabled or Windows) and Unix\
899                 </strong> only."
900             );
901         })
902     }
903 }