Make CfgExpr slimmer

This commit is contained in:
Lukas Wirth 2024-07-21 14:30:38 +02:00
parent 0851d21d1e
commit 1141c35a4b
6 changed files with 77 additions and 64 deletions

View file

@ -32,8 +32,8 @@ impl fmt::Display for CfgAtom {
pub enum CfgExpr { pub enum CfgExpr {
Invalid, Invalid,
Atom(CfgAtom), Atom(CfgAtom),
All(Vec<CfgExpr>), All(Box<[CfgExpr]>),
Any(Vec<CfgExpr>), Any(Box<[CfgExpr]>),
Not(Box<CfgExpr>), Not(Box<CfgExpr>),
} }
@ -90,12 +90,12 @@ fn next_cfg_expr<S>(it: &mut std::slice::Iter<'_, tt::TokenTree<S>>) -> Option<C
Some(tt::TokenTree::Subtree(subtree)) => { Some(tt::TokenTree::Subtree(subtree)) => {
it.next(); it.next();
let mut sub_it = subtree.token_trees.iter(); let mut sub_it = subtree.token_trees.iter();
let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect(); let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it));
match &name { match name {
s if *s == sym::all => CfgExpr::All(subs), s if s == sym::all => CfgExpr::All(subs.collect()),
s if *s == sym::any => CfgExpr::Any(subs), s if s == sym::any => CfgExpr::Any(subs.collect()),
s if *s == sym::not => { s if s == sym::not => {
CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))) CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
} }
_ => CfgExpr::Invalid, _ => CfgExpr::Invalid,
} }

View file

