Skip to content

Commit b8a4f1b

Browse files
committed
Auto merge of #14457 - epage:msrv, r=ehuss
fix(resolve): Report incompatible packages with precise Rust version ### What does this PR try to resolve? In #14401, we reported about MSRV issues as if we were the resolver. We can be smarter than that though and report precise MSRV information. This allows us to elevate the message from color from yellow to red. This is also prep work for telling users when a newer, MSRV-compatible version of a package is available. ### How should we test and review this PR? The report function I added is a little odd because a follow up commit will update it to report when a package is incompatible with rustc when the MSRV resolver is disabled and do this on stable. ### Additional information This builds on #14445 to improve #14401
2 parents ef854d2 + 3e31551 commit b8a4f1b

File tree

2 files changed

+200
-31
lines changed

2 files changed

+200
-31
lines changed

src/cargo/ops/cargo_update.rs

+88-31
Original file line numberDiff line numberDiff line change
@@ -492,18 +492,19 @@ fn print_lockfile_generation(
492492
resolve: &Resolve,
493493
registry: &mut PackageRegistry<'_>,
494494
) -> CargoResult<()> {
495-
let changes = PackageChange::new(ws, resolve);
495+
let mut changes = PackageChange::new(ws, resolve);
496496
let num_pkgs: usize = changes
497-
.iter()
497+
.values()
498498
.filter(|change| change.kind.is_new() && !change.is_member.unwrap_or(false))
499499
.count();
500500
if num_pkgs == 0 {
501501
// nothing worth reporting
502502
return Ok(());
503503
}
504-
status_locking(ws, num_pkgs)?;
504+
annotate_required_rust_version(ws, resolve, &mut changes);
505505

506-
for change in changes {
506+
status_locking(ws, num_pkgs)?;
507+
for change in changes.values() {
507508
if change.is_member.unwrap_or(false) {
508509
continue;
509510
};
@@ -523,7 +524,7 @@ fn print_lockfile_generation(
523524
};
524525

525526
let package_id = change.package_id;
526-
let required_rust_version = report_required_rust_version(ws, resolve, package_id);
527+
let required_rust_version = report_required_rust_version(resolve, change);
527528
let latest = report_latest(&possibilities, package_id);
528529
let note = required_rust_version.or(latest);
529530

@@ -553,18 +554,19 @@ fn print_lockfile_sync(
553554
resolve: &Resolve,
554555
registry: &mut PackageRegistry<'_>,
555556
) -> CargoResult<()> {
556-
let changes = PackageChange::diff(ws, previous_resolve, resolve);
557+
let mut changes = PackageChange::diff(ws, previous_resolve, resolve);
557558
let num_pkgs: usize = changes
558-
.iter()
559+
.values()
559560
.filter(|change| change.kind.is_new() && !change.is_member.unwrap_or(false))
560561
.count();
561562
if num_pkgs == 0 {
562563
// nothing worth reporting
563564
return Ok(());
564565
}
565-
status_locking(ws, num_pkgs)?;
566+
annotate_required_rust_version(ws, resolve, &mut changes);
566567

567-
for change in changes {
568+
status_locking(ws, num_pkgs)?;
569+
for change in changes.values() {
568570
if change.is_member.unwrap_or(false) {
569571
continue;
570572
};
@@ -586,7 +588,7 @@ fn print_lockfile_sync(
586588
};
587589

588590
let package_id = change.package_id;
589-
let required_rust_version = report_required_rust_version(ws, resolve, package_id);
591+
let required_rust_version = report_required_rust_version(resolve, change);
590592
let latest = report_latest(&possibilities, package_id);
591593
let note = required_rust_version.or(latest).unwrap_or_default();
592594

@@ -610,14 +612,18 @@ fn print_lockfile_updates(
610612
precise: bool,
611613
registry: &mut PackageRegistry<'_>,
612614
) -> CargoResult<()> {
613-
let changes = PackageChange::diff(ws, previous_resolve, resolve);
614-
let num_pkgs: usize = changes.iter().filter(|change| change.kind.is_new()).count();
615+
let mut changes = PackageChange::diff(ws, previous_resolve, resolve);
616+
let num_pkgs: usize = changes
617+
.values()
618+
.filter(|change| change.kind.is_new())
619+
.count();
620+
annotate_required_rust_version(ws, resolve, &mut changes);
621+
615622
if !precise {
616623
status_locking(ws, num_pkgs)?;
617624
}
618-
619625
let mut unchanged_behind = 0;
620-
for change in changes {
626+
for change in changes.values() {
621627
let possibilities = if let Some(query) = change.alternatives_query() {
622628
loop {
623629
match registry.query_vec(&query, QueryKind::Exact) {
@@ -636,7 +642,7 @@ fn print_lockfile_updates(
636642
| PackageChangeKind::Upgraded
637643
| PackageChangeKind::Downgraded => {
638644
let package_id = change.package_id;
639-
let required_rust_version = report_required_rust_version(ws, resolve, package_id);
645+
let required_rust_version = report_required_rust_version(resolve, change);
640646
let latest = report_latest(&possibilities, package_id);
641647
let note = required_rust_version.or(latest).unwrap_or_default();
642648

@@ -655,7 +661,7 @@ fn print_lockfile_updates(
655661
}
656662
PackageChangeKind::Unchanged => {
657663
let package_id = change.package_id;
658-
let required_rust_version = report_required_rust_version(ws, resolve, package_id);
664+
let required_rust_version = report_required_rust_version(resolve, change);
659665
let latest = report_latest(&possibilities, package_id);
660666
let note = required_rust_version.as_deref().or(latest.as_deref());
661667

@@ -731,24 +737,20 @@ fn required_rust_version(ws: &Workspace<'_>) -> Option<PartialVersion> {
731737
}
732738
}
733739

734-
fn report_required_rust_version(
735-
ws: &Workspace<'_>,
736-
resolve: &Resolve,
737-
package: PackageId,
738-
) -> Option<String> {
739-
if package.source_id().is_path() {
740+
fn report_required_rust_version(resolve: &Resolve, change: &PackageChange) -> Option<String> {
741+
if change.package_id.source_id().is_path() {
740742
return None;
741743
}
742-
let summary = resolve.summary(package);
744+
let summary = resolve.summary(change.package_id);
743745
let package_rust_version = summary.rust_version()?;
744-
let workspace_rust_version = required_rust_version(ws)?;
745-
if package_rust_version.is_compatible_with(&workspace_rust_version) {
746+
let required_rust_version = change.required_rust_version.as_ref()?;
747+
if package_rust_version.is_compatible_with(required_rust_version) {
746748
return None;
747749
}
748750

749-
let warn = style::WARN;
751+
let error = style::ERROR;
750752
Some(format!(
751-
" {warn}(requires Rust {package_rust_version}){warn:#}"
753+
" {error}(requires Rust {package_rust_version}){error:#}"
752754
))
753755
}
754756

@@ -801,20 +803,28 @@ struct PackageChange {
801803
previous_id: Option<PackageId>,
802804
kind: PackageChangeKind,
803805
is_member: Option<bool>,
806+
required_rust_version: Option<PartialVersion>,
804807
}
805808

806809
impl PackageChange {
807-
pub fn new(ws: &Workspace<'_>, resolve: &Resolve) -> Vec<Self> {
810+
pub fn new(ws: &Workspace<'_>, resolve: &Resolve) -> IndexMap<PackageId, Self> {
808811
let diff = PackageDiff::new(resolve);
809812
Self::with_diff(diff, ws)
810813
}
811814

812-
pub fn diff(ws: &Workspace<'_>, previous_resolve: &Resolve, resolve: &Resolve) -> Vec<Self> {
815+
pub fn diff(
816+
ws: &Workspace<'_>,
817+
previous_resolve: &Resolve,
818+
resolve: &Resolve,
819+
) -> IndexMap<PackageId, Self> {
813820
let diff = PackageDiff::diff(previous_resolve, resolve);
814821
Self::with_diff(diff, ws)
815822
}
816823

817-
fn with_diff(diff: impl Iterator<Item = PackageDiff>, ws: &Workspace<'_>) -> Vec<Self> {
824+
fn with_diff(
825+
diff: impl Iterator<Item = PackageDiff>,
826+
ws: &Workspace<'_>,
827+
) -> IndexMap<PackageId, Self> {
818828
let member_ids: HashSet<_> = ws.members().map(|p| p.package_id()).collect();
819829

820830
let mut changes = IndexMap::new();
@@ -837,6 +847,7 @@ impl PackageChange {
837847
previous_id: Some(previous_id),
838848
kind,
839849
is_member,
850+
required_rust_version: None,
840851
};
841852
changes.insert(change.package_id, change);
842853
} else {
@@ -848,6 +859,7 @@ impl PackageChange {
848859
previous_id: None,
849860
kind,
850861
is_member,
862+
required_rust_version: None,
851863
};
852864
changes.insert(change.package_id, change);
853865
}
@@ -859,6 +871,7 @@ impl PackageChange {
859871
previous_id: None,
860872
kind,
861873
is_member,
874+
required_rust_version: None,
862875
};
863876
changes.insert(change.package_id, change);
864877
}
@@ -871,12 +884,13 @@ impl PackageChange {
871884
previous_id: None,
872885
kind,
873886
is_member,
887+
required_rust_version: None,
874888
};
875889
changes.insert(change.package_id, change);
876890
}
877891
}
878892

879-
changes.into_values().collect()
893+
changes
880894
}
881895

882896
/// For querying [`PackageRegistry`] for alternative versions to report to the user
@@ -1066,3 +1080,46 @@ impl PackageDiff {
10661080
}
10671081
}
10681082
}
1083+
1084+
fn annotate_required_rust_version(
1085+
ws: &Workspace<'_>,
1086+
resolve: &Resolve,
1087+
changes: &mut IndexMap<PackageId, PackageChange>,
1088+
) {
1089+
let rustc = ws.gctx().load_global_rustc(Some(ws)).ok();
1090+
let rustc_version: Option<PartialVersion> =
1091+
rustc.as_ref().map(|rustc| rustc.version.clone().into());
1092+
1093+
if ws.resolve_honors_rust_version() {
1094+
let mut queue: std::collections::VecDeque<_> = ws
1095+
.members()
1096+
.map(|p| {
1097+
(
1098+
p.rust_version()
1099+
.map(|r| r.clone().into_partial())
1100+
.or_else(|| rustc_version.clone()),
1101+
p.package_id(),
1102+
)
1103+
})
1104+
.collect();
1105+
while let Some((required_rust_version, current_id)) = queue.pop_front() {
1106+
let Some(required_rust_version) = required_rust_version else {
1107+
continue;
1108+
};
1109+
if let Some(change) = changes.get_mut(&current_id) {
1110+
if let Some(existing) = change.required_rust_version.as_ref() {
1111+
if *existing <= required_rust_version {
1112+
// Stop early; we already walked down this path with a better match
1113+
continue;
1114+
}
1115+
}
1116+
change.required_rust_version = Some(required_rust_version.clone());
1117+
}
1118+
queue.extend(
1119+
resolve
1120+
.deps(current_id)
1121+
.map(|(dep, _)| (Some(required_rust_version.clone()), dep)),
1122+
);
1123+
}
1124+
}
1125+
}

tests/testsuite/rust_version.rs

+112
Original file line numberDiff line numberDiff line change
@@ -1076,3 +1076,115 @@ fn cargo_install_ignores_msrv_config() {
10761076
"#]])
10771077
.run();
10781078
}
1079+
1080+
#[cargo_test]
1081+
fn report_rust_versions() {
1082+
Package::new("dep-only-low-compatible", "1.55.0")
1083+
.rust_version("1.55.0")
1084+
.file("src/lib.rs", "fn other_stuff() {}")
1085+
.publish();
1086+
Package::new("dep-only-low-incompatible", "1.75.0")
1087+
.rust_version("1.75.0")
1088+
.file("src/lib.rs", "fn other_stuff() {}")
1089+
.publish();
1090+
Package::new("dep-only-high-compatible", "1.65.0")
1091+
.rust_version("1.65.0")
1092+
.file("src/lib.rs", "fn other_stuff() {}")
1093+
.publish();
1094+
Package::new("dep-only-high-incompatible", "1.75.0")
1095+
.rust_version("1.75.0")
1096+
.file("src/lib.rs", "fn other_stuff() {}")
1097+
.publish();
1098+
Package::new("dep-only-unset-unset", "1.0.0")
1099+
.file("src/lib.rs", "fn other_stuff() {}")
1100+
.publish();
1101+
Package::new("dep-only-unset-compatible", "1.75.0")
1102+
.rust_version("1.75.0")
1103+
.file("src/lib.rs", "fn other_stuff() {}")
1104+
.publish();
1105+
Package::new("dep-only-unset-incompatible", "1.2345.0")
1106+
.rust_version("1.2345.0")
1107+
.file("src/lib.rs", "fn other_stuff() {}")
1108+
.publish();
1109+
Package::new("dep-shared-compatible", "1.55.0")
1110+
.rust_version("1.55.0")
1111+
.file("src/lib.rs", "fn other_stuff() {}")
1112+
.publish();
1113+
Package::new("dep-shared-incompatible", "1.75.0")
1114+
.rust_version("1.75.0")
1115+
.file("src/lib.rs", "fn other_stuff() {}")
1116+
.publish();
1117+
1118+
let p = project()
1119+
.file(
1120+
"Cargo.toml",
1121+
r#"
1122+
[workspace]
1123+
members = ["high", "low", "unset"]
1124+
"#,
1125+
)
1126+
.file(
1127+
"high/Cargo.toml",
1128+
r#"
1129+
[package]
1130+
name = "high"
1131+
edition = "2015"
1132+
rust-version = "1.70.0"
1133+
1134+
[dependencies]
1135+
dep-only-high-compatible = "1"
1136+
dep-only-high-incompatible = "1"
1137+
dep-shared-compatible = "1"
1138+
dep-shared-incompatible = "1"
1139+
"#,
1140+
)
1141+
.file("high/src/main.rs", "fn main(){}")
1142+
.file(
1143+
"low/Cargo.toml",
1144+
r#"
1145+
[package]
1146+
name = "low"
1147+
edition = "2015"
1148+
rust-version = "1.60.0"
1149+
1150+
[dependencies]
1151+
dep-only-low-compatible = "1"
1152+
dep-only-low-incompatible = "1"
1153+
dep-shared-compatible = "1"
1154+
dep-shared-incompatible = "1"
1155+
"#,
1156+
)
1157+
.file("low/src/main.rs", "fn main(){}")
1158+
.file(
1159+
"unset/Cargo.toml",
1160+
r#"
1161+
[package]
1162+
name = "unset"
1163+
edition = "2015"
1164+
1165+
[dependencies]
1166+
dep-only-unset-unset = "1"
1167+
dep-only-unset-compatible = "1"
1168+
dep-only-unset-incompatible = "1"
1169+
dep-shared-compatible = "1"
1170+
dep-shared-incompatible = "1"
1171+
"#,
1172+
)
1173+
.file("unset/src/main.rs", "fn main(){}")
1174+
.build();
1175+
1176+
p.cargo("update")
1177+
.env("CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS", "fallback")
1178+
.arg("-Zmsrv-policy")
1179+
.masquerade_as_nightly_cargo(&["msrv-policy"])
1180+
.with_stderr_data(str![[r#"
1181+
[UPDATING] `dummy-registry` index
1182+
[LOCKING] 9 packages to latest Rust 1.60.0 compatible versions
1183+
[ADDING] dep-only-high-incompatible v1.75.0 (requires Rust 1.75.0)
1184+
[ADDING] dep-only-low-incompatible v1.75.0 (requires Rust 1.75.0)
1185+
[ADDING] dep-only-unset-incompatible v1.2345.0 (requires Rust 1.2345.0)
1186+
[ADDING] dep-shared-incompatible v1.75.0 (requires Rust 1.75.0)
1187+
1188+
"#]])
1189+
.run();
1190+
}

0 commit comments

Comments
 (0)