Skip to content

Commit 39b8f17

Browse files
committed
Auto merge of #13771 - epage:rust-version, r=weihanglo
fix(msrv): Error, rather than panic, on rust-version 'x' ### What does this PR try to resolve? Fixes #13768 ### How should we test and review this PR? ### Additional information
2 parents 5afc53a + 6d8d3b6 commit 39b8f17

File tree

5 files changed

+120
-110
lines changed

5 files changed

+120
-110
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cargo-util-schemas/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ url.workspace = true
2020

2121
[lints]
2222
workspace = true
23+
24+
[dev-dependencies]
25+
snapbox.workspace = true

crates/cargo-util-schemas/src/core/partial_version.rs

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@ impl std::str::FromStr for PartialVersion {
8383
type Err = PartialVersionError;
8484

8585
fn from_str(value: &str) -> Result<Self, Self::Err> {
86-
if is_req(value) {
87-
return Err(ErrorKind::VersionReq.into());
88-
}
8986
match semver::Version::parse(value) {
9087
Ok(ver) => Ok(ver.into()),
9188
Err(_) => {
@@ -96,9 +93,16 @@ impl std::str::FromStr for PartialVersion {
9693
Err(_) if value.contains('+') => return Err(ErrorKind::BuildMetadata.into()),
9794
Err(_) => return Err(ErrorKind::Unexpected.into()),
9895
};
99-
assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req");
96+
if version_req.comparators.len() != 1 {
97+
return Err(ErrorKind::VersionReq.into());
98+
}
10099
let comp = version_req.comparators.pop().unwrap();
101-
assert_eq!(comp.op, semver::Op::Caret, "guaranteed by is_req");
100+
if comp.op != semver::Op::Caret {
101+
return Err(ErrorKind::VersionReq.into());
102+
} else if value.starts_with('^') {
103+
// Can't distinguish between `^` present or not
104+
return Err(ErrorKind::VersionReq.into());
105+
}
102106
let pre = if comp.pre.is_empty() {
103107
None
104108
} else {
@@ -179,9 +183,65 @@ enum ErrorKind {
179183
Unexpected,
180184
}
181185

182-
fn is_req(value: &str) -> bool {
183-
let Some(first) = value.chars().next() else {
184-
return false;
185-
};
186-
"<>=^~".contains(first) || value.contains('*') || value.contains(',')
186+
#[cfg(test)]
187+
mod test {
188+
use super::*;
189+
use snapbox::str;
190+
191+
#[test]
192+
fn parse_success() {
193+
let cases = &[
194+
// Valid pre-release
195+
("1.43.0-beta.1", str!["1.43.0-beta.1"]),
196+
// Valid pre-release with wildcard
197+
("1.43.0-beta.1.x", str!["1.43.0-beta.1.x"]),
198+
];
199+
for (input, expected) in cases {
200+
let actual: Result<PartialVersion, _> = input.parse();
201+
let actual = match actual {
202+
Ok(result) => result.to_string(),
203+
Err(err) => format!("didn't pass: {err}"),
204+
};
205+
snapbox::assert_eq(expected.clone(), actual);
206+
}
207+
}
208+
209+
#[test]
210+
fn parse_errors() {
211+
let cases = &[
212+
// Disallow caret
213+
(
214+
"^1.43",
215+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
216+
),
217+
// Bad pre-release
218+
(
219+
"1.43-beta.1",
220+
str![[r#"unexpected prerelease field, expected a version like "1.32""#]],
221+
),
222+
// Weird wildcard
223+
(
224+
"x",
225+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
226+
),
227+
(
228+
"1.x",
229+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
230+
),
231+
(
232+
"1.1.x",
233+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
234+
),
235+
// Non-sense
236+
("foodaddle", str![[r#"expected a version like "1.32""#]]),
237+
];
238+
for (input, expected) in cases {
239+
let actual: Result<PartialVersion, _> = input.parse();
240+
let actual = match actual {
241+
Ok(result) => format!("didn't fail: {result:?}"),
242+
Err(err) => err.to_string(),
243+
};
244+
snapbox::assert_eq(expected.clone(), actual);
245+
}
246+
}
187247
}

crates/cargo-util-schemas/src/manifest/rust_version.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ enum RustVersionErrorKind {
106106
#[cfg(test)]
107107
mod test {
108108
use super::*;
109+
use snapbox::str;
109110

110111
#[test]
111112
fn is_compatible_with_rustc() {
@@ -170,4 +171,48 @@ mod test {
170171
}
171172
assert!(passed);
172173
}
174+
175+
#[test]
176+
fn parse_errors() {
177+
let cases = &[
178+
// Disallow caret
179+
(
180+
"^1.43",
181+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
182+
),
183+
// Valid pre-release
184+
(
185+
"1.43.0-beta.1",
186+
str![[r#"unexpected prerelease field, expected a version like "1.32""#]],
187+
),
188+
// Bad pre-release
189+
(
190+
"1.43-beta.1",
191+
str![[r#"unexpected prerelease field, expected a version like "1.32""#]],
192+
),
193+
// Weird wildcard
194+
(
195+
"x",
196+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
197+
),
198+
(
199+
"1.x",
200+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
201+
),
202+
(
203+
"1.1.x",
204+
str![[r#"unexpected version requirement, expected a version like "1.32""#]],
205+
),
206+
// Non-sense
207+
("foodaddle", str![[r#"expected a version like "1.32""#]]),
208+
];
209+
for (input, expected) in cases {
210+
let actual: Result<RustVersion, _> = input.parse();
211+
let actual = match actual {
212+
Ok(result) => format!("didn't fail: {result:?}"),
213+
Err(err) => err.to_string(),
214+
};
215+
snapbox::assert_eq(expected.clone(), actual);
216+
}
217+
}
173218
}

tests/testsuite/rust_version.rs

Lines changed: 1 addition & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn rust_version_satisfied() {
2626
}
2727

2828
#[cargo_test]
29-
fn rust_version_bad_caret() {
29+
fn rust_version_error() {
3030
project()
3131
.file(
3232
"Cargo.toml",
@@ -58,105 +58,6 @@ fn rust_version_bad_caret() {
5858
.run();
5959
}
6060

61-
#[cargo_test]
62-
fn rust_version_good_pre_release() {
63-
project()
64-
.file(
65-
"Cargo.toml",
66-
r#"
67-
[package]
68-
name = "foo"
69-
version = "0.0.1"
70-
edition = "2015"
71-
authors = []
72-
rust-version = "1.43.0-beta.1"
73-
[[bin]]
74-
name = "foo"
75-
"#,
76-
)
77-
.file("src/main.rs", "fn main() {}")
78-
.build()
79-
.cargo("check")
80-
.with_status(101)
81-
.with_stderr(
82-
"\
83-
[ERROR] unexpected prerelease field, expected a version like \"1.32\"
84-
--> Cargo.toml:7:28
85-
|
86-
7 | rust-version = \"1.43.0-beta.1\"
87-
| ^^^^^^^^^^^^^^^
88-
|
89-
",
90-
)
91-
.run();
92-
}
93-
94-
#[cargo_test]
95-
fn rust_version_bad_pre_release() {
96-
project()
97-
.file(
98-
"Cargo.toml",
99-
r#"
100-
[package]
101-
name = "foo"
102-
version = "0.0.1"
103-
edition = "2015"
104-
authors = []
105-
rust-version = "1.43-beta.1"
106-
[[bin]]
107-
name = "foo"
108-
"#,
109-
)
110-
.file("src/main.rs", "fn main() {}")
111-
.build()
112-
.cargo("check")
113-
.with_status(101)
114-
.with_stderr(
115-
"\
116-
[ERROR] unexpected prerelease field, expected a version like \"1.32\"
117-
--> Cargo.toml:7:28
118-
|
119-
7 | rust-version = \"1.43-beta.1\"
120-
| ^^^^^^^^^^^^^
121-
|
122-
",
123-
)
124-
.run();
125-
}
126-
127-
#[cargo_test]
128-
fn rust_version_bad_nonsense() {
129-
project()
130-
.file(
131-
"Cargo.toml",
132-
r#"
133-
[package]
134-
name = "foo"
135-
version = "0.0.1"
136-
edition = "2015"
137-
authors = []
138-
rust-version = "foodaddle"
139-
[[bin]]
140-
name = "foo"
141-
"#,
142-
)
143-
.file("src/main.rs", "fn main() {}")
144-
.build()
145-
.cargo("check")
146-
.with_status(101)
147-
.with_stderr(
148-
"\
149-
[ERROR] expected a version like \"1.32\"
150-
--> Cargo.toml:7:28
151-
|
152-
7 | rust-version = \"foodaddle\"
153-
| ^^^^^^^^^^^
154-
|
155-
",
156-
)
157-
.run();
158-
}
159-
16061
#[cargo_test]
16162
fn rust_version_older_than_edition() {
16263
project()

0 commit comments

Comments
 (0)