From 1e9bd724fd57f844bd0639260d799265e1dd159f Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 30 Jul 2019 16:29:50 +0200 Subject: [PATCH 1/2] fix #7007, improve error message for unmatched prerelease dependencies --- src/cargo/core/resolver/errors.rs | 55 +++++++++++++++++++------------ tests/testsuite/registry.rs | 49 +++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 21 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index e67ec9ede01..359ca186c50 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -251,14 +251,14 @@ 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 mut candidates = Vec::new(); - if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s.name()), true) { + if let Err(e) = registry.query(&new_dep, &mut |s| candidates.push(s.clone()), true) { return to_resolve_err(e); }; - candidates.sort_unstable(); - candidates.dedup(); + 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() - .map(|n| (lev_distance(&*new_dep.package_name(), &*n), n)) + .map(|n| (lev_distance(&*new_dep.package_name(), &*n.name()), n)) .filter(|&(d, _)| d < 4) .collect(); candidates.sort_by_key(|o| o.0); @@ -269,25 +269,38 @@ pub(super) fn activation_error( dep.source_id() ); if !candidates.is_empty() { - let mut names = candidates - .iter() - .take(3) - .map(|c| c.1.as_str()) - .collect::>(); - - if candidates.len() > 3 { - names.push("..."); + // 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("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("required by "); 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() From 577968e44828457dfa62fc10ccd29f42a359b746 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 31 Jul 2019 10:49:32 +0200 Subject: [PATCH 2/2] cargo fmt --- src/cargo/core/resolver/errors.rs | 194 +++++++++++++++--------------- 1 file changed, 98 insertions(+), 96 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 359ca186c50..6d56cb33ca8 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -203,113 +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::>(); - - 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 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?"); - } - - 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() - .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() - ); + let mut msg = 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 + let versions = { + let mut versions = candidates .iter() .take(3) - .map(|c| c.1.name().as_str()) + .map(|cand| cand.version().to_string()) .collect::>(); if candidates.len() > 3 { - names.push("..."); + versions.push("...".into()); } - 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, - }, - )); + 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", + ); } - msg.push_str("\n"); - } - msg.push_str("required by "); - msg.push_str(&describe_path( - &cx.parents.path_to_bottom(&parent.package_id()), - )); + if registry.is_replaced(dep.source_id()) { + msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?"); + } - msg - }; + 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() + .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("required by "); + msg.push_str(&describe_path( + &cx.parents.path_to_bottom(&parent.package_id()), + )); + + msg + }; if let Some(config) = config { if config.offline() {