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