revset: eliminate double negates

Writing double negates is silly, but it might be hidden by revset alias
if we added such feature.

I made fold_redundant_expression() a separate step from fold_difference()
since I'll probably want to apply the cleanup step before rewriting filter
expressions.
This commit is contained in:
Yuya Nishihara 2022-11-18 13:43:31 +09:00
parent 54044ea8d6
commit 0e99747728

View File

@ -1223,18 +1223,24 @@ fn internalize_filter_intersection(
})
}
/// Eliminates redundant intersection with `all()`.
fn fold_intersection_with_all(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
transform_expression_bottom_up(expression, |expression| {
if let RevsetExpression::Intersection(expression1, expression2) = expression.as_ref() {
/// Eliminates redundant nodes like `x & all()`, `~~x`.
///
/// This does not rewrite 'x & none()' to 'none()' because 'x' may be an invalid
/// symbol.
fn fold_redundant_expression(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
RevsetExpression::NotIn(outer) => match outer.as_ref() {
RevsetExpression::NotIn(inner) => Some(inner.clone()),
_ => None,
},
RevsetExpression::Intersection(expression1, expression2) => {
match (expression1.as_ref(), expression2.as_ref()) {
(_, RevsetExpression::All) => Some(expression1.clone()),
(RevsetExpression::All, _) => Some(expression2.clone()),
_ => None,
}
} else {
None
}
_ => None,
})
}
@ -1257,7 +1263,7 @@ fn fold_difference(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpress
/// tree.
pub fn optimize(expression: Rc<RevsetExpression>) -> Rc<RevsetExpression> {
let expression = internalize_filter_intersection(&expression).unwrap_or(expression);
let expression = fold_intersection_with_all(&expression).unwrap_or(expression);
let expression = fold_redundant_expression(&expression).unwrap_or(expression);
fold_difference(&expression).unwrap_or(expression)
}
@ -2505,6 +2511,38 @@ mod tests {
)
"###);
// Double/triple negates.
insta::assert_debug_snapshot!(optimize(parse("foo & ~~bar").unwrap()), @r###"
Intersection(
Symbol(
"foo",
),
Symbol(
"bar",
),
)
"###);
insta::assert_debug_snapshot!(optimize(parse("foo & ~~~bar").unwrap()), @r###"
Difference(
Symbol(
"foo",
),
Symbol(
"bar",
),
)
"###);
insta::assert_debug_snapshot!(optimize(parse("~(all() & ~foo) & bar").unwrap()), @r###"
Intersection(
Symbol(
"foo",
),
Symbol(
"bar",
),
)
"###);
// Should be better than '(all() & ~foo) & (all() & ~bar)'.
insta::assert_debug_snapshot!(optimize(parse("~foo & ~bar").unwrap()), @r###"
Difference(