From 0d2349dc05fd458e441e3014f9616a56cd16dc51 Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Mon, 30 Sep 2024 14:33:16 -0500 Subject: [PATCH 1/7] refactor(update): Tighten scope of transitive reporting checks --- src/cargo/ops/cargo_update.rs | 50 ++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index 0c9b885da70..08341b6ca79 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -795,21 +795,25 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti return Some(report); } - if !change.is_transitive.unwrap_or(true) { - let incompat_ver_compat_msrv_summary = possibilities - .iter() - .map(|s| s.as_summary()) - .filter(|s| { - if let (Some(summary_rust_version), Some(required_rust_version)) = - (s.rust_version(), required_rust_version) - { - summary_rust_version.is_compatible_with(required_rust_version) - } else { - true - } - }) - .filter(|s| is_latest(s.version(), package_id.version())) - .max_by_key(|s| s.version()); + { + let incompat_ver_compat_msrv_summary = if !change.is_transitive.unwrap_or(true) { + possibilities + .iter() + .map(|s| s.as_summary()) + .filter(|s| { + if let (Some(summary_rust_version), Some(required_rust_version)) = + (s.rust_version(), required_rust_version) + { + summary_rust_version.is_compatible_with(required_rust_version) + } else { + true + } + }) + .filter(|s| is_latest(s.version(), package_id.version())) + .max_by_key(|s| s.version()) + } else { + None + }; if let Some(summary) = incompat_ver_compat_msrv_summary { let warn = style::WARN; let version = summary.version(); @@ -834,12 +838,16 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti return Some(report); } - if !change.is_transitive.unwrap_or(true) { - let incompat_ver_summary = possibilities - .iter() - .map(|s| s.as_summary()) - .filter(|s| is_latest(s.version(), package_id.version())) - .max_by_key(|s| s.version()); + { + let incompat_ver_summary = if !change.is_transitive.unwrap_or(true) { + possibilities + .iter() + .map(|s| s.as_summary()) + .filter(|s| is_latest(s.version(), package_id.version())) + .max_by_key(|s| s.version()) + } else { + None + }; if let Some(summary) = incompat_ver_summary { let msrv_note = summary .rust_version() From 1b3c91bbb4c10fdefb754e6e57edf8c42c8c3068 Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Mon, 30 Sep 2024 14:33:46 -0500 Subject: [PATCH 2/7] refactor(update): Remove redundant scopes --- src/cargo/ops/cargo_update.rs | 86 +++++++++++++++++------------------ 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index 08341b6ca79..c8703afc766 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -795,31 +795,29 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti return Some(report); } - { - let incompat_ver_compat_msrv_summary = if !change.is_transitive.unwrap_or(true) { - possibilities - .iter() - .map(|s| s.as_summary()) - .filter(|s| { - if let (Some(summary_rust_version), Some(required_rust_version)) = - (s.rust_version(), required_rust_version) - { - summary_rust_version.is_compatible_with(required_rust_version) - } else { - true - } - }) - .filter(|s| is_latest(s.version(), package_id.version())) - .max_by_key(|s| s.version()) - } else { - None - }; - if let Some(summary) = incompat_ver_compat_msrv_summary { - let warn = style::WARN; - let version = summary.version(); - let report = format!(" {warn}(available: v{version}){warn:#}"); - return Some(report); - } + let incompat_ver_compat_msrv_summary = if !change.is_transitive.unwrap_or(true) { + possibilities + .iter() + .map(|s| s.as_summary()) + .filter(|s| { + if let (Some(summary_rust_version), Some(required_rust_version)) = + (s.rust_version(), required_rust_version) + { + summary_rust_version.is_compatible_with(required_rust_version) + } else { + true + } + }) + .filter(|s| is_latest(s.version(), package_id.version())) + .max_by_key(|s| s.version()) + } else { + None + }; + if let Some(summary) = incompat_ver_compat_msrv_summary { + let warn = style::WARN; + let version = summary.version(); + let report = format!(" {warn}(available: v{version}){warn:#}"); + return Some(report); } let compat_ver_summary = possibilities @@ -838,26 +836,24 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti return Some(report); } - { - let incompat_ver_summary = if !change.is_transitive.unwrap_or(true) { - possibilities - .iter() - .map(|s| s.as_summary()) - .filter(|s| is_latest(s.version(), package_id.version())) - .max_by_key(|s| s.version()) - } else { - None - }; - if let Some(summary) = incompat_ver_summary { - let msrv_note = summary - .rust_version() - .map(|rv| format!(", requires Rust {rv}")) - .unwrap_or_default(); - let warn = style::NOP; - let version = summary.version(); - let report = format!(" {warn}(available: v{version}{msrv_note}){warn:#}"); - return Some(report); - } + let incompat_ver_summary = if !change.is_transitive.unwrap_or(true) { + possibilities + .iter() + .map(|s| s.as_summary()) + .filter(|s| is_latest(s.version(), package_id.version())) + .max_by_key(|s| s.version()) + } else { + None + }; + if let Some(summary) = incompat_ver_summary { + let msrv_note = summary + .rust_version() + .map(|rv| format!(", requires Rust {rv}")) + .unwrap_or_default(); + let warn = style::NOP; + let version = summary.version(); + let report = format!(" {warn}(available: v{version}{msrv_note}){warn:#}"); + return Some(report); } None From 63e27c60cce6f03d1a5412b0a85b37abeb16b8f2 Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Mon, 30 Sep 2024 14:34:44 -0500 Subject: [PATCH 3/7] refactor(update): Calculate all reporting states --- src/cargo/ops/cargo_update.rs | 45 ++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index c8703afc766..1da0af54a14 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -788,12 +788,6 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti }) .filter(|s| package_id.version() != s.version() && version_req.matches(s.version())) .max_by_key(|s| s.version()); - if let Some(summary) = compat_ver_compat_msrv_summary { - let warn = style::WARN; - let version = summary.version(); - let report = format!(" {warn}(available: v{version}){warn:#}"); - return Some(report); - } let incompat_ver_compat_msrv_summary = if !change.is_transitive.unwrap_or(true) { possibilities @@ -813,28 +807,12 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti } else { None }; - if let Some(summary) = incompat_ver_compat_msrv_summary { - let warn = style::WARN; - let version = summary.version(); - let report = format!(" {warn}(available: v{version}){warn:#}"); - return Some(report); - } let compat_ver_summary = possibilities .iter() .map(|s| s.as_summary()) .filter(|s| package_id.version() != s.version() && version_req.matches(s.version())) .max_by_key(|s| s.version()); - if let Some(summary) = compat_ver_summary { - let msrv_note = summary - .rust_version() - .map(|rv| format!(", requires Rust {rv}")) - .unwrap_or_default(); - let warn = style::NOP; - let version = summary.version(); - let report = format!(" {warn}(available: v{version}{msrv_note}){warn:#}"); - return Some(report); - } let incompat_ver_summary = if !change.is_transitive.unwrap_or(true) { possibilities @@ -845,6 +823,29 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti } else { None }; + + if let Some(summary) = compat_ver_compat_msrv_summary { + let warn = style::WARN; + let version = summary.version(); + let report = format!(" {warn}(available: v{version}){warn:#}"); + return Some(report); + } + if let Some(summary) = incompat_ver_compat_msrv_summary { + let warn = style::WARN; + let version = summary.version(); + let report = format!(" {warn}(available: v{version}){warn:#}"); + return Some(report); + } + if let Some(summary) = compat_ver_summary { + let msrv_note = summary + .rust_version() + .map(|rv| format!(", requires Rust {rv}")) + .unwrap_or_default(); + let warn = style::NOP; + let version = summary.version(); + let report = format!(" {warn}(available: v{version}{msrv_note}){warn:#}"); + return Some(report); + } if let Some(summary) = incompat_ver_summary { let msrv_note = summary .rust_version() From fbe79f5456bbeeae71fc2ff8b168707e20e196c5 Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Mon, 30 Sep 2024 14:35:50 -0500 Subject: [PATCH 4/7] refactor(update): Clarify reporting states are mutually exclusive --- src/cargo/ops/cargo_update.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index 1da0af54a14..8fc406df943 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -828,15 +828,13 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti let warn = style::WARN; let version = summary.version(); let report = format!(" {warn}(available: v{version}){warn:#}"); - return Some(report); - } - if let Some(summary) = incompat_ver_compat_msrv_summary { + Some(report) + } else if let Some(summary) = incompat_ver_compat_msrv_summary { let warn = style::WARN; let version = summary.version(); let report = format!(" {warn}(available: v{version}){warn:#}"); - return Some(report); - } - if let Some(summary) = compat_ver_summary { + Some(report) + } else if let Some(summary) = compat_ver_summary { let msrv_note = summary .rust_version() .map(|rv| format!(", requires Rust {rv}")) @@ -844,9 +842,8 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti let warn = style::NOP; let version = summary.version(); let report = format!(" {warn}(available: v{version}{msrv_note}){warn:#}"); - return Some(report); - } - if let Some(summary) = incompat_ver_summary { + Some(report) + } else if let Some(summary) = incompat_ver_summary { let msrv_note = summary .rust_version() .map(|rv| format!(", requires Rust {rv}")) @@ -854,10 +851,10 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti let warn = style::NOP; let version = summary.version(); let report = format!(" {warn}(available: v{version}{msrv_note}){warn:#}"); - return Some(report); + Some(report) + } else { + None } - - None } fn is_latest(candidate: &semver::Version, current: &semver::Version) -> bool { From a246fd589bbee8b53c0313db531cc92c5fa42087 Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Fri, 13 Sep 2024 11:17:50 -0400 Subject: [PATCH 5/7] refactor(resolve): Change how we calculate unchanged --- src/cargo/ops/cargo_update.rs | 78 +++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index 8fc406df943..bcb9a3838ec 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -538,8 +538,8 @@ fn print_lockfile_generation( vec![] }; - let required_rust_version = report_required_rust_version(resolve, change); - let latest = report_latest(&possibilities, change); + let required_rust_version = report_required_rust_version(resolve, change, None); + let latest = report_latest(&possibilities, change, None); let note = required_rust_version.or(latest); if let Some(note) = note { @@ -601,8 +601,8 @@ fn print_lockfile_sync( vec![] }; - let required_rust_version = report_required_rust_version(resolve, change); - let latest = report_latest(&possibilities, change); + let required_rust_version = report_required_rust_version(resolve, change, None); + let latest = report_latest(&possibilities, change, None); let note = required_rust_version.or(latest).unwrap_or_default(); ws.gctx().shell().status_with_color( @@ -654,8 +654,8 @@ fn print_lockfile_updates( PackageChangeKind::Added | PackageChangeKind::Upgraded | PackageChangeKind::Downgraded => { - let required_rust_version = report_required_rust_version(resolve, change); - let latest = report_latest(&possibilities, change); + let required_rust_version = report_required_rust_version(resolve, change, None); + let latest = report_latest(&possibilities, change, None); let note = required_rust_version.or(latest).unwrap_or_default(); ws.gctx().shell().status_with_color( @@ -672,14 +672,16 @@ fn print_lockfile_updates( )?; } PackageChangeKind::Unchanged => { - let required_rust_version = report_required_rust_version(resolve, change); - let latest = report_latest(&possibilities, change); + let mut unchanged_stats = UpdateStats::default(); + let required_rust_version = + report_required_rust_version(resolve, change, Some(&mut unchanged_stats)); + let latest = report_latest(&possibilities, change, Some(&mut unchanged_stats)); let note = required_rust_version.as_deref().or(latest.as_deref()); + if unchanged_stats.behind() { + unchanged_behind += 1; + } if let Some(note) = note { - if latest.is_some() { - unchanged_behind += 1; - } if ws.gctx().shell().verbosity() == Verbosity::Verbose { ws.gctx().shell().status_with_color( change.kind.status(), @@ -748,7 +750,11 @@ fn required_rust_version(ws: &Workspace<'_>) -> Option<PartialVersion> { } } -fn report_required_rust_version(resolve: &Resolve, change: &PackageChange) -> Option<String> { +fn report_required_rust_version( + resolve: &Resolve, + change: &PackageChange, + stats: Option<&mut UpdateStats>, +) -> Option<String> { if change.package_id.source_id().is_path() { return None; } @@ -759,13 +765,20 @@ fn report_required_rust_version(resolve: &Resolve, change: &PackageChange) -> Op return None; } + if let Some(stats) = stats { + stats.required_rust_version += 1; + } let error = style::ERROR; Some(format!( " {error}(requires Rust {package_rust_version}){error:#}" )) } -fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Option<String> { +fn report_latest( + possibilities: &[IndexSummary], + change: &PackageChange, + mut stats: Option<&mut UpdateStats>, +) -> Option<String> { let package_id = change.package_id; if !package_id.source_id().is_registry() { return None; @@ -788,6 +801,11 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti }) .filter(|s| package_id.version() != s.version() && version_req.matches(s.version())) .max_by_key(|s| s.version()); + if let Some(ref mut stats) = stats { + if compat_ver_compat_msrv_summary.is_some() { + stats.compat_ver_compat_msrv += 1; + } + } let incompat_ver_compat_msrv_summary = if !change.is_transitive.unwrap_or(true) { possibilities @@ -807,12 +825,22 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti } else { None }; + if let Some(stats) = stats.as_mut() { + if incompat_ver_compat_msrv_summary.is_some() { + stats.incompat_ver_compat_msrv += 1; + } + } let compat_ver_summary = possibilities .iter() .map(|s| s.as_summary()) .filter(|s| package_id.version() != s.version() && version_req.matches(s.version())) .max_by_key(|s| s.version()); + if let Some(stats) = stats.as_mut() { + if compat_ver_summary.is_some() { + stats.compat_ver += 1; + } + } let incompat_ver_summary = if !change.is_transitive.unwrap_or(true) { possibilities @@ -823,6 +851,11 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti } else { None }; + if let Some(stats) = stats.as_mut() { + if incompat_ver_summary.is_some() { + stats.incompat_ver += 1; + } + } if let Some(summary) = compat_ver_compat_msrv_summary { let warn = style::WARN; @@ -857,6 +890,25 @@ fn report_latest(possibilities: &[IndexSummary], change: &PackageChange) -> Opti } } +#[derive(Default)] +struct UpdateStats { + required_rust_version: usize, + compat_ver_compat_msrv: usize, + incompat_ver_compat_msrv: usize, + compat_ver: usize, + incompat_ver: usize, +} + +impl UpdateStats { + fn behind(&self) -> bool { + self.compat_ver_compat_msrv + + self.incompat_ver_compat_msrv + + self.compat_ver + + self.incompat_ver + != 0 + } +} + fn is_latest(candidate: &semver::Version, current: &semver::Version) -> bool { current < candidate // Only match pre-release if major.minor.patch are the same From be2e7f4f94c3d87105840f0a0bed3cc63e03d6ed Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Fri, 13 Sep 2024 22:21:57 -0400 Subject: [PATCH 6/7] test(resolve): Show more reporting cases --- tests/testsuite/rust_version.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs index c36416e64af..4fef1d7c59a 100644 --- a/tests/testsuite/rust_version.rs +++ b/tests/testsuite/rust_version.rs @@ -1113,33 +1113,57 @@ fn report_rust_versions() { .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + + Package::new("dep-only-low-incompatible", "1.55.0") + .rust_version("1.55.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); Package::new("dep-only-low-incompatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + + Package::new("dep-only-high-compatible", "1.55.0") + .rust_version("1.55.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); Package::new("dep-only-high-compatible", "1.65.0") .rust_version("1.65.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + + Package::new("dep-only-high-incompatible", "1.55.0") + .rust_version("1.55.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); Package::new("dep-only-high-incompatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + Package::new("dep-only-unset-unset", "1.0.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + + Package::new("dep-only-unset-compatible", "1.55.0") + .rust_version("1.55.0") + .file("src/lib.rs", "fn other_stuff() {}") + .publish(); Package::new("dep-only-unset-compatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + Package::new("dep-only-unset-incompatible", "1.2345.0") .rust_version("1.2345.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + Package::new("dep-shared-compatible", "1.55.0") .rust_version("1.55.0") .file("src/lib.rs", "fn other_stuff() {}") .publish(); + Package::new("dep-shared-incompatible", "1.75.0") .rust_version("1.75.0") .file("src/lib.rs", "fn other_stuff() {}") @@ -1210,8 +1234,10 @@ fn report_rust_versions() { .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 9 packages to latest Rust 1.60.0 compatible versions -[ADDING] dep-only-high-incompatible v1.75.0 (requires Rust 1.75.0) -[ADDING] dep-only-low-incompatible v1.75.0 (requires Rust 1.75.0) +[ADDING] dep-only-high-compatible v1.55.0 (available: v1.65.0) +[ADDING] dep-only-high-incompatible v1.55.0 (available: v1.75.0, requires Rust 1.75.0) +[ADDING] dep-only-low-incompatible v1.55.0 (available: v1.75.0, requires Rust 1.75.0) +[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) From 818eead9dbfc8cb0f3731affd6ef95f9bb3cb262 Mon Sep 17 00:00:00 2001 From: Ed Page <eopage@gmail.com> Date: Fri, 13 Sep 2024 23:33:58 -0400 Subject: [PATCH 7/7] 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 bcb9a3838ec..a2852da7cc4 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 <name> --precise <version>" + ))?; + } else { + ws.gctx().shell().note(format!( + "{compat_ver_compat_msrv} packages may have a higher, compatible version. To update them, run `cargo update <name> --precise <version>" + ))?; + } + } + 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 <name> --precise <version>" + ))?; + } + 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 <name> --precise <version>" + ))?; + } if ws.gctx().shell().verbosity() == Verbosity::Verbose { ws.gctx().shell().note( "to see how you depend on a package, run `cargo tree --invert --package <dep>@<ver>`", @@ -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 4fef1d7c59a..c3d9e24eb38 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 <name> --precise <version> "#]]) .run(); diff --git a/tests/testsuite/workspaces.rs b/tests/testsuite/workspaces.rs index 9e831fa394f..f0489be99f9 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 <name> --precise <version> [DOWNLOADING] crates ... [DOWNLOADED] dep1 v0.1.3 (registry `dummy-registry`) [CHECKING] dep1 v0.1.3