rustc_ast/attr/
data_structures.rs

1use std::fmt;
2
3use rustc_macros::{Decodable, Encodable, HashStable_Generic};
4use rustc_span::{Span, Symbol};
5use thin_vec::ThinVec;
6
7use crate::attr::version::RustcVersion;
8
9#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)]
10pub enum CfgEntry {
11    All(ThinVec<CfgEntry>, Span),
12    Any(ThinVec<CfgEntry>, Span),
13    Not(Box<CfgEntry>, Span),
14    Bool(bool, Span),
15    NameValue { name: Symbol, value: Option<Symbol>, span: Span },
16    Version(Option<RustcVersion>, Span),
17}
18
19impl CfgEntry {
20    pub fn lower_spans(&mut self, lower_span: impl Copy + Fn(Span) -> Span) {
21        match self {
22            CfgEntry::All(subs, span) | CfgEntry::Any(subs, span) => {
23                *span = lower_span(*span);
24                subs.iter_mut().for_each(|sub| sub.lower_spans(lower_span));
25            }
26            CfgEntry::Not(sub, span) => {
27                *span = lower_span(*span);
28                sub.lower_spans(lower_span);
29            }
30            CfgEntry::Bool(_, span)
31            | CfgEntry::NameValue { span, .. }
32            | CfgEntry::Version(_, span) => {
33                *span = lower_span(*span);
34            }
35        }
36    }
37
38    pub fn span(&self) -> Span {
39        let (Self::All(_, span)
40        | Self::Any(_, span)
41        | Self::Not(_, span)
42        | Self::Bool(_, span)
43        | Self::NameValue { span, .. }
44        | Self::Version(_, span)) = self;
45        *span
46    }
47
48    /// Same as `PartialEq` but doesn't check spans and ignore order of cfgs.
49    pub fn is_equivalent_to(&self, other: &Self) -> bool {
50        match (self, other) {
51            (Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => {
52                a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b)))
53            }
54            (Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b),
55            (Self::Bool(a, _), Self::Bool(b, _)) => a == b,
56            (
57                Self::NameValue { name: name1, value: value1, .. },
58                Self::NameValue { name: name2, value: value2, .. },
59            ) => name1 == name2 && value1 == value2,
60            (Self::Version(a, _), Self::Version(b, _)) => a == b,
61            _ => false,
62        }
63    }
64}
65
66impl fmt::Display for CfgEntry {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        fn write_entries(
69            name: &str,
70            entries: &[CfgEntry],
71            f: &mut fmt::Formatter<'_>,
72        ) -> fmt::Result {
73            write!(f, "{name}(")?;
74            for (nb, entry) in entries.iter().enumerate() {
75                if nb != 0 {
76                    f.write_str(", ")?;
77                }
78                entry.fmt(f)?;
79            }
80            f.write_str(")")
81        }
82        match self {
83            Self::All(entries, _) => write_entries("all", entries, f),
84            Self::Any(entries, _) => write_entries("any", entries, f),
85            Self::Not(entry, _) => write!(f, "not({entry})"),
86            Self::Bool(value, _) => write!(f, "{value}"),
87            Self::NameValue { name, value, .. } => {
88                match value {
89                    // We use `as_str` and debug display to have characters escaped and `"`
90                    // characters surrounding the string.
91                    Some(value) => write!(f, "{name} = {:?}", value.as_str()),
92                    None => write!(f, "{name}"),
93                }
94            }
95            Self::Version(version, _) => match version {
96                Some(version) => write!(f, "{version}"),
97                None => Ok(()),
98            },
99        }
100    }
101}