Skip to content

Commit 8d43632

Browse files
committed
Auto merge of #12659 - Eh2406:Prerelease_candidates, r=epage
Prerelease candidates error message ### What does this PR try to resolve? Error messages reporting on versions that do not match the request incorrectly ignore pre-release versions. This is because the version requirement `"*"` cannot match prerelease versions. #12315 ### How should we test and review this PR? Sorry for the large amount of white space changes, fmt got to fmt. 🤷‍♂️ The process was: - Revise commit from #12316 (thanks to `@loloicci)` that change the requirement from `"*"` to `Any` - Move the handling of our special "did you mean to specify a pre-release" code and update tests - some small re-factoring ### Additional information The old "did you mean to specify a pre-release" #7191 check only occurred when version requirement does not match any versions and you depended on a package that did not have any non-prerelease versions. Making it rarely useful. The new one will appear any time your version requirement does not match any versions and the package does have pre-release versions. Which may be too common. I'm open to suggestions for better heuristic. It's also not clear that the new message make sense in the case of patched versions.
2 parents bc26ac0 + 50f1455 commit 8d43632

File tree

5 files changed

+158
-123
lines changed

5 files changed

+158
-123
lines changed

src/cargo/core/dependency.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ impl Dependency {
318318
}
319319

320320
/// Sets the version requirement for this dependency.
321-
pub fn set_version_req(&mut self, req: VersionReq) -> &mut Dependency {
322-
Rc::make_mut(&mut self.inner).req = OptVersionReq::Req(req);
321+
pub fn set_version_req(&mut self, req: OptVersionReq) -> &mut Dependency {
322+
Rc::make_mut(&mut self.inner).req = req;
323323
self
324324
}
325325

src/cargo/core/resolver/errors.rs

+107-113
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::task::Poll;
44
use crate::core::{Dependency, PackageId, Registry, Summary};
55
use crate::sources::source::QueryKind;
66
use crate::util::edit_distance::edit_distance;
7-
use crate::util::{Config, VersionExt};
7+
use crate::util::{Config, OptVersionReq, VersionExt};
88
use anyhow::Error;
99

1010
use super::context::Context;
@@ -224,9 +224,8 @@ pub(super) fn activation_error(
224224
// Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"`
225225
// was meant. So we re-query the registry with `dep="*"` so we can
226226
// list a few versions that were actually found.
227-
let all_req = semver::VersionReq::parse("*").unwrap();
228227
let mut new_dep = dep.clone();
229-
new_dep.set_version_req(all_req);
228+
new_dep.set_version_req(OptVersionReq::Any);
230229

231230
let mut candidates = loop {
232231
match registry.query_vec(&new_dep, QueryKind::Exact) {
@@ -241,127 +240,122 @@ pub(super) fn activation_error(
241240

242241
candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));
243242

244-
let mut msg =
245-
if !candidates.is_empty() {
246-
let versions = {
247-
let mut versions = candidates
248-
.iter()
249-
.take(3)
250-
.map(|cand| cand.version().to_string())
251-
.collect::<Vec<_>>();
252-
253-
if candidates.len() > 3 {
254-
versions.push("...".into());
255-
}
243+
let mut msg = if !candidates.is_empty() {
244+
let versions = {
245+
let mut versions = candidates
246+
.iter()
247+
.take(3)
248+
.map(|cand| cand.version().to_string())
249+
.collect::<Vec<_>>();
256250

257-
versions.join(", ")
258-
};
251+
if candidates.len() > 3 {
252+
versions.push("...".into());
253+
}
259254

260-
let locked_version = dep
261-
.version_req()
262-
.locked_version()
263-
.map(|v| format!(" (locked to {})", v))
264-
.unwrap_or_default();
255+
versions.join(", ")
256+
};
257+
258+
let locked_version = dep
259+
.version_req()
260+
.locked_version()
261+
.map(|v| format!(" (locked to {})", v))
262+
.unwrap_or_default();
263+
264+
let mut msg = format!(
265+
"failed to select a version for the requirement `{} = \"{}\"`{}\n\
266+
candidate versions found which didn't match: {}\n\
267+
location searched: {}\n",
268+
dep.package_name(),
269+
dep.version_req(),
270+
locked_version,
271+
versions,
272+
registry.describe_source(dep.source_id()),
273+
);
274+
msg.push_str("required by ");
275+
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
265276

266-
let mut msg = format!(
267-
"failed to select a version for the requirement `{} = \"{}\"`{}\n\
268-
candidate versions found which didn't match: {}\n\
269-
location searched: {}\n",
270-
dep.package_name(),
271-
dep.version_req(),
272-
locked_version,
273-
versions,
274-
registry.describe_source(dep.source_id()),
277+
// If we have a pre-release candidate, then that may be what our user is looking for
278+
if let Some(pre) = candidates.iter().find(|c| c.version().is_prerelease()) {
279+
msg.push_str("\nif you are looking for the prerelease package it needs to be specified explicitly");
280+
msg.push_str(&format!(
281+
"\n {} = {{ version = \"{}\" }}",
282+
pre.name(),
283+
pre.version()
284+
));
285+
}
286+
287+
// If we have a path dependency with a locked version, then this may
288+
// indicate that we updated a sub-package and forgot to run `cargo
289+
// update`. In this case try to print a helpful error!
290+
if dep.source_id().is_path() && dep.version_req().is_locked() {
291+
msg.push_str(
292+
"\nconsider running `cargo update` to update \
293+
a path dependency's locked version",
275294
);
276-
msg.push_str("required by ");
277-
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
278-
279-
// If we have a path dependency with a locked version, then this may
280-
// indicate that we updated a sub-package and forgot to run `cargo
281-
// update`. In this case try to print a helpful error!
282-
if dep.source_id().is_path() && dep.version_req().is_locked() {
283-
msg.push_str(
284-
"\nconsider running `cargo update` to update \
285-
a path dependency's locked version",
286-
);
287-
}
295+
}
296+
297+
if registry.is_replaced(dep.source_id()) {
298+
msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
299+
}
288300

289-
if registry.is_replaced(dep.source_id()) {
290-
msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?");
301+
msg
302+
} else {
303+
// Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
304+
// was meant. So we try asking the registry for a `fuzzy` search for suggestions.
305+
let mut candidates = loop {
306+
match registry.query_vec(&new_dep, QueryKind::Fuzzy) {
307+
Poll::Ready(Ok(candidates)) => break candidates,
308+
Poll::Ready(Err(e)) => return to_resolve_err(e),
309+
Poll::Pending => match registry.block_until_ready() {
310+
Ok(()) => continue,
311+
Err(e) => return to_resolve_err(e),
312+
},
291313
}
314+
};
292315

293-
msg
316+
candidates.sort_unstable_by_key(|a| a.name());
317+
candidates.dedup_by(|a, b| a.name() == b.name());
318+
let mut candidates: Vec<_> = candidates
319+
.iter()
320+
.filter_map(|n| Some((edit_distance(&*new_dep.package_name(), &*n.name(), 3)?, n)))
321+
.collect();
322+
candidates.sort_by_key(|o| o.0);
323+
let mut msg: String;
324+
if candidates.is_empty() {
325+
msg = format!("no matching package named `{}` found\n", dep.package_name());
294326
} else {
295-
// Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
296-
// was meant. So we try asking the registry for a `fuzzy` search for suggestions.
297-
let mut candidates = loop {
298-
match registry.query_vec(&new_dep, QueryKind::Fuzzy) {
299-
Poll::Ready(Ok(candidates)) => break candidates,
300-
Poll::Ready(Err(e)) => return to_resolve_err(e),
301-
Poll::Pending => match registry.block_until_ready() {
302-
Ok(()) => continue,
303-
Err(e) => return to_resolve_err(e),
304-
},
305-
}
306-
};
307-
308-
candidates.sort_unstable_by_key(|a| a.name());
309-
candidates.dedup_by(|a, b| a.name() == b.name());
310-
let mut candidates: Vec<_> = candidates
327+
msg = format!(
328+
"no matching package found\nsearched package name: `{}`\n",
329+
dep.package_name()
330+
);
331+
let mut names = candidates
311332
.iter()
312-
.filter_map(|n| Some((edit_distance(&*new_dep.package_name(), &*n.name(), 3)?, n)))
313-
.collect();
314-
candidates.sort_by_key(|o| o.0);
315-
let mut msg: String;
316-
if candidates.is_empty() {
317-
msg = format!("no matching package named `{}` found\n", dep.package_name());
318-
} else {
319-
msg = format!(
320-
"no matching package found\nsearched package name: `{}`\n",
321-
dep.package_name()
322-
);
323-
324-
// If dependency package name is equal to the name of the candidate here
325-
// it may be a prerelease package which hasn't been specified correctly
326-
if dep.package_name() == candidates[0].1.name()
327-
&& candidates[0].1.package_id().version().is_prerelease()
328-
{
329-
msg.push_str("prerelease package needs to be specified explicitly\n");
330-
msg.push_str(&format!(
331-
"{name} = {{ version = \"{version}\" }}",
332-
name = candidates[0].1.name(),
333-
version = candidates[0].1.package_id().version()
334-
));
335-
} else {
336-
let mut names = candidates
337-
.iter()
338-
.take(3)
339-
.map(|c| c.1.name().as_str())
340-
.collect::<Vec<_>>();
341-
342-
if candidates.len() > 3 {
343-
names.push("...");
344-
}
345-
// Vertically align first suggestion with missing crate name
346-
// so a typo jumps out at you.
347-
msg.push_str("perhaps you meant: ");
348-
msg.push_str(&names.iter().enumerate().fold(
349-
String::default(),
350-
|acc, (i, el)| match i {
351-
0 => acc + el,
352-
i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
353-
_ => acc + ", " + el,
354-
},
355-
));
356-
}
357-
msg.push('\n');
333+
.take(3)
334+
.map(|c| c.1.name().as_str())
335+
.collect::<Vec<_>>();
336+
337+
if candidates.len() > 3 {
338+
names.push("...");
358339
}
359-
msg.push_str(&format!("location searched: {}\n", dep.source_id()));
360-
msg.push_str("required by ");
361-
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
340+
// Vertically align first suggestion with missing crate name
341+
// so a typo jumps out at you.
342+
msg.push_str("perhaps you meant: ");
343+
msg.push_str(&names.iter().enumerate().fold(
344+
String::default(),
345+
|acc, (i, el)| match i {
346+
0 => acc + el,
347+
i if names.len() - 1 == i && candidates.len() <= 3 => acc + " or " + el,
348+
_ => acc + ", " + el,
349+
},
350+
));
351+
msg.push('\n');
352+
}
353+
msg.push_str(&format!("location searched: {}\n", dep.source_id()));
354+
msg.push_str("required by ");
355+
msg.push_str(&describe_path_in_context(cx, &parent.package_id()));
362356

363-
msg
364-
};
357+
msg
358+
};
365359

366360
if let Some(config) = config {
367361
if config.offline() {

src/cargo/util/toml/mod.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use cargo_platform::Platform;
1010
use cargo_util::paths;
1111
use itertools::Itertools;
1212
use lazycell::LazyCell;
13-
use semver::{self, VersionReq};
1413
use serde::de::{self, IntoDeserializer as _, Unexpected};
1514
use serde::ser;
1615
use serde::{Deserialize, Serialize};
@@ -30,8 +29,8 @@ use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
3029
use crate::util::errors::{CargoResult, ManifestError};
3130
use crate::util::interning::InternedString;
3231
use crate::util::{
33-
self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, PartialVersion,
34-
VersionReqExt,
32+
self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, OptVersionReq,
33+
PartialVersion,
3534
};
3635

3736
pub mod embedded;
@@ -2656,7 +2655,7 @@ impl TomlManifest {
26562655
replacement.unused_keys(),
26572656
&mut cx.warnings,
26582657
);
2659-
dep.set_version_req(VersionReq::exact(version))
2658+
dep.set_version_req(OptVersionReq::exact(version))
26602659
.lock_version(version);
26612660
replace.push((spec, dep));
26622661
}

tests/testsuite/patch.rs

+42
Original file line numberDiff line numberDiff line change
@@ -2658,3 +2658,45 @@ failed to select a version for `qux` which could resolve this conflict"#,
26582658
)
26592659
.run();
26602660
}
2661+
2662+
#[cargo_test]
2663+
fn mismatched_version_with_prerelease() {
2664+
Package::new("prerelease-deps", "0.0.1").publish();
2665+
// A patch to a location that has an prerelease version
2666+
let p = project()
2667+
.file(
2668+
"Cargo.toml",
2669+
r#"
2670+
[package]
2671+
name = "foo"
2672+
version = "0.1.0"
2673+
2674+
[dependencies]
2675+
prerelease-deps = "0.1.0"
2676+
2677+
[patch.crates-io]
2678+
prerelease-deps = { path = "./prerelease-deps" }
2679+
"#,
2680+
)
2681+
.file("src/lib.rs", "")
2682+
.file(
2683+
"prerelease-deps/Cargo.toml",
2684+
&basic_manifest("prerelease-deps", "0.1.1-pre1"),
2685+
)
2686+
.file("prerelease-deps/src/lib.rs", "")
2687+
.build();
2688+
2689+
p.cargo("generate-lockfile")
2690+
.with_status(101)
2691+
.with_stderr(
2692+
r#"[UPDATING] `dummy-registry` index
2693+
[ERROR] failed to select a version for the requirement `prerelease-deps = "^0.1.0"`
2694+
candidate versions found which didn't match: 0.1.1-pre1, 0.0.1
2695+
location searched: `dummy-registry` index (which is replacing registry `crates-io`)
2696+
required by package `foo v0.1.0 [..]`
2697+
if you are looking for the prerelease package it needs to be specified explicitly
2698+
prerelease-deps = { version = "0.1.1-pre1" }
2699+
perhaps a crate was updated and forgotten to be re-vendored?"#,
2700+
)
2701+
.run();
2702+
}

tests/testsuite/registry.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1756,12 +1756,12 @@ fn use_semver_package_incorrectly() {
17561756
.with_status(101)
17571757
.with_stderr(
17581758
"\
1759-
error: no matching package found
1760-
searched package name: `a`
1761-
prerelease package needs to be specified explicitly
1762-
a = { version = \"0.1.1-alpha.0\" }
1759+
error: failed to select a version for the requirement `a = \"^0.1\"`
1760+
candidate versions found which didn't match: 0.1.1-alpha.0
17631761
location searched: [..]
17641762
required by package `b v0.1.0 ([..])`
1763+
if you are looking for the prerelease package it needs to be specified explicitly
1764+
a = { version = \"0.1.1-alpha.0\" }
17651765
",
17661766
)
17671767
.run();

0 commit comments

Comments
 (0)