From 4d7913bfe3c08bcbe1980cfcac2434c027fd504c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 10:03:27 -0600 Subject: [PATCH 01/11] refactor(source): Qualify what alternatives we look for --- crates/resolver-tests/src/lib.rs | 2 +- src/cargo/core/resolver/errors.rs | 2 +- src/cargo/sources/directory.rs | 2 +- src/cargo/sources/path.rs | 4 ++-- src/cargo/sources/registry/mod.rs | 6 +++--- src/cargo/sources/source.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/resolver-tests/src/lib.rs b/crates/resolver-tests/src/lib.rs index 0fda8cf62c9..d77cbda97f2 100644 --- a/crates/resolver-tests/src/lib.rs +++ b/crates/resolver-tests/src/lib.rs @@ -143,7 +143,7 @@ pub fn resolve_with_global_context_raw( for summary in self.list.iter() { let matched = match kind { QueryKind::Exact => dep.matches(summary), - QueryKind::Alternatives => true, + QueryKind::AlternativeNames => true, QueryKind::Normalized => true, }; if matched { diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 3c5504bdf39..88c44a93dfd 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -304,7 +304,7 @@ pub(super) fn activation_error( // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing` // was meant. So we try asking the registry for a `fuzzy` search for suggestions. let candidates = loop { - match registry.query_vec(&new_dep, QueryKind::Alternatives) { + match registry.query_vec(&new_dep, QueryKind::AlternativeNames) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), Poll::Pending => match registry.block_until_ready() { diff --git a/src/cargo/sources/directory.rs b/src/cargo/sources/directory.rs index 6fff8ed2763..166624b8dcd 100644 --- a/src/cargo/sources/directory.rs +++ b/src/cargo/sources/directory.rs @@ -109,7 +109,7 @@ impl<'gctx> Source for DirectorySource<'gctx> { let packages = self.packages.values().map(|p| &p.0); let matches = packages.filter(|pkg| match kind { QueryKind::Exact => dep.matches(pkg.summary()), - QueryKind::Alternatives => true, + QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(pkg.summary()), }); for summary in matches.map(|pkg| pkg.summary().clone()) { diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 829f63ce103..e9dc0a0fbbf 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -146,7 +146,7 @@ impl<'gctx> Source for PathSource<'gctx> { if let Some(s) = self.package.as_ref().map(|p| p.summary()) { let matched = match kind { QueryKind::Exact => dep.matches(s), - QueryKind::Alternatives => true, + QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(s), }; if matched { @@ -333,7 +333,7 @@ impl<'gctx> Source for RecursivePathSource<'gctx> { { let matched = match kind { QueryKind::Exact => dep.matches(s), - QueryKind::Alternatives => true, + QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(s), }; if matched { diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index ed36c31f331..5bb5b2f4ebd 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -804,7 +804,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { dep.matches(s.as_summary()) } } - QueryKind::Alternatives => true, + QueryKind::AlternativeNames => true, QueryKind::Normalized => true, }; if !matched { @@ -839,7 +839,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { return Poll::Ready(Ok(())); } let mut any_pending = false; - if kind == QueryKind::Alternatives || kind == QueryKind::Normalized { + if kind == QueryKind::AlternativeNames || kind == QueryKind::Normalized { // Attempt to handle misspellings by searching for a chain of related // names to the original name. The resolver will later // reject any candidates that have the wrong name, and with this it'll @@ -859,7 +859,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { .query_inner(name_permutation, &req, &mut *self.ops, &mut |s| { if !s.is_yanked() { f(s); - } else if kind == QueryKind::Alternatives { + } else if kind == QueryKind::AlternativeNames { f(s); } })? diff --git a/src/cargo/sources/source.rs b/src/cargo/sources/source.rs index 377ac385e11..bdf6fb6870a 100644 --- a/src/cargo/sources/source.rs +++ b/src/cargo/sources/source.rs @@ -185,7 +185,7 @@ pub enum QueryKind { /// Path/Git sources may return all dependencies that are at that URI, /// whereas an `Registry` source may return dependencies that have the same /// canonicalization. - Alternatives, + AlternativeNames, /// Match a dependency in all ways and will normalize the package name. /// Each source defines what normalizing means. Normalized, From 31a884ef9e5d48899f2c1c4edf3f5825b70a69eb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 10:09:27 -0600 Subject: [PATCH 02/11] refactor(resolver): Qualify what kind of alt candidates we're discussing --- src/cargo/core/resolver/errors.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 88c44a93dfd..5ee38916842 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -303,7 +303,7 @@ pub(super) fn activation_error( } else { // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing` // was meant. So we try asking the registry for a `fuzzy` search for suggestions. - let candidates = loop { + let name_candidates = loop { match registry.query_vec(&new_dep, QueryKind::AlternativeNames) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), @@ -314,30 +314,33 @@ pub(super) fn activation_error( } }; - let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect(); + let mut name_candidates: Vec<_> = name_candidates + .into_iter() + .map(|s| s.into_summary()) + .collect(); - candidates.sort_unstable_by_key(|a| a.name()); - candidates.dedup_by(|a, b| a.name() == b.name()); - let mut candidates: Vec<_> = candidates + name_candidates.sort_unstable_by_key(|a| a.name()); + name_candidates.dedup_by(|a, b| a.name() == b.name()); + let mut name_candidates: Vec<_> = name_candidates .iter() .filter_map(|n| Some((edit_distance(&*new_dep.package_name(), &*n.name(), 3)?, n))) .collect(); - candidates.sort_by_key(|o| o.0); + name_candidates.sort_by_key(|o| o.0); let mut msg: String; - if candidates.is_empty() { + if name_candidates.is_empty() { msg = format!("no matching package named `{}` found\n", dep.package_name()); } else { msg = format!( "no matching package found\nsearched package name: `{}`\n", dep.package_name() ); - let mut names = candidates + let mut names = name_candidates .iter() .take(3) .map(|c| c.1.name().as_str()) .collect::>(); - if candidates.len() > 3 { + if name_candidates.len() > 3 { names.push("..."); } // Vertically align first suggestion with missing crate name @@ -347,7 +350,7 @@ pub(super) fn activation_error( String::default(), |acc, (i, el)| match i { 0 => acc + el, - i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el, + i if names.len() - 1 == i && name_candidates.len() <= 3 => acc + " or " + el, _ => acc + ", " + el, }, )); From eb0daec9915d029fd52cb77f629a32588286eeb3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 10:10:38 -0600 Subject: [PATCH 03/11] refactor(resolver): Group related alternatives logic --- src/cargo/core/resolver/errors.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 5ee38916842..8e55f1662b5 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -313,12 +313,10 @@ pub(super) fn activation_error( }, } }; - let mut name_candidates: Vec<_> = name_candidates .into_iter() .map(|s| s.into_summary()) .collect(); - name_candidates.sort_unstable_by_key(|a| a.name()); name_candidates.dedup_by(|a, b| a.name() == b.name()); let mut name_candidates: Vec<_> = name_candidates @@ -326,6 +324,7 @@ pub(super) fn activation_error( .filter_map(|n| Some((edit_distance(&*new_dep.package_name(), &*n.name(), 3)?, n))) .collect(); name_candidates.sort_by_key(|o| o.0); + let mut msg: String; if name_candidates.is_empty() { msg = format!("no matching package named `{}` found\n", dep.package_name()); From efe17de8a1748f42477b2fb421bc1959437f3a1d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 10:10:17 -0600 Subject: [PATCH 04/11] refactor(resolver): Make no alternatives the fall-through --- src/cargo/core/resolver/errors.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 8e55f1662b5..cd73db4cc79 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -326,9 +326,7 @@ pub(super) fn activation_error( name_candidates.sort_by_key(|o| o.0); let mut msg: String; - if name_candidates.is_empty() { - msg = format!("no matching package named `{}` found\n", dep.package_name()); - } else { + if !name_candidates.is_empty() { msg = format!( "no matching package found\nsearched package name: `{}`\n", dep.package_name() @@ -354,6 +352,8 @@ pub(super) fn activation_error( }, )); msg.push('\n'); + } else { + msg = format!("no matching package named `{}` found\n", dep.package_name()); } let mut location_searched_msg = registry.describe_source(dep.source_id()); From 271df34996edb103f679668bf6113b694bd2dfcb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 10:32:41 -0600 Subject: [PATCH 05/11] refactor(resolver): Switch some code from string construction to writing --- src/cargo/core/resolver/errors.rs | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index cd73db4cc79..946378ccb60 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::fmt::Write as _; use std::task::Poll; use crate::core::{Dependency, PackageId, Registry, Summary}; @@ -325,12 +326,10 @@ pub(super) fn activation_error( .collect(); name_candidates.sort_by_key(|o| o.0); - let mut msg: String; + let mut msg = String::new(); if !name_candidates.is_empty() { - msg = format!( - "no matching package found\nsearched package name: `{}`\n", - dep.package_name() - ); + let _ = writeln!(&mut msg, "no matching package found",); + let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name()); let mut names = name_candidates .iter() .take(3) @@ -342,18 +341,21 @@ pub(super) fn activation_error( } // Vertically align first suggestion with missing crate name // so a typo jumps out at you. - msg.push_str("perhaps you meant: "); - msg.push_str(&names.iter().enumerate().fold( - String::default(), - |acc, (i, el)| match i { + let suggestions = names + .iter() + .enumerate() + .fold(String::default(), |acc, (i, el)| match i { 0 => acc + el, i if names.len() - 1 == i && name_candidates.len() <= 3 => acc + " or " + el, _ => acc + ", " + el, - }, - )); - msg.push('\n'); + }); + let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}"); } else { - msg = format!("no matching package named `{}` found\n", dep.package_name()); + let _ = writeln!( + &mut msg, + "no matching package named `{}` found", + dep.package_name() + ); } let mut location_searched_msg = registry.describe_source(dep.source_id()); @@ -361,12 +363,12 @@ pub(super) fn activation_error( location_searched_msg = format!("{}", dep.source_id()); } - msg.push_str(&format!("location searched: {}\n", location_searched_msg)); - msg.push_str("required by "); - msg.push_str(&describe_path_in_context( - resolver_ctx, - &parent.package_id(), - )); + let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg); + let _ = write!( + &mut msg, + "required by {}", + describe_path_in_context(resolver_ctx, &parent.package_id()), + ); msg }; From 0160cb7537e8d752e315e82f9293599fcc67a92f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 10:48:43 -0600 Subject: [PATCH 06/11] refactor(registry): Move IndexSummary filtering from Index to RegistrySource --- src/cargo/sources/registry/index/mod.rs | 18 ++------------ src/cargo/sources/registry/mod.rs | 32 ++++++++++++++++++------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/cargo/sources/registry/index/mod.rs b/src/cargo/sources/registry/index/mod.rs index 0bcd2009c6f..b2a23139b41 100644 --- a/src/cargo/sources/registry/index/mod.rs +++ b/src/cargo/sources/registry/index/mod.rs @@ -37,7 +37,7 @@ use std::collections::HashMap; use std::path::Path; use std::str; use std::task::{ready, Poll}; -use tracing::{debug, info}; +use tracing::info; mod cache; use self::cache::CacheManager; @@ -370,21 +370,7 @@ impl<'gctx> RegistryIndex<'gctx> { .filter_map(move |(k, v)| if req.matches(k) { Some(v) } else { None }) .filter_map(move |maybe| { match maybe.parse(raw_data, source_id, bindeps) { - Ok(sum @ IndexSummary::Candidate(_) | sum @ IndexSummary::Yanked(_)) => { - Some(sum) - } - Ok(IndexSummary::Unsupported(summary, v)) => { - debug!( - "unsupported schema version {} ({} {})", - v, - summary.name(), - summary.version() - ); - None - } - Ok(IndexSummary::Offline(_)) => { - unreachable!("We do not check for off-line until later") - } + Ok(sum) => Some(sum), Err(e) => { info!("failed to parse `{}` registry package: {}", name, e); None diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 5bb5b2f4ebd..6242c1e26ec 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -779,7 +779,9 @@ impl<'gctx> Source for RegistrySource<'gctx> { ready!(self .index .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| { - if dep.matches(s.as_summary()) { + if matches!(s, IndexSummary::Candidate(_) | IndexSummary::Yanked(_)) + && dep.matches(s.as_summary()) + { // We are looking for a package from a lock file so we do not care about yank callback(s) } @@ -813,13 +815,27 @@ impl<'gctx> Source for RegistrySource<'gctx> { // Next filter out all yanked packages. Some yanked packages may // leak through if they're in a whitelist (aka if they were // previously in `Cargo.lock` - if !s.is_yanked() { - callback(s); - } else if self.yanked_whitelist.contains(&s.package_id()) { - callback(s); - } else if req.is_precise() { - precise_yanked_in_use = true; - callback(s); + match s { + s @ IndexSummary::Candidate(_) => callback(s), + s @ IndexSummary::Yanked(_) => { + if self.yanked_whitelist.contains(&s.package_id()) { + callback(s); + } else if req.is_precise() { + precise_yanked_in_use = true; + callback(s); + } + } + IndexSummary::Unsupported(summary, v) => { + tracing::debug!( + "unsupported schema version {} ({} {})", + v, + summary.name(), + summary.version() + ); + } + IndexSummary::Offline(summary) => { + tracing::debug!("offline ({} {})", summary.name(), summary.version()); + } } }))?; if precise_yanked_in_use { From e48a00825a2e04aa7f7b77760e7a5ebc1863f2bc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 11:03:21 -0600 Subject: [PATCH 07/11] refactor(registry): Pass along original IndexSummary state for deps --- src/cargo/core/registry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs index 37af3d7a130..50010772a84 100644 --- a/src/cargo/core/registry.rs +++ b/src/cargo/core/registry.rs @@ -733,8 +733,8 @@ impl<'gctx> Registry for PackageRegistry<'gctx> { return; } } - let summary = summary.into_summary(); - f(IndexSummary::Candidate(lock(locked, all_patches, summary))) + let summary = summary.map_summary(|summary| lock(locked, all_patches, summary)); + f(summary) }; return source.query(dep, kind, callback); } From 015e9802dacc20a2b187e66493d7f38993602b51 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 11:03:47 -0600 Subject: [PATCH 08/11] refactor(registry): Pass along original IndexSummary state for overrides --- src/cargo/core/registry.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs index 50010772a84..52cb1b0cc38 100644 --- a/src/cargo/core/registry.rs +++ b/src/cargo/core/registry.rs @@ -674,9 +674,10 @@ impl<'gctx> Registry for PackageRegistry<'gctx> { let patch = patches.remove(0); match override_summary { Some(override_summary) => { - let override_summary = override_summary.into_summary(); - self.warn_bad_override(&override_summary, &patch)?; - f(IndexSummary::Candidate(self.lock(override_summary))); + self.warn_bad_override(override_summary.as_summary(), &patch)?; + let override_summary = + override_summary.map_summary(|summary| self.lock(summary)); + f(override_summary); } None => f(IndexSummary::Candidate(patch)), } @@ -760,11 +761,11 @@ impl<'gctx> Registry for PackageRegistry<'gctx> { "found an override with a non-locked list" ))); } - let override_summary = override_summary.into_summary(); if let Some(to_warn) = to_warn { - self.warn_bad_override(&override_summary, to_warn.as_summary())?; + self.warn_bad_override(override_summary.as_summary(), to_warn.as_summary())?; } - f(IndexSummary::Candidate(self.lock(override_summary))); + let override_summary = override_summary.map_summary(|summary| self.lock(summary)); + f(override_summary); } } From d2fcf6b13f13e5b3aa53ca5581e1e140b15efad1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 11:30:56 -0600 Subject: [PATCH 09/11] fix(resolver): Report unmatched versions, rather than saying no package --- crates/resolver-tests/src/lib.rs | 1 + src/cargo/core/resolver/errors.rs | 46 ++++++++++++++++++++++++++++++- src/cargo/sources/directory.rs | 2 +- src/cargo/sources/path.rs | 4 +-- src/cargo/sources/registry/mod.rs | 3 +- src/cargo/sources/source.rs | 7 +++++ tests/testsuite/registry.rs | 15 ++++++---- 7 files changed, 68 insertions(+), 10 deletions(-) diff --git a/crates/resolver-tests/src/lib.rs b/crates/resolver-tests/src/lib.rs index d77cbda97f2..f34dd737650 100644 --- a/crates/resolver-tests/src/lib.rs +++ b/crates/resolver-tests/src/lib.rs @@ -143,6 +143,7 @@ pub fn resolve_with_global_context_raw( for summary in self.list.iter() { let matched = match kind { QueryKind::Exact => dep.matches(summary), + QueryKind::AlternativeVersions => dep.matches(summary), QueryKind::AlternativeNames => true, QueryKind::Normalized => true, }; diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 946378ccb60..fdf5db2501e 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -4,6 +4,7 @@ use std::task::Poll; use crate::core::{Dependency, PackageId, Registry, Summary}; use crate::sources::source::QueryKind; +use crate::sources::IndexSummary; use crate::util::edit_distance::edit_distance; use crate::util::{GlobalContext, OptVersionReq, VersionExt}; use anyhow::Error; @@ -302,6 +303,19 @@ pub(super) fn activation_error( msg } else { + // Maybe something is wrong with the available versions + let mut version_candidates = loop { + match registry.query_vec(&new_dep, QueryKind::AlternativeVersions) { + Poll::Ready(Ok(candidates)) => break candidates, + Poll::Ready(Err(e)) => return to_resolve_err(e), + Poll::Pending => match registry.block_until_ready() { + Ok(()) => continue, + Err(e) => return to_resolve_err(e), + }, + } + }; + version_candidates.sort_unstable_by_key(|a| a.as_summary().version().clone()); + // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing` // was meant. So we try asking the registry for a `fuzzy` search for suggestions. let name_candidates = loop { @@ -327,7 +341,37 @@ pub(super) fn activation_error( name_candidates.sort_by_key(|o| o.0); let mut msg = String::new(); - if !name_candidates.is_empty() { + if !version_candidates.is_empty() { + let _ = writeln!( + &mut msg, + "no matching versions for `{}` found", + dep.package_name() + ); + for candidate in version_candidates { + match candidate { + IndexSummary::Candidate(summary) => { + // HACK: If this was a real candidate, we wouldn't hit this case. + // so it must be a patch which get normalized to being a candidate + let _ = + writeln!(&mut msg, " version {} is unavailable", summary.version()); + } + IndexSummary::Yanked(summary) => { + let _ = writeln!(&mut msg, " version {} is yanked", summary.version()); + } + IndexSummary::Offline(summary) => { + let _ = writeln!(&mut msg, " version {} is not cached", summary.version()); + } + IndexSummary::Unsupported(summary, schema_version) => { + let _ = writeln!( + &mut msg, + " version {} requires a Cargo version that supports index version {}", + summary.version(), + schema_version + ); + } + } + } + } else if !name_candidates.is_empty() { let _ = writeln!(&mut msg, "no matching package found",); let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name()); let mut names = name_candidates diff --git a/src/cargo/sources/directory.rs b/src/cargo/sources/directory.rs index 166624b8dcd..b9c34690c52 100644 --- a/src/cargo/sources/directory.rs +++ b/src/cargo/sources/directory.rs @@ -108,7 +108,7 @@ impl<'gctx> Source for DirectorySource<'gctx> { } let packages = self.packages.values().map(|p| &p.0); let matches = packages.filter(|pkg| match kind { - QueryKind::Exact => dep.matches(pkg.summary()), + QueryKind::Exact | QueryKind::AlternativeVersions => dep.matches(pkg.summary()), QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(pkg.summary()), }); diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index e9dc0a0fbbf..f47225af63c 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -145,7 +145,7 @@ impl<'gctx> Source for PathSource<'gctx> { self.load()?; if let Some(s) = self.package.as_ref().map(|p| p.summary()) { let matched = match kind { - QueryKind::Exact => dep.matches(s), + QueryKind::Exact | QueryKind::AlternativeVersions => dep.matches(s), QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(s), }; @@ -332,7 +332,7 @@ impl<'gctx> Source for RecursivePathSource<'gctx> { .map(|p| p.summary()) { let matched = match kind { - QueryKind::Exact => dep.matches(s), + QueryKind::Exact | QueryKind::AlternativeVersions => dep.matches(s), QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(s), }; diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 6242c1e26ec..6a1451feb0c 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -799,7 +799,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { .index .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| { let matched = match kind { - QueryKind::Exact => { + QueryKind::Exact | QueryKind::AlternativeVersions => { if req.is_precise() && self.gctx.cli_unstable().unstable_options { dep.matches_prerelease(s.as_summary()) } else { @@ -816,6 +816,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { // leak through if they're in a whitelist (aka if they were // previously in `Cargo.lock` match s { + s @ _ if kind == QueryKind::AlternativeVersions => callback(s), s @ IndexSummary::Candidate(_) => callback(s), s @ IndexSummary::Yanked(_) => { if self.yanked_whitelist.contains(&s.package_id()) { diff --git a/src/cargo/sources/source.rs b/src/cargo/sources/source.rs index bdf6fb6870a..f4a29016bb9 100644 --- a/src/cargo/sources/source.rs +++ b/src/cargo/sources/source.rs @@ -183,6 +183,13 @@ pub enum QueryKind { /// Each source gets to define what `close` means for it. /// /// Path/Git sources may return all dependencies that are at that URI, + /// whereas an `Registry` source may return dependencies that are yanked or invalid. + AlternativeVersions, + /// A query for packages close to the given dependency requirement. + /// + /// Each source gets to define what `close` means for it. + /// + /// Path/Git sources may return all dependencies that are at that URI, /// whereas an `Registry` source may return dependencies that have the same /// canonicalization. AlternativeNames, diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 896aab44bb8..c8697dcc06c 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -924,7 +924,8 @@ fn yanks_in_lockfiles_are_ok_http() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching package named `bar` found +[ERROR] no matching versions for `bar` found + version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -941,7 +942,8 @@ fn yanks_in_lockfiles_are_ok_git() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching package named `bar` found +[ERROR] no matching versions for `bar` found + version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -993,7 +995,8 @@ fn yanks_in_lockfiles_are_ok_for_other_update_http() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching package named `bar` found +[ERROR] no matching versions for `bar` found + version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -1016,7 +1019,8 @@ fn yanks_in_lockfiles_are_ok_for_other_update_git() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching package named `bar` found +[ERROR] no matching versions for `bar` found + version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -3225,7 +3229,8 @@ fn unknown_index_version_error() { .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching package named `bar` found +[ERROR] no matching versions for `bar` found + version 1.0.1 requires a Cargo version that supports index version 4294967295 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` From 06b5ec5765e31223f00bfc404c935d61d86e0453 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 11:32:32 -0600 Subject: [PATCH 10/11] test(resolver): Show error with MSRV set --- tests/testsuite/registry.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index c8697dcc06c..56ca49dea3c 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -3238,6 +3238,43 @@ required by package `foo v0.1.0 ([ROOT]/foo)` .run(); } +#[cargo_test] +fn unknown_index_version_with_msrv_error() { + // If the version field is not understood, it is ignored. + Package::new("bar", "1.0.1") + .schema_version(u32::MAX) + .rust_version("1.2345") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile") + .with_status(101) + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[ERROR] no matching versions for `bar` found + version 1.0.1 requires a Cargo version that supports index version 4294967295 +location searched: `dummy-registry` index (which is replacing registry `crates-io`) +required by package `foo v0.1.0 ([ROOT]/foo)` + +"#]]) + .run(); +} + #[cargo_test] fn protocol() { cargo_process("install bar") From 0ed5c215a20fcc3d99d6256aa5757afad58edc11 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 5 Dec 2024 11:35:10 -0600 Subject: [PATCH 11/11] fix(resolver): Prefer rust-version over schema version --- src/cargo/core/resolver/errors.rs | 13 ++++++++++++- tests/testsuite/registry.rs | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index fdf5db2501e..74eb871c8d4 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -362,12 +362,23 @@ pub(super) fn activation_error( let _ = writeln!(&mut msg, " version {} is not cached", summary.version()); } IndexSummary::Unsupported(summary, schema_version) => { - let _ = writeln!( + if let Some(rust_version) = summary.rust_version() { + // HACK: technically its unsupported and we shouldn't make assumptions + // about the entry but this is limited and for diagnostics purposes + let _ = writeln!( + &mut msg, + " version {} requires cargo {}", + summary.version(), + rust_version + ); + } else { + let _ = writeln!( &mut msg, " version {} requires a Cargo version that supports index version {}", summary.version(), schema_version ); + } } } } diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 56ca49dea3c..2c3da8261a2 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -3267,7 +3267,7 @@ fn unknown_index_version_with_msrv_error() { .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] no matching versions for `bar` found - version 1.0.1 requires a Cargo version that supports index version 4294967295 + version 1.0.1 requires cargo 1.2345 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)`