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