Skip to content

Commit f19321e

Browse files
authored
Add the future edition (#15595)
This adds support for the "future" edition which was added to rustc in rust-lang/rust#137606. To enable support for unstable editions, this introduces a new `unstable-editions` cargo feature. The intent is that instead of having a new feature for each edition that we reuse this feature for all new editions. I don't see a particular reason we should have a separate one for each edition, and this helps a bit with scalability and simplifies some of the edition process. This also includes a change to rework `supports_compat_lint` explained in the comment.
2 parents 68db374 + 5f5ad05 commit f19321e

File tree

5 files changed

+127
-31
lines changed

5 files changed

+127
-31
lines changed

src/cargo/core/features.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,22 +151,17 @@ pub type AllowFeatures = BTreeSet<String>;
151151
/// - Update the [`FromStr`] impl.
152152
/// - Update [`CLI_VALUES`] to include the new edition.
153153
/// - Set [`LATEST_UNSTABLE`] to Some with the new edition.
154-
/// - Add an unstable feature to the [`features!`] macro invocation below for the new edition.
155-
/// - Gate on that new feature in [`toml`].
156154
/// - Update the shell completion files.
157155
/// - Update any failing tests (hopefully there are very few).
158-
/// - Update unstable.md to add a new section for this new edition (see [this example]).
159156
///
160157
/// ## Stabilization instructions
161158
///
162159
/// - Set [`LATEST_UNSTABLE`] to None.
163160
/// - Set [`LATEST_STABLE`] to the new version.
164161
/// - Update [`is_stable`] to `true`.
165162
/// - Set [`first_version`] to the version it will be released.
166-
/// - Set the editionNNNN feature to stable in the [`features!`] macro invocation below.
167163
/// - Update any tests that are affected.
168164
/// - Update the man page for the `--edition` flag.
169-
/// - Update unstable.md to move the edition section to the bottom.
170165
/// - Update the documentation:
171166
/// - Update any features impacted by the edition.
172167
/// - Update manifest.md#the-edition-field.
@@ -178,7 +173,6 @@ pub type AllowFeatures = BTreeSet<String>;
178173
/// [`CLI_VALUES`]: Edition::CLI_VALUES
179174
/// [`LATEST_UNSTABLE`]: Edition::LATEST_UNSTABLE
180175
/// [`LATEST_STABLE`]: Edition::LATEST_STABLE
181-
/// [this example]: https://github.com/rust-lang/cargo/blob/3ebb5f15a940810f250b68821149387af583a79e/src/doc/src/reference/unstable.md?plain=1#L1238-L1264
182176
/// [`first_version`]: Edition::first_version
183177
/// [`is_stable`]: Edition::is_stable
184178
/// [`toml`]: crate::util::toml
@@ -196,12 +190,17 @@ pub enum Edition {
196190
Edition2021,
197191
/// The 2024 edition
198192
Edition2024,
193+
/// The future edition (permanently unstable)
194+
EditionFuture,
199195
}
200196

201197
impl Edition {
202198
/// The latest edition that is unstable.
203199
///
204200
/// This is `None` if there is no next unstable edition.
201+
///
202+
/// Note that this does *not* include "future" since this is primarily
203+
/// used for tests that need to step between stable and unstable.
205204
pub const LATEST_UNSTABLE: Option<Edition> = None;
206205
/// The latest stable edition.
207206
pub const LATEST_STABLE: Edition = Edition::Edition2024;
@@ -210,11 +209,15 @@ impl Edition {
210209
Self::Edition2018,
211210
Self::Edition2021,
212211
Self::Edition2024,
212+
Self::EditionFuture,
213213
];
214214
/// Possible values allowed for the `--edition` CLI flag.
215215
///
216216
/// This requires a static value due to the way clap works, otherwise I
217217
/// would have built this dynamically.
218+
///
219+
/// This does not include `future` since we don't need to create new
220+
/// packages with it.
218221
pub const CLI_VALUES: [&'static str; 4] = ["2015", "2018", "2021", "2024"];
219222

220223
/// Returns the first version that a particular edition was released on
@@ -226,6 +229,7 @@ impl Edition {
226229
Edition2018 => Some(semver::Version::new(1, 31, 0)),
227230
Edition2021 => Some(semver::Version::new(1, 56, 0)),
228231
Edition2024 => Some(semver::Version::new(1, 85, 0)),
232+
EditionFuture => None,
229233
}
230234
}
231235

@@ -237,6 +241,7 @@ impl Edition {
237241
Edition2018 => true,
238242
Edition2021 => true,
239243
Edition2024 => true,
244+
EditionFuture => false,
240245
}
241246
}
242247

@@ -250,18 +255,21 @@ impl Edition {
250255
Edition2018 => Some(Edition2015),
251256
Edition2021 => Some(Edition2018),
252257
Edition2024 => Some(Edition2021),
258+
EditionFuture => panic!("future does not have a previous edition"),
253259
}
254260
}
255261

