|
| 1 | +//! Support for the permanently unstable `-Zfix-edition` flag. |
| 2 | +
|
| 3 | +use super::{EditionFixMode, FixOptions}; |
| 4 | +use crate::core::features::{Edition, FixEdition}; |
| 5 | +use crate::core::{Package, Workspace}; |
| 6 | +use crate::ops; |
| 7 | +use crate::util::toml_mut::manifest::LocalManifest; |
| 8 | +use crate::{CargoResult, GlobalContext}; |
| 9 | +use toml_edit::{Formatted, Item, Value}; |
| 10 | + |
| 11 | +/// Performs the actions for the `-Zfix-edition` flag. |
| 12 | +pub fn fix_edition( |
| 13 | + gctx: &GlobalContext, |
| 14 | + original_ws: &Workspace<'_>, |
| 15 | + opts: &mut FixOptions, |
| 16 | + fix_edition: &FixEdition, |
| 17 | +) -> CargoResult<()> { |
| 18 | + let packages = opts.compile_opts.spec.get_packages(original_ws)?; |
| 19 | + let skip_if_not_edition = |edition| -> CargoResult<bool> { |
| 20 | + if !packages.iter().all(|p| p.manifest().edition() == edition) { |
| 21 | + gctx.shell().status( |
| 22 | + "Skipping", |
| 23 | + &format!("not all packages are at edition {edition}"), |
| 24 | + )?; |
| 25 | + Ok(true) |
| 26 | + } else { |
| 27 | + Ok(false) |
| 28 | + } |
| 29 | + }; |
| 30 | + |
| 31 | + match fix_edition { |
| 32 | + FixEdition::Start(edition) => { |
| 33 | + // The start point just runs `cargo check` if the edition is the |
| 34 | + // starting edition. This is so that crater can set a baseline of |
| 35 | + // whether or not the package builds at all. For other editions, |
| 36 | + // we skip entirely since they are not of interest since we can't |
| 37 | + // migrate them. |
| 38 | + if skip_if_not_edition(*edition)? { |
| 39 | + return Ok(()); |
| 40 | + } |
| 41 | + ops::compile(&original_ws, &opts.compile_opts)?; |
| 42 | + } |
| 43 | + FixEdition::End { initial, next } => { |
| 44 | + // Skip packages that are not the starting edition, since we can |
| 45 | + // only migrate from one edition to the next. |
| 46 | + if skip_if_not_edition(*initial)? { |
| 47 | + return Ok(()); |
| 48 | + } |
| 49 | + // Do the edition fix. |
| 50 | + opts.edition = Some(EditionFixMode::OverrideSpecific(*next)); |
| 51 | + opts.allow_dirty = true; |
| 52 | + opts.allow_no_vcs = true; |
| 53 | + opts.allow_staged = true; |
| 54 | + ops::fix(gctx, original_ws, opts)?; |
| 55 | + // Do `cargo check` with the new edition so that we can verify |
| 56 | + // that it also works on the next edition. |
| 57 | + replace_edition(&packages, *next)?; |
| 58 | + gctx.shell() |
| 59 | + .status("Updated", &format!("edition to {next}"))?; |
| 60 | + let ws = original_ws.reload(gctx)?; |
| 61 | + // Unset these since we just want to do a normal `cargo check`. |
| 62 | + *opts |
| 63 | + .compile_opts |
| 64 | + .build_config |
| 65 | + .rustfix_diagnostic_server |
| 66 | + .borrow_mut() = None; |
| 67 | + opts.compile_opts.build_config.primary_unit_rustc = None; |
| 68 | + |
| 69 | + ops::compile(&ws, &opts.compile_opts)?; |
| 70 | + } |
| 71 | + } |
| 72 | + Ok(()) |
| 73 | +} |
| 74 | + |
| 75 | +/// Modifies the `edition` value of the given packages to the new edition. |
| 76 | +fn replace_edition(packages: &[&Package], to_edition: Edition) -> CargoResult<()> { |
| 77 | + for package in packages { |
| 78 | + let mut manifest_mut = LocalManifest::try_new(package.manifest_path())?; |
| 79 | + let document = &mut manifest_mut.data; |
| 80 | + let root = document.as_table_mut(); |
| 81 | + // Update edition to the new value. |
| 82 | + if let Some(package) = root.get_mut("package").and_then(|t| t.as_table_like_mut()) { |
| 83 | + package.insert( |
| 84 | + "edition", |
| 85 | + Item::Value(Value::String(Formatted::new(to_edition.to_string()))), |
| 86 | + ); |
| 87 | + } |
| 88 | + // If the edition is unstable, add it to cargo-features. |
| 89 | + if !to_edition.is_stable() { |
| 90 | + let feature = "unstable-edition"; |
| 91 | + |
| 92 | + if let Some(features) = root |
| 93 | + .entry("cargo-features") |
| 94 | + .or_insert_with(|| Item::Value(Value::Array(toml_edit::Array::new()))) |
| 95 | + .as_array_mut() |
| 96 | + { |
| 97 | + if !features |
| 98 | + .iter() |
| 99 | + .any(|f| f.as_str().map_or(false, |f| f == feature)) |
| 100 | + { |
| 101 | + features.push(feature); |
| 102 | + } |
| 103 | + } |
| 104 | + } |
| 105 | + manifest_mut.write()?; |
| 106 | + } |
| 107 | + Ok(()) |
| 108 | +} |
0 commit comments