From 4091cce1f1f57d4424a20ceeb8be82c7b670b63b Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Mon, 16 Dec 2024 15:31:35 -0600 Subject: [PATCH] Fix redundant enumeration of all package versions in some resolver errors (#9885) Closes #4075 There are many more redundant enumerations I want to look into as well. --- crates/uv-resolver/src/error.rs | 57 +++++++++++++++++++++++++++++++ crates/uv/tests/it/pip_compile.rs | 11 +----- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index 01b3688fa..45d0184d2 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -333,6 +333,7 @@ impl std::fmt::Display for NoSolutionError { } collapse_unavailable_versions(&mut tree); + collapse_redundant_no_versions(&mut tree); collapse_redundant_depends_on_no_versions(&mut tree); if should_display_tree { @@ -427,6 +428,62 @@ fn display_tree_inner( } } +fn collapse_redundant_no_versions( + tree: &mut DerivationTree, UnavailableReason>, +) { + match tree { + DerivationTree::External(_) => {} + DerivationTree::Derived(derived) => { + match ( + Arc::make_mut(&mut derived.cause1), + Arc::make_mut(&mut derived.cause2), + ) { + // If we have a node for a package with no versions... + ( + DerivationTree::External(External::NoVersions(package, versions)), + ref mut other, + ) + | ( + ref mut other, + DerivationTree::External(External::NoVersions(package, versions)), + ) => { + // First, always recursively visit the other side of the tree + collapse_redundant_no_versions(other); + + let DerivationTree::Derived(derived) = other else { + return; + }; + + // If the range in the conclusion (terms) matches the range of no versions, + // then we'll drop this node + let Some(Term::Positive(term)) = derived.terms.get(package) else { + return; + }; + let versions = versions.complement(); + + // If we're disqualifying a single version, this is important to retain, e.g, + // for `only foo==1.0.0 is available` + if versions.as_singleton().is_some() { + return; + } + + if *term != versions { + return; + } + + // Replace this node with the other tree + *tree = other.clone(); + } + // If not, just recurse + _ => { + collapse_redundant_no_versions(Arc::make_mut(&mut derived.cause1)); + collapse_redundant_no_versions(Arc::make_mut(&mut derived.cause2)); + } + } + } + } +} + /// Given a [`DerivationTree`], collapse any `NoVersion` incompatibilities for workspace members /// to avoid saying things like "only ==0.1.0 is available". fn collapse_no_versions_of_workspace_members( diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 9d570446e..7df2cc33e 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -13126,16 +13126,7 @@ fn compile_enumerate_no_versions() -> Result<()> { ----- stderr ----- × No solution found when resolving dependencies: ╰─▶ Because the current Python version (3.10.[X]) does not satisfy Python>=3.11,<4.0 and all versions of rooster-blue depend on Python>=3.11,<4.0, we can conclude that all versions of rooster-blue cannot be used. - And because only the following versions of rooster-blue are available: - rooster-blue==0.0.1 - rooster-blue==0.0.2 - rooster-blue==0.0.3 - rooster-blue==0.0.4 - rooster-blue==0.0.5 - rooster-blue==0.0.6 - rooster-blue==0.0.7 - rooster-blue==0.0.8 - and you require rooster-blue, we can conclude that your requirements are unsatisfiable. + And because you require rooster-blue, we can conclude that your requirements are unsatisfiable. "###); Ok(())