#
cargo/ops/fix/
fix_edition.rs

    1//! Support for the permanently unstable `-Zfix-edition` flag.
    2
    3use super::{EditionFixMode, FixOptions};
    4use crate::core::features::{Edition, FixEdition};
    5use crate::core::{Package, Workspace};
    6use crate::ops;
    7use crate::util::toml_mut::manifest::LocalManifest;
    8use crate::{CargoResult, GlobalContext};
    9use toml_edit::{Formatted, Item, Value};
    10
    11/// Performs the actions for the `-Zfix-edition` flag.
    12pub 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.
    76fn 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-editions";
    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}