@ -27,7 +27,7 @@ struct Literal {
} }
impl DnfExpr { impl DnfExpr {
pub fn new(expr: CfgExpr) -> Self { pub fn new(expr: &CfgExpr) -> Self {
let builder = Builder { expr: DnfExpr { conjunctions: Vec::new() } }; let builder = Builder { expr: DnfExpr { conjunctions: Vec::new() } };
builder.lower(expr) builder.lower(expr)
@ -154,9 +154,9 @@ impl fmt::Display for DnfExpr {
} }
impl Conjunction { impl Conjunction {
fn new(parts: Vec<CfgExpr>) -> Self { fn new(parts: Box<[CfgExpr]>) -> Self {
let mut literals = Vec::new(); let mut literals = Vec::new();
for part in parts { for part in parts.into_vec() {
match part { match part {
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => {
literals.push(Literal::new(part)); literals.push(Literal::new(part));
@ -232,27 +232,28 @@ struct Builder {
} }
impl Builder { impl Builder {
fn lower(mut self, expr: CfgExpr) -> DnfExpr { fn lower(mut self, expr: &CfgExpr) -> DnfExpr {
let expr = make_nnf(expr); let expr = make_nnf(expr);
let expr = make_dnf(expr); let expr = make_dnf(expr);
match expr { match expr {
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => {
self.expr.conjunctions.push(Conjunction::new(vec![expr])); self.expr.conjunctions.push(Conjunction::new(Box::new([expr])));
} }
CfgExpr::All(conj) => { CfgExpr::All(conj) => {
self.expr.conjunctions.push(Conjunction::new(conj)); self.expr.conjunctions.push(Conjunction::new(conj));
} }
CfgExpr::Any(mut disj) => { CfgExpr::Any(disj) => {
let mut disj = disj.into_vec();
disj.reverse(); disj.reverse();
while let Some(conj) = disj.pop() { while let Some(conj) = disj.pop() {
match conj { match conj {
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::All(_) | CfgExpr::Not(_) => { CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::All(_) | CfgExpr::Not(_) => {
self.expr.conjunctions.push(Conjunction::new(vec![conj])); self.expr.conjunctions.push(Conjunction::new(Box::new([conj])));
} }
CfgExpr::Any(inner_disj) => { CfgExpr::Any(inner_disj) => {
// Flatten. // Flatten.
disj.extend(inner_disj.into_iter().rev()); disj.extend(inner_disj.into_vec().into_iter().rev());
} }
} }
} }
@ -266,11 +267,11 @@ impl Builder {
fn make_dnf(expr: CfgExpr) -> CfgExpr { fn make_dnf(expr: CfgExpr) -> CfgExpr {
match expr { match expr {
CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr, CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr,
CfgExpr::Any(e) => flatten(CfgExpr::Any(e.into_iter().map(make_dnf).collect())), CfgExpr::Any(e) => flatten(CfgExpr::Any(e.into_vec().into_iter().map(make_dnf).collect())),
CfgExpr::All(e) => { CfgExpr::All(e) => {
let e = e.into_iter().map(make_dnf).collect::<Vec<_>>(); let e = e.into_vec().into_iter().map(make_dnf).collect::<Vec<_>>();
flatten(CfgExpr::Any(distribute_conj(&e))) flatten(CfgExpr::Any(distribute_conj(&e).into_boxed_slice()))
} }
} }
} }
@ -281,7 +282,7 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
match rest { match rest {
[head, tail @ ..] => match head { [head, tail @ ..] => match head {
CfgExpr::Any(disj) => { CfgExpr::Any(disj) => {
for part in disj { for part in disj.iter() {
with.push(part.clone()); with.push(part.clone());
go(out, with, tail); go(out, with, tail);
with.pop(); with.pop();
@ -295,7 +296,7 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
}, },
_ => { _ => {
// Turn accumulated parts into a new conjunction. // Turn accumulated parts into a new conjunction.
out.push(CfgExpr::All(with.clone())); out.push(CfgExpr::All(with.clone().into_boxed_slice()));
} }
} }
} }
@ -308,25 +309,27 @@ fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
out out
} }
fn make_nnf(expr: CfgExpr) -> CfgExpr { fn make_nnf(expr: &CfgExpr) -> CfgExpr {
match expr { match expr {
CfgExpr::Invalid | CfgExpr::Atom(_) => expr, CfgExpr::Invalid | CfgExpr::Atom(_) => expr.clone(),
CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(make_nnf).collect()), CfgExpr::Any(expr) => CfgExpr::Any(expr.iter().map(make_nnf).collect()),
CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(make_nnf).collect()), CfgExpr::All(expr) => CfgExpr::All(expr.iter().map(make_nnf).collect()),
CfgExpr::Not(operand) => match *operand { CfgExpr::Not(operand) => make_nnf_neg(operand),
CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr
CfgExpr::Not(expr) => {
// Remove double negation.
make_nnf(*expr)
} }
}
fn make_nnf_neg(operand: &CfgExpr) -> CfgExpr {
match operand {
// Original negated expr
CfgExpr::Invalid => CfgExpr::Not(Box::new(CfgExpr::Invalid)), // Original negated expr
// Original negated expr
CfgExpr::Atom(atom) => CfgExpr::Not(Box::new(CfgExpr::Atom(atom.clone()))),
// Remove double negation.
CfgExpr::Not(expr) => make_nnf(expr),
// Convert negated conjunction/disjunction using DeMorgan's Law. // Convert negated conjunction/disjunction using DeMorgan's Law.
CfgExpr::Any(inner) => CfgExpr::All( CfgExpr::Any(inner) => CfgExpr::All(inner.iter().map(make_nnf_neg).collect()),
inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(), // Convert negated conjunction/disjunction using DeMorgan's Law.
), CfgExpr::All(inner) => CfgExpr::Any(inner.iter().map(make_nnf_neg).collect()),
CfgExpr::All(inner) => CfgExpr::Any(
inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(),
),
},
} }
} }
@ -335,20 +338,22 @@ fn flatten(expr: CfgExpr) -> CfgExpr {
match expr { match expr {
CfgExpr::All(inner) => CfgExpr::All( CfgExpr::All(inner) => CfgExpr::All(
inner inner
.into_iter() .iter()
.flat_map(|e| match e { .flat_map(|e| match e {
CfgExpr::All(inner) => inner, CfgExpr::All(inner) => inner.as_ref(),
_ => vec![e], _ => std::slice::from_ref(e),
}) })
.cloned()
.collect(), .collect(),
), ),
CfgExpr::Any(inner) => CfgExpr::Any( CfgExpr::Any(inner) => CfgExpr::Any(
inner inner
.into_iter() .iter()
.flat_map(|e| match e { .flat_map(|e| match e {
CfgExpr::Any(inner) => inner, CfgExpr::Any(inner) => inner.as_ref(),
_ => vec![e], _ => std::slice::from_ref(e),
}) })
.cloned()
.collect(), .collect(),
), ),
_ => expr, _ => expr,

View file

@ -29,7 +29,7 @@ fn check_dnf(input: &str, expect: Expect) {
DocCommentDesugarMode::ProcMacro, DocCommentDesugarMode::ProcMacro,
); );
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);
let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
expect.assert_eq(&actual); expect.assert_eq(&actual);
} }
@ -43,7 +43,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
DocCommentDesugarMode::ProcMacro, DocCommentDesugarMode::ProcMacro,
); );
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);
let dnf = DnfExpr::new(cfg); let dnf = DnfExpr::new(&cfg);
let why_inactive = dnf.why_inactive(opts).unwrap().to_string(); let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
expect.assert_eq(&why_inactive); expect.assert_eq(&why_inactive);
} }
@ -59,7 +59,7 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
DocCommentDesugarMode::ProcMacro, DocCommentDesugarMode::ProcMacro,
); );
let cfg = CfgExpr::parse(&tt); let cfg = CfgExpr::parse(&tt);
let dnf = DnfExpr::new(cfg); let dnf = DnfExpr::new(&cfg);
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>(); let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
assert_eq!(hints, expected_hints); assert_eq!(hints, expected_hints);
} }
@ -82,20 +82,28 @@ fn test_cfg_expr_parser() {
assert_parse_result( assert_parse_result(
r#"#![cfg(all(foo, bar = "baz"))]"#, r#"#![cfg(all(foo, bar = "baz"))]"#,
CfgExpr::All(vec![ CfgExpr::All(
vec![
CfgAtom::Flag(Symbol::intern("foo")).into(), CfgAtom::Flag(Symbol::intern("foo")).into(),
CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }.into(), CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
]), .into(),
]
.into_boxed_slice(),
),
); );
assert_parse_result( assert_parse_result(
r#"#![cfg(any(not(), all(), , bar = "baz",))]"#, r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
CfgExpr::Any(vec![ CfgExpr::Any(
vec![
CfgExpr::Not(Box::new(CfgExpr::Invalid)), CfgExpr::Not(Box::new(CfgExpr::Invalid)),
CfgExpr::All(vec![]), CfgExpr::All(Box::new([])),
CfgExpr::Invalid, CfgExpr::Invalid,
CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }.into(), CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
]), .into(),
]
.into_boxed_slice(),
),
); );
} }
@ -235,6 +243,6 @@ fn proptest() {
let mut u = Unstructured::new(&buf); let mut u = Unstructured::new(&buf);
let cfg = CfgExpr::arbitrary(&mut u).unwrap(); let cfg = CfgExpr::arbitrary(&mut u).unwrap();
DnfExpr::new(cfg); DnfExpr::new(&cfg);
} }
} }

View file

@ -287,8 +287,8 @@ where
} }
} }
let group = match &name { let group = match &name {
s if *s == sym::all => CfgExpr::All(preds), s if *s == sym::all => CfgExpr::All(preds.into_boxed_slice()),
s if *s == sym::any => CfgExpr::Any(preds), s if *s == sym::any => CfgExpr::Any(preds.into_boxed_slice()),
s if *s == sym::not => { s if *s == sym::not => {
CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))) CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid)))
} }
@ -343,7 +343,7 @@ mod tests {
assert_eq!(node.syntax().text_range().start(), 0.into()); assert_eq!(node.syntax().text_range().start(), 0.into());
let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap(); let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap();
let actual = format!("#![cfg({})]", DnfExpr::new(cfg)); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
expect.assert_eq(&actual); expect.assert_eq(&actual);
} }
#[test] #[test]

View file

@ -15,7 +15,7 @@ pub(crate) fn inactive_code(
return None; return None;
} }
let inactive = DnfExpr::new(d.cfg.clone()).why_inactive(&d.opts); let inactive = DnfExpr::new(&d.cfg).why_inactive(&d.opts);
let mut message = "code is inactive due to #[cfg] directives".to_owned(); let mut message = "code is inactive due to #[cfg] directives".to_owned();
if let Some(inactive) = inactive { if let Some(inactive) = inactive {

View file

@ -246,7 +246,7 @@ fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
preds.iter().for_each(|cfg| required_features(cfg, features)); preds.iter().for_each(|cfg| required_features(cfg, features));
} }
CfgExpr::Any(preds) => { CfgExpr::Any(preds) => {
for cfg in preds { for cfg in preds.iter() {
let len_features = features.len(); let len_features = features.len();
required_features(cfg, features); required_features(cfg, features);
if len_features != features.len() { if len_features != features.len() {