diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index f0cd9c48d..76ccdadb6 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -180,9 +180,12 @@ pub type AllowFeatures = BTreeSet; /// [`is_stable`]: Edition::is_stable /// [`toml::to_real_manifest`]: crate::util::toml::to_real_manifest /// [`features!`]: macro.features.html -#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)] +#[derive( + Default, Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize, +)] pub enum Edition { /// The 2015 edition + #[default] Edition2015, /// The 2018 edition Edition2018, @@ -199,6 +202,12 @@ impl Edition { pub const LATEST_UNSTABLE: Option = Some(Edition::Edition2024); /// The latest stable edition. pub const LATEST_STABLE: Edition = Edition::Edition2021; + pub const ALL: &'static [Edition] = &[ + Self::Edition2015, + Self::Edition2018, + Self::Edition2021, + Self::Edition2024, + ]; /// Possible values allowed for the `--edition` CLI flag. /// /// This requires a static value due to the way clap works, otherwise I diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 8049f538c..1f10d37d3 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -9,6 +9,7 @@ use crate::AlreadyPrintedError; use anyhow::{anyhow, bail, Context as _}; use cargo_platform::Platform; use cargo_util::paths; +use cargo_util_schemas::core::PartialVersion; use cargo_util_schemas::manifest; use cargo_util_schemas::manifest::RustVersion; use itertools::Itertools; @@ -563,14 +564,69 @@ pub fn to_real_manifest( source_id, ); + let rust_version = if let Some(rust_version) = &package.rust_version { + let rust_version = field_inherit_with(rust_version.clone(), "rust_version", || { + inherit()?.rust_version() + })?; + Some(rust_version) + } else { + None + }; + let edition = if let Some(edition) = package.edition.clone() { let edition: Edition = field_inherit_with(edition, "edition", || inherit()?.edition())? .parse() .with_context(|| "failed to parse the `edition` key")?; package.edition = Some(manifest::InheritableField::Value(edition.to_string())); + if let Some(rust_version) = &rust_version { + let req = rust_version.to_caret_req(); + if let Some(first_version) = edition.first_version() { + let unsupported = + semver::Version::new(first_version.major, first_version.minor - 1, 9999); + if req.matches(&unsupported) { + bail!( + "rust-version {} is older than first version ({}) required by \ + the specified edition ({})", + rust_version, + first_version, + edition, + ) + } + } + } edition } else { - Edition::Edition2015 + let msrv_edition = if let Some(rust_version) = &rust_version { + Edition::ALL + .iter() + .filter(|e| { + e.first_version() + .map(|e| { + let e = PartialVersion::from(e); + e <= **rust_version + }) + .unwrap_or_default() + }) + .max() + .copied() + } else { + None + } + .unwrap_or_default(); + let default_edition = Edition::default(); + let latest_edition = Edition::LATEST_STABLE; + + let tip = if msrv_edition == default_edition { + String::new() + } else if msrv_edition == latest_edition { + format!(" while the latest is {latest_edition}") + } else { + format!(" while {msrv_edition} is compatible with `rust-version`") + }; + warnings.push(format!( + "no edition set: defaulting to the {default_edition} edition{tip}", + )); + default_edition }; // Add these lines if start a new unstable edition. // ``` @@ -588,29 +644,6 @@ pub fn to_real_manifest( ))); } - let rust_version = if let Some(rust_version) = &package.rust_version { - let rust_version = field_inherit_with(rust_version.clone(), "rust_version", || { - inherit()?.rust_version() - })?; - let req = rust_version.to_caret_req(); - if let Some(first_version) = edition.first_version() { - let unsupported = - semver::Version::new(first_version.major, first_version.minor - 1, 9999); - if req.matches(&unsupported) { - bail!( - "rust-version {} is older than first version ({}) required by \ - the specified edition ({})", - rust_version, - first_version, - edition, - ) - } - } - Some(rust_version) - } else { - None - }; - if package.metabuild.is_some() { features.require(Feature::metabuild())?; } diff --git a/tests/testsuite/bench.rs b/tests/testsuite/bench.rs index 9e5cc1b17..35c78454a 100644 --- a/tests/testsuite/bench.rs +++ b/tests/testsuite/bench.rs @@ -331,6 +331,7 @@ fn bench_with_lib_dep() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [[bin]] @@ -396,6 +397,7 @@ fn bench_with_deep_lib_dep() { [package] name = "bar" version = "0.0.1" + edition = "2015" authors = [] [dependencies.foo] @@ -454,6 +456,7 @@ fn external_bench_explicit() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [[bench]] @@ -697,6 +700,7 @@ fn lib_bin_same_name() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -796,6 +800,7 @@ fn lib_with_standard_name2() { [package] name = "syntax" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -842,6 +847,7 @@ fn bench_dylib() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -883,6 +889,7 @@ fn bench_dylib() { [package] name = "bar" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -932,6 +939,7 @@ fn bench_twice_with_build_cmd() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] build = "build.rs" "#, @@ -977,6 +985,7 @@ fn bench_with_examples() { [package] name = "foo" version = "6.6.6" + edition = "2015" authors = [] [[example]] @@ -1061,6 +1070,7 @@ fn test_a_bench() { name = "foo" authors = [] version = "0.1.0" + edition = "2015" [lib] name = "foo" @@ -1219,6 +1229,7 @@ fn test_bench_multiple_packages() { name = "foo" authors = [] version = "0.1.0" + edition = "2015" [dependencies.bar] path = "../bar" @@ -1239,6 +1250,7 @@ fn test_bench_multiple_packages() { name = "bar" authors = [] version = "0.1.0" + edition = "2015" [[bench]] name = "bbar" @@ -1269,6 +1281,7 @@ fn test_bench_multiple_packages() { name = "baz" authors = [] version = "0.1.0" + edition = "2015" [[bench]] name = "bbaz" @@ -1307,6 +1320,7 @@ fn bench_all_workspace() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = { path = "bar" } @@ -1360,6 +1374,7 @@ fn bench_all_exclude() { [package] name = "foo" version = "0.1.0" + edition = "2015" [workspace] members = ["bar", "baz"] @@ -1405,6 +1420,7 @@ fn bench_all_exclude_glob() { [package] name = "foo" version = "0.1.0" + edition = "2015" [workspace] members = ["bar", "baz"] @@ -1549,6 +1565,7 @@ fn legacy_bench_name() { [package] name = "foo" version = "0.1.0" + edition = "2015" [[bench]] name = "bench" diff --git a/tests/testsuite/check_cfg.rs b/tests/testsuite/check_cfg.rs index 3f96451e0..004fc482a 100644 --- a/tests/testsuite/check_cfg.rs +++ b/tests/testsuite/check_cfg.rs @@ -38,6 +38,7 @@ fn features() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_a = [] @@ -63,6 +64,7 @@ fn features_with_deps() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = { path = "bar/" } @@ -93,6 +95,7 @@ fn features_with_opt_deps() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = { path = "bar/", optional = true } @@ -124,6 +127,7 @@ fn features_with_namespaced_features() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = { path = "bar/", optional = true } @@ -154,6 +158,7 @@ fn features_fingerprint() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_a = [] @@ -181,6 +186,7 @@ fn features_fingerprint() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_b = [] @@ -199,6 +205,7 @@ fn features_fingerprint() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_a = [] @@ -239,6 +246,7 @@ fn features_test() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_a = [] @@ -264,6 +272,7 @@ fn features_doctest() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] default = ["f_a"] @@ -322,6 +331,7 @@ fn features_doc() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] default = ["f_a"] @@ -348,6 +358,7 @@ fn build_script_feedback() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] build = "build.rs" "#, @@ -375,6 +386,7 @@ fn build_script_doc() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] build = "build.rs" "#, @@ -415,6 +427,7 @@ fn build_script_override() { [package] name = "foo" version = "0.5.0" + edition = "2015" authors = [] links = "a" build = "build.rs" @@ -453,6 +466,7 @@ fn build_script_override_feature_gate() { [package] name = "foo" version = "0.1.0" + edition = "2015" links = "a" "#, ) @@ -484,6 +498,7 @@ fn build_script_test() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] build = "build.rs" "#, @@ -539,6 +554,7 @@ fn build_script_feature_gate() { [package] name = "foo" version = "0.0.1" + edition = "2015" build = "build.rs" "#, ) @@ -567,6 +583,7 @@ fn config_valid() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_a = [] @@ -599,6 +616,7 @@ fn config_invalid() { [package] name = "foo" version = "0.1.0" + edition = "2015" "#, ) .file("src/main.rs", "fn main() {}") @@ -627,6 +645,7 @@ fn config_feature_gate() { [package] name = "foo" version = "0.1.0" + edition = "2015" [features] f_a = [] diff --git a/tests/testsuite/custom_target.rs b/tests/testsuite/custom_target.rs index 518b19672..191d4cd51 100644 --- a/tests/testsuite/custom_target.rs +++ b/tests/testsuite/custom_target.rs @@ -72,6 +72,7 @@ fn custom_target_dependency() { name = "foo" version = "0.0.1" + edition = "2015" authors = ["author@example.com"] [dependencies] @@ -189,6 +190,7 @@ fn changing_spec_relearns_crate_types() { [package] name = "foo" version = "0.1.0" + edition = "2015" [lib] crate-type = ["cdylib"] diff --git a/tests/testsuite/docscrape.rs b/tests/testsuite/docscrape.rs index 0d96ad718..7398ee0a3 100644 --- a/tests/testsuite/docscrape.rs +++ b/tests/testsuite/docscrape.rs @@ -11,6 +11,7 @@ fn basic() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -83,6 +84,7 @@ fn main() { [package] name = "a" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -136,6 +138,7 @@ fn avoid_build_script_cycle() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] links = "foo" @@ -155,6 +158,7 @@ fn avoid_build_script_cycle() { [package] name = "bar" version = "0.0.1" + edition = "2015" authors = [] links = "bar" "#, @@ -177,6 +181,7 @@ fn complex_reverse_dependencies() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [dev-dependencies] @@ -195,6 +200,7 @@ fn complex_reverse_dependencies() { [package] name = "a" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -214,6 +220,7 @@ fn complex_reverse_dependencies() { [package] name = "b" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -234,6 +241,7 @@ fn crate_with_dash() { [package] name = "da-sh" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -258,6 +266,7 @@ fn configure_target() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -305,6 +314,7 @@ fn configure_profile_issue_10500() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [profile.dev] @@ -379,6 +389,7 @@ fn cache() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -419,6 +430,7 @@ fn no_fail_bad_lib() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -463,6 +475,7 @@ fn fail_bad_build_script() { [package] name = "foo" version = "0.0.1" + edition = "2015" "#, ) .file("src/lib.rs", "") @@ -493,6 +506,7 @@ fn no_fail_bad_example() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -560,6 +574,7 @@ fn no_scrape_with_dev_deps() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [dev-dependencies] @@ -574,6 +589,7 @@ fn no_scrape_with_dev_deps() { [package] name = "a" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -622,6 +638,7 @@ fn use_dev_deps_if_explicitly_enabled() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] [[example]] @@ -640,6 +657,7 @@ fn use_dev_deps_if_explicitly_enabled() { [package] name = "a" version = "0.0.1" + edition = "2015" authors = [] "#, ) @@ -673,6 +691,7 @@ fn only_scrape_documented_targets() { [package] name = "bar" version = "0.0.1" + edition = "2015" authors = [] [lib] @@ -694,6 +713,7 @@ fn only_scrape_documented_targets() { [package] name = "foo" version = "0.0.1" + edition = "2015" authors = [] "#, ) diff --git a/tests/testsuite/edition.rs b/tests/testsuite/edition.rs index 377a86ec0..6f68233c1 100644 --- a/tests/testsuite/edition.rs +++ b/tests/testsuite/edition.rs @@ -122,3 +122,57 @@ fn edition_unstable() { ) .run(); } + +#[cargo_test] +fn unset_edition_works_with_no_newer_compatible_edition() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + rust-version = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_stderr( + "\ +[WARNING] no edition set: defaulting to the 2015 edition +[CHECKING] foo [..] +[RUNNING] `rustc [..] --edition=2015 [..]` +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] +fn unset_edition_works_on_old_msrv() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = 'foo' + version = '0.1.0' + rust-version = "1.50" # contains 2018 edition + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_stderr( + "\ +[WARNING] no edition set: defaulting to the 2015 edition while 2018 is compatible with `rust-version` +[CHECKING] foo [..] +[RUNNING] `rustc [..] --edition=2015 [..]` +[FINISHED] [..] +", + ) + .run(); +} diff --git a/tests/testsuite/https.rs b/tests/testsuite/https.rs index c7aec9111..85a894a31 100644 --- a/tests/testsuite/https.rs +++ b/tests/testsuite/https.rs @@ -20,6 +20,7 @@ fn self_signed_should_fail() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = {{ git = "{url}" }} @@ -106,6 +107,7 @@ fn self_signed_with_cacert() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = {{ git = "{url}" }} @@ -139,6 +141,7 @@ fn github_works() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bitflags = { git = "https://github.com/rust-lang/bitflags.git", tag="1.3.2" } diff --git a/tests/testsuite/profile_trim_paths.rs b/tests/testsuite/profile_trim_paths.rs index 67a7785fe..2cb266bf8 100644 --- a/tests/testsuite/profile_trim_paths.rs +++ b/tests/testsuite/profile_trim_paths.rs @@ -15,6 +15,7 @@ fn gated_manifest() { [package] name = "foo" version = "0.0.1" + edition = "2015" [profile.dev] trim-paths = "macro" @@ -71,6 +72,7 @@ fn release_profile_default_to_object() { [package] name = "foo" version = "0.0.1" + edition = "2015" "#, ) .file("src/lib.rs", "") @@ -101,6 +103,7 @@ fn one_option() { [package] name = "foo" version = "0.0.1" + edition = "2015" [profile.dev] trim-paths = "{option}" @@ -143,6 +146,7 @@ fn multiple_options() { [package] name = "foo" version = "0.0.1" + edition = "2015" [profile.dev] trim-paths = ["diagnostics", "macro", "object"] @@ -174,6 +178,7 @@ fn profile_merge_works() { [package] name = "foo" version = "0.0.1" + edition = "2015" [profile.dev] trim-paths = ["macro"] @@ -213,6 +218,7 @@ fn registry_dependency() { [package] name = "foo" version = "0.0.1" + edition = "2015" [dependencies] bar = "0.0.1" @@ -268,6 +274,7 @@ fn git_dependency() { [package] name = "foo" version = "0.0.1" + edition = "2015" [dependencies] bar = {{ git = "{url}" }} @@ -314,6 +321,7 @@ fn path_dependency() { [package] name = "foo" version = "0.0.1" + edition = "2015" [dependencies] bar = { path = "cocktail-bar" } @@ -368,6 +376,7 @@ fn path_dependency_outside_workspace() { [package] name = "foo" version = "0.0.1" + edition = "2015" [dependencies] bar = { path = "../bar" } @@ -413,6 +422,7 @@ fn diagnostics_works() { [package] name = "foo" version = "0.0.1" + edition = "2015" [dependencies] bar = "0.0.1" @@ -532,6 +542,7 @@ fn object_works_helper(split_debuginfo: &str, run: impl Fn(&std::path::Path) -> [package] name = "foo" version = "0.0.1" + edition = "2015" [dependencies] bar = "0.0.1" @@ -635,6 +646,7 @@ fn custom_build_env_var_trim_paths() { [package] name = "foo" version = "0.0.1" + edition = "2015" "#, ) .file("src/lib.rs", "") @@ -664,6 +676,7 @@ fn custom_build_env_var_trim_paths() { [package] name = "foo" version = "0.0.1" + edition = "2015" [profile.dev] trim-paths = {opts} @@ -722,6 +735,7 @@ fn lldb_works_after_trimmed() { [package] name = "foo" version = "0.0.1" + edition = "2015" [profile.dev] trim-paths = "object" diff --git a/tests/testsuite/ssh.rs b/tests/testsuite/ssh.rs index f8c12819b..66ec55d53 100644 --- a/tests/testsuite/ssh.rs +++ b/tests/testsuite/ssh.rs @@ -110,6 +110,7 @@ fn foo_bar_project(url: &str) -> Project { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bar = {{ git = "{url}" }} @@ -406,6 +407,7 @@ fn invalid_github_key() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bitflags = { git = "ssh://git@github.com/rust-lang/bitflags.git", tag = "1.3.2" } @@ -444,6 +446,7 @@ fn bundled_github_works() { [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bitflags = { git = "ssh://git@github.com/rust-lang/bitflags.git", tag = "1.3.2" } @@ -552,6 +555,7 @@ Caused by: [package] name = "foo" version = "0.1.0" + edition = "2015" [dependencies] bitflags = { git = "ssh://git@github.com:22/rust-lang/bitflags.git", tag = "1.3.2" }