@@ -51,6 +51,7 @@ use rustfix::CodeFix;
5151use semver:: Version ;
5252use tracing:: { debug, trace, warn} ;
5353
54+ pub use self :: fix_edition:: fix_edition;
5455use crate :: core:: compiler:: CompileKind ;
5556use crate :: core:: compiler:: RustcTargetData ;
5657use crate :: core:: resolver:: features:: { DiffMap , FeatureOpts , FeatureResolver , FeaturesFor } ;
@@ -66,6 +67,8 @@ use crate::util::GlobalContext;
6667use crate :: util:: { existing_vcs_repo, LockServer , LockServerClient } ;
6768use crate :: { drop_eprint, drop_eprintln} ;
6869
70+ mod fix_edition;
71+
6972/// **Internal only.**
7073/// Indicates Cargo is in fix-proxy-mode if presents.
7174/// The value of it is the socket address of the [`LockServer`] being used.
@@ -90,7 +93,7 @@ const IDIOMS_ENV_INTERNAL: &str = "__CARGO_FIX_IDIOMS";
9093const SYSROOT_INTERNAL : & str = "__CARGO_FIX_RUST_SRC" ;
9194
9295pub struct FixOptions {
93- pub edition : bool ,
96+ pub edition : Option < EditionFixMode > ,
9497 pub idioms : bool ,
9598 pub compile_opts : CompileOptions ,
9699 pub allow_dirty : bool ,
@@ -100,30 +103,66 @@ pub struct FixOptions {
100103 pub requested_lockfile_path : Option < PathBuf > ,
101104}
102105
106+ /// The behavior of `--edition` migration.
107+ #[ derive( Clone , Copy ) ]
108+ pub enum EditionFixMode {
109+ /// Migrates the package from the current edition to the next.
110+ ///
111+ /// This is the normal (stable) behavior of `--edition`.
112+ NextRelative ,
113+ /// Migrates to a specific edition.
114+ ///
115+ /// This is used by `-Zfix-edition` to force a specific edition like
116+ /// `future`, which does not have a relative value.
117+ OverrideSpecific ( Edition ) ,
118+ }
119+
120+ impl EditionFixMode {
121+ /// Returns the edition to use for the given current edition.
122+ pub fn next_edition ( & self , current_edition : Edition ) -> Edition {
123+ match self {
124+ EditionFixMode :: NextRelative => current_edition. saturating_next ( ) ,
125+ EditionFixMode :: OverrideSpecific ( edition) => * edition,
126+ }
127+ }
128+
129+ /// Serializes to a string.
130+ fn to_string ( & self ) -> String {
131+ match self {
132+ EditionFixMode :: NextRelative => "1" . to_string ( ) ,
133+ EditionFixMode :: OverrideSpecific ( edition) => edition. to_string ( ) ,
134+ }
135+ }
136+
137+ /// Deserializes from the given string.
138+ fn from_str ( s : & str ) -> EditionFixMode {
139+ match s {
140+ "1" => EditionFixMode :: NextRelative ,
141+ edition => EditionFixMode :: OverrideSpecific ( edition. parse ( ) . unwrap ( ) ) ,
142+ }
143+ }
144+ }
145+
103146pub fn fix (
104147 gctx : & GlobalContext ,
105148 original_ws : & Workspace < ' _ > ,
106- root_manifest : & Path ,
107149 opts : & mut FixOptions ,
108150) -> CargoResult < ( ) > {
109151 check_version_control ( gctx, opts) ?;
110152
111153 let mut target_data =
112154 RustcTargetData :: new ( original_ws, & opts. compile_opts . build_config . requested_kinds ) ?;
113- if opts. edition {
155+ if let Some ( edition_mode ) = opts. edition {
114156 let specs = opts. compile_opts . spec . to_package_id_specs ( & original_ws) ?;
115157 let members: Vec < & Package > = original_ws
116158 . members ( )
117159 . filter ( |m| specs. iter ( ) . any ( |spec| spec. matches ( m. package_id ( ) ) ) )
118160 . collect ( ) ;
119- migrate_manifests ( original_ws, & members) ?;
161+ migrate_manifests ( original_ws, & members, edition_mode ) ?;
120162
121163 check_resolver_change ( & original_ws, & mut target_data, opts) ?;
122164 }
123- let mut ws = Workspace :: new ( & root_manifest, gctx) ?;
124- ws. set_resolve_honors_rust_version ( Some ( original_ws. resolve_honors_rust_version ( ) ) ) ;
125- ws. set_resolve_feature_unification ( original_ws. resolve_feature_unification ( ) ) ;
126- ws. set_requested_lockfile_path ( opts. requested_lockfile_path . clone ( ) ) ;
165+ let ws = original_ws. reload ( gctx) ?;
127166
128167 // Spin up our lock server, which our subprocesses will use to synchronize fixes.
129168 let lock_server = LockServer :: new ( ) ?;
@@ -137,8 +176,8 @@ pub fn fix(
137176 wrapper. env ( BROKEN_CODE_ENV_INTERNAL , "1" ) ;
138177 }
139178
140- if opts. edition {
141- wrapper. env ( EDITION_ENV_INTERNAL , "1" ) ;
179+ if let Some ( mode ) = & opts. edition {
180+ wrapper. env ( EDITION_ENV_INTERNAL , mode . to_string ( ) ) ;
142181 }
143182 if opts. idioms {
144183 wrapper. env ( IDIOMS_ENV_INTERNAL , "1" ) ;
@@ -252,7 +291,11 @@ fn check_version_control(gctx: &GlobalContext, opts: &FixOptions) -> CargoResult
252291 ) ;
253292}
254293
255- fn migrate_manifests ( ws : & Workspace < ' _ > , pkgs : & [ & Package ] ) -> CargoResult < ( ) > {
294+ fn migrate_manifests (
295+ ws : & Workspace < ' _ > ,
296+ pkgs : & [ & Package ] ,
297+ edition_mode : EditionFixMode ,
298+ ) -> CargoResult < ( ) > {
256299 // HACK: Duplicate workspace migration logic between virtual manifests and real manifests to
257300 // reduce multiple Migrating messages being reported for the same file to the user
258301 if matches ! ( ws. root_maybe( ) , MaybePackage :: Virtual ( _) ) {
@@ -263,7 +306,7 @@ fn migrate_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> {
263306 . map ( |p| p. manifest ( ) . edition ( ) )
264307 . max ( )
265308 . unwrap_or_default ( ) ;
266- let prepare_for_edition = highest_edition . saturating_next ( ) ;
309+ let prepare_for_edition = edition_mode . next_edition ( highest_edition ) ;
267310 if highest_edition == prepare_for_edition
268311 || ( !prepare_for_edition. is_stable ( ) && !ws. gctx ( ) . nightly_features_allowed )
269312 {
@@ -308,7 +351,7 @@ fn migrate_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> {
308351
309352 for pkg in pkgs {
310353 let existing_edition = pkg. manifest ( ) . edition ( ) ;
311- let prepare_for_edition = existing_edition . saturating_next ( ) ;
354+ let prepare_for_edition = edition_mode . next_edition ( existing_edition ) ;
312355 if existing_edition == prepare_for_edition
313356 || ( !prepare_for_edition. is_stable ( ) && !ws. gctx ( ) . nightly_features_allowed )
314357 {
@@ -1195,10 +1238,10 @@ impl FixArgs {
11951238 // ALLOWED: For the internal mechanism of `cargo fix` only.
11961239 // Shouldn't be set directly by anyone.
11971240 #[ allow( clippy:: disallowed_methods) ]
1198- let prepare_for_edition = env:: var ( EDITION_ENV_INTERNAL ) . ok ( ) . map ( |_ | {
1199- enabled_edition
1200- . unwrap_or ( Edition :: Edition2015 )
1201- . saturating_next ( )
1241+ let prepare_for_edition = env:: var ( EDITION_ENV_INTERNAL ) . ok ( ) . map ( |v | {
1242+ let enabled_edition = enabled_edition . unwrap_or ( Edition :: Edition2015 ) ;
1243+ let mode = EditionFixMode :: from_str ( & v ) ;
1244+ mode . next_edition ( enabled_edition )
12021245 } ) ;
12031246
12041247 // ALLOWED: For the internal mechanism of `cargo fix` only.
0 commit comments