]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/clean/cfg.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / src / librustdoc / clean / cfg.rs
CommitLineData
9fa01778 1//! The representation of a `#[doc(cfg(...))]` attribute.
3b2f2976 2
0731742a
XL
3// FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
4// switch to use those structures instead.
3b2f2976 5
3b2f2976 6use std::fmt::{self, Write};
dfeec247 7use std::mem;
3b2f2976 8use std::ops;
3b2f2976 9
3dfed10e 10use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
60c5eb7d 11use rustc_feature::Features;
74b04a01 12use rustc_session::parse::ParseSess;
dfeec247 13use rustc_span::symbol::{sym, Symbol};
3b2f2976 14
dfeec247 15use rustc_span::Span;
3b2f2976 16
9fa01778 17use crate::html::escape::Escape;
3b2f2976 18
416331ca
XL
19#[cfg(test)]
20mod tests;
21
dc9dc135 22#[derive(Clone, Debug, PartialEq, Eq, Hash)]
fc512014 23crate enum Cfg {
3b2f2976
XL
24 /// Accepts all configurations.
25 True,
26 /// Denies all configurations.
27 False,
0731742a 28 /// A generic configuration option, e.g., `test` or `target_os = "linux"`.
3b2f2976 29 Cfg(Symbol, Option<Symbol>),
9fa01778 30 /// Negates a configuration requirement, i.e., `not(x)`.
3b2f2976 31 Not(Box<Cfg>),
0731742a 32 /// Union of a list of configuration requirements, i.e., `any(...)`.
3b2f2976 33 Any(Vec<Cfg>),
0731742a 34 /// Intersection of a list of configuration requirements, i.e., `all(...)`.
3b2f2976
XL
35 All(Vec<Cfg>),
36}
37
38#[derive(PartialEq, Debug)]
fc512014
XL
39crate struct InvalidCfgError {
40 crate msg: &'static str,
41 crate span: Span,
3b2f2976
XL
42}
43
44impl Cfg {
45 /// Parses a `NestedMetaItem` into a `Cfg`.
46 fn parse_nested(nested_cfg: &NestedMetaItem) -> Result<Cfg, InvalidCfgError> {
532ac7d7
XL
47 match nested_cfg {
48 NestedMetaItem::MetaItem(ref cfg) => Cfg::parse(cfg),
dfeec247
XL
49 NestedMetaItem::Literal(ref lit) => {
50 Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
51 }
3b2f2976
XL
52 }
53 }
54
55 /// Parses a `MetaItem` into a `Cfg`.
56 ///
0731742a 57 /// The `MetaItem` should be the content of the `#[cfg(...)]`, e.g., `unix` or
3b2f2976
XL
58 /// `target_os = "redox"`.
59 ///
60 /// If the content is not properly formatted, it will return an error indicating what and where
61 /// the error is.
fc512014 62 crate fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
9fa01778
XL
63 let name = match cfg.ident() {
64 Some(ident) => ident.name,
dfeec247
XL
65 None => {
66 return Err(InvalidCfgError {
67 msg: "expected a single identifier",
68 span: cfg.span,
69 });
70 }
9fa01778 71 };
e74abb32 72 match cfg.kind {
3b2f2976 73 MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
e74abb32 74 MetaItemKind::NameValue(ref lit) => match lit.kind {
3b2f2976
XL
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",
80 span: lit.span,
81 }),
82 },
83 MetaItemKind::List(ref items) => {
84 let mut sub_cfgs = items.iter().map(Cfg::parse_nested);
e1599b0c
XL
85 match name {
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?)),
dfeec247
XL
88 sym::not => {
89 if sub_cfgs.len() == 1 {
90 Ok(!sub_cfgs.next().unwrap()?)
91 } else {
92 Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span })
93 }
94 }
95 _ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }),
3b2f2976
XL
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`.
fc512014 105 crate fn matches(&self, parse_sess: &ParseSess, features: Option<&Features>) -> bool {
3b2f2976
XL
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))
dfeec247 112 }
3b2f2976
XL
113 Cfg::Any(ref sub_cfgs) => {
114 sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(parse_sess, features))
dfeec247 115 }
3b2f2976
XL
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 {
1b1a35ee 138 let mut msg = Display(self, Format::ShortHtml).to_string();
3b2f2976
XL
139 if self.should_capitalize_first_letter() {
140 if let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric()) {
dfeec247 141 msg[i..i + 1].make_ascii_uppercase();
3b2f2976
XL
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 {
dfeec247 149 let on = if self.should_use_with_in_description() { "with" } else { "on" };
0531ce1d 150
1b1a35ee
XL
151 let mut msg = format!(
152 "This is supported {} <strong>{}</strong>",
153 on,
154 Display(self, Format::LongHtml)
155 );
3b2f2976
XL
156 if self.should_append_only_to_description() {
157 msg.push_str(" only");
158 }
159 msg.push('.');
160 msg
161 }
162
1b1a35ee
XL
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" };
166
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");
170 }
171 msg
172 }
173
3b2f2976
XL
174 fn should_capitalize_first_letter(&self) -> bool {
175 match *self {
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)
dfeec247 179 }
3dfed10e
XL
180 Cfg::Cfg(name, _) => match name {
181 sym::debug_assertions | sym::target_endian => true,
3b2f2976
XL
182 _ => false,
183 },
184 }
185 }
186
187 fn should_append_only_to_description(&self) -> bool {
188 match *self {
189 Cfg::False | Cfg::True => false,
190 Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
191 Cfg::Not(ref child) => match **child {
192 Cfg::Cfg(..) => true,
193 _ => false,
dfeec247 194 },
3b2f2976
XL
195 }
196 }
0531ce1d
XL
197
198 fn should_use_with_in_description(&self) -> bool {
199 match *self {
48663c56 200 Cfg::Cfg(name, _) if name == sym::target_feature => true,
0531ce1d
XL
201 _ => false,
202 }
203 }
29967ef6
XL
204
205 /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will
206 /// return `None` if simplification managed to completely eliminate any requirements from this
207 /// `Cfg`.
208 ///
209 /// See `tests::test_simplify_with` for examples.
210 pub(crate) fn simplify_with(&self, assume: &Cfg) -> Option<Cfg> {
211 if self == assume {
212 return None;
213 }
214
215 if let Cfg::All(a) = self {
216 let mut sub_cfgs: Vec<Cfg> = if let Cfg::All(b) = assume {
217 a.iter().filter(|a| !b.contains(a)).cloned().collect()
218 } else {
219 a.iter().filter(|&a| a != assume).cloned().collect()
220 };
221 let len = sub_cfgs.len();
222 return match len {
223 0 => None,
224 1 => sub_cfgs.pop(),
225 _ => Some(Cfg::All(sub_cfgs)),
226 };
227 } else if let Cfg::All(b) = assume {
228 if b.contains(self) {
229 return None;
230 }
231 }
232
233 Some(self.clone())
234 }
3b2f2976
XL
235}
236
237impl ops::Not for Cfg {
238 type Output = Cfg;
239 fn not(self) -> Cfg {
240 match self {
241 Cfg::False => Cfg::True,
242 Cfg::True => Cfg::False,
243 Cfg::Not(cfg) => *cfg,
244 s => Cfg::Not(Box::new(s)),
245 }
246 }
247}
248
249impl ops::BitAndAssign for Cfg {
250 fn bitand_assign(&mut self, other: Cfg) {
251 match (self, other) {
dfeec247 252 (&mut Cfg::False, _) | (_, Cfg::True) => {}
3b2f2976
XL
253 (s, Cfg::False) => *s = Cfg::False,
254 (s @ &mut Cfg::True, b) => *s = b,
dfeec247
XL
255 (&mut Cfg::All(ref mut a), Cfg::All(ref mut b)) => {
256 for c in b.drain(..) {
257 if !a.contains(&c) {
258 a.push(c);
259 }
260 }
261 }
262 (&mut Cfg::All(ref mut a), ref mut b) => {
263 if !a.contains(b) {
264 a.push(mem::replace(b, Cfg::True));
265 }
266 }
3b2f2976
XL
267 (s, Cfg::All(mut a)) => {
268 let b = mem::replace(s, Cfg::True);
dfeec247
XL
269 if !a.contains(&b) {
270 a.push(b);
271 }
3b2f2976 272 *s = Cfg::All(a);
dfeec247 273 }
3b2f2976 274 (s, b) => {
dfeec247
XL
275 if *s != b {
276 let a = mem::replace(s, Cfg::True);
277 *s = Cfg::All(vec![a, b]);
278 }
279 }
3b2f2976
XL
280 }
281 }
282}
283
284impl ops::BitAnd for Cfg {
285 type Output = Cfg;
286 fn bitand(mut self, other: Cfg) -> Cfg {
287 self &= other;
288 self
289 }
290}
291
292impl ops::BitOrAssign for Cfg {
293 fn bitor_assign(&mut self, other: Cfg) {
294 match (self, other) {
dfeec247 295 (&mut Cfg::True, _) | (_, Cfg::False) => {}
3b2f2976
XL
296 (s, Cfg::True) => *s = Cfg::True,
297 (s @ &mut Cfg::False, b) => *s = b,
dfeec247
XL
298 (&mut Cfg::Any(ref mut a), Cfg::Any(ref mut b)) => {
299 for c in b.drain(..) {
300 if !a.contains(&c) {
301 a.push(c);
302 }
303 }
304 }
305 (&mut Cfg::Any(ref mut a), ref mut b) => {
306 if !a.contains(b) {
307 a.push(mem::replace(b, Cfg::True));
308 }
309 }
3b2f2976
XL
310 (s, Cfg::Any(mut a)) => {
311 let b = mem::replace(s, Cfg::True);
dfeec247
XL
312 if !a.contains(&b) {
313 a.push(b);
314 }
3b2f2976 315 *s = Cfg::Any(a);
dfeec247 316 }
3b2f2976 317 (s, b) => {
dfeec247
XL
318 if *s != b {
319 let a = mem::replace(s, Cfg::True);
320 *s = Cfg::Any(vec![a, b]);
321 }
322 }
3b2f2976
XL
323 }
324 }
325}
326
327impl ops::BitOr for Cfg {
328 type Output = Cfg;
329 fn bitor(mut self, other: Cfg) -> Cfg {
330 self |= other;
331 self
332 }
333}
334
1b1a35ee
XL
335#[derive(Clone, Copy)]
336enum Format {
337 LongHtml,
338 LongPlain,
339 ShortHtml,
340}
341
342impl Format {
343 fn is_long(self) -> bool {
344 match self {
345 Format::LongHtml | Format::LongPlain => true,
346 Format::ShortHtml => false,
347 }
348 }
349
350 fn is_html(self) -> bool {
351 match self {
352 Format::LongHtml | Format::ShortHtml => true,
353 Format::LongPlain => false,
354 }
355 }
356}
357
358/// Pretty-print wrapper for a `Cfg`. Also indicates what form of rendering should be used.
359struct Display<'a>(&'a Cfg, Format);
3b2f2976
XL
360
361fn write_with_opt_paren<T: fmt::Display>(
9fa01778 362 fmt: &mut fmt::Formatter<'_>,
3b2f2976
XL
363 has_paren: bool,
364 obj: T,
365) -> fmt::Result {
366 if has_paren {
367 fmt.write_char('(')?;
368 }
369 obj.fmt(fmt)?;
370 if has_paren {
371 fmt.write_char(')')?;
372 }
373 Ok(())
374}
375
1b1a35ee 376impl<'a> fmt::Display for Display<'a> {
9fa01778 377 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
3b2f2976
XL
378 match *self.0 {
379 Cfg::Not(ref child) => match **child {
380 Cfg::Any(ref sub_cfgs) => {
dfeec247
XL
381 let separator =
382 if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
3b2f2976
XL
383 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
384 fmt.write_str(if i == 0 { "neither " } else { separator })?;
1b1a35ee 385 write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
3b2f2976
XL
386 }
387 Ok(())
388 }
1b1a35ee
XL
389 ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)),
390 ref c => write!(fmt, "not ({})", Display(c, self.1)),
3b2f2976
XL
391 },
392
393 Cfg::Any(ref sub_cfgs) => {
dfeec247 394 let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " };
1b1a35ee
XL
395
396 let short_longhand = self.1.is_long() && {
397 let all_crate_features = sub_cfgs
398 .iter()
399 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
400 let all_target_features = sub_cfgs
401 .iter()
402 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
403
404 if all_crate_features {
405 fmt.write_str("crate features ")?;
406 true
407 } else if all_target_features {
408 fmt.write_str("target features ")?;
409 true
410 } else {
411 false
412 }
413 };
414
3b2f2976
XL
415 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
416 if i != 0 {
417 fmt.write_str(separator)?;
418 }
1b1a35ee
XL
419 if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
420 if self.1.is_html() {
421 write!(fmt, "<code>{}</code>", feat)?;
422 } else {
423 write!(fmt, "`{}`", feat)?;
424 }
425 } else {
426 write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?;
427 }
3b2f2976
XL
428 }
429 Ok(())
dfeec247 430 }
3b2f2976
XL
431
432 Cfg::All(ref sub_cfgs) => {
1b1a35ee
XL
433 let short_longhand = self.1.is_long() && {
434 let all_crate_features = sub_cfgs
435 .iter()
436 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
437 let all_target_features = sub_cfgs
438 .iter()
439 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
440
441 if all_crate_features {
442 fmt.write_str("crate features ")?;
443 true
444 } else if all_target_features {
445 fmt.write_str("target features ")?;
446 true
447 } else {
448 false
449 }
450 };
451
3b2f2976
XL
452 for (i, sub_cfg) in sub_cfgs.iter().enumerate() {
453 if i != 0 {
454 fmt.write_str(" and ")?;
455 }
1b1a35ee
XL
456 if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) {
457 if self.1.is_html() {
458 write!(fmt, "<code>{}</code>", feat)?;
459 } else {
460 write!(fmt, "`{}`", feat)?;
461 }
462 } else {
463 write_with_opt_paren(fmt, !sub_cfg.is_simple(), Display(sub_cfg, self.1))?;
464 }
3b2f2976
XL
465 }
466 Ok(())
dfeec247 467 }
3b2f2976
XL
468
469 Cfg::True => fmt.write_str("everywhere"),
470 Cfg::False => fmt.write_str("nowhere"),
471
472 Cfg::Cfg(name, value) => {
3dfed10e
XL
473 let human_readable = match (name, value) {
474 (sym::unix, None) => "Unix",
475 (sym::windows, None) => "Windows",
476 (sym::debug_assertions, None) => "debug-assertions enabled",
477 (sym::target_os, Some(os)) => match &*os.as_str() {
3b2f2976 478 "android" => "Android",
3b2f2976
XL
479 "dragonfly" => "DragonFly BSD",
480 "emscripten" => "Emscripten",
481 "freebsd" => "FreeBSD",
482 "fuchsia" => "Fuchsia",
483 "haiku" => "Haiku",
e74abb32 484 "hermit" => "HermitCore",
ba9703b0 485 "illumos" => "illumos",
3b2f2976
XL
486 "ios" => "iOS",
487 "l4re" => "L4Re",
488 "linux" => "Linux",
489 "macos" => "macOS",
3b2f2976
XL
490 "netbsd" => "NetBSD",
491 "openbsd" => "OpenBSD",
492 "redox" => "Redox",
493 "solaris" => "Solaris",
494 "windows" => "Windows",
495 _ => "",
496 },
3dfed10e 497 (sym::target_arch, Some(arch)) => match &*arch.as_str() {
3b2f2976
XL
498 "aarch64" => "AArch64",
499 "arm" => "ARM",
e74abb32 500 "asmjs" => "JavaScript",
3b2f2976
XL
501 "mips" => "MIPS",
502 "mips64" => "MIPS-64",
503 "msp430" => "MSP430",
504 "powerpc" => "PowerPC",
505 "powerpc64" => "PowerPC-64",
506 "s390x" => "s390x",
507 "sparc64" => "SPARC64",
508 "wasm32" => "WebAssembly",
509 "x86" => "x86",
510 "x86_64" => "x86-64",
511 _ => "",
512 },
3dfed10e 513 (sym::target_vendor, Some(vendor)) => match &*vendor.as_str() {
3b2f2976
XL
514 "apple" => "Apple",
515 "pc" => "PC",
516 "rumprun" => "Rumprun",
517 "sun" => "Sun",
0731742a 518 "fortanix" => "Fortanix",
dfeec247 519 _ => "",
3b2f2976 520 },
3dfed10e 521 (sym::target_env, Some(env)) => match &*env.as_str() {
3b2f2976
XL
522 "gnu" => "GNU",
523 "msvc" => "MSVC",
524 "musl" => "musl",
525 "newlib" => "Newlib",
526 "uclibc" => "uClibc",
0731742a 527 "sgx" => "SGX",
3b2f2976
XL
528 _ => "",
529 },
3dfed10e
XL
530 (sym::target_endian, Some(endian)) => return write!(fmt, "{}-endian", endian),
531 (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{}-bit", bits),
1b1a35ee
XL
532 (sym::target_feature, Some(feat)) => match self.1 {
533 Format::LongHtml => {
94b46f34 534 return write!(fmt, "target feature <code>{}</code>", feat);
dfeec247 535 }
1b1a35ee
XL
536 Format::LongPlain => return write!(fmt, "target feature `{}`", feat),
537 Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat),
538 },
539 (sym::feature, Some(feat)) => match self.1 {
540 Format::LongHtml => {
541 return write!(fmt, "crate feature <code>{}</code>", feat);
542 }
543 Format::LongPlain => return write!(fmt, "crate feature `{}`", feat),
544 Format::ShortHtml => return write!(fmt, "<code>{}</code>", feat),
545 },
3b2f2976
XL
546 _ => "",
547 };
548 if !human_readable.is_empty() {
549 fmt.write_str(human_readable)
550 } else if let Some(v) = value {
1b1a35ee
XL
551 if self.1.is_html() {
552 write!(
553 fmt,
554 r#"<code>{}="{}"</code>"#,
555 Escape(&name.as_str()),
556 Escape(&v.as_str())
557 )
558 } else {
559 write!(fmt, r#"`{}="{}"`"#, name, v)
560 }
561 } else if self.1.is_html() {
3dfed10e 562 write!(fmt, "<code>{}</code>", Escape(&name.as_str()))
1b1a35ee
XL
563 } else {
564 write!(fmt, "`{}`", name)
3b2f2976
XL
565 }
566 }
567 }
568 }
569}