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