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