Skip to content

Commit 1891c40

Browse files
committed
fix: Do not downgrade with update --breaking if VersionReq is prerelease
1 parent 7880fc8 commit 1891c40

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

src/cargo/util/toml_mut/upgrade.rs

+42-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ pub(crate) fn upgrade_requirement(
1818
let comparators: CargoResult<Vec<_>> = raw_req
1919
.comparators
2020
.into_iter()
21+
// Don't downgrade if pre-release was used, see rust-lang/cargo#14178 and rust-lang/cargo#13290.
22+
.filter(|p| p.pre.is_empty() || matches_greater(p, version))
2123
.map(|p| set_comparator(p, version))
2224
.collect();
23-
let comparators = comparators?;
25+
let comparators: Vec<_> = comparators?;
26+
if comparators.is_empty() {
27+
return Ok(None);
28+
}
2429
let new_req = semver::VersionReq { comparators };
2530
let mut new_req_text = new_req.to_string();
2631
if new_req_text.starts_with('^') && !req.starts_with('^') {
@@ -74,6 +79,33 @@ fn set_comparator(
7479
}
7580
}
7681

82+
// See https://github.com/dtolnay/semver/blob/master/src/eval.rs#L64
83+
fn matches_greater(cmp: &semver::Comparator, ver: &semver::Version) -> bool {
84+
if ver.major != cmp.major {
85+
return ver.major > cmp.major;
86+
}
87+
88+
match cmp.minor {
89+
None => return false,
90+
Some(minor) => {
91+
if ver.minor != minor {
92+
return ver.minor > minor;
93+
}
94+
}
95+
}
96+
97+
match cmp.patch {
98+
None => return false,
99+
Some(patch) => {
100+
if ver.patch != patch {
101+
return ver.patch > patch;
102+
}
103+
}
104+
}
105+
106+
ver.pre > cmp.pre
107+
}
108+
77109
fn assign_partial_req(
78110
version: &semver::Version,
79111
mut pred: semver::Comparator,
@@ -219,8 +251,15 @@ mod test {
219251
}
220252

221253
#[test]
222-
fn caret_prerelease() {
223-
assert_req_bump("1.7.0", "2.0.0-beta.21", "1.7.0");
254+
fn greater_prerelease() {
255+
assert_req_bump("1.7.0", "2.0.0-beta.21", None);
256+
assert_req_bump("1.7.0", "=2.0.0-beta.21", None);
257+
assert_req_bump("1.7.0", "~2.0.0-beta.21", None);
258+
assert_req_bump("2.0.0-beta.20", "2.0.0-beta.21", None);
259+
assert_req_bump("2.0.0-beta.21", "2.0.0-beta.21", None);
260+
assert_req_bump("2.0.0-beta.22", "2.0.0-beta.21", "2.0.0-beta.22");
261+
assert_req_bump("2.0.0", "2.0.0-beta.21", "2.0.0");
262+
assert_req_bump("3.0.0", "2.0.0-beta.21", "3.0.0");
224263
}
225264
}
226265
}

tests/testsuite/update.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -2640,15 +2640,14 @@ fn update_breaking_pre_release() {
26402640

26412641
p.cargo("generate-lockfile").run();
26422642

2643+
// The purpose of this test is
2644+
// to demonstrate that `update --breaking` will not try to downgrade to the latest stable version (1.7.0),
2645+
// but will rather keep the latest pre-release (2.0.0-beta.21).
26432646
Package::new("bar", "1.7.0").publish();
2644-
26452647
p.cargo("update -Zunstable-options --breaking bar")
26462648
.masquerade_as_nightly_cargo(&["update-breaking"])
26472649
.with_stderr_data(str![[r#"
26482650
[UPDATING] `dummy-registry` index
2649-
[UPGRADING] bar ^2.0.0-beta.21 -> ^1.7.0
2650-
[LOCKING] 1 package to latest compatible version
2651-
[DOWNGRADING] bar v2.0.0-beta.21 -> v1.7.0
26522651
26532652
"#]])
26542653
.run();

0 commit comments

Comments
 (0)