diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index e67ec9ede01..6d56cb33ca8 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -203,100 +203,115 @@ pub(super) fn activation_error( }; candidates.sort_unstable_by(|a, b| b.version().cmp(a.version())); - let mut msg = if !candidates.is_empty() { - let versions = { - let mut versions = candidates - .iter() - .take(3) - .map(|cand| cand.version().to_string()) - .collect::>(); + let mut msg = + if !candidates.is_empty() { + let versions = { + let mut versions = candidates + .iter() + .take(3) + .map(|cand| cand.version().to_string()) + .collect::>(); + + if candidates.len() > 3 { + versions.push("...".into()); + } + + versions.join(", ") + }; + + let mut msg = format!( + "failed to select a version for the requirement `{} = \"{}\"`\n \ + candidate versions found which didn't match: {}\n \ + location searched: {}\n", + dep.package_name(), + dep.version_req(), + versions, + registry.describe_source(dep.source_id()), + ); + msg.push_str("required by "); + msg.push_str(&describe_path( + &cx.parents.path_to_bottom(&parent.package_id()), + )); - if candidates.len() > 3 { - versions.push("...".into()); + // If we have a path dependency with a locked version, then this may + // indicate that we updated a sub-package and forgot to run `cargo + // update`. In this case try to print a helpful error! + if dep.source_id().is_path() && dep.version_req().to_string().starts_with('=') { + msg.push_str( + "\nconsider running `cargo update` to update \ + a path dependency's locked version", + ); } - versions.join(", ") - }; - - let mut msg = format!( - "failed to select a version for the requirement `{} = \"{}\"`\n \ - candidate versions found which didn't match: {}\n \ - location searched: {}\n", - dep.package_name(), - dep.version_req(), - versions, - registry.describe_source(dep.source_id()), - ); - msg.push_str("required by "); - msg.push_str(&describe_path( - &cx.parents.path_to_bottom(&parent.package_id()), - )); - - // If we have a path dependency with a locked version, then this may - // indicate that we updated a sub-package and forgot to run `cargo - // update`. In this case try to print a helpful error! - if dep.source_id().is_path() && dep.version_req().to_string().starts_with('=') { - msg.push_str( - "\nconsider running `cargo update` to update \ - a path dependency's locked version", - ); - } - - if registry.is_replaced(dep.source_id()) { - msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?"); - } + if registry.is_replaced(dep.source_id()) { + msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?"); + } - msg - } 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 mut candidates = Vec::new(); - if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s.name()), true) { - return to_resolve_err(e); - }; - candidates.sort_unstable(); - candidates.dedup(); - let mut candidates: Vec<_> = candidates - .iter() - .map(|n| (lev_distance(&*new_dep.package_name(), &*n), n)) - .filter(|&(d, _)| d < 4) - .collect(); - candidates.sort_by_key(|o| o.0); - let mut msg = format!( - "no matching package named `{}` found\n\ - location searched: {}\n", - dep.package_name(), - dep.source_id() - ); - if !candidates.is_empty() { - let mut names = candidates + msg + } 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 mut candidates = Vec::new(); + if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s.clone()), true) { + return to_resolve_err(e); + }; + candidates.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + candidates.dedup_by(|a, b| a.name() == b.name()); + let mut candidates: Vec<_> = candidates .iter() - .take(3) - .map(|c| c.1.as_str()) - .collect::>(); - - if candidates.len() > 3 { - names.push("..."); + .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n)) + .filter(|&(d, _)| d < 4) + .collect(); + candidates.sort_by_key(|o| o.0); + let mut msg = format!( + "no matching package named `{}` found\n\ + location searched: {}\n", + dep.package_name(), + dep.source_id() + ); + if !candidates.is_empty() { + // If dependency package name is equal to the name of the candidate here + // it may be a prerelease package which hasn't been speficied correctly + if dep.package_name() == candidates[0].1.name() + && candidates[0].1.package_id().version().is_prerelease() + { + msg.push_str("prerelease package needs to be specified explicitly\n"); + msg.push_str(&format!( + "{name} = {{ version = \"{version}\" }}", + name = candidates[0].1.name(), + version = candidates[0].1.package_id().version() + )); + } else { + let mut names = candidates + .iter() + .take(3) + .map(|c| c.1.name().as_str()) + .collect::>(); + + if candidates.len() > 3 { + names.push("..."); + } + + msg.push_str("perhaps you meant: "); + msg.push_str(&names.iter().enumerate().fold( + String::default(), + |acc, (i, el)| match i { + 0 => acc + el, + i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el, + _ => acc + ", " + el, + }, + )); + } + + msg.push_str("\n"); } - - msg.push_str("perhaps you meant: "); - msg.push_str(&names.iter().enumerate().fold( - String::default(), - |acc, (i, el)| match i { - 0 => acc + el, - i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el, - _ => acc + ", " + el, - }, + msg.push_str("required by "); + msg.push_str(&describe_path( + &cx.parents.path_to_bottom(&parent.package_id()), )); - msg.push_str("\n"); - } - msg.push_str("required by "); - msg.push_str(&describe_path( - &cx.parents.path_to_bottom(&parent.package_id()), - )); - msg - }; + msg + }; if let Some(config) = config { if config.offline() { diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 9fb92d80f65..2db759ac1e2 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -1395,6 +1395,55 @@ fn use_semver() { p.cargo("build").run(); } +#[cargo_test] +fn use_semver_package_incorrectly() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [project] + name = "a" + version = "0.1.1-alpha.0" + authors = [] + "#, + ) + .file( + "b/Cargo.toml", + r#" + [project] + name = "b" + version = "0.1.0" + authors = [] + + [dependencies] + a = { version = "^0.1", path = "../a" } + "#, + ) + .file("a/src/main.rs", "fn main() {}") + .file("b/src/main.rs", "fn main() {}") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: no matching package named `a` found +location searched: [..] +prerelease package needs to be specified explicitly +a = { version = \"0.1.1-alpha.0\" } +required by package `b v0.1.0 ([..])` +", + ) + .run(); +} + #[cargo_test] fn only_download_relevant() { let p = project()