256262
/// Returns the next edition from this edition, returning the last edition
257263
/// if this is already the last one.
258264
pub fn saturating_next(&self) -> Edition {
259265
use Edition::*;
266+
// Nothing should treat "future" as being next.
260267
match self {
261268
Edition2015 => Edition2018,
262269
Edition2018 => Edition2021,
263270
Edition2021 => Edition2024,
264271
Edition2024 => Edition2024,
272+
EditionFuture => EditionFuture,
265273
}
266274
}
267275

@@ -274,18 +282,23 @@ impl Edition {
274282
}
275283
}
276284

277-
/// Whether or not this edition supports the `rust_*_compatibility` lint.
278-
///
279-
/// Ideally this would not be necessary, but editions may not have any
280-
/// lints, and thus `rustc` doesn't recognize it. Perhaps `rustc` could
281-
/// create an empty group instead?
282-
pub(crate) fn supports_compat_lint(&self) -> bool {
285+
/// Adds the appropriate argument to generate warnings for this edition.
286+
pub(crate) fn force_warn_arg(&self, cmd: &mut ProcessBuilder) {
283287
use Edition::*;
284288
match self {
285-
Edition2015 => false,
286-
Edition2018 => true,
287-
Edition2021 => true,
288-
Edition2024 => true,
289+
Edition2015 => {}
290+
EditionFuture => {
291+
cmd.arg("--force-warn=edition_future_compatibility");
292+
}
293+
e => {
294+
// Note that cargo always passes this even if the
295+
// compatibility lint group does not exist. When a new edition
296+
// is introduced, but there are no migration lints, rustc does
297+
// not create the lint group. That's OK because rustc will
298+
// just generate a warning about an unknown lint which will be
299+
// suppressed due to cap-lints.
300+
cmd.arg(format!("--force-warn=rust-{e}-compatibility"));
301+
}
289302
}
290303
}
291304

@@ -299,6 +312,7 @@ impl Edition {
299312
Edition2018 => true,
300313
Edition2021 => false,
301314
Edition2024 => false,
315+
EditionFuture => false,
302316
}
303317
}
304318

@@ -320,6 +334,7 @@ impl fmt::Display for Edition {
320334
Edition::Edition2018 => f.write_str("2018"),
321335
Edition::Edition2021 => f.write_str("2021"),
322336
Edition::Edition2024 => f.write_str("2024"),
337+
Edition::EditionFuture => f.write_str("future"),
323338
}
324339
}
325340
}
@@ -332,6 +347,7 @@ impl FromStr for Edition {
332347
"2018" => Ok(Edition::Edition2018),
333348
"2021" => Ok(Edition::Edition2021),
334349
"2024" => Ok(Edition::Edition2024),
350+
"future" => Ok(Edition::EditionFuture),
335351
s if s.parse().map_or(false, |y: u16| y > 2024 && y < 2050) => bail!(
336352
"this version of Cargo is older than the `{}` edition, \
337353
and only supports `2015`, `2018`, `2021`, and `2024` editions.",
@@ -519,6 +535,9 @@ features! {
519535

520536
/// Allow paths that resolve relatively to a base specified in the config.
521537
(unstable, path_bases, "", "reference/unstable.html#path-bases"),
538+
539+
/// Allows use of editions that are not yet stable.
540+
(unstable, unstable_editions, "", "reference/unstable.html#unstable-editions"),
522541
}
523542

524543
/// Status and metadata for a single unstable feature.

src/cargo/ops/fix.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,10 +1238,7 @@ impl FixArgs {
12381238
}
12391239

12401240
if let Some(edition) = self.prepare_for_edition {
1241-
if edition.supports_compat_lint() {
1242-
cmd.arg("--force-warn")
1243-
.arg(format!("rust-{}-compatibility", edition));
1244-
}
1241+
edition.force_warn_arg(cmd);
12451242
}
12461243
}
12471244

src/cargo/util/toml/mod.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,18 +1342,8 @@ pub fn to_real_manifest(
13421342
}
13431343
default_edition
13441344
};
1345-
// Add these lines if start a new unstable edition.
1346-
// ```
1347-
// if edition == Edition::Edition20xx {
1348-
// features.require(Feature::edition20xx())?;
1349-
// }
1350-
// ```
13511345
if !edition.is_stable() {
1352-
// Guard in case someone forgets to add .require()
1353-
return Err(util::errors::internal(format!(
1354-
"edition {} should be gated",
1355-
edition
1356-
)));
1346+
features.require(Feature::unstable_editions())?;
13571347
}
13581348

