| use std::fmt; | |
| use rustc_macros::{Decodable, Encodable, HashStable_Generic}; | |
| use rustc_span::{Span, Symbol}; | |
| use thin_vec::ThinVec; | |
| use crate::attr::version::RustcVersion; | |
| #[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)] | |
| pub enum CfgEntry { | |
| All(ThinVec<CfgEntry>, Span), | |
| Any(ThinVec<CfgEntry>, Span), | |
| Not(Box<CfgEntry>, Span), | |
| Bool(bool, Span), | |
| NameValue { name: Symbol, value: Option<Symbol>, span: Span }, | |
| Version(Option<RustcVersion>, Span), | |
| } | |
| impl CfgEntry { | |
| pub fn lower_spans(&mut self, lower_span: impl Copy + Fn(Span) -> Span) { | |
| match self { | |
| CfgEntry::All(subs, span) | CfgEntry::Any(subs, span) => { | |
| *span = lower_span(*span); | |
| subs.iter_mut().for_each(|sub| sub.lower_spans(lower_span)); | |
| } | |
| CfgEntry::Not(sub, span) => { | |
| *span = lower_span(*span); | |
| sub.lower_spans(lower_span); | |
| } | |
| CfgEntry::Bool(_, span) | |
| | CfgEntry::NameValue { span, .. } | |
| | CfgEntry::Version(_, span) => { | |
| *span = lower_span(*span); | |
| } | |
| } | |
| } | |
| pub fn span(&self) -> Span { | |
| let (Self::All(_, span) | |
| | Self::Any(_, span) | |
| | Self::Not(_, span) | |
| | Self::Bool(_, span) | |
| | Self::NameValue { span, .. } | |
| | Self::Version(_, span)) = self; | |
| *span | |
| } | |
| /// Same as `PartialEq` but doesn't check spans and ignore order of cfgs. | |
| pub fn is_equivalent_to(&self, other: &Self) -> bool { | |
| match (self, other) { | |
| (Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => { | |
| a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b))) | |
| } | |
| (Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b), | |
| (Self::Bool(a, _), Self::Bool(b, _)) => a == b, | |
| ( | |
| Self::NameValue { name: name1, value: value1, .. }, | |
| Self::NameValue { name: name2, value: value2, .. }, | |
| ) => name1 == name2 && value1 == value2, | |
| (Self::Version(a, _), Self::Version(b, _)) => a == b, | |
| _ => false, | |
| } | |
| } | |
| } | |
| impl fmt::Display for CfgEntry { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| fn write_entries( | |
| name: &str, | |
| entries: &[CfgEntry], | |
| f: &mut fmt::Formatter<'_>, | |
| ) -> fmt::Result { | |
| write!(f, "{name}(")?; | |
| for (nb, entry) in entries.iter().enumerate() { | |
| if nb != 0 { | |
| f.write_str(", ")?; | |
| } | |
| entry.fmt(f)?; | |
| } | |
| f.write_str(")") | |
| } | |
| match self { | |
| Self::All(entries, _) => write_entries("all", entries, f), | |
| Self::Any(entries, _) => write_entries("any", entries, f), | |
| Self::Not(entry, _) => write!(f, "not({entry})"), | |
| Self::Bool(value, _) => write!(f, "{value}"), | |
| Self::NameValue { name, value, .. } => { | |
| match value { | |
| // We use `as_str` and debug display to have characters escaped and `"` | |
| // characters surrounding the string. | |
| Some(value) => write!(f, "{name} = {:?}", value.as_str()), | |
| None => write!(f, "{name}"), | |
| } | |
| } | |
| Self::Version(version, _) => match version { | |
| Some(version) => write!(f, "{version}"), | |
| None => Ok(()), | |
| }, | |
| } | |
| } | |
| } |