diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 0a4835ccdcc..d71d20619ef 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -733,6 +733,19 @@ impl Features { /// Checks if the given feature is enabled. pub fn require(&self, feature: &Feature) -> CargoResult<()> { + self.require_with_hint(feature, None) + } + + /// Like [`require`][Self::require], but appends an optional help message + /// to the error, placed just before the documentation link. + /// + /// Use this when the call site has additional context (e.g. the package's + /// `rust-version`) that can make the error more actionable. + pub(crate) fn require_with_hint( + &self, + feature: &Feature, + hint: Option<&str>, + ) -> CargoResult<()> { if feature.is_enabled(self) { return Ok(()); } @@ -773,6 +786,9 @@ impl Features { about the status of this feature.", feature.docs ); + if let Some(hint) = hint { + let _ = writeln!(msg, "{hint}"); + } bail!("{}", msg); } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 46313e9e771..b3faf6afd69 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1359,7 +1359,15 @@ pub fn to_real_manifest( default_edition }; if !edition.is_stable() { - features.require(Feature::unstable_editions())?; + let version = normalized_package + .normalized_version() + .expect("previously normalized") + .map(|v| format!("@{v}")) + .unwrap_or_default(); + let hint = rust_version + .as_ref() + .map(|rv| format!("help: {package_name}{version} requires rust {rv}")); + features.require_with_hint(Feature::unstable_editions(), hint.as_deref())?; } if original_toml.project.is_some() { diff --git a/tests/testsuite/edition.rs b/tests/testsuite/edition.rs index c845207e278..55472d046d2 100644 --- a/tests/testsuite/edition.rs +++ b/tests/testsuite/edition.rs @@ -242,6 +242,43 @@ Caused by: .run(); } +#[cargo_test] +fn future_edition_with_rust_version_hint() { + // When an unstable edition is used and the package has `rust-version` set, + // the error message should include a `help:` line pointing the user at the + // required Rust toolchain version, matching the format used elsewhere in + // Cargo (e.g. `{name}@{version} requires rust {msrv}`). + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "future" + rust-version = "1.90" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + feature `unstable-editions` is required + + The package requires the Cargo feature called `unstable-editions`, but that feature is not stabilized in this version of Cargo ([..]). + Consider trying a newer version of Cargo (this may require the nightly release). + See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unstable-editions for more information about the status of this feature. + [HELP] foo@0.1.0 requires rust 1.90 + +"#]]) + .run(); +} + #[cargo_test(nightly, reason = "future edition is always unstable")] fn future_edition_works() { let p = project()