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