From 8a99b0eda20587f7538d0d4ae70a9b1def1c5bbc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Sep 2024 23:33:58 -0400 Subject: [PATCH] feat(resolve): Direct people to working around less optimal MSRV-resolver results In discussing #14414, the general problem of the resolver picking a version older than a package needs for its MSRV (or lack of one) because of the MSRV of other packages came up. This tries to patch over that problem by telling users that a dependency might be able to be newer than the resolver selected. The message is fairly generic and might be misread to be about any MSRV update which an MSRV `fallback` strategy allows, which would make the count off. The reason it is so generic is we don't know with precision why it was held back - Direct dependents may have a non-semver upper bound on the version as we aren't trying to unify the version requirements across direct dependents at this time - A dependency could have removed a feature without making a breaking change - This seems like it should instead be an error but thats a conversation for another day - ~~The user enabled `-Zminimal-versions`~~ - This is now detected and the message skipped Note: separate from this, we may also print the status suffix for this case if the package was not selected for update (e.g. passing `--workspace`). --- src/cargo/ops/cargo_update.rs | 55 ++++++++++++++++++++++++++++----- tests/testsuite/rust_version.rs | 1 + tests/testsuite/workspaces.rs | 1 + 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index bcb9a3838ecb..a2852da7cc4d 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -519,6 +519,7 @@ fn print_lockfile_generation( annotate_required_rust_version(ws, resolve, &mut changes); status_locking(ws, num_pkgs)?; + let mut changed_stats = UpdateStats::default(); for change in changes.values() { if change.is_member.unwrap_or(false) { continue; @@ -538,8 +539,9 @@ fn print_lockfile_generation( vec![] }; - let required_rust_version = report_required_rust_version(resolve, change, None); - let latest = report_latest(&possibilities, change, None); + let required_rust_version = + report_required_rust_version(resolve, change, Some(&mut changed_stats)); + let latest = report_latest(&possibilities, change, Some(&mut changed_stats)); let note = required_rust_version.or(latest); if let Some(note) = note { @@ -559,6 +561,22 @@ fn print_lockfile_generation( } } + let compat_ver_compat_msrv = changed_stats.compat_ver_compat_msrv; + if 0 < compat_ver_compat_msrv + && !ws.gctx().cli_unstable().direct_minimal_versions + && !ws.gctx().cli_unstable().minimal_versions + { + if compat_ver_compat_msrv == 1 { + ws.gctx().shell().note(format!( + "{compat_ver_compat_msrv} package may have a higher, compatible version. To update it, run `cargo update --precise " + ))?; + } else { + ws.gctx().shell().note(format!( + "{compat_ver_compat_msrv} packages may have a higher, compatible version. To update them, run `cargo update --precise " + ))?; + } + } + Ok(()) } @@ -580,6 +598,7 @@ fn print_lockfile_sync( annotate_required_rust_version(ws, resolve, &mut changes); status_locking(ws, num_pkgs)?; + let mut changed_stats = UpdateStats::default(); for change in changes.values() { if change.is_member.unwrap_or(false) { continue; @@ -601,8 +620,9 @@ fn print_lockfile_sync( vec![] }; - let required_rust_version = report_required_rust_version(resolve, change, None); - let latest = report_latest(&possibilities, change, None); + let required_rust_version = + report_required_rust_version(resolve, change, Some(&mut changed_stats)); + let latest = report_latest(&possibilities, change, Some(&mut changed_stats)); let note = required_rust_version.or(latest).unwrap_or_default(); ws.gctx().shell().status_with_color( @@ -615,6 +635,16 @@ fn print_lockfile_sync( } } + let compat_ver_compat_msrv = changed_stats.compat_ver_compat_msrv; + if 0 < compat_ver_compat_msrv + && !ws.gctx().cli_unstable().direct_minimal_versions + && !ws.gctx().cli_unstable().minimal_versions + { + ws.gctx().shell().note(format!( + "{compat_ver_compat_msrv} were updated but higher versions may be available by manually updating with `cargo update --precise " + ))?; + } + Ok(()) } @@ -636,6 +666,7 @@ fn print_lockfile_updates( status_locking(ws, num_pkgs)?; } let mut unchanged_behind = 0; + let mut changed_stats = UpdateStats::default(); for change in changes.values() { let possibilities = if let Some(query) = change.alternatives_query() { loop { @@ -654,8 +685,9 @@ fn print_lockfile_updates( PackageChangeKind::Added | PackageChangeKind::Upgraded | PackageChangeKind::Downgraded => { - let required_rust_version = report_required_rust_version(resolve, change, None); - let latest = report_latest(&possibilities, change, None); + let required_rust_version = + report_required_rust_version(resolve, change, Some(&mut changed_stats)); + let latest = report_latest(&possibilities, change, Some(&mut changed_stats)); let note = required_rust_version.or(latest).unwrap_or_default(); ws.gctx().shell().status_with_color( @@ -694,6 +726,15 @@ fn print_lockfile_updates( } } + let compat_ver_compat_msrv = changed_stats.compat_ver_compat_msrv; + if 0 < compat_ver_compat_msrv + && !ws.gctx().cli_unstable().direct_minimal_versions + && !ws.gctx().cli_unstable().minimal_versions + { + ws.gctx().shell().note(format!( + "{compat_ver_compat_msrv} were updated but higher versions may be available by manually updating with `cargo update --precise " + ))?; + } if ws.gctx().shell().verbosity() == Verbosity::Verbose { ws.gctx().shell().note( "to see how you depend on a package, run `cargo tree --invert --package @`", @@ -890,7 +931,7 @@ fn report_latest( } } -#[derive(Default)] +#[derive(Copy, Clone, Default, Debug)] struct UpdateStats { required_rust_version: usize, compat_ver_compat_msrv: usize, diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs index 4fef1d7c59a8..c3d9e24eb38d 100644 --- a/tests/testsuite/rust_version.rs +++ b/tests/testsuite/rust_version.rs @@ -1240,6 +1240,7 @@ fn report_rust_versions() { [ADDING] dep-only-unset-compatible v1.55.0 (available: v1.75.0) [ADDING] dep-only-unset-incompatible v1.2345.0 (requires Rust 1.2345.0) [ADDING] dep-shared-incompatible v1.75.0 (requires Rust 1.75.0) +[NOTE] 2 packages may have a higher, compatible version. To update them, run `cargo update --precise "#]]) .run(); diff --git a/tests/testsuite/workspaces.rs b/tests/testsuite/workspaces.rs index 9e831fa394f4..f0489be99f93 100644 --- a/tests/testsuite/workspaces.rs +++ b/tests/testsuite/workspaces.rs @@ -698,6 +698,7 @@ fn share_dependencies() { [UPDATING] `dummy-registry` index [LOCKING] 1 package to latest compatible version [ADDING] dep1 v0.1.3 (available: v0.1.8) +[NOTE] 1 package may have a higher, compatible version. To update it, run `cargo update --precise [DOWNLOADING] crates ... [DOWNLOADED] dep1 v0.1.3 (registry `dummy-registry`) [CHECKING] dep1 v0.1.3