1use std::sync::Arc;
7use std::{fmt, mem, ops};
8
9use itertools::Either;
10use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
11use rustc_data_structures::fx::{FxHashMap, FxHashSet};
12use rustc_hir::attrs::AttributeKind;
13use rustc_middle::ty::TyCtxt;
14use rustc_session::parse::ParseSess;
15use rustc_span::Span;
16use rustc_span::symbol::{Symbol, sym};
17use {rustc_ast as ast, rustc_hir as hir};
18
19use crate::display::{Joined as _, MaybeDisplay, Wrapped};
20use crate::html::escape::Escape;
21
22#[cfg(test)]
23mod tests;
24
25#[derive(Clone, Debug, PartialEq, Eq, Hash)]
26pub(crate) enum Cfg {
27 True,
29 False,
31 Cfg(Symbol, Option<Symbol>),
33 Not(Box<Cfg>),
35 Any(Vec<Cfg>),
37 All(Vec<Cfg>),
39}
40
41#[derive(PartialEq, Debug)]
42pub(crate) struct InvalidCfgError {
43 pub(crate) msg: &'static str,
44 pub(crate) span: Span,
45}
46
47impl Cfg {
48 fn parse_nested(
50 nested_cfg: &MetaItemInner,
51 exclude: &FxHashSet<Cfg>,
52 ) -> Result<Option<Cfg>, InvalidCfgError> {
53 match nested_cfg {
54 MetaItemInner::MetaItem(cfg) => Cfg::parse_without(cfg, exclude),
55 MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b {
56 true => Ok(Some(Cfg::True)),
57 false => Ok(Some(Cfg::False)),
58 },
59 MetaItemInner::Lit(lit) => {
60 Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
61 }
62 }
63 }
64
65 pub(crate) fn parse_without(
66 cfg: &MetaItem,
67 exclude: &FxHashSet<Cfg>,
68 ) -> Result<Option<Cfg>, InvalidCfgError> {
69 let name = match cfg.ident() {
70 Some(ident) => ident.name,
71 None => {
72 return Err(InvalidCfgError {
73 msg: "expected a single identifier",
74 span: cfg.span,
75 });
76 }
77 };
78 match cfg.kind {
79 MetaItemKind::Word => {
80 let cfg = Cfg::Cfg(name, None);
81 if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) }
82 }
83 MetaItemKind::NameValue(ref lit) => match lit.kind {
84 LitKind::Str(value, _) => {
85 let cfg = Cfg::Cfg(name, Some(value));
86 if exclude.contains(&cfg) { Ok(None) } else { Ok(Some(cfg)) }
87 }
88 _ => Err(InvalidCfgError {
89 msg: "value of cfg option should be a string literal",
92 span: lit.span,
93 }),
94 },
95 MetaItemKind::List(ref items) => {
96 let orig_len = items.len();
97 let mut sub_cfgs =
98 items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose());
99 let ret = match name {
100 sym::all => sub_cfgs.try_fold(Cfg::True, |x, y| Ok(x & y?)),
101 sym::any => sub_cfgs.try_fold(Cfg::False, |x, y| Ok(x | y?)),
102 sym::not => {
103 if orig_len == 1 {
104 let mut sub_cfgs = sub_cfgs.collect::<Vec<_>>();
105 if sub_cfgs.len() == 1 {
106 Ok(!sub_cfgs.pop().unwrap()?)
107 } else {
108 return Ok(None);
109 }
110 } else {
111 Err(InvalidCfgError { msg: "expected 1 cfg-pattern", span: cfg.span })
112 }
113 }
114 _ => Err(InvalidCfgError { msg: "invalid predicate", span: cfg.span }),
115 };
116 match ret {
117 Ok(c) => Ok(Some(c)),
118 Err(e) => Err(e),
119 }
120 }
121 }
122 }
123
124 pub(crate) fn parse(cfg: &MetaItemInner) -> Result<Cfg, InvalidCfgError> {
132 Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
133 }
134
135 pub(crate) fn matches(&self, psess: &ParseSess) -> bool {
139 match *self {
140 Cfg::False => false,
141 Cfg::True => true,
142 Cfg::Not(ref child) => !child.matches(psess),
143 Cfg::All(ref sub_cfgs) => sub_cfgs.iter().all(|sub_cfg| sub_cfg.matches(psess)),
144 Cfg::Any(ref sub_cfgs) => sub_cfgs.iter().any(|sub_cfg| sub_cfg.matches(psess)),
145 Cfg::Cfg(name, value) => psess.config.contains(&(name, value)),
146 }
147 }
148
149 fn is_simple(&self) -> bool {
151 match self {
152 Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) => true,
153 Cfg::All(..) | Cfg::Any(..) => false,
154 }
155 }
156
157 fn is_all(&self) -> bool {
159 match self {
160 Cfg::False | Cfg::True | Cfg::Cfg(..) | Cfg::Not(..) | Cfg::All(..) => true,
161 Cfg::Any(..) => false,
162 }
163 }
164
165 pub(crate) fn render_short_html(&self) -> String {
167 let mut msg = Display(self, Format::ShortHtml).to_string();
168 if self.should_capitalize_first_letter()
169 && let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric())
170 {
171 msg[i..i + 1].make_ascii_uppercase();
172 }
173 msg
174 }
175
176 fn render_long_inner(&self, format: Format) -> String {
177 let on = if self.omit_preposition() {
178 " "
179 } else if self.should_use_with_in_description() {
180 " with "
181 } else {
182 " on "
183 };
184
185 let mut msg = if matches!(format, Format::LongHtml) {
186 format!("Available{on}<strong>{}</strong>", Display(self, format))
187 } else {
188 format!("Available{on}{}", Display(self, format))
189 };
190 if self.should_append_only_to_description() {
191 msg.push_str(" only");
192 }
193 msg
194 }
195
196 pub(crate) fn render_long_html(&self) -> String {
198 let mut msg = self.render_long_inner(Format::LongHtml);
199 msg.push('.');
200 msg
201 }
202
203 pub(crate) fn render_long_plain(&self) -> String {
205 self.render_long_inner(Format::LongPlain)
206 }
207
208 fn should_capitalize_first_letter(&self) -> bool {
209 match *self {
210 Cfg::False | Cfg::True | Cfg::Not(..) => true,
211 Cfg::Any(ref sub_cfgs) | Cfg::All(ref sub_cfgs) => {
212 sub_cfgs.first().map(Cfg::should_capitalize_first_letter).unwrap_or(false)
213 }
214 Cfg::Cfg(name, _) => name == sym::debug_assertions || name == sym::target_endian,
215 }
216 }
217
218 fn should_append_only_to_description(&self) -> bool {
219 match self {
220 Cfg::False | Cfg::True => false,
221 Cfg::Any(..) | Cfg::All(..) | Cfg::Cfg(..) => true,
222 Cfg::Not(box Cfg::Cfg(..)) => true,
223 Cfg::Not(..) => false,
224 }
225 }
226
227 fn should_use_with_in_description(&self) -> bool {
228 matches!(self, Cfg::Cfg(sym::target_feature, _))
229 }
230
231 pub(crate) fn simplify_with(&self, assume: &Self) -> Option<Self> {
237 if self == assume {
238 None
239 } else if let Cfg::All(a) = self {
240 let mut sub_cfgs: Vec<Cfg> = if let Cfg::All(b) = assume {
241 a.iter().filter(|a| !b.contains(a)).cloned().collect()
242 } else {
243 a.iter().filter(|&a| a != assume).cloned().collect()
244 };
245 let len = sub_cfgs.len();
246 match len {
247 0 => None,
248 1 => sub_cfgs.pop(),
249 _ => Some(Cfg::All(sub_cfgs)),
250 }
251 } else if let Cfg::All(b) = assume
252 && b.contains(self)
253 {
254 None
255 } else {
256 Some(self.clone())
257 }
258 }
259
260 fn omit_preposition(&self) -> bool {
261 matches!(self, Cfg::True | Cfg::False)
262 }
263
264 pub(crate) fn strip_hidden(&self, hidden: &FxHashSet<Cfg>) -> Option<Self> {
265 match self {
266 Self::True | Self::False => Some(self.clone()),
267 Self::Cfg(..) => {
268 if !hidden.contains(self) {
269 Some(self.clone())
270 } else {
271 None
272 }
273 }
274 Self::Not(cfg) => {
275 if let Some(cfg) = cfg.strip_hidden(hidden) {
276 Some(Self::Not(Box::new(cfg)))
277 } else {
278 None
279 }
280 }
281 Self::Any(cfgs) => {
282 let cfgs =
283 cfgs.iter().filter_map(|cfg| cfg.strip_hidden(hidden)).collect::<Vec<_>>();
284 if cfgs.is_empty() { None } else { Some(Self::Any(cfgs)) }
285 }
286 Self::All(cfgs) => {
287 let cfgs =
288 cfgs.iter().filter_map(|cfg| cfg.strip_hidden(hidden)).collect::<Vec<_>>();
289 if cfgs.is_empty() { None } else { Some(Self::All(cfgs)) }
290 }
291 }
292 }
293}
294
295impl ops::Not for Cfg {
296 type Output = Cfg;
297 fn not(self) -> Cfg {
298 match self {
299 Cfg::False => Cfg::True,
300 Cfg::True => Cfg::False,
301 Cfg::Not(cfg) => *cfg,
302 s => Cfg::Not(Box::new(s)),
303 }
304 }
305}
306
307impl ops::BitAndAssign for Cfg {
308 fn bitand_assign(&mut self, other: Cfg) {
309 match (self, other) {
310 (Cfg::False, _) | (_, Cfg::True) => {}
311 (s, Cfg::False) => *s = Cfg::False,
312 (s @ Cfg::True, b) => *s = b,
313 (Cfg::All(a), Cfg::All(ref mut b)) => {
314 for c in b.drain(..) {
315 if !a.contains(&c) {
316 a.push(c);
317 }
318 }
319 }
320 (Cfg::All(a), ref mut b) => {
321 if !a.contains(b) {
322 a.push(mem::replace(b, Cfg::True));
323 }
324 }
325 (s, Cfg::All(mut a)) => {
326 let b = mem::replace(s, Cfg::True);
327 if !a.contains(&b) {
328 a.push(b);
329 }
330 *s = Cfg::All(a);
331 }
332 (s, b) => {
333 if *s != b {
334 let a = mem::replace(s, Cfg::True);
335 *s = Cfg::All(vec![a, b]);
336 }
337 }
338 }
339 }
340}
341
342impl ops::BitAnd for Cfg {
343 type Output = Cfg;
344 fn bitand(mut self, other: Cfg) -> Cfg {
345 self &= other;
346 self
347 }
348}
349
350impl ops::BitOrAssign for Cfg {
351 fn bitor_assign(&mut self, other: Cfg) {
352 match (self, other) {
353 (Cfg::True, _) | (_, Cfg::False) | (_, Cfg::True) => {}
354 (s @ Cfg::False, b) => *s = b,
355 (Cfg::Any(a), Cfg::Any(ref mut b)) => {
356 for c in b.drain(..) {
357 if !a.contains(&c) {
358 a.push(c);
359 }
360 }
361 }
362 (Cfg::Any(a), ref mut b) => {
363 if !a.contains(b) {
364 a.push(mem::replace(b, Cfg::True));
365 }
366 }
367 (s, Cfg::Any(mut a)) => {
368 let b = mem::replace(s, Cfg::True);
369 if !a.contains(&b) {
370 a.push(b);
371 }
372 *s = Cfg::Any(a);
373 }
374 (s, b) => {
375 if *s != b {
376 let a = mem::replace(s, Cfg::True);
377 *s = Cfg::Any(vec![a, b]);
378 }
379 }
380 }
381 }
382}
383
384impl ops::BitOr for Cfg {
385 type Output = Cfg;
386 fn bitor(mut self, other: Cfg) -> Cfg {
387 self |= other;
388 self
389 }
390}
391
392#[derive(Clone, Copy)]
393enum Format {
394 LongHtml,
395 LongPlain,
396 ShortHtml,
397}
398
399impl Format {
400 fn is_long(self) -> bool {
401 match self {
402 Format::LongHtml | Format::LongPlain => true,
403 Format::ShortHtml => false,
404 }
405 }
406
407 fn is_html(self) -> bool {
408 match self {
409 Format::LongHtml | Format::ShortHtml => true,
410 Format::LongPlain => false,
411 }
412 }
413
414 fn escape(self, s: &str) -> impl fmt::Display {
415 if self.is_html() { Either::Left(Escape(s)) } else { Either::Right(s) }
416 }
417}
418
419struct Display<'a>(&'a Cfg, Format);
421
422impl Display<'_> {
423 fn code_wrappers(&self) -> Wrapped<&'static str> {
424 if self.1.is_html() { Wrapped::with("<code>", "</code>") } else { Wrapped::with("`", "`") }
425 }
426
427 fn display_sub_cfgs(
428 &self,
429 fmt: &mut fmt::Formatter<'_>,
430 sub_cfgs: &[Cfg],
431 separator: &str,
432 ) -> fmt::Result {
433 use fmt::Display as _;
434
435 let short_longhand = self.1.is_long() && {
436 let all_crate_features =
437 sub_cfgs.iter().all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_))));
438 let all_target_features = sub_cfgs
439 .iter()
440 .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_))));
441
442 if all_crate_features {
443 fmt.write_str("crate features ")?;
444 true
445 } else if all_target_features {
446 fmt.write_str("target features ")?;
447 true
448 } else {
449 false
450 }
451 };
452
453 fmt::from_fn(|f| {
454 sub_cfgs
455 .iter()
456 .map(|sub_cfg| {
457 if let Cfg::Cfg(_, Some(feat)) = sub_cfg
458 && short_longhand
459 {
460 Either::Left(self.code_wrappers().wrap(feat))
461 } else {
462 Either::Right(
463 Wrapped::with_parens()
464 .when(!sub_cfg.is_all())
465 .wrap(Display(sub_cfg, self.1)),
466 )
467 }
468 })
469 .joined(separator, f)
470 })
471 .fmt(fmt)?;
472
473 Ok(())
474 }
475}
476
477impl fmt::Display for Display<'_> {
478 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
479 match self.0 {
480 Cfg::Not(box Cfg::Any(sub_cfgs)) => {
481 let separator =
482 if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " };
483 fmt.write_str("neither ")?;
484
485 sub_cfgs
486 .iter()
487 .map(|sub_cfg| {
488 Wrapped::with_parens()
489 .when(!sub_cfg.is_all())
490 .wrap(Display(sub_cfg, self.1))
491 })
492 .joined(separator, fmt)
493 }
494 Cfg::Not(box simple @ Cfg::Cfg(..)) => write!(fmt, "non-{}", Display(simple, self.1)),
495 Cfg::Not(box c) => write!(fmt, "not ({})", Display(c, self.1)),
496
497 Cfg::Any(sub_cfgs) => {
498 let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " };
499 self.display_sub_cfgs(fmt, sub_cfgs, separator)
500 }
501 Cfg::All(sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "),
502
503 Cfg::True => fmt.write_str("everywhere"),
504 Cfg::False => fmt.write_str("nowhere"),
505
506 &Cfg::Cfg(name, value) => {
507 let human_readable = match (name, value) {
508 (sym::unix, None) => "Unix",
509 (sym::windows, None) => "Windows",
510 (sym::debug_assertions, None) => "debug-assertions enabled",
511 (sym::target_os, Some(os)) => match os.as_str() {
512 "android" => "Android",
513 "cygwin" => "Cygwin",
514 "dragonfly" => "DragonFly BSD",
515 "emscripten" => "Emscripten",
516 "freebsd" => "FreeBSD",
517 "fuchsia" => "Fuchsia",
518 "haiku" => "Haiku",
519 "hermit" => "HermitCore",
520 "illumos" => "illumos",
521 "ios" => "iOS",
522 "l4re" => "L4Re",
523 "linux" => "Linux",
524 "macos" => "macOS",
525 "netbsd" => "NetBSD",
526 "openbsd" => "OpenBSD",
527 "redox" => "Redox",
528 "solaris" => "Solaris",
529 "tvos" => "tvOS",
530 "wasi" => "WASI",
531 "watchos" => "watchOS",
532 "windows" => "Windows",
533 "visionos" => "visionOS",
534 _ => "",
535 },
536 (sym::target_arch, Some(arch)) => match arch.as_str() {
537 "aarch64" => "AArch64",
538 "arm" => "ARM",
539 "loongarch32" => "LoongArch LA32",
540 "loongarch64" => "LoongArch LA64",
541 "m68k" => "M68k",
542 "csky" => "CSKY",
543 "mips" => "MIPS",
544 "mips32r6" => "MIPS Release 6",
545 "mips64" => "MIPS-64",
546 "mips64r6" => "MIPS-64 Release 6",
547 "msp430" => "MSP430",
548 "powerpc" => "PowerPC",
549 "powerpc64" => "PowerPC-64",
550 "riscv32" => "RISC-V RV32",
551 "riscv64" => "RISC-V RV64",
552 "s390x" => "s390x",
553 "sparc64" => "SPARC64",
554 "wasm32" | "wasm64" => "WebAssembly",
555 "x86" => "x86",
556 "x86_64" => "x86-64",
557 _ => "",
558 },
559 (sym::target_vendor, Some(vendor)) => match vendor.as_str() {
560 "apple" => "Apple",
561 "pc" => "PC",
562 "sun" => "Sun",
563 "fortanix" => "Fortanix",
564 _ => "",
565 },
566 (sym::target_env, Some(env)) => match env.as_str() {
567 "gnu" => "GNU",
568 "msvc" => "MSVC",
569 "musl" => "musl",
570 "newlib" => "Newlib",
571 "uclibc" => "uClibc",
572 "sgx" => "SGX",
573 _ => "",
574 },
575 (sym::target_endian, Some(endian)) => return write!(fmt, "{endian}-endian"),
576 (sym::target_pointer_width, Some(bits)) => return write!(fmt, "{bits}-bit"),
577 (sym::target_feature, Some(feat)) => match self.1 {
578 Format::LongHtml => {
579 return write!(fmt, "target feature <code>{feat}</code>");
580 }
581 Format::LongPlain => return write!(fmt, "target feature `{feat}`"),
582 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
583 },
584 (sym::feature, Some(feat)) => match self.1 {
585 Format::LongHtml => {
586 return write!(fmt, "crate feature <code>{feat}</code>");
587 }
588 Format::LongPlain => return write!(fmt, "crate feature `{feat}`"),
589 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
590 },
591 _ => "",
592 };
593 if !human_readable.is_empty() {
594 fmt.write_str(human_readable)
595 } else {
596 let value = value
597 .map(|v| fmt::from_fn(move |f| write!(f, "={}", self.1.escape(v.as_str()))))
598 .maybe_display();
599 self.code_wrappers()
600 .wrap(format_args!("{}{value}", self.1.escape(name.as_str())))
601 .fmt(fmt)
602 }
603 }
604 }
605 }
606}
607
608#[derive(Clone, Debug)]
610pub(crate) struct CfgInfo {
611 hidden_cfg: FxHashSet<Cfg>,
614 current_cfg: Cfg,
617 auto_cfg_active: bool,
619 parent_is_doc_cfg: bool,
623}
624
625impl Default for CfgInfo {
626 fn default() -> Self {
627 Self {
628 hidden_cfg: FxHashSet::from_iter([
629 Cfg::Cfg(sym::test, None),
630 Cfg::Cfg(sym::doc, None),
631 Cfg::Cfg(sym::doctest, None),
632 ]),
633 current_cfg: Cfg::True,
634 auto_cfg_active: true,
635 parent_is_doc_cfg: false,
636 }
637 }
638}
639
640fn show_hide_show_conflict_error(
641 tcx: TyCtxt<'_>,
642 item_span: rustc_span::Span,
643 previous: rustc_span::Span,
644) {
645 let mut diag = tcx.sess.dcx().struct_span_err(
646 item_span,
647 format!(
648 "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
649 ),
650 );
651 diag.span_note(previous, "first change was here");
652 diag.emit();
653}
654
655fn handle_auto_cfg_hide_show(
663 tcx: TyCtxt<'_>,
664 cfg_info: &mut CfgInfo,
665 sub_attr: &MetaItemInner,
666 is_show: bool,
667 new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
668 new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
669) {
670 if let MetaItemInner::MetaItem(item) = sub_attr
671 && let MetaItemKind::List(items) = &item.kind
672 {
673 for item in items {
674 if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
676 if is_show {
677 if let Some(span) = new_hide_attrs.get(&(key, value)) {
678 show_hide_show_conflict_error(tcx, item.span(), *span);
679 } else {
680 new_show_attrs.insert((key, value), item.span());
681 }
682 cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
683 } else {
684 if let Some(span) = new_show_attrs.get(&(key, value)) {
685 show_hide_show_conflict_error(tcx, item.span(), *span);
686 } else {
687 new_hide_attrs.insert((key, value), item.span());
688 }
689 cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
690 }
691 }
692 }
693 }
694}
695
696pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
697 attrs: I,
698 tcx: TyCtxt<'_>,
699 cfg_info: &mut CfgInfo,
700) -> Option<Arc<Cfg>> {
701 fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
702 let mut iter = it.into_iter();
703 let item = iter.next()?;
704 if iter.next().is_some() {
705 return None;
706 }
707 Some(item)
708 }
709
710 fn check_changed_auto_active_status(
711 changed_auto_active_status: &mut Option<rustc_span::Span>,
712 attr: &ast::MetaItem,
713 cfg_info: &mut CfgInfo,
714 tcx: TyCtxt<'_>,
715 new_value: bool,
716 ) -> bool {
717 if let Some(first_change) = changed_auto_active_status {
718 if cfg_info.auto_cfg_active != new_value {
719 tcx.sess
720 .dcx()
721 .struct_span_err(
722 vec![*first_change, attr.span],
723 "`auto_cfg` was disabled and enabled more than once on the same item",
724 )
725 .emit();
726 return true;
727 }
728 } else {
729 *changed_auto_active_status = Some(attr.span);
730 }
731 cfg_info.auto_cfg_active = new_value;
732 false
733 }
734
735 let mut new_show_attrs = FxHashMap::default();
736 let mut new_hide_attrs = FxHashMap::default();
737
738 let mut doc_cfg = attrs
739 .clone()
740 .filter(|attr| attr.has_name(sym::doc))
741 .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
742 .filter(|attr| attr.has_name(sym::cfg))
743 .peekable();
744 if doc_cfg.peek().is_some() {
746 let sess = tcx.sess;
747 if !cfg_info.parent_is_doc_cfg {
749 cfg_info.current_cfg = Cfg::True;
750 cfg_info.parent_is_doc_cfg = true;
751 }
752 for attr in doc_cfg {
753 if let Some(cfg_mi) =
754 attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
755 {
756 match Cfg::parse(cfg_mi) {
757 Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
758 Err(e) => {
759 sess.dcx().span_err(e.span, e.msg);
760 }
761 }
762 }
763 }
764 } else {
765 cfg_info.parent_is_doc_cfg = false;
766 }
767
768 let mut changed_auto_active_status = None;
769
770 for attr in attrs {
772 if let Some(ident) = attr.ident()
773 && ident.name == sym::doc
774 && let Some(attrs) = attr.meta_item_list()
775 {
776 for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
777 let MetaItemInner::MetaItem(attr) = attr else {
778 continue;
779 };
780 match &attr.kind {
781 MetaItemKind::Word => {
782 if check_changed_auto_active_status(
783 &mut changed_auto_active_status,
784 attr,
785 cfg_info,
786 tcx,
787 true,
788 ) {
789 return None;
790 }
791 }
792 MetaItemKind::NameValue(lit) => {
793 if let LitKind::Bool(value) = lit.kind {
794 if check_changed_auto_active_status(
795 &mut changed_auto_active_status,
796 attr,
797 cfg_info,
798 tcx,
799 value,
800 ) {
801 return None;
802 }
803 }
804 }
805 MetaItemKind::List(sub_attrs) => {
806 if check_changed_auto_active_status(
807 &mut changed_auto_active_status,
808 attr,
809 cfg_info,
810 tcx,
811 true,
812 ) {
813 return None;
814 }
815 for sub_attr in sub_attrs.iter() {
816 if let Some(ident) = sub_attr.ident()
817 && (ident.name == sym::show || ident.name == sym::hide)
818 {
819 handle_auto_cfg_hide_show(
820 tcx,
821 cfg_info,
822 &sub_attr,
823 ident.name == sym::show,
824 &mut new_show_attrs,
825 &mut new_hide_attrs,
826 );
827 }
828 }
829 }
830 }
831 }
832 } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
833 for (feature, _) in features {
836 cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
837 }
838 continue;
839 } else if !cfg_info.parent_is_doc_cfg
840 && let Some(ident) = attr.ident()
841 && matches!(ident.name, sym::cfg | sym::cfg_trace)
842 && let Some(attr) = single(attr.meta_item_list()?)
843 && let Ok(new_cfg) = Cfg::parse(&attr)
844 {
845 cfg_info.current_cfg &= new_cfg;
846 }
847 }
848
849 if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
852 None
853 } else if cfg_info.parent_is_doc_cfg {
854 if cfg_info.current_cfg == Cfg::True {
855 None
856 } else {
857 Some(Arc::new(cfg_info.current_cfg.clone()))
858 }
859 } else {
860 match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
863 None | Some(Cfg::True) => None,
864 Some(cfg) => Some(Arc::new(cfg)),
865 }
866 }
867}