mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
Future-incompat report: Add suggestions of newer versions.
This commit is contained in:
parent
1f7141f892
commit
71cb59b6ef
@ -1,6 +1,6 @@
|
||||
use crate::command_prelude::*;
|
||||
use anyhow::anyhow;
|
||||
use cargo::core::compiler::future_incompat::OnDiskReports;
|
||||
use cargo::core::compiler::future_incompat::{OnDiskReports, REPORT_PREAMBLE};
|
||||
use cargo::drop_println;
|
||||
|
||||
pub fn cli() -> App {
|
||||
@ -39,6 +39,7 @@ fn report_future_incompatibilies(config: &Config, args: &ArgMatches<'_>) -> CliR
|
||||
.value_of_u32("id")?
|
||||
.unwrap_or_else(|| reports.last_id());
|
||||
let report = reports.get_report(id, config)?;
|
||||
drop_println!(config, "{}", REPORT_PREAMBLE);
|
||||
drop_println!(config, "{}", report);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,11 +1,26 @@
|
||||
//! Support for future-incompatible warning reporting.
|
||||
|
||||
use crate::core::{PackageId, Workspace};
|
||||
use crate::core::{Dependency, PackageId, Workspace};
|
||||
use crate::sources::SourceConfigMap;
|
||||
use crate::util::{iter_join, CargoResult, Config};
|
||||
use anyhow::{bail, format_err, Context};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::fmt::Write as _;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
pub const REPORT_PREAMBLE: &str = "\
|
||||
The following warnings were discovered during the build. These warnings are an
|
||||
indication that the packages contain code that will become an error in a
|
||||
future release of Rust. These warnings typically cover changes to close
|
||||
soundness problems, unintended or undocumented behavior, or critical problems
|
||||
that cannot be fixed in a backwards-compatible fashion, and are not expected
|
||||
to be in wide use.
|
||||
|
||||
Each warning should contain a link for more information on what the warning
|
||||
means and how to resolve it.
|
||||
";
|
||||
|
||||
/// The future incompatibility report, emitted by the compiler as a JSON message.
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct FutureIncompatReport {
|
||||
@ -90,7 +105,7 @@ impl OnDiskReports {
|
||||
};
|
||||
let report = OnDiskReport {
|
||||
id: current_reports.next_id,
|
||||
report: render_report(per_package_reports),
|
||||
report: render_report(ws, per_package_reports),
|
||||
};
|
||||
current_reports.next_id += 1;
|
||||
current_reports.reports.push(report);
|
||||
@ -178,11 +193,14 @@ impl OnDiskReports {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> String {
|
||||
fn render_report(
|
||||
ws: &Workspace<'_>,
|
||||
per_package_reports: &[FutureIncompatReportPackage],
|
||||
) -> String {
|
||||
let mut per_package_reports: Vec<_> = per_package_reports.iter().collect();
|
||||
per_package_reports.sort_by_key(|r| r.package_id);
|
||||
let mut rendered = String::new();
|
||||
for per_package in per_package_reports {
|
||||
for per_package in &per_package_reports {
|
||||
rendered.push_str(&format!(
|
||||
"The package `{}` currently triggers the following future \
|
||||
incompatibility lints:\n",
|
||||
@ -198,5 +216,75 @@ fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> String
|
||||
}
|
||||
rendered.push('\n');
|
||||
}
|
||||
if let Some(s) = render_suggestions(ws, &per_package_reports) {
|
||||
rendered.push_str(&s);
|
||||
}
|
||||
rendered
|
||||
}
|
||||
|
||||
fn render_suggestions(
|
||||
ws: &Workspace<'_>,
|
||||
per_package_reports: &[&FutureIncompatReportPackage],
|
||||
) -> Option<String> {
|
||||
// This in general ignores all errors since this is opportunistic.
|
||||
let _lock = ws.config().acquire_package_cache_lock().ok()?;
|
||||
// Create a set of updated registry sources.
|
||||
let map = SourceConfigMap::new(ws.config()).ok()?;
|
||||
let package_ids: BTreeSet<_> = per_package_reports
|
||||
.iter()
|
||||
.map(|r| r.package_id)
|
||||
.filter(|pkg_id| pkg_id.source_id().is_registry())
|
||||
.collect();
|
||||
let source_ids: HashSet<_> = package_ids
|
||||
.iter()
|
||||
.map(|pkg_id| pkg_id.source_id())
|
||||
.collect();
|
||||
let mut sources: HashMap<_, _> = source_ids
|
||||
.into_iter()
|
||||
.filter_map(|sid| {
|
||||
let unlocked = sid.clone().with_precise(None);
|
||||
let mut source = map.load(unlocked, &HashSet::new()).ok()?;
|
||||
// Ignore errors updating.
|
||||
if let Err(e) = source.update() {
|
||||
log::debug!("failed to update source: {:?}", e);
|
||||
}
|
||||
Some((sid, source))
|
||||
})
|
||||
.collect();
|
||||
// Query the sources for new versions.
|
||||
let mut suggestions = String::new();
|
||||
for pkg_id in package_ids {
|
||||
let source = match sources.get_mut(&pkg_id.source_id()) {
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
};
|
||||
let dep = Dependency::parse(pkg_id.name(), None, pkg_id.source_id()).ok()?;
|
||||
let summaries = source.query_vec(&dep).ok()?;
|
||||
let versions = itertools::sorted(
|
||||
summaries
|
||||
.iter()
|
||||
.map(|summary| summary.version())
|
||||
.filter(|version| *version > pkg_id.version()),
|
||||
);
|
||||
let versions = versions.map(|version| version.to_string());
|
||||
let versions = iter_join(versions, ", ");
|
||||
if !versions.is_empty() {
|
||||
writeln!(
|
||||
suggestions,
|
||||
"{} has the following newer versions available: {}",
|
||||
pkg_id, versions
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
if suggestions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!(
|
||||
"The following packages appear to have newer versions available.\n\
|
||||
You may want to consider updating them to a newer version to see if the \
|
||||
issue has been fixed.\n\n{}",
|
||||
suggestions
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +195,11 @@ fn test_multi_crate() {
|
||||
.exec_with_output()
|
||||
.unwrap();
|
||||
let output = std::str::from_utf8(&output.stdout).unwrap();
|
||||
let mut lines = output.lines();
|
||||
assert!(output.starts_with("The following warnings were discovered"));
|
||||
let mut lines = output
|
||||
.lines()
|
||||
// Skip the beginning of the per-package information.
|
||||
.skip_while(|line| !line.starts_with("The package"));
|
||||
for expected in &["first-dep v0.0.1", "second-dep v0.0.2"] {
|
||||
assert_eq!(
|
||||
&format!(
|
||||
@ -276,3 +280,67 @@ Available IDs are: 1
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn suggestions_for_updates() {
|
||||
if !is_nightly() {
|
||||
return;
|
||||
}
|
||||
|
||||
Package::new("with_updates", "1.0.0")
|
||||
.file("src/lib.rs", FUTURE_EXAMPLE)
|
||||
.publish();
|
||||
Package::new("big_update", "1.0.0")
|
||||
.file("src/lib.rs", FUTURE_EXAMPLE)
|
||||
.publish();
|
||||
Package::new("without_updates", "1.0.0")
|
||||
.file("src/lib.rs", FUTURE_EXAMPLE)
|
||||
.publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
with_updates = "1"
|
||||
big_update = "1"
|
||||
without_updates = "1"
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("generate-lockfile").run();
|
||||
|
||||
Package::new("with_updates", "1.0.1")
|
||||
.file("src/lib.rs", "")
|
||||
.publish();
|
||||
Package::new("with_updates", "1.0.2")
|
||||
.file("src/lib.rs", "")
|
||||
.publish();
|
||||
Package::new("big_update", "2.0.0")
|
||||
.file("src/lib.rs", "")
|
||||
.publish();
|
||||
|
||||
p.cargo("check -Zfuture-incompat-report")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains("[..]cargo report future-incompatibilities --id 1[..]")
|
||||
.run();
|
||||
|
||||
p.cargo("report future-incompatibilities")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stdout_contains(
|
||||
"\
|
||||
The following packages appear to have newer versions available.
|
||||
You may want to consider updating them to a newer version to see if the issue has been fixed.
|
||||
|
||||
big_update v1.0.0 has the following newer versions available: 2.0.0
|
||||
with_updates v1.0.0 has the following newer versions available: 1.0.1, 1.0.2
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user