@@ -53,7 +53,8 @@ use tracing::{debug, trace, warn};
53
53
use crate :: core:: compiler:: RustcTargetData ;
54
54
use crate :: core:: resolver:: features:: { DiffMap , FeatureOpts , FeatureResolver , FeaturesFor } ;
55
55
use 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 } ;
57
58
use crate :: ops:: resolve:: WorkspaceResolve ;
58
59
use crate :: ops:: { self , CompileOptions } ;
59
60
use crate :: util:: diagnostic_server:: { Message , RustfixDiagnosticServer } ;
@@ -87,11 +88,26 @@ pub struct FixOptions {
87
88
pub broken_code : bool ,
88
89
}
89
90
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
+
92
99
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) ?;
94
108
}
109
+ let mut ws = Workspace :: new ( & root_manifest, gctx) ?;
110
+ ws. set_honor_rust_version ( original_ws. honor_rust_version ( ) ) ;
95
111
96
112
// Spin up our lock server, which our subprocesses will use to synchronize fixes.
97
113
let lock_server = LockServer :: new ( ) ?;
@@ -128,7 +144,7 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions) -> CargoResult<()> {
128
144
server. configure ( & mut wrapper) ;
129
145
}
130
146
131
- let rustc = ws. gctx ( ) . load_global_rustc ( Some ( ws) ) ?;
147
+ let rustc = ws. gctx ( ) . load_global_rustc ( Some ( & ws) ) ?;
132
148
wrapper. arg ( & rustc. path ) ;
133
149
// This is calling rustc in cargo fix-proxy-mode, so it also need to retry.
134
150
// The argfile handling are located at `FixArgs::from_args`.
@@ -138,7 +154,7 @@ pub fn fix(ws: &Workspace<'_>, opts: &mut FixOptions) -> CargoResult<()> {
138
154
// repeating build until there are no more changes to be applied
139
155
opts. compile_opts . build_config . primary_unit_rustc = Some ( wrapper) ;
140
156
141
- ops:: compile ( ws, & opts. compile_opts ) ?;
157
+ ops:: compile ( & ws, & opts. compile_opts ) ?;
142
158
Ok ( ( ) )
143
159
}
144
160
@@ -215,6 +231,62 @@ fn check_version_control(gctx: &GlobalContext, opts: &FixOptions) -> CargoResult
215
231
) ;
216
232
}
217
233
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
+
218
290
fn check_resolver_change ( ws : & Workspace < ' _ > , opts : & FixOptions ) -> CargoResult < ( ) > {
219
291
let root = ws. root_maybe ( ) ;
220
292
match root {
0 commit comments