diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index 01b3688fabe6..45d0184d2509 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 9d570446e013..7df2cc33e90f 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(())