uv-resolver: fix bug in marker disjointness checking

I found this while testing the tracking of marker expressions across
resolver forks. Namely, given

    sys_platform == 'darwin' and implementation_name == 'pypy'

And:

    sys_platform == 'bar' or implementation_name == 'foo'

These should be disjoint, but the disjointness checker was reporting
them as overlapping. I fixed this by giving handling of disjunctions
higher precedence than conjunctions, although I am not 100% confident
that this is correct for all cases.
This commit is contained in:
Andrew Gallant 2024-06-15 09:12:41 -04:00 committed by Andrew Gallant
parent 407f1e370b
commit dbb12bcfe4

View file

@ -18,14 +18,14 @@ use pubgrub::range::Range as PubGrubRange;
pub(crate) fn is_disjoint(first: &MarkerTree, second: &MarkerTree) -> bool { pub(crate) fn is_disjoint(first: &MarkerTree, second: &MarkerTree) -> bool {
let (expr1, expr2) = match (first, second) { let (expr1, expr2) = match (first, second) {
(MarkerTree::Expression(expr1), MarkerTree::Expression(expr2)) => (expr1, expr2), (MarkerTree::Expression(expr1), MarkerTree::Expression(expr2)) => (expr1, expr2),
// `And` expressions are disjoint if any clause is disjoint.
(other, MarkerTree::And(exprs)) | (MarkerTree::And(exprs), other) => {
return exprs.iter().any(|tree1| is_disjoint(tree1, other))
}
// `Or` expressions are disjoint if all clauses are disjoint. // `Or` expressions are disjoint if all clauses are disjoint.
(other, MarkerTree::Or(exprs)) | (MarkerTree::Or(exprs), other) => { (other, MarkerTree::Or(exprs)) | (MarkerTree::Or(exprs), other) => {
return exprs.iter().all(|tree1| is_disjoint(tree1, other)) return exprs.iter().all(|tree1| is_disjoint(tree1, other))
} }
// `And` expressions are disjoint if any clause is disjoint.
(other, MarkerTree::And(exprs)) | (MarkerTree::And(exprs), other) => {
return exprs.iter().any(|tree1| is_disjoint(tree1, other));
}
}; };
match (expr1, expr2) { match (expr1, expr2) {
@ -529,6 +529,15 @@ mod tests {
"os_name == 'a' or platform_version == '1'", "os_name == 'a' or platform_version == '1'",
"os_name == 'a' or platform_version == '2'" "os_name == 'a' or platform_version == '2'"
)); ));
assert!(is_disjoint(
"sys_platform == 'darwin' and implementation_name == 'pypy'",
"sys_platform == 'bar' or implementation_name == 'foo'",
));
assert!(is_disjoint(
"sys_platform == 'bar' or implementation_name == 'foo'",
"sys_platform == 'darwin' and implementation_name == 'pypy'",
));
} }
fn test_version_bounds_disjointness(version: &str) { fn test_version_bounds_disjointness(version: &str) {