@@ -53,7 +53,8 @@ use tracing::{debug, trace, warn};
5353use crate :: core:: compiler:: RustcTargetData ;
5454use crate :: core:: resolver:: features:: { DiffMap , FeatureOpts , FeatureResolver , FeaturesFor } ;
5555use crate :: core:: resolver:: { HasDevUnits , Resolve , ResolveBehavior } ;
56- use crate :: core:: { Edition , MaybePackage , PackageId , Workspace } ;
56+ use crate :: core:: PackageIdSpecQuery as _;
57+ use crate :: core:: { Edition , MaybePackage , Package , PackageId , Workspace } ;
5758use crate :: ops:: resolve:: WorkspaceResolve ;
5859use crate :: ops:: { self , CompileOptions } ;
5960use crate :: util:: diagnostic_server:: { Message , RustfixDiagnosticServer } ;
@@ -87,11 +88,26 @@ pub struct FixOptions {
8788 pub broken_code : bool ,
8889}
8990
90- pub fn fix ( ws : & Workspace < ' _ > , opts : & mut FixOptions ) -> CargoResult < ( ) > {
91- check_version_control ( ws. gctx ( ) , opts) ?;
91+ pub fn fix (
92+ gctx : & GlobalContext ,
93+ original_ws : & Workspace < ' _ > ,
94+ root_manifest : & Path ,
95+ opts : & mut FixOptions ,
96+ ) -> CargoResult < ( ) > {
97+ check_version_control ( gctx, opts) ?;
98+
9299 if opts. edition {
93- check_resolver_change ( ws, opts) ?;
100+ let specs = opts. compile_opts . spec . to_package_id_specs ( & original_ws) ?;
101+ let members: Vec < & Package > = original_ws
102+ . members ( )
103+ . filter ( |m| specs. iter ( ) . any ( |spec| spec. matches ( m. package_id ( ) ) ) )
104+ . collect ( ) ;
105+ migrate_manifests ( original_ws, & members) ?;
106+
107+ check_resolver_change ( & original_ws, opts) ?;
94108 }
109+ let mut ws = Workspace :: new ( & root_manifest, gctx) ?;
110+ ws. set_honor_rust_version ( original_ws. honor_rust_version ( ) ) ;
95111
96112 // Spin up our lock server, which our subprocesses will use to synchronize fixes.
97113 let lock_server = LockServer :: new ( ) ?;
@@ -128,7 +144,7 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions) -> CargoResult<()> {
128144 server. configure ( & mut wrapper) ;
129145 }
130146
131- let rustc = ws. gctx ( ) . load_global_rustc ( Some ( ws) ) ?;
147+ let rustc = ws. gctx ( ) . load_global_rustc ( Some ( & ws) ) ?;
132148 wrapper. arg ( & rustc. path ) ;
133149 // This is calling rustc in cargo fix-proxy-mode, so it also need to retry.
134150 // The argfile handling are located at `FixArgs::from_args`.
@@ -138,7 +154,7 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions) -> CargoResult<()> {
138154 // repeating build until there are no more changes to be applied
139155 opts. compile_opts . build_config . primary_unit_rustc = Some ( wrapper) ;
140156
141- ops:: compile ( ws, & opts. compile_opts ) ?;
157+ ops:: compile ( & ws, & opts. compile_opts ) ?;
142158 Ok ( ( ) )
143159}
144160
@@ -215,6 +231,62 @@ fn check_version_control(gctx: &GlobalContext, opts: &FixOptions) -> CargoResult
215231 ) ;
216232}
217233
234+ fn migrate_manifests ( ws : & Workspace < ' _ > , pkgs : & [ & Package ] ) -> CargoResult < ( ) > {
235+ for pkg in pkgs {
236+ let existing_edition = pkg. manifest ( ) . edition ( ) ;
237+ let prepare_for_edition = existing_edition. saturating_next ( ) ;
238+ if existing_edition == prepare_for_edition
239+ || ( !prepare_for_edition. is_stable ( ) && !ws. gctx ( ) . nightly_features_allowed )
240+ {
241+ continue ;
242+ }
243+ let file = pkg. manifest_path ( ) ;
244+ let file = file. strip_prefix ( ws. root ( ) ) . unwrap_or ( file) ;
245+ let file = file. display ( ) ;
246+ ws. gctx ( ) . shell ( ) . status (
247+ "Migrating" ,
248+ format ! ( "{file} from {existing_edition} edition to {prepare_for_edition}" ) ,
249+ ) ?;
250+
251+ if Edition :: Edition2024 <= prepare_for_edition {
252+ let mut document = pkg. manifest ( ) . document ( ) . clone ( ) . into_mut ( ) ;
253+ let mut fixes = 0 ;
254+
255+ let root = document. as_table_mut ( ) ;
256+ if rename_table ( root, "project" , "package" ) {
257+ fixes += 1 ;
258+ }
259+
260+ if 0 < fixes {
261+ let verb = if fixes == 1 { "fix" } else { "fixes" } ;
262+ let msg = format ! ( "{file} ({fixes} {verb})" ) ;
263+ ws. gctx ( ) . shell ( ) . status ( "Fixed" , msg) ?;
264+
265+ let s = document. to_string ( ) ;
266+ let new_contents_bytes = s. as_bytes ( ) ;
267+ cargo_util:: paths:: write_atomic ( pkg. manifest_path ( ) , new_contents_bytes) ?;
268+ }
269+ }
270+ }
271+
272+ Ok ( ( ) )
273+ }
274+
275+ fn rename_table ( parent : & mut dyn toml_edit:: TableLike , old : & str , new : & str ) -> bool {
276+ let Some ( old_key) = parent. key ( old) . cloned ( ) else {
277+ return false ;
278+ } ;
279+
280+ let project = parent. remove ( old) . expect ( "returned early" ) ;
281+ if !parent. contains_key ( new) {
282+ parent. insert ( new, project) ;
283+ let mut new_key = parent. key_mut ( new) . expect ( "just inserted" ) ;
284+ * new_key. dotted_decor_mut ( ) = old_key. dotted_decor ( ) . clone ( ) ;
285+ * new_key. leaf_decor_mut ( ) = old_key. leaf_decor ( ) . clone ( ) ;
286+ }
287+ true
288+ }
289+
218290fn check_resolver_change ( ws : & Workspace < ' _ > , opts : & FixOptions ) -> CargoResult < ( ) > {
219291 let root = ws. root_maybe ( ) ;
220292 match root {
0 commit comments