Auto merge of #13505 - epage:edition-warn, r=weihanglo

feat(toml): Warn on unset Edition

### What does this PR try to resolve?

On [Internals](https://internals.rust-lang.org/t/idea-rustc-cargo-should-warn-on-unspecified-edition/20309), the idea came up for warning on unset Edition.

Besides helping people who forget to set the Edition, this creates symmetry between `Cargo.toml` and cargo scripts (#12207).  While the default is different in each case, we are making the default obvious and guiding people away from it.

### How should we test and review this PR?

There are separate commits for adding tests (and refactors) so the changes in behavior will be more obvious

### Additional information

This builds on
- #13499
- #13504
This commit is contained in:
bors 2024-03-01 22:57:35 +00:00
commit f772ec0224
10 changed files with 200 additions and 25 deletions

View file

@ -180,9 +180,12 @@ pub type AllowFeatures = BTreeSet<String>;
/// [`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<Edition> = 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

View file

@ -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())?;
}

View file

@ -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"

View file

@ -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 = []

View file

@ -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"]

View file

@ -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 = []
"#,
)

View file

@ -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();
}

View file

@ -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" }

View file

@ -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"

View file

@ -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" }