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