From 83ff1e4e962fff43911480ad222e3f69f623df4b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 25 Feb 2026 15:35:00 -0600 Subject: [PATCH 1/2] test(fix): Show cargo script behavior --- tests/testsuite/fix.rs | 172 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index b4d411f781c..1715d638429 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -3066,3 +3066,175 @@ edition = "future" "#]], ); } + +#[cargo_test(nightly, reason = "-Zscript is unstable")] +fn script_without_frontmatter() { + let p = cargo_test_support::project() + .file("echo.rs", "fn main() {}") + .build(); + + p.cargo("fix -Zscript --allow-no-vcs --manifest-path echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout_data(str![""]) + .with_stderr_data(str![[r#" +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[CHECKING] echo v0.0.0 ([ROOT]/foo/echo.rs) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + assert_e2e().eq( + p.read_file("echo.rs"), + str!["fn main() {}"], + ); +} + +#[cargo_test(nightly, reason = "-Zscript is unstable")] +fn script_with_frontmatter() { + let p = cargo_test_support::project() + .file( + "echo.rs", + "#!/usr/bin/env cargo +--- +[dependencies] +--- +fn main() {}", + ) + .build(); + + p.cargo("fix -Zscript --allow-no-vcs --manifest-path echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout_data(str![""]) + .with_stderr_data(str![[r#" +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[CHECKING] echo v0.0.0 ([ROOT]/foo/echo.rs) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + assert_e2e().eq( + p.read_file("echo.rs"), + str![[r#" +#!/usr/bin/env cargo +--- +[dependencies] +--- +fn main() {} +"#]], + ); +} + +#[cargo_test(nightly, reason = "-Zscript is unstable")] +fn script_with_package_table() { + let p = cargo_test_support::project() + .file( + "echo.rs", + r#"#!/usr/bin/env cargo +--- +[package] +name = "foo" +--- +fn main() {}"#, + ) + .build(); + + p.cargo("fix -Zscript --allow-no-vcs --manifest-path echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout_data(str![""]) + .with_stderr_data(str![[r#" +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[CHECKING] foo v0.0.0 ([ROOT]/foo/echo.rs) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + assert_e2e().eq( + p.read_file("echo.rs"), + str![[r##" +#!/usr/bin/env cargo +--- +[package] +name = "foo" +--- +fn main() {} +"##]], + ); +} + +#[cargo_test(nightly, reason = "-Zscript is unstable")] +fn script_with_package_dotted() { + let p = cargo_test_support::project() + .file( + "echo.rs", + r#"#!/usr/bin/env cargo +--- +package.name = "foo" +--- +fn main() {}"#, + ) + .build(); + + p.cargo("fix -Zscript --allow-no-vcs --manifest-path echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout_data(str![""]) + .with_stderr_data(str![[r#" +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[CHECKING] foo v0.0.0 ([ROOT]/foo/echo.rs) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + assert_e2e().eq( + p.read_file("echo.rs"), + str![[r##" +#!/usr/bin/env cargo +--- +package.name = "foo" +--- +fn main() {} +"##]], + ); +} + +#[cargo_test(nightly, reason = "-Zscript is unstable")] +fn script_with_edition() { + let p = cargo_test_support::project() + .file( + "echo.rs", + r#"#!/usr/bin/env cargo +--- +package.edition = "2015" +--- +fn main() {}"#, + ) + .build(); + + p.cargo("fix -Zscript --allow-no-vcs --manifest-path echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout_data(str![""]) + .with_stderr_data(str![[r#" +[CHECKING] echo v0.0.0 ([ROOT]/foo/echo.rs) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + assert_e2e().eq( + p.read_file("echo.rs"), + str![[r##" +#!/usr/bin/env cargo +--- +package.edition = "2015" +--- +fn main() {} +"##]], + ); +} From 608e12294313fa7bd61b1fadfca5796f4ae44db7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 25 Feb 2026 15:42:58 -0600 Subject: [PATCH 2/2] feat(fix): Inject an edition into scripts --- src/cargo/ops/fix/mod.rs | 56 ++++++++++++++++++++++++++++++++++++---- tests/testsuite/fix.rs | 25 +++++++++++++----- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/cargo/ops/fix/mod.rs b/src/cargo/ops/fix/mod.rs index 6bd0199a494..e7632e43e76 100644 --- a/src/cargo/ops/fix/mod.rs +++ b/src/cargo/ops/fix/mod.rs @@ -151,16 +151,18 @@ pub fn fix( let mut target_data = RustcTargetData::new(original_ws, &opts.compile_opts.build_config.requested_kinds)?; + + let specs = opts.compile_opts.spec.to_package_id_specs(&original_ws)?; + let members: Vec<&Package> = original_ws + .members() + .filter(|m| specs.iter().any(|spec| spec.matches(m.package_id()))) + .collect(); if let Some(edition_mode) = opts.edition { - let specs = opts.compile_opts.spec.to_package_id_specs(&original_ws)?; - let members: Vec<&Package> = original_ws - .members() - .filter(|m| specs.iter().any(|spec| spec.matches(m.package_id()))) - .collect(); migrate_manifests(original_ws, &members, edition_mode)?; check_resolver_change(&original_ws, &mut target_data, opts)?; } + fix_manifests(original_ws, &members)?; let ws = original_ws.reload(gctx)?; // Spin up our lock server, which our subprocesses will use to synchronize fixes. @@ -290,6 +292,50 @@ fn check_version_control(gctx: &GlobalContext, opts: &FixOptions) -> CargoResult ); } +fn fix_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> { + for pkg in pkgs { + if !pkg.manifest().is_embedded() + || pkg + .manifest() + .original_toml() + .and_then(|m| m.package()) + .map(|pkg| pkg.edition.is_some()) + .unwrap_or(false) + { + continue; + } + let file = pkg.manifest_path(); + let file = file.strip_prefix(ws.root()).unwrap_or(file); + let file = file.display(); + + let mut manifest_mut = LocalManifest::try_new(pkg.manifest_path())?; + let document = &mut manifest_mut.data; + let mut fixes = 0; + + let root = document.as_table_mut(); + + fixes += 1; + root.entry("package").or_insert_with(|| { + let mut t = toml_edit::Table::new(); + t.set_position(Some(-1)); + t.into() + }); + root["package"]["edition"] = crate::core::features::Edition::LATEST_STABLE + .to_string() + .into(); + + if 0 < fixes { + let verb = if fixes == 1 { "fix" } else { "fixes" }; + let msg = format!("{file} ({fixes} {verb})"); + ws.gctx().shell().status("Fixed", msg)?; + + manifest_mut.write()?; + } + } + + Ok(()) +} + fn migrate_manifests( ws: &Workspace<'_>, pkgs: &[&Package], diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index 1715d638429..1fafbbfa163 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -3078,7 +3078,7 @@ fn script_without_frontmatter() { .with_stdout_data(str![""]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) -[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[FIXED] echo.rs (1 fix) [CHECKING] echo v0.0.0 ([ROOT]/foo/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -3087,7 +3087,14 @@ fn script_without_frontmatter() { assert_e2e().eq( p.read_file("echo.rs"), - str!["fn main() {}"], + str![[r#" +--- +[package] +edition = "2024" +--- + +fn main() {} +"#]], ); } @@ -3109,7 +3116,7 @@ fn main() {}", .with_stdout_data(str![""]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) -[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[FIXED] echo.rs (1 fix) [CHECKING] echo v0.0.0 ([ROOT]/foo/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -3118,13 +3125,15 @@ fn main() {}", assert_e2e().eq( p.read_file("echo.rs"), - str![[r#" + str![[r##" #!/usr/bin/env cargo --- +[package] +edition = "2024" [dependencies] --- fn main() {} -"#]], +"##]], ); } @@ -3147,7 +3156,7 @@ fn main() {}"#, .with_stdout_data(str![""]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) -[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[FIXED] echo.rs (1 fix) [CHECKING] foo v0.0.0 ([ROOT]/foo/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -3161,6 +3170,7 @@ fn main() {}"#, --- [package] name = "foo" +edition = "2024" --- fn main() {} "##]], @@ -3185,7 +3195,7 @@ fn main() {}"#, .with_stdout_data(str![""]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) -[WARNING] `package.edition` is unspecified, defaulting to the latest edition (currently `[..]`) +[FIXED] echo.rs (1 fix) [CHECKING] foo v0.0.0 ([ROOT]/foo/echo.rs) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -3198,6 +3208,7 @@ fn main() {}"#, #!/usr/bin/env cargo --- package.name = "foo" +package.edition = "2024" --- fn main() {} "##]],