13591349
if original_toml.project.is_some() {

src/doc/src/reference/unstable.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ Each new feature described below should explain how to use it.
107107
* [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs.
108108
* [`[lints.cargo]`](#lintscargo) --- Allows configuring lints for Cargo.
109109
* [path bases](#path-bases) --- Named base directories for path dependencies.
110+
* [`unstable-editions`](#unstable-editions) --- Allows use of editions that are not yet stable.
110111
* Information and metadata
111112
* [Build-plan](#build-plan) --- Emits JSON information on which commands will be run.
112113
* [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure.
@@ -1898,6 +1899,22 @@ be stored in `.rmeta` files.
18981899
cargo +nightly -Zno-embed-metadata build
18991900
```
19001901

1902+
## `unstable-editions`
1903+
1904+
The `unstable-editions` value in the `cargo-features` list allows a `Cargo.toml` manifest to specify an edition that is not yet stable.
1905+
1906+
```toml
1907+
cargo-features = ["unstable-editions"]
1908+
1909+
[package]
1910+
name = "my-package"
1911+
edition = "future"
1912+
```
1913+
1914+
When new editions are introduced, the `unstable-editions` feature is required until the edition is stabilized.
1915+
1916+
The special "future" edition is a home for new features that are under development, and is permanently unstable. The "future" edition also has no new behavior by itself. Each change in the future edition requires an opt-in such as a `#![feature(...)]` attribute.
1917+
19011918
# Stabilized and removed features
19021919

19031920
## Compile progress

tests/testsuite/edition.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,76 @@ fn unset_edition_works_on_old_msrv() {
194194
"#]])
195195
.run();
196196
}
197+
198+
#[cargo_test]
199+
fn future_edition_is_gated() {
200+
let p = project()
201+
.file(
202+
"Cargo.toml",
203+
r#"
204+
[package]
205+
name = "foo"
206+
edition = "future"
207+
"#,
208+
)
209+
.file("src/lib.rs", "")
210+
.build();
211+
212+
p.cargo("check")
213+
.with_status(101)
214+
.with_stderr_data(str![[r#"
215+
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
216+
217+
Caused by:
218+
feature `unstable-editions` is required
219+
220+
The package requires the Cargo feature called `unstable-editions`, but that feature is not stabilized in this version of Cargo ([..]).
221+
Consider trying a newer version of Cargo (this may require the nightly release).
222+
See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unstable-editions for more information about the status of this feature.
223+
224+
"#]])
225+
.run();
226+
227+
// Repeat on nightly.
228+
p.cargo("check")
229+
.masquerade_as_nightly_cargo(&["unstable-editions"])
230+
.with_status(101)
231+
.with_stderr_data(str![[r#"
232+
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
233+
234+
Caused by:
235+
feature `unstable-editions` is required
236+
237+
The package requires the Cargo feature called `unstable-editions`, but that feature is not stabilized in this version of Cargo ([..]).
238+
Consider adding `cargo-features = ["unstable-editions"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature.
239+
See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unstable-editions for more information about the status of this feature.
240+
241+
"#]])
242+
.run();
243+
}
244+
245+
#[cargo_test(nightly, reason = "future edition is always unstable")]
246+
fn future_edition_works() {
247+
let p = project()
248+
.file(
249+
"Cargo.toml",
250+
r#"
251+
cargo-features = ["unstable-editions"]
252+
253+
[package]
254+
name = "foo"
255+
edition = "future"
256+
"#,
257+
)
258+
.file("src/lib.rs", "")
259+
.build();
260+
261+
p.cargo("check")
262+
.masquerade_as_nightly_cargo(&["unstable-editions"])
263+
.with_stderr_data(str![[r#"
264+
[CHECKING] foo v0.0.0 ([ROOT]/foo)
265+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
266+
267+
"#]])
268+
.run();
269+
}

0 commit comments

Comments
 (0)