1use std::sync::Arc;
7use std::{fmt, mem, ops};
8
9use itertools::Either;
10use rustc_data_structures::fx::{FxHashMap, FxHashSet};
11use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
12use rustc_hir as hir;
13use rustc_hir::Attribute;
14use rustc_hir::attrs::{self, AttributeKind, CfgEntry, CfgHideShow, HideOrShow};
15use rustc_middle::ty::TyCtxt;
16use rustc_span::symbol::{Symbol, sym};
17use rustc_span::{DUMMY_SP, Span};
18
19use crate::display::{Joined as _, MaybeDisplay, Wrapped};
20use crate::html::escape::Escape;
21
22#[cfg(test)]
23mod tests;
24
25#[derive(Clone, Debug, Hash)]
26#[cfg_attr(test, derive(PartialEq))]
29pub(crate) struct Cfg(CfgEntry);
30
31fn is_simple_cfg(cfg: &CfgEntry) -> bool {
33 match cfg {
34 CfgEntry::Bool(..)
35 | CfgEntry::NameValue { .. }
36 | CfgEntry::Not(..)
37 | CfgEntry::Version(..) => true,
38 CfgEntry::All(..) | CfgEntry::Any(..) => false,
39 }
40}
41
42fn is_all_cfg(cfg: &CfgEntry) -> bool {
44 match cfg {
45 CfgEntry::Bool(..)
46 | CfgEntry::NameValue { .. }
47 | CfgEntry::Not(..)
48 | CfgEntry::Version(..)
49 | CfgEntry::All(..) => true,
50 CfgEntry::Any(..) => false,
51 }
52}
53
54fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashSet<NameValueCfg>) -> Option<CfgEntry> {
55 match cfg {
56 CfgEntry::Bool(..) => Some(cfg.clone()),
57 CfgEntry::NameValue { .. } => {
58 if !hidden.contains(&NameValueCfg::from(cfg)) {
59 Some(cfg.clone())
60 } else {
61 None
62 }
63 }
64 CfgEntry::Not(cfg, _) => {
65 if let Some(cfg) = strip_hidden(cfg, hidden) {
66 Some(CfgEntry::Not(Box::new(cfg), DUMMY_SP))
67 } else {
68 None
69 }
70 }
71 CfgEntry::Any(cfgs, _) => {
72 let cfgs =
73 cfgs.iter().filter_map(|cfg| strip_hidden(cfg, hidden)).collect::<ThinVec<_>>();
74 if cfgs.is_empty() { None } else { Some(CfgEntry::Any(cfgs, DUMMY_SP)) }
75 }
76 CfgEntry::All(cfgs, _) => {
77 let cfgs =
78 cfgs.iter().filter_map(|cfg| strip_hidden(cfg, hidden)).collect::<ThinVec<_>>();
79 if cfgs.is_empty() { None } else { Some(CfgEntry::All(cfgs, DUMMY_SP)) }
80 }
81 CfgEntry::Version(..) => {
82 Some(cfg.clone())
84 }
85 }
86}
87
88fn should_capitalize_first_letter(cfg: &CfgEntry) -> bool {
89 match cfg {
90 CfgEntry::Bool(..) | CfgEntry::Not(..) | CfgEntry::Version(..) => true,
91 CfgEntry::Any(sub_cfgs, _) | CfgEntry::All(sub_cfgs, _) => {
92 sub_cfgs.first().map(should_capitalize_first_letter).unwrap_or(false)
93 }
94 CfgEntry::NameValue { name, .. } => {
95 *name == sym::debug_assertions || *name == sym::target_endian
96 }
97 }
98}
99
100impl Cfg {
101 pub(crate) fn render_short_html(&self) -> String {
103 let mut msg = Display(&self.0, Format::ShortHtml).to_string();
104 if should_capitalize_first_letter(&self.0)
105 && let Some(i) = msg.find(|c: char| c.is_ascii_alphanumeric())
106 {
107 msg[i..i + 1].make_ascii_uppercase();
108 }
109 msg
110 }
111
112 fn render_long_inner(&self, format: Format) -> String {
113 let on = if self.omit_preposition() {
114 " "
115 } else if self.should_use_with_in_description() {
116 " with "
117 } else {
118 " on "
119 };
120
121 let mut msg = if matches!(format, Format::LongHtml) {
122 format!("Available{on}<strong>{}</strong>", Display(&self.0, format))
123 } else {
124 format!("Available{on}{}", Display(&self.0, format))
125 };
126 if self.should_append_only_to_description() {
127 msg.push_str(" only");
128 }
129 msg
130 }
131
132 pub(crate) fn render_long_html(&self) -> String {
134 let mut msg = self.render_long_inner(Format::LongHtml);
135 msg.push('.');
136 msg
137 }
138
139 pub(crate) fn render_long_plain(&self) -> String {
141 self.render_long_inner(Format::LongPlain)
142 }
143
144 fn should_append_only_to_description(&self) -> bool {
145 match self.0 {
146 CfgEntry::Any(..)
147 | CfgEntry::All(..)
148 | CfgEntry::NameValue { .. }
149 | CfgEntry::Version(..)
150 | CfgEntry::Not(box CfgEntry::NameValue { .. }, _) => true,
151 CfgEntry::Not(..) | CfgEntry::Bool(..) => false,
152 }
153 }
154
155 fn should_use_with_in_description(&self) -> bool {
156 matches!(self.0, CfgEntry::NameValue { name, .. } if name == sym::target_feature)
157 }
158
159 pub(crate) fn simplify_with(&self, assume: &Self) -> Option<Self> {
165 if self.0.is_equivalent_to(&assume.0) {
166 None
167 } else if let CfgEntry::All(a, _) = &self.0 {
168 let mut sub_cfgs: ThinVec<CfgEntry> = if let CfgEntry::All(b, _) = &assume.0 {
169 a.iter().filter(|a| !b.iter().any(|b| a.is_equivalent_to(b))).cloned().collect()
170 } else {
171 a.iter().filter(|&a| !a.is_equivalent_to(&assume.0)).cloned().collect()
172 };
173 let len = sub_cfgs.len();
174 match len {
175 0 => None,
176 1 => sub_cfgs.pop().map(Cfg),
177 _ => Some(Cfg(CfgEntry::All(sub_cfgs, DUMMY_SP))),
178 }
179 } else if let CfgEntry::All(b, _) = &assume.0
180 && b.iter().any(|b| b.is_equivalent_to(&self.0))
181 {
182 None
183 } else {
184 Some(self.clone())
185 }
186 }
187
188 fn omit_preposition(&self) -> bool {
189 matches!(self.0, CfgEntry::Bool(..))
190 }
191
192 pub(crate) fn inner(&self) -> &CfgEntry {
193 &self.0
194 }
195}
196
197impl ops::Not for Cfg {
198 type Output = Cfg;
199 fn not(self) -> Cfg {
200 Cfg(match self.0 {
201 CfgEntry::Bool(v, s) => CfgEntry::Bool(!v, s),
202 CfgEntry::Not(cfg, _) => *cfg,
203 s => CfgEntry::Not(Box::new(s), DUMMY_SP),
204 })
205 }
206}
207
208impl ops::BitAndAssign for Cfg {
209 fn bitand_assign(&mut self, other: Cfg) {
210 match (&mut self.0, other.0) {
211 (CfgEntry::Bool(false, _), _) | (_, CfgEntry::Bool(true, _)) => {}
212 (s, CfgEntry::Bool(false, _)) => *s = CfgEntry::Bool(false, DUMMY_SP),
213 (s @ CfgEntry::Bool(true, _), b) => *s = b,
214 (CfgEntry::All(a, _), CfgEntry::All(ref mut b, _)) => {
215 for c in b.drain(..) {
216 if !a.iter().any(|a| a.is_equivalent_to(&c)) {
217 a.push(c);
218 }
219 }
220 }
221 (CfgEntry::All(a, _), ref mut b) => {
222 if !a.iter().any(|a| a.is_equivalent_to(b)) {
223 a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
224 }
225 }
226 (s, CfgEntry::All(mut a, _)) => {
227 let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
228 if !a.iter().any(|a| a.is_equivalent_to(&b)) {
229 a.push(b);
230 }
231 *s = CfgEntry::All(a, DUMMY_SP);
232 }
233 (s, b) => {
234 if !s.is_equivalent_to(&b) {
235 let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
236 *s = CfgEntry::All(thin_vec![a, b], DUMMY_SP);
237 }
238 }
239 }
240 }
241}
242
243impl ops::BitAnd for Cfg {
244 type Output = Cfg;
245 fn bitand(mut self, other: Cfg) -> Cfg {
246 self &= other;
247 self
248 }
249}
250
251impl ops::BitOrAssign for Cfg {
252 fn bitor_assign(&mut self, other: Cfg) {
253 match (&mut self.0, other.0) {
254 (CfgEntry::Bool(true, _), _)
255 | (_, CfgEntry::Bool(false, _))
256 | (_, CfgEntry::Bool(true, _)) => {}
257 (s @ CfgEntry::Bool(false, _), b) => *s = b,
258 (CfgEntry::Any(a, _), CfgEntry::Any(ref mut b, _)) => {
259 for c in b.drain(..) {
260 if !a.iter().any(|a| a.is_equivalent_to(&c)) {
261 a.push(c);
262 }
263 }
264 }
265 (CfgEntry::Any(a, _), ref mut b) => {
266 if !a.iter().any(|a| a.is_equivalent_to(b)) {
267 a.push(mem::replace(b, CfgEntry::Bool(true, DUMMY_SP)));
268 }
269 }
270 (s, CfgEntry::Any(mut a, _)) => {
271 let b = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
272 if !a.iter().any(|a| a.is_equivalent_to(&b)) {
273 a.push(b);
274 }
275 *s = CfgEntry::Any(a, DUMMY_SP);
276 }
277 (s, b) => {
278 if !s.is_equivalent_to(&b) {
279 let a = mem::replace(s, CfgEntry::Bool(true, DUMMY_SP));
280 *s = CfgEntry::Any(thin_vec![a, b], DUMMY_SP);
281 }
282 }
283 }
284 }
285}
286
287impl ops::BitOr for Cfg {
288 type Output = Cfg;
289 fn bitor(mut self, other: Cfg) -> Cfg {
290 self |= other;
291 self
292 }
293}
294
295#[derive(Clone, Copy)]
296enum Format {
297 LongHtml,
298 LongPlain,
299 ShortHtml,
300}
301
302impl Format {
303 fn is_long(self) -> bool {
304 match self {
305 Format::LongHtml | Format::LongPlain => true,
306 Format::ShortHtml => false,
307 }
308 }
309
310 fn is_html(self) -> bool {
311 match self {
312 Format::LongHtml | Format::ShortHtml => true,
313 Format::LongPlain => false,
314 }
315 }
316
317 fn escape(self, s: &str) -> impl fmt::Display {
318 if self.is_html() { Either::Left(Escape(s)) } else { Either::Right(s) }
319 }
320}
321
322struct Display<'a>(&'a CfgEntry, Format);
324
325impl Display<'_> {
326 fn code_wrappers(&self) -> Wrapped<&'static str> {
327 if self.1.is_html() { Wrapped::with("<code>", "</code>") } else { Wrapped::with("`", "`") }
328 }
329
330 fn display_sub_cfgs(
331 &self,
332 fmt: &mut fmt::Formatter<'_>,
333 sub_cfgs: &[CfgEntry],
334 separator: &str,
335 ) -> fmt::Result {
336 use fmt::Display as _;
337
338 let short_longhand = self.1.is_long() && {
339 let all_crate_features = sub_cfgs.iter().all(|sub_cfg| {
340 matches!(sub_cfg, CfgEntry::NameValue { name: sym::feature, value: Some(_), .. })
341 });
342 let all_target_features = sub_cfgs.iter().all(|sub_cfg| {
343 matches!(
344 sub_cfg,
345 CfgEntry::NameValue { name: sym::target_feature, value: Some(_), .. }
346 )
347 });
348
349 if all_crate_features {
350 fmt.write_str("crate features ")?;
351 true
352 } else if all_target_features {
353 fmt.write_str("target features ")?;
354 true
355 } else {
356 false
357 }
358 };
359
360 fmt::from_fn(|f| {
361 sub_cfgs
362 .iter()
363 .map(|sub_cfg| {
364 if let CfgEntry::NameValue { value: Some(feat), .. } = sub_cfg
365 && short_longhand
366 {
367 Either::Left(self.code_wrappers().wrap(feat))
368 } else {
369 Either::Right(
370 Wrapped::with_parens()
371 .when(!is_all_cfg(sub_cfg))
372 .wrap(Display(sub_cfg, self.1)),
373 )
374 }
375 })
376 .joined(separator, f)
377 })
378 .fmt(fmt)?;
379
380 Ok(())
381 }
382}
383
384impl fmt::Display for Display<'_> {
385 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
386 match &self.0 {
387 CfgEntry::Not(box CfgEntry::Any(sub_cfgs, _), _) => {
388 let separator = if sub_cfgs.iter().all(is_simple_cfg) { " nor " } else { ", nor " };
389 fmt.write_str("neither ")?;
390
391 sub_cfgs
392 .iter()
393 .map(|sub_cfg| {
394 Wrapped::with_parens()
395 .when(!is_all_cfg(sub_cfg))
396 .wrap(Display(sub_cfg, self.1))
397 })
398 .joined(separator, fmt)
399 }
400 CfgEntry::Not(box simple @ CfgEntry::NameValue { .. }, _) => {
401 write!(fmt, "non-{}", Display(simple, self.1))
402 }
403 CfgEntry::Not(box c, _) => write!(fmt, "not ({})", Display(c, self.1)),
404
405 CfgEntry::Any(sub_cfgs, _) => {
406 let separator = if sub_cfgs.iter().all(is_simple_cfg) { " or " } else { ", or " };
407 self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), separator)
408 }
409 CfgEntry::All(sub_cfgs, _) => self.display_sub_cfgs(fmt, sub_cfgs.as_slice(), " and "),
410
411 CfgEntry::Bool(v, _) => {
412 if *v {
413 fmt.write_str("everywhere")
414 } else {
415 fmt.write_str("nowhere")
416 }
417 }
418
419 &CfgEntry::NameValue { name, value, .. } => {
420 let human_readable = match (*name, value) {
421 (sym::unix, None) => "Unix",
422 (sym::windows, None) => "Windows",
423 (sym::debug_assertions, None) => "debug-assertions enabled",
424 (sym::target_os, Some(os)) => match os.as_str() {
425 "android" => "Android",
426 "cygwin" => "Cygwin",
427 "dragonfly" => "DragonFly BSD",
428 "emscripten" => "Emscripten",
429 "freebsd" => "FreeBSD",
430 "fuchsia" => "Fuchsia",
431 "haiku" => "Haiku",
432 "hermit" => "HermitCore",
433 "illumos" => "illumos",
434 "ios" => "iOS",
435 "l4re" => "L4Re",
436 "linux" => "Linux",
437 "macos" => "macOS",
438 "netbsd" => "NetBSD",
439 "openbsd" => "OpenBSD",
440 "redox" => "Redox",
441 "solaris" => "Solaris",
442 "tvos" => "tvOS",
443 "wasi" => "WASI",
444 "watchos" => "watchOS",
445 "windows" => "Windows",
446 "visionos" => "visionOS",
447 _ => "",
448 },
449 (sym::target_arch, Some(arch)) => match arch.as_str() {
450 "aarch64" => "AArch64",
451 "arm" => "ARM",
452 "loongarch32" => "LoongArch LA32",
453 "loongarch64" => "LoongArch LA64",
454 "m68k" => "M68k",
455 "csky" => "CSKY",
456 "mips" => "MIPS",
457 "mips32r6" => "MIPS Release 6",
458 "mips64" => "MIPS-64",
459 "mips64r6" => "MIPS-64 Release 6",
460 "msp430" => "MSP430",
461 "powerpc" => "PowerPC",
462 "powerpc64" => "PowerPC-64",
463 "riscv32" => "RISC-V RV32",
464 "riscv64" => "RISC-V RV64",
465 "s390x" => "s390x",
466 "sparc64" => "SPARC64",
467 "wasm32" | "wasm64" => "WebAssembly",
468 "x86" => "x86",
469 "x86_64" => "x86-64",
470 _ => "",
471 },
472 (sym::target_vendor, Some(vendor)) => match vendor.as_str() {
473 "apple" => "Apple",
474 "pc" => "PC",
475 "sun" => "Sun",
476 "fortanix" => "Fortanix",
477 _ => "",
478 },
479 (sym::target_env, Some(env)) => match env.as_str() {
480 "gnu" => "GNU",
481 "msvc" => "MSVC",
482 "musl" => "musl",
483 "newlib" => "Newlib",
484 "uclibc" => "uClibc",
485 "sgx" => "SGX",
486 _ => "",
487 },
488 (sym::target_endian, Some(endian)) => {
489 return write!(fmt, "{endian}-endian");
490 }
491 (sym::target_pointer_width, Some(bits)) => {
492 return write!(fmt, "{bits}-bit");
493 }
494 (sym::target_feature, Some(feat)) => match self.1 {
495 Format::LongHtml => {
496 return write!(fmt, "target feature <code>{feat}</code>");
497 }
498 Format::LongPlain => return write!(fmt, "target feature `{feat}`"),
499 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
500 },
501 (sym::feature, Some(feat)) => match self.1 {
502 Format::LongHtml => {
503 return write!(fmt, "crate feature <code>{feat}</code>");
504 }
505 Format::LongPlain => return write!(fmt, "crate feature `{feat}`"),
506 Format::ShortHtml => return write!(fmt, "<code>{feat}</code>"),
507 },
508 _ => "",
509 };
510 if !human_readable.is_empty() {
511 fmt.write_str(human_readable)
512 } else {
513 let value = value
514 .map(|v| fmt::from_fn(move |f| write!(f, "={}", self.1.escape(v.as_str()))))
515 .maybe_display();
516 self.code_wrappers()
517 .wrap(format_args!("{}{value}", self.1.escape(name.as_str())))
518 .fmt(fmt)
519 }
520 }
521
522 CfgEntry::Version(..) => {
523 Ok(())
525 }
526 }
527 }
528}
529
530#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
531struct NameValueCfg {
532 name: Symbol,
533 value: Option<Symbol>,
534}
535
536impl NameValueCfg {
537 fn new(name: Symbol) -> Self {
538 Self { name, value: None }
539 }
540}
541
542impl<'a> From<&'a CfgEntry> for NameValueCfg {
543 fn from(cfg: &'a CfgEntry) -> Self {
544 match cfg {
545 CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value },
546 _ => NameValueCfg { name: sym::empty, value: None },
547 }
548 }
549}
550
551impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg {
552 fn from(cfg: &'a attrs::CfgInfo) -> Self {
553 Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) }
554 }
555}
556
557#[derive(Clone, Debug)]
559pub(crate) struct CfgInfo {
560 hidden_cfg: FxHashSet<NameValueCfg>,
563 current_cfg: Cfg,
566 auto_cfg_active: bool,
568 parent_is_doc_cfg: bool,
572}
573
574impl Default for CfgInfo {
575 fn default() -> Self {
576 Self {
577 hidden_cfg: FxHashSet::from_iter([
578 NameValueCfg::new(sym::test),
579 NameValueCfg::new(sym::doc),
580 NameValueCfg::new(sym::doctest),
581 ]),
582 current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)),
583 auto_cfg_active: true,
584 parent_is_doc_cfg: false,
585 }
586 }
587}
588
589fn show_hide_show_conflict_error(
590 tcx: TyCtxt<'_>,
591 item_span: rustc_span::Span,
592 previous: rustc_span::Span,
593) {
594 let mut diag = tcx.sess.dcx().struct_span_err(
595 item_span,
596 format!(
597 "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
598 ),
599 );
600 diag.span_note(previous, "first change was here");
601 diag.emit();
602}
603
604fn handle_auto_cfg_hide_show(
612 tcx: TyCtxt<'_>,
613 cfg_info: &mut CfgInfo,
614 attr: &CfgHideShow,
615 new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
616 new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
617) {
618 for value in &attr.values {
619 let simple = NameValueCfg::from(value);
620 if attr.kind == HideOrShow::Show {
621 if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) {
622 show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
623 } else {
624 new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
625 }
626 cfg_info.hidden_cfg.remove(&simple);
627 } else {
628 if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) {
629 show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span);
630 } else {
631 new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value());
632 }
633 cfg_info.hidden_cfg.insert(simple);
634 }
635 }
636}
637
638pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
639 attrs: I,
640 tcx: TyCtxt<'_>,
641 cfg_info: &mut CfgInfo,
642) -> Option<Arc<Cfg>> {
643 fn check_changed_auto_active_status(
644 changed_auto_active_status: &mut Option<rustc_span::Span>,
645 attr_span: Span,
646 cfg_info: &mut CfgInfo,
647 tcx: TyCtxt<'_>,
648 new_value: bool,
649 ) -> bool {
650 if let Some(first_change) = changed_auto_active_status {
651 if cfg_info.auto_cfg_active != new_value {
652 tcx.sess
653 .dcx()
654 .struct_span_err(
655 vec![*first_change, attr_span],
656 "`auto_cfg` was disabled and enabled more than once on the same item",
657 )
658 .emit();
659 return true;
660 }
661 } else {
662 *changed_auto_active_status = Some(attr_span);
663 }
664 cfg_info.auto_cfg_active = new_value;
665 false
666 }
667
668 let mut new_show_attrs = FxHashMap::default();
669 let mut new_hide_attrs = FxHashMap::default();
670
671 let mut doc_cfg = attrs
672 .clone()
673 .filter_map(|attr| match attr {
674 Attribute::Parsed(AttributeKind::Doc(d)) if !d.cfg.is_empty() => Some(d),
675 _ => None,
676 })
677 .peekable();
678 if doc_cfg.peek().is_some() {
680 if !cfg_info.parent_is_doc_cfg {
682 cfg_info.current_cfg = Cfg(CfgEntry::Bool(true, DUMMY_SP));
683 cfg_info.parent_is_doc_cfg = true;
684 }
685 for attr in doc_cfg {
686 for new_cfg in attr.cfg.clone() {
687 cfg_info.current_cfg &= Cfg(new_cfg);
688 }
689 }
690 } else {
691 cfg_info.parent_is_doc_cfg = false;
692 }
693
694 let mut changed_auto_active_status = None;
695
696 for attr in attrs {
698 if let Attribute::Parsed(AttributeKind::Doc(d)) = attr {
699 for (new_value, span) in &d.auto_cfg_change {
700 if check_changed_auto_active_status(
701 &mut changed_auto_active_status,
702 *span,
703 cfg_info,
704 tcx,
705 *new_value,
706 ) {
707 return None;
708 }
709 }
710 if let Some((_, span)) = d.auto_cfg.first() {
711 if check_changed_auto_active_status(
712 &mut changed_auto_active_status,
713 *span,
714 cfg_info,
715 tcx,
716 true,
717 ) {
718 return None;
719 }
720 for (value, _) in &d.auto_cfg {
721 handle_auto_cfg_hide_show(
722 tcx,
723 cfg_info,
724 value,
725 &mut new_show_attrs,
726 &mut new_hide_attrs,
727 );
728 }
729 }
730 } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
731 for (feature, _) in features {
734 cfg_info.current_cfg &= Cfg(CfgEntry::NameValue {
735 name: sym::target_feature,
736 value: Some(*feature),
737 span: DUMMY_SP,
738 });
739 }
740 continue;
741 } else if !cfg_info.parent_is_doc_cfg
742 && let hir::Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) = attr
743 {
744 for (new_cfg, _) in cfgs {
745 cfg_info.current_cfg &= Cfg(new_cfg.clone());
746 }
747 }
748 }
749
750 if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
753 None
754 } else if cfg_info.parent_is_doc_cfg {
755 if matches!(cfg_info.current_cfg.0, CfgEntry::Bool(true, _)) {
756 None
757 } else {
758 Some(Arc::new(cfg_info.current_cfg.clone()))
759 }
760 } else {
761 match strip_hidden(&cfg_info.current_cfg.0, &cfg_info.hidden_cfg) {
764 None | Some(CfgEntry::Bool(true, _)) => None,
765 Some(cfg) => Some(Arc::new(Cfg(cfg))),
766 }
767 }
768}