1#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
4
5#[cfg(feature = "in-rust-tree")]
6extern crate rustc_driver as _;
7
8mod cfg_expr;
9mod dnf;
10#[cfg(test)]
11mod tests;
12
13use std::fmt;
14
15use rustc_hash::FxHashSet;
16
17use intern::{Symbol, sym};
18
19pub use cfg_expr::{CfgAtom, CfgExpr};
20pub use dnf::DnfExpr;
21
22#[derive(Clone, PartialEq, Eq)]
33pub struct CfgOptions {
34 enabled: FxHashSet<CfgAtom>,
35}
36
37impl Default for CfgOptions {
38 fn default() -> Self {
39 Self { enabled: FxHashSet::from_iter([CfgAtom::Flag(sym::true_)]) }
40 }
41}
42
43impl fmt::Debug for CfgOptions {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let mut items = self
46 .enabled
47 .iter()
48 .map(|atom| match atom {
49 CfgAtom::Flag(it) => it.to_string(),
50 CfgAtom::KeyValue { key, value } => format!("{key}={value}"),
51 })
52 .collect::<Vec<_>>();
53 items.sort();
54 f.debug_tuple("CfgOptions").field(&items).finish()
55 }
56}
57
58impl CfgOptions {
59 pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
60 cfg.fold(&|atom| self.enabled.contains(atom))
61 }
62
63 pub fn check_atom(&self, cfg: &CfgAtom) -> bool {
64 self.enabled.contains(cfg)
65 }
66
67 pub fn insert_atom(&mut self, key: Symbol) {
68 self.insert_any_atom(CfgAtom::Flag(key));
69 }
70
71 pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) {
72 self.insert_any_atom(CfgAtom::KeyValue { key, value });
73 }
74
75 pub fn apply_diff(&mut self, diff: CfgDiff) {
76 for atom in diff.enable {
77 self.insert_any_atom(atom);
78 }
79
80 for atom in diff.disable {
81 let (CfgAtom::Flag(sym) | CfgAtom::KeyValue { key: sym, .. }) = &atom;
82 if *sym == sym::true_ || *sym == sym::false_ {
83 tracing::error!("cannot remove `true` or `false` from cfg");
84 continue;
85 }
86 self.enabled.remove(&atom);
87 }
88 }
89
90 fn insert_any_atom(&mut self, atom: CfgAtom) {
91 let (CfgAtom::Flag(sym) | CfgAtom::KeyValue { key: sym, .. }) = &atom;
92 if *sym == sym::true_ || *sym == sym::false_ {
93 tracing::error!("cannot insert `true` or `false` to cfg");
94 return;
95 }
96 self.enabled.insert(atom);
97 }
98
99 pub fn get_cfg_keys(&self) -> impl Iterator<Item = &Symbol> {
100 self.enabled.iter().map(|it| match it {
101 CfgAtom::Flag(key) => key,
102 CfgAtom::KeyValue { key, .. } => key,
103 })
104 }
105
106 pub fn get_cfg_values<'a>(&'a self, cfg_key: &'a str) -> impl Iterator<Item = &'a Symbol> + 'a {
107 self.enabled.iter().filter_map(move |it| match it {
108 CfgAtom::KeyValue { key, value } if cfg_key == key.as_str() => Some(value),
109 _ => None,
110 })
111 }
112
113 pub fn to_hashable(&self) -> HashableCfgOptions {
114 let mut enabled = self.enabled.iter().cloned().collect::<Box<[_]>>();
115 enabled.sort_unstable();
116 HashableCfgOptions { _enabled: enabled }
117 }
118
119 #[inline]
120 pub fn shrink_to_fit(&mut self) {
121 self.enabled.shrink_to_fit();
122 }
123
124 pub fn append(&mut self, other: CfgOptions) {
125 self.enabled.extend(other.enabled);
129 }
130}
131
132impl Extend<CfgAtom> for CfgOptions {
133 fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
134 iter.into_iter().for_each(|cfg_flag| self.insert_any_atom(cfg_flag));
135 }
136}
137
138impl IntoIterator for CfgOptions {
139 type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;
140
141 type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
142
143 fn into_iter(self) -> Self::IntoIter {
144 <FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
145 }
146}
147
148impl<'a> IntoIterator for &'a CfgOptions {
149 type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;
150
151 type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
152
153 fn into_iter(self) -> Self::IntoIter {
154 <&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
155 }
156}
157
158impl FromIterator<CfgAtom> for CfgOptions {
159 fn from_iter<T: IntoIterator<Item = CfgAtom>>(iter: T) -> Self {
160 let mut options = CfgOptions::default();
161 options.extend(iter);
162 options
163 }
164}
165
166#[derive(Default, Clone, Debug, PartialEq, Eq)]
167pub struct CfgDiff {
168 enable: Vec<CfgAtom>,
170 disable: Vec<CfgAtom>,
171}
172
173impl CfgDiff {
174 pub fn new(mut enable: Vec<CfgAtom>, mut disable: Vec<CfgAtom>) -> CfgDiff {
176 enable.sort();
177 enable.dedup();
178 disable.sort();
179 disable.dedup();
180 for i in (0..enable.len()).rev() {
181 if let Some(j) = disable.iter().position(|atom| *atom == enable[i]) {
182 enable.remove(i);
183 disable.remove(j);
184 }
185 }
186
187 CfgDiff { enable, disable }
188 }
189
190 pub fn len(&self) -> usize {
192 self.enable.len() + self.disable.len()
193 }
194
195 pub fn is_empty(&self) -> bool {
196 self.len() == 0
197 }
198}
199
200impl fmt::Display for CfgDiff {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 if !self.enable.is_empty() {
203 f.write_str("enable ")?;
204 for (i, atom) in self.enable.iter().enumerate() {
205 let sep = match i {
206 0 => "",
207 _ if i == self.enable.len() - 1 => " and ",
208 _ => ", ",
209 };
210 f.write_str(sep)?;
211
212 atom.fmt(f)?;
213 }
214
215 if !self.disable.is_empty() {
216 f.write_str("; ")?;
217 }
218 }
219
220 if !self.disable.is_empty() {
221 f.write_str("disable ")?;
222 for (i, atom) in self.disable.iter().enumerate() {
223 let sep = match i {
224 0 => "",
225 _ if i == self.enable.len() - 1 => " and ",
226 _ => ", ",
227 };
228 f.write_str(sep)?;
229
230 atom.fmt(f)?;
231 }
232 }
233
234 Ok(())
235 }
236}
237
238pub struct InactiveReason {
239 enabled: Vec<CfgAtom>,
240 disabled: Vec<CfgAtom>,
241}
242
243impl fmt::Display for InactiveReason {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 if !self.enabled.is_empty() {
246 for (i, atom) in self.enabled.iter().enumerate() {
247 let sep = match i {
248 0 => "",
249 _ if i == self.enabled.len() - 1 => " and ",
250 _ => ", ",
251 };
252 f.write_str(sep)?;
253
254 atom.fmt(f)?;
255 }
256 let is_are = if self.enabled.len() == 1 { "is" } else { "are" };
257 write!(f, " {is_are} enabled")?;
258
259 if !self.disabled.is_empty() {
260 f.write_str(" and ")?;
261 }
262 }
263
264 if !self.disabled.is_empty() {
265 for (i, atom) in self.disabled.iter().enumerate() {
266 let sep = match i {
267 0 => "",
268 _ if i == self.disabled.len() - 1 => " and ",
269 _ => ", ",
270 };
271 f.write_str(sep)?;
272
273 atom.fmt(f)?;
274 }
275 let is_are = if self.disabled.len() == 1 { "is" } else { "are" };
276 write!(f, " {is_are} disabled")?;
277 }
278
279 Ok(())
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, Hash)]
285pub struct HashableCfgOptions {
286 _enabled: Box<[CfgAtom]>,
287}