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(box 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 fn omit_preposition(&self) -> bool {
191 matches!(self.0, CfgEntry::Bool(..))
192 }
193
194 pub(crate) fn inner(&self) -> &CfgEntry {
195 &self.0
196 }
197}
198
199impl ops::Not for Cfg {
200 type Output = Cfg;
201 fn not(self) -> Cfg {
202 Cfg(match self.0 {
203 CfgEntry::Bool(v, s) => CfgEntry::Bool(!v, s),
204 CfgEntry::Not(cfg, _) => *cfg,
205 s => CfgEntry::Not(Box::new(s), DUMMY_SP),
206 })
207 }
208}
209
210impl ops::BitAndAssign for Cfg {
211 fn bitand_assign(&mut self, other: Cfg) {
212 match (&mut self.0, other.0) {
213 (CfgEntry::Bool(false, _), _) | (_, CfgEntry::Bool(true, _)) => {}
214 (s, CfgEntry::Bool(false, _)) => *s = CfgEntry::Bool(false, DUMMY_SP),
215 (s @ CfgEntry::Bool(true, _), b) => *s = b,
216 (CfgEntry::All(a, _), CfgEntry::All(ref mut b, _)) => {
217 for c in b.drain(..) {
218 if !a.iter().any(|a| a.is_equivalent_to(&c)) {
219 a.push(c);
220 }
221 }
222 }
223 (CfgEntry::All(a, _), ref mut b) => {
224 if !a.iter().any(|a| a.is_equivalent_to(b)) {
225 a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
226 }
227 }
228 (s, CfgEntry::All(mut a, _)) => {
229 let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
230 if !a.iter().any(|a| a.is_equivalent_to(&b)) {
231 a.push(b);
232 }
233 *s = CfgEntry::All(a, DUMMY_SP);
234 }
235 (s, b) => {
236 if !s.is_equivalent_to(&b) {
237 let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
238 *s = CfgEntry::All(thin_vec![a, b], DUMMY_SP);
239 }
240 }
241 }
242 }
243}
244
245impl ops::BitAnd for Cfg {
246 type Output = Cfg;
247 fn bitand(mut self, other: Cfg) -> Cfg {
248 self &= other;
249 self
250 }
251}
252
253impl ops::BitOrAssign for Cfg {
254 fn bitor_assign(&mut self, other: Cfg) {
255 match (&mut self.0, other.0) {
256 (CfgEntry::Bool(true, _), _)
257 | (_, CfgEntry::Bool(false, _))
258 | (_, CfgEntry::Bool(true, _)) => {}
259 (s @ CfgEntry::Bool(false, _), b) => *s = b,
260 (CfgEntry::Any(a, _), CfgEntry::Any(ref mut b, _)) => {
261 for c in b.drain(..) {
262 if !a.iter().any(|a| a.is_equivalent_to(&c)) {
263 a.push(c);
264 }
265 }
266 }
267 (CfgEntry::Any(a, _), ref mut b) => {
268 if !a.iter().any(|a| a.is_equivalent_to(b)) {
269 a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
270 }
271 }
272 (s, CfgEntry::Any(mut a, _)) => {
273 let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
274 if !a.iter().any(|a| a.is_equivalent_to(&b)) {
275 a.push(b);
276 }
277 *s = CfgEntry::Any(a, DUMMY_SP);
278 }
279 (s, b) => {
280 if !s.is_equivalent_to(&b) {
281 let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
282 *s = CfgEntry::Any(thin_vec![a, b], DUMMY_SP);
283 }
284 }
285 }
286 }
287}
288
289impl ops::BitOr for Cfg {
290 type Output = Cfg;
291 fn bitor(mut self, other: Cfg) -> Cfg {
292 self |= other;
293 self
294 }
295}
296
297#[derive(Clone, Copy)]
298enum Format {
299 LongHtml,
300 LongPlain,
301 ShortHtml,
302}
303
304impl Format {
305 fn is_long(self) -> bool {
306 match self {
307 Format::LongHtml | Format::LongPlain => true,
308 Format::ShortHtml => false,
309 }
310 }
311
312 fn is_html(self) -> bool {
313 match self {
314 Format::LongHtml | Format::ShortHtml => true,
315 Format::LongPlain => false,
316 }
317 }
318
319 fn escape(self, s: &str) -> impl fmt::Display {
320 if self.is_html() { Either::Left(Escape(s)) } else { Either::Right(s) }
321 }
322}
323
324struct Display<'a>(&'a CfgEntry, Format);
326
327impl Display<'_> {
328 fn code_wrappers(&self) -> Wrapped<&'static str> {
329 if self.1.is_html() { Wrapped::with("<code>", "</code>") } else { Wrapped::with("`", "`") }
330 }
331
332 fn display_sub_cfgs(
333 &self,
334 fmt: &mut fmt::Formatter<'_>,
335 sub_cfgs: &[CfgEntry],
336 separator: &str,
337 ) -> fmt::Result {
338 use fmt::Display as _;
339
340 let short_longhand = self.1.is_long() && {
341 let all_crate_features = sub_cfgs.iter().all(|sub_cfg| {
342 matches!(sub_cfg, CfgEntry::NameValue { name: sym::feature, value: Some(_), .. })
343 });
344 let all_target_features = sub_cfgs.iter().all(|sub_cfg| {
345 matches!(
346 sub_cfg,
347 CfgEntry::NameValue { name: sym::target_feature, value: Some(_), .. }
348 )
349 });
350
351 if all_crate_features {
352 fmt.write_str("crate features ")?;
353 true
354 } else if all_target_features {
355 fmt.write_str("target features ")?;
356 true
357 } else {
358 false
359 }
360 };
361
362 fmt::from_fn(|f| {
363 sub_cfgs
364 .iter()
365 .map(|sub_cfg| {
366 if let CfgEntry::NameValue { value: Some(feat), .. } = sub_cfg
367 && short_longhand
368 {
369 Either::Left(self.code_wrappers().wrap(feat))
370 } else {
371 Either::Right(
372 Wrapped::with_parens()
373 .when(is_any_cfg(sub_cfg))
374 .wrap(Display(sub_cfg, self.1)),
375 )
376 }
377 })
378 .joined(separator, f)
379 })
380 .fmt(fmt)?;
381
382 Ok(())
383 }
384}
385
386impl fmt::Display for Display<'_> {
387 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
388 match &self.0 {
389 CfgEntry::Not(box CfgEntry::Any(sub_cfgs, _), _) => {
390 let separator = if sub_cfgs.iter().all(is_simple_cfg) { " nor " } else { ", nor " };
391 fmt.write_str("neither ")?;
392
393 sub_cfgs
394 .iter()
395 .map(|sub_cfg| {
396 Wrapped::with_parens()
397 .when(is_any_cfg(sub_cfg))
398 .wrap(Display(sub_cfg, self.1))
399 })
400 .joined(separator, fmt)
401 }
402 CfgEntry::Not(box simple @ CfgEntry::NameValue { .. }, _) => {
403 write!(fmt, "non-{}", Display(simple, self.1))
404 }
405 CfgEntry::Not(box c, _) => write!(fmt, "not ({})", Display(c, self.1)),
406
407 CfgEntry::Any(sub_cfgs, _) => {
408 let separator = if sub_cfgs.iter().all(is_simple_cfg) { " or " } else { ", or " };
409 self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), separator)
410 }
411 CfgEntry::All(sub_cfgs, _) => self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), " and "),
412
413 CfgEntry::Bool(v, _) => {
414 if *v {
415 fmt.write_str("everywhere")
416 } else {
417 fmt.write_str("nowhere")
418 }
419 }
420
421 &CfgEntry::NameValue { name, value, .. } => {
422 let human_readable = match (*name, value) {
423 (sym::unix, None) => "Unix",
424 (sym::windows, None) => "Windows",
425 (sym::debug_assertions, None) => "debug-assertions enabled",
426 (sym::target_os, Some(os)) => human_readable_target_os(*os).unwrap_or_default(),
427 (sym::target_arch, Some(arch)) => {
428 human_readable_target_arch(*arch).unwrap_or_default()
429 }
430 (sym::target_vendor, Some(vendor)) => match vendor.as_str() {
431 "apple" => "Apple",
432 "pc" => "PC",
433 "sun" => "Sun",
434 "fortanix" => "Fortanix",
435 _ => "",
436 },
437 (sym::target_env, Some(env)) => {
438 human_readable_target_env(*env).unwrap_or_default()
439 }
440 (sym::target_endian, Some(endian)) => {
441 return write!(fmt, "{endian}-endian");
442 }
443 (sym::target_pointer_width, Some(bits)) => {
444 return write!(fmt, "{bits}-bit");
445 }
446 (sym::target_feature, Some(feat)) => match self.1 {
447 Format::LongHtml => {
448 return write!(fmt, "target feature <code>{feat}</code>");
449 }
450 Format::LongPlain => return write!(fmt, "target feature `{feat}`"),
451 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
452 },
453 (sym::feature, Some(feat)) => match self.1 {
454 Format::LongHtml => {
455 return write!(fmt, "crate feature <code>{feat}</code>");
456 }
457 Format::LongPlain => return write!(fmt, "crate feature `{feat}`"),
458 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
459 },
460 _ => "",
461 };
462 if !human_readable.is_empty() {
463 fmt.write_str(human_readable)
464 } else {
465 let value = value
466 .map(|v| fmt::from_fn(move |f| write!(f, "={}", self.1.escape(v.as_str()))))
467 .maybe_display();
468 self.code_wrappers()
469 .wrap(format_args!("{}{value}", self.1.escape(name.as_str())))
470 .fmt(fmt)
471 }
472 }
473
474 CfgEntry::Version(..) => {
475 Ok(())
477 }
478 }
479 }
480}
481
482fn human_readable_target_os(os: Symbol) -> Option<&'static str> {
483 let os = spec::Os::from_str(os.as_str()).ok()?;
484
485 use spec::Os::*;
486 Some(match os {
487 Aix => "AIX",
489 AmdHsa => "AMD HSA",
490 Android => "Android",
491 Cuda => "CUDA",
492 Cygwin => "Cygwin",
493 Dragonfly => "DragonFly BSD",
494 Emscripten => "Emscripten",
495 EspIdf => "ESP-IDF",
496 FreeBsd => "FreeBSD",
497 Fuchsia => "Fuchsia",
498 Haiku => "Haiku",
499 HelenOs => "HelenOS",
500 Hermit => "Hermit",
501 Horizon => "Horizon",
502 Hurd => "GNU/Hurd",
503 IOs => "iOS",
504 Illumos => "illumos",
505 L4Re => "L4Re",
506 Linux => "Linux",
507 LynxOs178 => "LynxOS-178",
508 MacOs => "macOS",
509 Managarm => "Managarm",
510 Motor => "Motor OS",
511 NetBsd => "NetBSD",
512 None => "bare-metal", Nto => "QNX Neutrino",
514 NuttX => "NuttX",
515 OpenBsd => "OpenBSD",
516 Psp => "Play Station Portable",
517 Psx => "Play Station 1",
518 Qurt => "QuRT",
519 Redox => "Redox OS",
520 Rtems => "RTEMS OS",
521 Solaris => "Solaris",
522 SolidAsp3 => "SOLID ASP3",
523 TeeOs => "TEEOS",
524 Trusty => "Trusty",
525 TvOs => "tvOS",
526 Uefi => "UEFI",
527 VexOs => "VEXos",
528 VisionOs => "visionOS",
529 Vita => "Play Station Vita",
530 VxWorks => "VxWorks",
531 Wasi => "WASI",
532 WatchOs => "watchOS",
533 Windows => "Windows",
534 Xous => "Xous",
535 Zkvm => "zero knowledge Virtual Machine",
536 Unknown | Other(_) => return Option::None,
538 })
539}
540
541fn human_readable_target_arch(os: Symbol) -> Option<&'static str> {
542 let arch = spec::Arch::from_str(os.as_str()).ok()?;
543
544 use spec::Arch::*;
545 Some(match arch {
546 AArch64 => "AArch64",
548 AmdGpu => "AMG GPU",
549 Arm => "ARM",
550 Arm64EC => "ARM64EC",
551 Avr => "AVR",
552 Bpf => "BPF",
553 CSky => "C-SKY",
554 Hexagon => "Hexagon",
555 LoongArch32 => "LoongArch64",
556 LoongArch64 => "LoongArch32",
557 M68k => "Motorola 680x0",
558 Mips => "MIPS",
559 Mips32r6 => "MIPS release 6",
560 Mips64 => "MIPS-64",
561 Mips64r6 => "MIPS-64 release 6",
562 Msp430 => "MSP430",
563 Nvptx64 => "NVidia GPU",
564 PowerPC => "PowerPC",
565 PowerPC64 => "PowerPC64",
566 RiscV32 => "RISC-V RV32",
567 RiscV64 => "RISC-V RV64",
568 S390x => "s390x",
569 Sparc => "SPARC",
570 Sparc64 => "SPARC-64",
571 SpirV => "SPIR-V",
572 Wasm32 | Wasm64 => "WebAssembly",
573 X86 => "x86",
574 X86_64 => "x86-64",
575 Xtensa => "Xtensa",
576 Other(_) => return None,
578 })
579}
580
581fn human_readable_target_env(env: Symbol) -> Option<&'static str> {
582 let env = spec::Env::from_str(env.as_str()).ok()?;
583
584 use spec::Env::*;
585 Some(match env {
586 Gnu => "GNU",
588 MacAbi => "Catalyst",
589 Mlibc => "mac ABI",
590 Msvc => "MSVC",
591 Musl => "musl",
592 Newlib => "Newlib",
593 Nto70 => "Neutrino 7.0",
594 Nto71 => "Neutrino 7.1",
595 Nto71IoSock => "Neutrino 7.1 with io-sock",
596 Nto80 => "Neutrino 8.0",
597 Ohos => "OpenHarmony",
598 P1 => "WASIp1",
599 P2 => "WASIp2",
600 P3 => "WASIp3",
601 Relibc => "relibc",
602 Sgx => "SGX",
603 Sim => "Simulator",
604 Uclibc => "uClibc",
605 V5 => "V5",
606 Unspecified | Other(_) => return None,
608 })
609}
610
611#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
612struct NameValueCfg {
613 name: Symbol,
614 value: Option<Symbol>,
615}
616
617impl NameValueCfg {
618 fn new(name: Symbol) -> Self {
619 Self { name, value: None }
620 }
621}
622
623impl<'a> From<&'a CfgEntry> for NameValueCfg {
624 fn from(cfg: &'a CfgEntry) -> Self {
625 match cfg {
626 CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value },
627 _ => NameValueCfg { name: sym::empty, value: None },
628 }
629 }
630}
631
632impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg {
633 fn from(cfg: &'a attrs::CfgInfo) -> Self {
634 Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) }
635 }
636}
637
638#[derive(Clone, Debug)]
640pub(crate) struct CfgInfo {
641 hidden_cfg: FxHashSet<NameValueCfg>,
644 current_cfg: Cfg,
647 auto_cfg_active: bool,
649 parent_is_doc_cfg: bool,
653}
654
655impl Default for CfgInfo {
656 fn default() -> Self {
657 Self {
658 hidden_cfg: FxHashSet::from_iter([
659 NameValueCfg::new(sym::test),
660 NameValueCfg::new(sym::doc),
661 NameValueCfg::new(sym::doctest),
662 ]),
663 current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)),
664 auto_cfg_active: true,
665 parent_is_doc_cfg: false,
666 }
667 }
668}
669
670fn show_hide_show_conflict_error(
671 tcx: TyCtxt<'_>,
672 item_span: rustc_span::Span,
673 previous: rustc_span::Span,
674) {
675 let mut diag = tcx.sess.dcx().struct_span_err(
676 item_span,
677 format!(
678 "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
679 ),
680 );
681 diag.span_note(previous, "first change was here");
682 diag.emit();
683}
684
685fn handle_auto_cfg_hide_show(
693 tcx: TyCtxt<'_>,
694 cfg_info: &mut CfgInfo,
695 attr: &CfgHideShow,
696 new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
697 new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
698) {
699 for value in &attr.values {
700 let simple = NameValueCfg::from(value);
701 if attr.kind == HideOrShow::Show {
702 if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) {
703 show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
704 } else {
705 new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
706 }
707 cfg_info.hidden_cfg.remove(&simple);
708 } else {
709 if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) {
710 show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
711 } else {
712 new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
713 }
714 cfg_info.hidden_cfg.insert(simple);
715 }
716 }
717}
718
719pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
720 attrs: I,
721 tcx: TyCtxt<'_>,
722 cfg_info: &mut CfgInfo,
723) -> Option<Arc<Cfg>> {
724 fn check_changed_auto_active_status(
725 changed_auto_active_status: &mut Option<rustc_span::Span>,
726 attr_span: Span,
727 cfg_info: &mut CfgInfo,
728 tcx: TyCtxt<'_>,
729 new_value: bool,
730 ) -> bool {
731 if let Some(first_change) = changed_auto_active_status {
732 if cfg_info.auto_cfg_active != new_value {
733 tcx.sess
734 .dcx()
735 .struct_span_err(
736 vec![*first_change, attr_span],
737 "`auto_cfg` was disabled and enabled more than once on the same item",
738 )
739 .emit();
740 return true;
741 }
742 } else {
743 *changed_auto_active_status = Some(attr_span);
744 }
745 cfg_info.auto_cfg_active = new_value;
746 false
747 }
748
749 let mut new_show_attrs = FxHashMap::default();
750 let mut new_hide_attrs = FxHashMap::default();
751
752 let mut doc_cfg = attrs
753 .clone()
754 .filter_map(|attr| match attr {
755 Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => Some(d),
756 _ => None,
757 })
758 .peekable();
759 if doc_cfg.peek().is_some() {
761 if !cfg_info.parent_is_doc_cfg {
763 cfg_info.current_cfg = Cfg(CfgEntry::Bool(true, DUMMY_SP));
764 cfg_info.parent_is_doc_cfg = true;
765 }
766 for attr in doc_cfg {
767 for new_cfg in attr.cfg.clone() {
768 cfg_info.current_cfg &= Cfg(new_cfg);
769 }
770 }
771 } else {
772 cfg_info.parent_is_doc_cfg = false;
773 }
774
775 let mut changed_auto_active_status = None;
776
777 for attr in attrs {
779 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr {
780 for (new_value, span) in &d.auto_cfg_change {
781 if check_changed_auto_active_status(
782 &mut changed_auto_active_status,
783 *span,
784 cfg_info,
785 tcx,
786 *new_value,
787 ) {
788 return None;
789 }
790 }
791 if let Some((_, span)) = d.auto_cfg.first() {
792 if check_changed_auto_active_status(
793 &mut changed_auto_active_status,
794 *span,
795 cfg_info,
796 tcx,
797 true,
798 ) {
799 return None;
800 }
801 for (value, _) in &d.auto_cfg {
802 handle_auto_cfg_hide_show(
803 tcx,
804 cfg_info,
805 value,
806 &mut new_show_attrs,
807 &mut new_hide_attrs,
808 );
809 }
810 }
811 } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
812 for (feature, _) in features {
815 cfg_info.current_cfg &= Cfg(CfgEntry::NameValue {
816 name: sym::target_feature,
817 value: Some(*feature),
818 span: DUMMY_SP,
819 });
820 }
821 continue;
822 } else if !cfg_info.parent_is_doc_cfg
823 && let hir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr
824 {
825 for (new_cfg, _) in cfgs {
826 cfg_info.current_cfg &= Cfg(new_cfg.clone());
827 }
828 }
829 }
830
831 if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
834 None
835 } else if cfg_info.parent_is_doc_cfg {
836 if matches!(cfg_info.current_cfg.0, CfgEntry::Bool(true, _)) {
837 None
838 } else {
839 Some(Arc::new(cfg_info.current_cfg.clone()))
840 }
841 } else {
842 match strip_hidden(&cfg_info.current_cfg.0, &cfg_info.hidden_cfg) {
845 None | Some(CfgEntry::Bool(true, _)) => None,
846 Some(cfg) => Some(Arc::new(Cfg(cfg))),
847 }
848 }
849}