]> git.lizzy.rs Git - rust.git/blob - crates/cfg/src/lib.rs
Merge #11067
[rust.git] / crates / cfg / src / lib.rs
1 //! cfg defines conditional compiling options, `cfg` attribute parser and evaluator
2
3 mod cfg_expr;
4 mod dnf;
5 #[cfg(test)]
6 mod tests;
7
8 use std::fmt;
9
10 use rustc_hash::FxHashSet;
11 use tt::SmolStr;
12
13 pub use cfg_expr::{CfgAtom, CfgExpr};
14 pub use dnf::DnfExpr;
15
16 /// Configuration options used for conditional compilation on items with `cfg` attributes.
17 /// We have two kind of options in different namespaces: atomic options like `unix`, and
18 /// key-value options like `target_arch="x86"`.
19 ///
20 /// Note that for key-value options, one key can have multiple values (but not none).
21 /// `feature` is an example. We have both `feature="foo"` and `feature="bar"` if features
22 /// `foo` and `bar` are both enabled. And here, we store key-value options as a set of tuple
23 /// of key and value in `key_values`.
24 ///
25 /// See: <https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options>
26 #[derive(Clone, PartialEq, Eq, Default)]
27 pub struct CfgOptions {
28     enabled: FxHashSet<CfgAtom>,
29 }
30
31 impl fmt::Debug for CfgOptions {
32     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33         let mut items = self
34             .enabled
35             .iter()
36             .map(|atom| match atom {
37                 CfgAtom::Flag(it) => it.to_string(),
38                 CfgAtom::KeyValue { key, value } => format!("{}={}", key, value),
39             })
40             .collect::<Vec<_>>();
41         items.sort();
42         f.debug_tuple("CfgOptions").field(&items).finish()
43     }
44 }
45
46 impl CfgOptions {
47     pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
48         cfg.fold(&|atom| self.enabled.contains(atom))
49     }
50
51     pub fn insert_atom(&mut self, key: SmolStr) {
52         self.enabled.insert(CfgAtom::Flag(key));
53     }
54
55     pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) {
56         self.enabled.insert(CfgAtom::KeyValue { key, value });
57     }
58
59     pub fn apply_diff(&mut self, diff: CfgDiff) {
60         for atom in diff.enable {
61             self.enabled.insert(atom);
62         }
63
64         for atom in diff.disable {
65             self.enabled.remove(&atom);
66         }
67     }
68
69     pub fn get_cfg_keys(&self) -> impl Iterator<Item = &SmolStr> {
70         self.enabled.iter().map(|x| match x {
71             CfgAtom::Flag(key) => key,
72             CfgAtom::KeyValue { key, .. } => key,
73         })
74     }
75
76     pub fn get_cfg_values<'a>(
77         &'a self,
78         cfg_key: &'a str,
79     ) -> impl Iterator<Item = &'a SmolStr> + 'a {
80         self.enabled.iter().filter_map(move |x| match x {
81             CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
82             _ => None,
83         })
84     }
85 }
86
87 #[derive(Clone, Debug, PartialEq, Eq)]
88 pub struct CfgDiff {
89     // Invariants: No duplicates, no atom that's both in `enable` and `disable`.
90     enable: Vec<CfgAtom>,
91     disable: Vec<CfgAtom>,
92 }
93
94 impl CfgDiff {
95     /// Create a new CfgDiff. Will return None if the same item appears more than once in the set
96     /// of both.
97     pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> {
98         let mut occupied = FxHashSet::default();
99         for item in enable.iter().chain(disable.iter()) {
100             if !occupied.insert(item) {
101                 // was present
102                 return None;
103             }
104         }
105
106         Some(CfgDiff { enable, disable })
107     }
108
109     /// Returns the total number of atoms changed by this diff.
110     pub fn len(&self) -> usize {
111         self.enable.len() + self.disable.len()
112     }
113
114     pub fn is_empty(&self) -> bool {
115         self.len() == 0
116     }
117 }
118
119 impl fmt::Display for CfgDiff {
120     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121         if !self.enable.is_empty() {
122             f.write_str("enable ")?;
123             for (i, atom) in self.enable.iter().enumerate() {
124                 let sep = match i {
125                     0 => "",
126                     _ if i == self.enable.len() - 1 => " and ",
127                     _ => ", ",
128                 };
129                 f.write_str(sep)?;
130
131                 write!(f, "{}", atom)?;
132             }
133
134             if !self.disable.is_empty() {
135                 f.write_str("; ")?;
136             }
137         }
138
139         if !self.disable.is_empty() {
140             f.write_str("disable ")?;
141             for (i, atom) in self.disable.iter().enumerate() {
142                 let sep = match i {
143                     0 => "",
144                     _ if i == self.enable.len() - 1 => " and ",
145                     _ => ", ",
146                 };
147                 f.write_str(sep)?;
148
149                 write!(f, "{}", atom)?;
150             }
151         }
152
153         Ok(())
154     }
155 }
156
157 pub struct InactiveReason {
158     enabled: Vec<CfgAtom>,
159     disabled: Vec<CfgAtom>,
160 }
161
162 impl fmt::Display for InactiveReason {
163     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164         if !self.enabled.is_empty() {
165             for (i, atom) in self.enabled.iter().enumerate() {
166                 let sep = match i {
167                     0 => "",
168                     _ if i == self.enabled.len() - 1 => " and ",
169                     _ => ", ",
170                 };
171                 f.write_str(sep)?;
172
173                 write!(f, "{}", atom)?;
174             }
175             let is_are = if self.enabled.len() == 1 { "is" } else { "are" };
176             write!(f, " {} enabled", is_are)?;
177
178             if !self.disabled.is_empty() {
179                 f.write_str(" and ")?;
180             }
181         }
182
183         if !self.disabled.is_empty() {
184             for (i, atom) in self.disabled.iter().enumerate() {
185                 let sep = match i {
186                     0 => "",
187                     _ if i == self.disabled.len() - 1 => " and ",
188                     _ => ", ",
189                 };
190                 f.write_str(sep)?;
191
192                 write!(f, "{}", atom)?;
193             }
194             let is_are = if self.disabled.len() == 1 { "is" } else { "are" };
195             write!(f, " {} disabled", is_are)?;
196         }
197
198         Ok(())
199     }
200 }