1 //! The representation of a `#[doc(cfg(...))]` attribute.
3 // FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
4 // switch to use those structures instead.
6 use std::fmt::{self, Write};
10 use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
11 use rustc_feature::Features;
12 use rustc_session::parse::ParseSess;
13 use rustc_span::symbol::{sym, Symbol};
17 use crate::html::escape::Escape;
22 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
24 /// Accepts all configurations.
26 /// Denies all configurations.
28 /// A generic configuration option, e.g., `test` or `target_os = "linux"`.
29 Cfg(Symbol, Option<Symbol>),
30 /// Negates a configuration requirement, i.e., `not(x)`.
32 /// Union of a list of configuration requirements, i.e., `any(...)`.
34 /// Intersection of a list of configuration requirements, i.e., `all(...)`.
38 #[derive(PartialEq, Debug)]
39 crate struct InvalidCfgError {
40 crate msg: &'static str,
45 /// Parses a `NestedMetaItem` into a `Cfg`.
46 fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
48 NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg),
49 NestedMetaItem::Literal(ref lit) => {
50 Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
55 /// Parses a `MetaItem` into a `Cfg`.
57 /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or
58 /// `target_os = "redox"`.
60 /// If the content is not properly formatted, it will return an error indicating what and where
62 crate fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
63 let name = match cfg.ident() {
64 Some(ident) => ident.name,
66 return Err(InvalidCfgError {
67 msg: "expected a single identifier",
73 MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
74 MetaItemKind::NameValue(ref lit) => match lit.kind {
75 LitKind::Str(value, _) => Ok(Cfg::Cfg(name, Some(value))),
76 _ => Err(InvalidCfgError {
77 // FIXME: if the main #[cfg] syntax decided to support non-string literals,
78 // this should be changed as well.
79 msg: "value of cfg option should be a string literal",
83 MetaItemKind::List(ref items) => {
84 let mut sub_cfgs = items.iter().map(Cfg::parse_nested);
86 sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)),
87 sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)),
89 if sub_cfgs.len() == 1 {
90 Ok(!sub_cfgs.next().unwrap()?)
92 Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span })
95 _ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }),
101 /// Checks whether the given configuration can be matched in the current session.
103 /// Equivalent to `attr::cfg_matches`.
104 // FIXME: Actually make use of `features`.
105 crate fn matches(&self, parse_sess: &ParseSess, features: Option<&Features>) -> bool {
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))
113 Cfg::Any(ref sub_cfgs) => {
114 sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(parse_sess, features))
116 Cfg::Cfg(name, value) => parse_sess.config.contains(&(name, value)),
120 /// Whether the configuration consists of just `Cfg` or `Not`.
121 fn is_simple(&self) -> bool {
123 Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true,
124 Cfg::All(..) | Cfg::Any(..) => false,
128 /// Whether the configuration consists of just `Cfg`, `Not` or `All`.
129 fn is_all(&self) -> bool {
131 Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true,
132 Cfg::Any(..) => false,
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 = Display(self, Format::ShortHtml).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();
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() { "with" } else { "on" };
151 let mut msg = format!(
152 "This is supported {} <strong>{}</strong>",
154 Display(self, Format::LongHtml)
156 if self.should_append_only_to_description() {
157 msg.push_str(" only");
163 /// Renders the configuration for long display, as a long plain text description.
164 pub(crate) fn render_long_plain(&self) -> String {
165 let on = if self.should_use_with_in_description() { "with" } else { "on" };
167 let mut msg = format!("This is supported {} {}", on, Display(self, Format::LongPlain));
168 if self.should_append_only_to_description() {
169 msg.push_str(" only");
174 fn should_capitalize_first_letter(&self) -> bool {
176 Cfg::False | Cfg::True | Cfg::Not(..) => true,
177 Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => {
178 sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false)
180 Cfg::Cfg(name, _) => name == sym::debug_assertions || name == sym::target_endian,
184 fn should_append_only_to_description(&self) -> bool {
186 Cfg::False | Cfg::True => false,
187 Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
188 Cfg::Not(box Cfg::Cfg(..)) => true,
189 Cfg::Not(..) => false,
193 fn should_use_with_in_description(&self) -> bool {
194 matches!(self, Cfg::Cfg(sym::target_feature, _))
197 /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will
198 /// return `None` if simplification managed to completely eliminate any requirements from this
201 /// See `tests::test_simplify_with` for examples.
202 pub(crate) fn simplify_with(&self, assume: &Cfg) -> Option<Cfg> {
207 if let Cfg::All(a) = self {
208 let mut sub_cfgs: Vec<Cfg> = if let Cfg::All(b) = assume {
209 a.iter().filter(|a| !b.contains(a)).cloned().collect()
211 a.iter().filter(|&a| a != assume).cloned().collect()
213 let len = sub_cfgs.len();
217 _ => Some(Cfg::All(sub_cfgs)),
219 } else if let Cfg::All(b) = assume {
220 if b.contains(self) {
229 impl ops::Not for Cfg {
231 fn not(self) -> Cfg {
233 Cfg::False => Cfg::True,
234 Cfg::True => Cfg::False,
235 Cfg::Not(cfg) => *cfg,
236 s => Cfg::Not(Box::new(s)),
241 impl ops::BitAndAssign for Cfg {
242 fn bitand_assign(&mut self, other: Cfg) {
243 match (self, other) {
244 (&mut Cfg::False, _) | (_, Cfg::True) => {}
245 (s, Cfg::False) => *s = Cfg::False,
246 (s @ &mut Cfg::True, b) => *s = b,
247 (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => {
248 for c in b.drain(..) {
254 (&mut Cfg::All(ref mut a), ref mut b) => {
256 a.push(mem::replace(b, Cfg::True));
259 (s, Cfg::All(mut a)) => {
260 let b = mem::replace(s, Cfg::True);
268 let a = mem::replace(s, Cfg::True);
269 *s = Cfg::All(vec![a, b]);
276 impl ops::BitAnd for Cfg {
278 fn bitand(mut self, other: Cfg) -> Cfg {
284 impl ops::BitOrAssign for Cfg {
285 fn bitor_assign(&mut self, other: Cfg) {
286 match (self, other) {
287 (&mut Cfg::True, _) | (_, Cfg::False) => {}
288 (s, Cfg::True) => *s = Cfg::True,
289 (s @ &mut Cfg::False, b) => *s = b,
290 (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => {
291 for c in b.drain(..) {
297 (&mut Cfg::Any(ref mut a), ref mut b) => {
299 a.push(mem::replace(b, Cfg::True));
302 (s, Cfg::Any(mut a)) => {
303 let b = mem::replace(s, Cfg::True);
311 let a = mem::replace(s, Cfg::True);
312 *s = Cfg::Any(vec![a, b]);
319 impl ops::BitOr for Cfg {
321 fn bitor(mut self, other: Cfg) -> Cfg {
327 #[derive(Clone, Copy)]
335 fn is_long(self) -> bool {
337 Format::LongHtml | Format::LongPlain => true,
338 Format::ShortHtml => false,
342 fn is_html(self) -> bool {
344 Format::LongHtml | Format::ShortHtml => true,
345 Format::LongPlain => false,
350 /// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used.
351 struct Display<'a>(&'a Cfg, Format);
353 fn write_with_opt_paren<T: fmt::Display>(
354 fmt: &mut fmt::Formatter<'_>,
359 fmt.write_char('(')?;
363 fmt.write_char(')')?;
368 impl<'a> fmt::Display for Display<'a> {
369 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
371 Cfg::Not(ref child) => match **child {
372 Cfg::Any(ref sub_cfgs) => {
374 if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
375 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
376 fmt.write_str(if i == 0 { "neither " } else { separator })?;
377 write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
381 ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)),
382 ref c => write!(fmt, "not ({})", Display(c, self.1)),
385 Cfg::Any(ref sub_cfgs) => {
386 let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " };
388 let short_longhand = self.1.is_long() && {
389 let all_crate_features = sub_cfgs
391 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
392 let all_target_features = sub_cfgs
394 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
396 if all_crate_features {
397 fmt.write_str("crate features ")?;
399 } else if all_target_features {
400 fmt.write_str("target features ")?;
407 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
409 fmt.write_str(separator)?;
411 if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
412 if self.1.is_html() {
413 write!(fmt, "<code>{}</code>", feat)?;
415 write!(fmt, "`{}`", feat)?;
418 write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
424 Cfg::All(ref sub_cfgs) => {
425 let short_longhand = self.1.is_long() && {
426 let all_crate_features = sub_cfgs
428 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
429 let all_target_features = sub_cfgs
431 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
433 if all_crate_features {
434 fmt.write_str("crate features ")?;
436 } else if all_target_features {
437 fmt.write_str("target features ")?;
444 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
446 fmt.write_str(" and ")?;
448 if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
449 if self.1.is_html() {
450 write!(fmt, "<code>{}</code>", feat)?;
452 write!(fmt, "`{}`", feat)?;
455 write_with_opt_paren(fmt, !sub_cfg.is_simple(), Display(sub_cfg, self.1))?;
461 Cfg::True => fmt.write_str("everywhere"),
462 Cfg::False => fmt.write_str("nowhere"),
464 Cfg::Cfg(name, value) => {
465 let human_readable = match (name, value) {
466 (sym::unix, None) => "Unix",
467 (sym::windows, None) => "Windows",
468 (sym::debug_assertions, None) => "debug-assertions enabled",
469 (sym::target_os, Some(os)) => match &*os.as_str() {
470 "android" => "Android",
471 "dragonfly" => "DragonFly BSD",
472 "emscripten" => "Emscripten",
473 "freebsd" => "FreeBSD",
474 "fuchsia" => "Fuchsia",
476 "hermit" => "HermitCore",
477 "illumos" => "illumos",
482 "netbsd" => "NetBSD",
483 "openbsd" => "OpenBSD",
485 "solaris" => "Solaris",
487 "windows" => "Windows",
490 (sym::target_arch, Some(arch)) => match &*arch.as_str() {
491 "aarch64" => "AArch64",
493 "asmjs" => "JavaScript",
495 "mips64" => "MIPS-64",
496 "msp430" => "MSP430",
497 "powerpc" => "PowerPC",
498 "powerpc64" => "PowerPC-64",
500 "sparc64" => "SPARC64",
501 "wasm32" | "wasm64" => "WebAssembly",
503 "x86_64" => "x86-64",
506 (sym::target_vendor, Some(vendor)) => match &*vendor.as_str() {
510 "fortanix" => "Fortanix",
513 (sym::target_env, Some(env)) => match &*env.as_str() {
517 "newlib" => "Newlib",
518 "uclibc" => "uClibc",
522 (sym::target_endian, Some(endian)) => return write!(fmt, "{}-endian", endian),
523 (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{}-bit", bits),
524 (sym::target_feature, Some(feat)) => match self.1 {
525 Format::LongHtml => {
526 return write!(fmt, "target feature <code>{}</code>", feat);
528 Format::LongPlain => return write!(fmt, "target feature `{}`", feat),
529 Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat),
531 (sym::feature, Some(feat)) => match self.1 {
532 Format::LongHtml => {
533 return write!(fmt, "crate feature <code>{}</code>", feat);
535 Format::LongPlain => return write!(fmt, "crate feature `{}`", feat),
536 Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat),
540 if !human_readable.is_empty() {
541 fmt.write_str(human_readable)
542 } else if let Some(v) = value {
543 if self.1.is_html() {
546 r#"<code>{}="{}"</code>"#,
547 Escape(&name.as_str()),
551 write!(fmt, r#"`{}="{}"`"#, name, v)
553 } else if self.1.is_html() {
554 write!(fmt, "<code>{}</code>", Escape(&name.as_str()))
556 write!(fmt, "`{}`", name)