1use std::str::FromStr;
7use std::sync::Arc;
8use std::{fmt, mem, ops};
9
10use itertools::Either;
11use rustc_data_structures::fx::{FxHashMap, FxHashSet};
12use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
13use rustc_hir as hir;
14use rustc_hir::Attribute;
15use rustc_hir::attrs::{self, AttributeKind, CfgEntry, CfgHideShow, HideOrShow};
16use rustc_middle::ty::TyCtxt;
17use rustc_span::symbol::{Symbol, sym};
18use rustc_span::{DUMMY_SP, Span};
19use rustc_target::spec;
20
21use crate::display::{Joined as _, MaybeDisplay, Wrapped};
22use crate::html::escape::Escape;
23
24#[cfg(test)]
25mod tests;
26
27#[derive(Clone, Debug, Hash)]
28#[cfg_attr(test, derive(PartialEq))]
31pub(crate) struct Cfg(CfgEntry);
32
33fn is_simple_cfg(cfg: &CfgEntry) -> bool {
35 match cfg {
36 CfgEntry::Bool(..)
37 | CfgEntry::NameValue { .. }
38 | CfgEntry::Not(..)
39 | CfgEntry::Version(..) => true,
40 CfgEntry::All(..) | CfgEntry::Any(..) => false,
41 }
42}
43
44fn is_any_cfg(cfg: &CfgEntry) -> bool {
46 match cfg {
47 CfgEntry::Bool(..)
48 | CfgEntry::NameValue { .. }
49 | CfgEntry::Not(..)
50 | CfgEntry::Version(..)
51 | CfgEntry::All(..) => false,
52 CfgEntry::Any(..) => true,
53 }
54}
55
56fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashSet<NameValueCfg>) -> Option<CfgEntry> {
57 match cfg {
58 CfgEntry::Bool(..) => Some(cfg.clone()),
59 CfgEntry::NameValue { .. } => {
60 if !hidden.contains(&NameValueCfg::from(cfg)) {
61 Some(cfg.clone())
62 } else {
63 None
64 }
65 }
66 CfgEntry::Not(cfg, _) => {
67 if let Some(cfg) = strip_hidden(cfg, hidden) {
68 Some(CfgEntry::Not(Box::new(cfg), DUMMY_SP))
69 } else {
70 None
71 }
72 }
73 CfgEntry::Any(cfgs, _) => {
74 let cfgs =
75 cfgs.iter().filter_map(|cfg| strip_hidden(cfg, hidden)).collect::<ThinVec<_>>();
76 if cfgs.is_empty() { None } else { Some(CfgEntry::Any(cfgs, DUMMY_SP)) }
77 }
78 CfgEntry::All(cfgs, _) => {
79 let cfgs =
80 cfgs.iter().filter_map(|cfg| strip_hidden(cfg, hidden)).collect::<ThinVec<_>>();
81 if cfgs.is_empty() { None } else { Some(CfgEntry::All(cfgs, DUMMY_SP)) }
82 }
83 CfgEntry::Version(..) => {
84 Some(cfg.clone())
86 }
87 }
88}
89
90fn should_capitalize_first_letter(cfg: &CfgEntry) -> bool {
91 match cfg {
92 CfgEntry::Bool(..) | CfgEntry::Not(..) | CfgEntry::Version(..) => true,
93 CfgEntry::Any(sub_cfgs, _) | CfgEntry::All(sub_cfgs, _) => {
94 sub_cfgs.first().map(should_capitalize_first_letter).unwrap_or(false)
95 }
96 CfgEntry::NameValue { name, .. } => {
97 *name == sym::debug_assertions || *name == sym::target_endian
98 }
99 }
100}
101
102impl Cfg {
103 pub(crate) fn render_short_html(&self) -> String {
105 let mut msg = Display(&self.0, Format::ShortHtml).to_string();
106 if should_capitalize_first_letter(&self.0)
107 && let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric())
108 {
109 msg[i..i + 1].make_ascii_uppercase();
110 }
111 msg
112 }
113
114 fn render_long_inner(&self, format: Format) -> String {
115 let on = if self.omit_preposition() {
116 " "
117 } else if self.should_use_with_in_description() {
118 " with "
119 } else {
120 " on "
121 };
122
123 let mut msg = if matches!(format, Format::LongHtml) {
124 format!("Available{on}<strong>{}</strong>", Display(&self.0, format))
125 } else {
126 format!("Available{on}{}", Display(&self.0, format))
127 };
128 if self.should_append_only_to_description() {
129 msg.push_str(" only");
130 }
131 msg
132 }
133
134 pub(crate) fn render_long_html(&self) -> String {
136 let mut msg = self.render_long_inner(Format::LongHtml);
137 msg.push('.');
138 msg
139 }
140
141 pub(crate) fn render_long_plain(&self) -> String {
143 self.render_long_inner(Format::LongPlain)
144 }
145
146 fn should_append_only_to_description(&self) -> bool {
147 match self.0 {
148 CfgEntry::Any(..)
149 | CfgEntry::All(..)
150 | CfgEntry::NameValue { .. }
151 | CfgEntry::Version(..)
152 | CfgEntry::Not(CfgEntry::NameValue { .. }, _) => true,
153 CfgEntry::Not(..) | CfgEntry::Bool(..) => false,
154 }
155 }
156
157 fn should_use_with_in_description(&self) -> bool {
158 matches!(self.0, CfgEntry::NameValue { name, .. } if name == sym::target_feature)
159 }
160
161 pub(crate) fn simplify_with(&self, assume: &Self) -> Option<Self> {
167 if self.0.is_equivalent_to(&assume.0) {
168 None
169 } else if let CfgEntry::All(a, _) = &self.0 {
170 let mut sub_cfgs: ThinVec<CfgEntry> = if let CfgEntry::All(b, _) = &assume.0 {
171 a.iter().filter(|a| !b.iter().any(|b| a.is_equivalent_to(b))).cloned().collect()
172 } else {
173 a.iter().filter(|&a| !a.is_equivalent_to(&assume.0)).cloned().collect()
174 };
175 let len = sub_cfgs.len();
176 match len {
177 0 => None,
178 1 => sub_cfgs.pop().map(Cfg),
179 _ => Some(Cfg(CfgEntry::All(sub_cfgs, DUMMY_SP))),
180 }
181 } else if let CfgEntry::All(b, _) = &assume.0
182 && b.iter().any(|b| b.is_equivalent_to(&self.0))
183 {
184 None
185 } else {
186 Some(self.clone())
187 }
188 }
189
190 pub(crate) fn sort_for_rendering(&mut self) {
196 fn sort_cfg_entry(cfg: &mut CfgEntry) {
197 match cfg {
198 CfgEntry::Any(sub_cfgs, _) | CfgEntry::All(sub_cfgs, _) => {
199 for sub_cfg in sub_cfgs.iter_mut() {
200 sort_cfg_entry(sub_cfg);
201 }
202
203 sub_cfgs.sort_by_cached_key(|a| {
204 (
205 cfg_category(a),
206 Display(a, Format::LongPlain).to_string().to_ascii_lowercase(),
207 )
208 });
209 }
210 CfgEntry::Not(box_cfg, _) => sort_cfg_entry(box_cfg),
211 _ => {}
212 }
213 }
214
215 fn cfg_category(cfg: &CfgEntry) -> u8 {
216 match cfg {
217 CfgEntry::NameValue { name, .. } if *name == sym::feature => 2,
218 CfgEntry::NameValue { name, .. } if *name == sym::target_feature => 1,
219 CfgEntry::NameValue { .. } | CfgEntry::Bool(..) => 0,
220 CfgEntry::Any(..) | CfgEntry::All(..) | CfgEntry::Not(..) => 3,
221 _ => 4,
222 }
223 }
224
225 sort_cfg_entry(&mut self.0);
226 }
227
228 fn omit_preposition(&self) -> bool {
229 matches!(self.0, CfgEntry::Bool(..))
230 }
231
232 pub(crate) fn inner(&self) -> &CfgEntry {
233 &self.0
234 }
235}
236
237impl ops::Not for Cfg {
238 type Output = Cfg;
239 fn not(self) -> Cfg {
240 Cfg(match self.0 {
241 CfgEntry::Bool(v, s) => CfgEntry::Bool(!v, s),
242 CfgEntry::Not(cfg, _) => *cfg,
243 s => CfgEntry::Not(Box::new(s), DUMMY_SP),
244 })
245 }
246}
247
248impl ops::BitAndAssign for Cfg {
249 fn bitand_assign(&mut self, other: Cfg) {
250 match (&mut self.0, other.0) {
251 (CfgEntry::Bool(false, _), _) | (_, CfgEntry::Bool(true, _)) => {}
252 (s, CfgEntry::Bool(false, _)) => *s = CfgEntry::Bool(false, DUMMY_SP),
253 (s @ CfgEntry::Bool(true, _), b) => *s = b,
254 (CfgEntry::All(a, _), CfgEntry::All(ref mut b, _)) => {
255 for c in b.drain(..) {
256 if !a.iter().any(|a| a.is_equivalent_to(&c)) {
257 a.push(c);
258 }
259 }
260 }
261 (CfgEntry::All(a, _), ref mut b) => {
262 if !a.iter().any(|a| a.is_equivalent_to(b)) {
263 a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
264 }
265 }
266 (s, CfgEntry::All(mut a, _)) => {
267 let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
268 if !a.iter().any(|a| a.is_equivalent_to(&b)) {
269 a.push(b);
270 }
271 *s = CfgEntry::All(a, DUMMY_SP);
272 }
273 (s, b) => {
274 if !s.is_equivalent_to(&b) {
275 let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
276 *s = CfgEntry::All(thin_vec![a, b], DUMMY_SP);
277 }
278 }
279 }
280 }
281}
282
283impl ops::BitAnd for Cfg {
284 type Output = Cfg;
285 fn bitand(mut self, other: Cfg) -> Cfg {
286 self &= other;
287 self
288 }
289}
290
291impl ops::BitOrAssign for Cfg {
292 fn bitor_assign(&mut self, other: Cfg) {
293 match (&mut self.0, other.0) {
294 (CfgEntry::Bool(true, _), _)
295 | (_, CfgEntry::Bool(false, _))
296 | (_, CfgEntry::Bool(true, _)) => {}
297 (s @ CfgEntry::Bool(false, _), b) => *s = b,
298 (CfgEntry::Any(a, _), CfgEntry::Any(ref mut b, _)) => {
299 for c in b.drain(..) {
300 if !a.iter().any(|a| a.is_equivalent_to(&c)) {
301 a.push(c);
302 }
303 }
304 }
305 (CfgEntry::Any(a, _), ref mut b) => {
306 if !a.iter().any(|a| a.is_equivalent_to(b)) {
307 a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
308 }
309 }
310 (s, CfgEntry::Any(mut a, _)) => {
311 let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
312 if !a.iter().any(|a| a.is_equivalent_to(&b)) {
313 a.push(b);
314 }
315 *s = CfgEntry::Any(a, DUMMY_SP);
316 }
317 (s, b) => {
318 if !s.is_equivalent_to(&b) {
319 let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
320 *s = CfgEntry::Any(thin_vec![a, b], DUMMY_SP);
321 }
322 }
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
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 fn escape(self, s: &str) -> impl fmt::Display {
358 if self.is_html() { Either::Left(Escape(s)) } else { Either::Right(s) }
359 }
360}
361
362struct Display<'a>(&'a CfgEntry, Format);
364
365impl Display<'_> {
366 fn code_wrappers(&self) -> Wrapped<&'static str> {
367 if self.1.is_html() { Wrapped::with("<code>", "</code>") } else { Wrapped::with("`", "`") }
368 }
369
370 fn display_sub_cfgs(
371 &self,
372 fmt: &mut fmt::Formatter<'_>,
373 sub_cfgs: &[CfgEntry],
374 separator: &str,
375 ) -> fmt::Result {
376 use fmt::Display as _;
377
378 let short_longhand = self.1.is_long() && {
379 let all_crate_features = sub_cfgs.iter().all(|sub_cfg| {
380 matches!(sub_cfg, CfgEntry::NameValue { name: sym::feature, value: Some(_), .. })
381 });
382 let all_target_features = sub_cfgs.iter().all(|sub_cfg| {
383 matches!(
384 sub_cfg,
385 CfgEntry::NameValue { name: sym::target_feature, value: Some(_), .. }
386 )
387 });
388
389 if all_crate_features {
390 fmt.write_str("crate features ")?;
391 true
392 } else if all_target_features {
393 fmt.write_str("target features ")?;
394 true
395 } else {
396 false
397 }
398 };
399
400 fmt::from_fn(|f| {
401 sub_cfgs
402 .iter()
403 .map(|sub_cfg| {
404 if let CfgEntry::NameValue { value: Some(feat), .. } = sub_cfg
405 && short_longhand
406 {
407 Either::Left(self.code_wrappers().wrap(feat))
408 } else {
409 Either::Right(
410 Wrapped::with_parens()
411 .when(is_any_cfg(sub_cfg))
412 .wrap(Display(sub_cfg, self.1)),
413 )
414 }
415 })
416 .joined(separator, f)
417 })
418 .fmt(fmt)?;
419
420 Ok(())
421 }
422}
423
424impl fmt::Display for Display<'_> {
425 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
426 match &self.0 {
427 CfgEntry::Not(CfgEntry::Any(sub_cfgs, _), _) => {
428 let separator = if sub_cfgs.iter().all(is_simple_cfg) { " nor " } else { ", nor " };
429 fmt.write_str("neither ")?;
430
431 sub_cfgs
432 .iter()
433 .map(|sub_cfg| {
434 Wrapped::with_parens()
435 .when(is_any_cfg(sub_cfg))
436 .wrap(Display(sub_cfg, self.1))
437 })
438 .joined(separator, fmt)
439 }
440 CfgEntry::Not(simple @ CfgEntry::NameValue { .. }, _) => {
441 write!(fmt, "non-{}", Display(simple, self.1))
442 }
443 CfgEntry::Not(c, _) => write!(fmt, "not ({})", Display(c, self.1)),
444
445 CfgEntry::Any(sub_cfgs, _) => {
446 let separator = if sub_cfgs.iter().all(is_simple_cfg) { " or " } else { ", or " };
447 self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), separator)
448 }
449 CfgEntry::All(sub_cfgs, _) => self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), " and "),
450
451 CfgEntry::Bool(v, _) => {
452 if *v {
453 fmt.write_str("everywhere")
454 } else {
455 fmt.write_str("nowhere")
456 }
457 }
458
459 &CfgEntry::NameValue { name, value, .. } => {
460 let human_readable = match (*name, value) {
461 (sym::unix, None) => "Unix",
462 (sym::windows, None) => "Windows",
463 (sym::debug_assertions, None) => "debug-assertions enabled",
464 (sym::target_object_format, Some(format)) => match self.1 {
465 Format::LongHtml => {
466 return write!(fmt, "object format <code>{format}</code>");
467 }
468 Format::LongPlain => return write!(fmt, "object format `{format}`"),
469 Format::ShortHtml => return write!(fmt, "<code>{format}</code>"),
470 },
471 (sym::target_os, Some(os)) => human_readable_target_os(*os).unwrap_or_default(),
472 (sym::target_arch, Some(arch)) => {
473 human_readable_target_arch(*arch).unwrap_or_default()
474 }
475 (sym::target_vendor, Some(vendor)) => match vendor.as_str() {
476 "apple" => "Apple",
477 "pc" => "PC",
478 "sun" => "Sun",
479 "fortanix" => "Fortanix",
480 _ => "",
481 },
482 (sym::target_env, Some(env)) => {
483 human_readable_target_env(*env).unwrap_or_default()
484 }
485 (sym::target_endian, Some(endian)) => {
486 return write!(fmt, "{endian}-endian");
487 }
488 (sym::target_pointer_width, Some(bits)) => {
489 return write!(fmt, "{bits}-bit");
490 }
491 (sym::target_feature, Some(feat)) => match self.1 {
492 Format::LongHtml => {
493 return write!(fmt, "target feature <code>{feat}</code>");
494 }
495 Format::LongPlain => return write!(fmt, "target feature `{feat}`"),
496 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
497 },
498 (sym::feature, Some(feat)) => match self.1 {
499 Format::LongHtml => {
500 return write!(fmt, "crate feature <code>{feat}</code>");
501 }
502 Format::LongPlain => return write!(fmt, "crate feature `{feat}`"),
503 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
504 },
505 _ => "",
506 };
507 if !human_readable.is_empty() {
508 fmt.write_str(human_readable)
509 } else {
510 let value = value
511 .map(|v| fmt::from_fn(move |f| write!(f, "={}", self.1.escape(v.as_str()))))
512 .maybe_display();
513 self.code_wrappers()
514 .wrap(format_args!("{}{value}", self.1.escape(name.as_str())))
515 .fmt(fmt)
516 }
517 }
518
519 CfgEntry::Version(..) => {
520 Ok(())
522 }
523 }
524 }
525}
526
527fn human_readable_target_os(os: Symbol) -> Option<&'static str> {
528 let os = spec::Os::from_str(os.as_str()).ok()?;
529
530 use spec::Os::*;
531 Some(match os {
532 Aix => "AIX",
534 AmdHsa => "AMD HSA",
535 Android => "Android",
536 Cuda => "CUDA",
537 Cygwin => "Cygwin",
538 Dragonfly => "DragonFly BSD",
539 Emscripten => "Emscripten",
540 EspIdf => "ESP-IDF",
541 FreeBsd => "FreeBSD",
542 Fuchsia => "Fuchsia",
543 Haiku => "Haiku",
544 HelenOs => "HelenOS",
545 Hermit => "Hermit",
546 Horizon => "Horizon",
547 Hurd => "GNU/Hurd",
548 IOs => "iOS",
549 Illumos => "illumos",
550 L4Re => "L4Re",
551 Linux => "Linux",
552 LynxOs178 => "LynxOS-178",
553 MacOs => "macOS",
554 Managarm => "Managarm",
555 Motor => "Motor OS",
556 NetBsd => "NetBSD",
557 None => "bare-metal",
558 Nto => "QNX Neutrino",
559 NuttX => "NuttX",
560 OpenBsd => "OpenBSD",
561 Psp => "Play Station Portable",
562 Psx => "Play Station 1",
563 Qurt => "QuRT",
564 Redox => "Redox OS",
565 Rtems => "RTEMS OS",
566 Solaris => "Solaris",
567 SolidAsp3 => "SOLID ASP3",
568 TeeOs => "TEEOS",
569 Trusty => "Trusty",
570 TvOs => "tvOS",
571 Uefi => "UEFI",
572 VexOs => "VEXos",
573 VisionOs => "visionOS",
574 Vita => "Play Station Vita",
575 VxWorks => "VxWorks",
576 Wasi => "WASI",
577 WatchOs => "watchOS",
578 Windows => "Windows",
579 Xous => "Xous",
580 Zkvm => "zero knowledge Virtual Machine",
581 Unknown | Other(_) => return Option::None,
583 })
584}
585
586fn human_readable_target_arch(os: Symbol) -> Option<&'static str> {
587 let arch = spec::Arch::from_str(os.as_str()).ok()?;
588
589 use spec::Arch::*;
590 Some(match arch {
591 AArch64 => "AArch64",
593 AmdGpu => "AMD GPU",
594 Arm => "ARM",
595 Arm64EC => "ARM64EC",
596 Avr => "AVR",
597 Bpf => "BPF",
598 CSky => "C-SKY",
599 Hexagon => "Hexagon",
600 LoongArch32 => "LoongArch32",
601 LoongArch64 => "LoongArch64",
602 M68k => "Motorola 680x0",
603 Mips => "MIPS",
604 Mips32r6 => "MIPS release 6",
605 Mips64 => "MIPS-64",
606 Mips64r6 => "MIPS-64 release 6",
607 Msp430 => "MSP430",
608 Nvptx64 => "NVidia GPU",
609 PowerPC => "PowerPC",
610 PowerPC64 => "PowerPC64",
611 RiscV32 => "RISC-V RV32",
612 RiscV64 => "RISC-V RV64",
613 S390x => "s390x",
614 Sparc => "SPARC",
615 Sparc64 => "SPARC-64",
616 SpirV => "SPIR-V",
617 Wasm32 | Wasm64 => "WebAssembly",
618 X86 => "x86",
619 X86_64 => "x86-64",
620 Xtensa => "Xtensa",
621 Other(_) => return None,
623 })
624}
625
626fn human_readable_target_env(env: Symbol) -> Option<&'static str> {
627 let env = spec::Env::from_str(env.as_str()).ok()?;
628
629 use spec::Env::*;
630 Some(match env {
631 Gnu => "GNU",
633 MacAbi => "Catalyst",
634 Mlibc => "Managarm C Library",
635 Msvc => "MSVC",
636 Musl => "musl",
637 Newlib => "Newlib",
638 Nto70 => "Neutrino 7.0",
639 Nto71 => "Neutrino 7.1",
640 Nto71IoSock => "Neutrino 7.1 with io-sock",
641 Nto80 => "Neutrino 8.0",
642 Ohos => "OpenHarmony",
643 P1 => "WASIp1",
644 P2 => "WASIp2",
645 P3 => "WASIp3",
646 Relibc => "relibc",
647 Sgx => "SGX",
648 Sim => "Simulator",
649 Uclibc => "uClibc",
650 V5 => "V5",
651 Unspecified | Other(_) => return None,
653 })
654}
655
656#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
657struct NameValueCfg {
658 name: Symbol,
659 value: Option<Symbol>,
660}
661
662impl NameValueCfg {
663 fn new(name: Symbol) -> Self {
664 Self { name, value: None }
665 }
666}
667
668impl<'a> From<&'a CfgEntry> for NameValueCfg {
669 fn from(cfg: &'a CfgEntry) -> Self {
670 match cfg {
671 CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value },
672 _ => NameValueCfg { name: sym::empty, value: None },
673 }
674 }
675}
676
677impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg {
678 fn from(cfg: &'a attrs::CfgInfo) -> Self {
679 Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) }
680 }
681}
682
683#[derive(Clone, Debug)]
685pub(crate) struct CfgInfo {
686 hidden_cfg: FxHashSet<NameValueCfg>,
689 current_cfg: Cfg,
692 auto_cfg_active: bool,
694 parent_is_doc_cfg: bool,
698}
699
700impl Default for CfgInfo {
701 fn default() -> Self {
702 Self {
703 hidden_cfg: FxHashSet::from_iter([
704 NameValueCfg::new(sym::test),
705 NameValueCfg::new(sym::doc),
706 NameValueCfg::new(sym::doctest),
707 ]),
708 current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)),
709 auto_cfg_active: true,
710 parent_is_doc_cfg: false,
711 }
712 }
713}
714
715fn show_hide_show_conflict_error(
716 tcx: TyCtxt<'_>,
717 item_span: rustc_span::Span,
718 previous: rustc_span::Span,
719) {
720 let mut diag = tcx.sess.dcx().struct_span_err(
721 item_span,
722 format!(
723 "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
724 ),
725 );
726 diag.span_note(previous, "first change was here");
727 diag.emit();
728}
729
730fn handle_auto_cfg_hide_show(
738 tcx: TyCtxt<'_>,
739 cfg_info: &mut CfgInfo,
740 attr: &CfgHideShow,
741 new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
742 new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
743) {
744 for value in &attr.values {
745 let simple = NameValueCfg::from(value);
746 if attr.kind == HideOrShow::Show {
747 if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) {
748 show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
749 } else {
750 new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
751 }
752 cfg_info.hidden_cfg.remove(&simple);
753 } else {
754 if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) {
755 show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
756 } else {
757 new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
758 }
759 cfg_info.hidden_cfg.insert(simple);
760 }
761 }
762}
763
764pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
765 attrs: I,
766 tcx: TyCtxt<'_>,
767 cfg_info: &mut CfgInfo,
768) -> Option<Arc<Cfg>> {
769 fn check_changed_auto_active_status(
770 changed_auto_active_status: &mut Option<rustc_span::Span>,
771 attr_span: Span,
772 cfg_info: &mut CfgInfo,
773 tcx: TyCtxt<'_>,
774 new_value: bool,
775 ) -> bool {
776 if let Some(first_change) = changed_auto_active_status {
777 if cfg_info.auto_cfg_active != new_value {
778 tcx.sess
779 .dcx()
780 .struct_span_err(
781 vec![*first_change, attr_span],
782 "`auto_cfg` was disabled and enabled more than once on the same item",
783 )
784 .emit();
785 return true;
786 }
787 } else {
788 *changed_auto_active_status = Some(attr_span);
789 }
790 cfg_info.auto_cfg_active = new_value;
791 false
792 }
793
794 let mut new_show_attrs = FxHashMap::default();
795 let mut new_hide_attrs = FxHashMap::default();
796
797 let mut doc_cfg = attrs
798 .clone()
799 .filter_map(|attr| match attr {
800 Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => Some(d),
801 _ => None,
802 })
803 .peekable();
804 if doc_cfg.peek().is_some() {
806 if !cfg_info.parent_is_doc_cfg {
808 cfg_info.current_cfg = Cfg(CfgEntry::Bool(true, DUMMY_SP));
809 cfg_info.parent_is_doc_cfg = true;
810 }
811 for attr in doc_cfg {
812 for new_cfg in attr.cfg.clone() {
813 cfg_info.current_cfg &= Cfg(new_cfg);
814 }
815 }
816 } else {
817 cfg_info.parent_is_doc_cfg = false;
818 }
819
820 let mut changed_auto_active_status = None;
821
822 for attr in attrs {
824 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr {
825 for (new_value, span) in &d.auto_cfg_change {
826 if check_changed_auto_active_status(
827 &mut changed_auto_active_status,
828 *span,
829 cfg_info,
830 tcx,
831 *new_value,
832 ) {
833 return None;
834 }
835 }
836 if let Some((_, span)) = d.auto_cfg.first() {
837 if check_changed_auto_active_status(
838 &mut changed_auto_active_status,
839 *span,
840 cfg_info,
841 tcx,
842 true,
843 ) {
844 return None;
845 }
846 for (value, _) in &d.auto_cfg {
847 handle_auto_cfg_hide_show(
848 tcx,
849 cfg_info,
850 value,
851 &mut new_show_attrs,
852 &mut new_hide_attrs,
853 );
854 }
855 }
856 } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
857 for (feature, _) in features {
860 cfg_info.current_cfg &= Cfg(CfgEntry::NameValue {
861 name: sym::target_feature,
862 value: Some(*feature),
863 span: DUMMY_SP,
864 });
865 }
866 continue;
867 } else if !cfg_info.parent_is_doc_cfg
868 && let hir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr
869 {
870 for (new_cfg, _) in cfgs {
871 cfg_info.current_cfg &= Cfg(new_cfg.clone());
872 }
873 }
874 }
875
876 if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
879 None
880 } else if cfg_info.parent_is_doc_cfg {
881 if matches!(cfg_info.current_cfg.0, CfgEntry::Bool(true, _)) {
882 None
883 } else {
884 let mut cfg = cfg_info.current_cfg.clone();
885 cfg.sort_for_rendering();
886 Some(Arc::new(cfg))
887 }
888 } else {
889 match strip_hidden(&cfg_info.current_cfg.0, &cfg_info.hidden_cfg) {
892 None | Some(CfgEntry::Bool(true, _)) => None,
893 Some(cfg_entry) => {
894 let mut cfg = Cfg(cfg_entry);
895 cfg.sort_for_rendering();
896 Some(Arc::new(cfg))
897 }
898 }
899 }
900}