diff --git a/src/cargo/util/semver_ext.rs b/src/cargo/util/semver_ext.rs index 8eb4c79200aa..a26121713c59 100644 --- a/src/cargo/util/semver_ext.rs +++ b/src/cargo/util/semver_ext.rs @@ -136,9 +136,18 @@ impl std::str::FromStr for PartialVersion { // HACK: `PartialVersion` is a subset of the `VersionReq` syntax that only ever // has one comparator with a required minor and optional patch, and uses no // other features. + if is_req(value) { + anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"") + } let version_req = match semver::VersionReq::parse(value) { // Exclude semver operators like `^` and pre-release identifiers Ok(req) if value.chars().all(|c| c.is_ascii_digit() || c == '.') => req, + Err(_) if value.contains('+') => { + anyhow::bail!("unexpected build field, expected a version like \"1.32\"") + } + Err(_) if value.contains('-') => { + anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"") + } _ => anyhow::bail!("expected a version like \"1.32\""), }; assert_eq!( @@ -211,6 +220,13 @@ impl<'de> serde::Deserialize<'de> for PartialVersion { } } +fn is_req(value: &str) -> bool { + let Some(first) = value.chars().next() else { + return false; + }; + "<>=^~".contains(first) || value.contains('*') || value.contains(',') +} + #[cfg(test)] mod tests { use super::*; diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs index 4e40fd49e6fa..05f8e6744dfb 100644 --- a/tests/testsuite/rust_version.rs +++ b/tests/testsuite/rust_version.rs @@ -48,7 +48,7 @@ fn rust_version_bad_caret() { error: failed to parse manifest at `[..]` Caused by: - expected a version like \"1.32\" + unexpected version requirement, expected a version like \"1.32\" in `package.rust-version`", ) .run(); @@ -78,7 +78,7 @@ fn rust_version_bad_pre_release() { error: failed to parse manifest at `[..]` Caused by: - expected a version like \"1.32\" + unexpected prerelease field, expected a version like \"1.32\" in `package.rust-version`", ) .run();