mirror of
https://github.com/rust-lang/cargo
synced 2024-08-28 19:59:27 +00:00
Updates to future-incompatible reporting.
This commit is contained in:
parent
f2c22a3690
commit
c2b02b3926
|
@ -73,7 +73,6 @@ itertools = "0.10.0"
|
||||||
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
|
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
|
||||||
# for more information.
|
# for more information.
|
||||||
rustc-workspace-hack = "1.0.0"
|
rustc-workspace-hack = "1.0.0"
|
||||||
rand = "0.8.3"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
fwdansi = "1.1.0"
|
fwdansi = "1.1.0"
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::command_prelude::*;
|
use crate::command_prelude::*;
|
||||||
use anyhow::{anyhow, Context as _};
|
use anyhow::anyhow;
|
||||||
use cargo::core::compiler::future_incompat::{OnDiskReport, FUTURE_INCOMPAT_FILE};
|
use cargo::core::compiler::future_incompat::OnDiskReports;
|
||||||
use cargo::drop_eprint;
|
use cargo::drop_println;
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
pub fn cli() -> App {
|
pub fn cli() -> App {
|
||||||
subcommand("report")
|
subcommand("report")
|
||||||
|
@ -17,8 +16,7 @@ pub fn cli() -> App {
|
||||||
"id",
|
"id",
|
||||||
"identifier of the report generated by a Cargo command invocation",
|
"identifier of the report generated by a Cargo command invocation",
|
||||||
)
|
)
|
||||||
.value_name("id")
|
.value_name("id"),
|
||||||
.required(true),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -35,31 +33,11 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
|
||||||
|
|
||||||
fn report_future_incompatibilies(config: &Config, args: &ArgMatches<'_>) -> CliResult {
|
fn report_future_incompatibilies(config: &Config, args: &ArgMatches<'_>) -> CliResult {
|
||||||
let ws = args.workspace(config)?;
|
let ws = args.workspace(config)?;
|
||||||
let report_file = ws.target_dir().open_ro(
|
let reports = OnDiskReports::load(&ws)?;
|
||||||
FUTURE_INCOMPAT_FILE,
|
let id = args
|
||||||
ws.config(),
|
.value_of_u32("id")?
|
||||||
"Future incompatible report",
|
.unwrap_or_else(|| reports.last_id());
|
||||||
)?;
|
let report = reports.get_report(id, config)?;
|
||||||
|
drop_println!(config, "{}", report);
|
||||||
let mut file_contents = String::new();
|
|
||||||
report_file
|
|
||||||
.file()
|
|
||||||
.read_to_string(&mut file_contents)
|
|
||||||
.with_context(|| "failed to read report")?;
|
|
||||||
let on_disk_report: OnDiskReport =
|
|
||||||
serde_json::from_str(&file_contents).with_context(|| "failed to load report")?;
|
|
||||||
|
|
||||||
let id = args.value_of("id").unwrap();
|
|
||||||
if id != on_disk_report.id {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Expected an id of `{}`, but `{}` was provided on the command line. \
|
|
||||||
Your report may have been overwritten by a different one.",
|
|
||||||
on_disk_report.id,
|
|
||||||
id
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_eprint!(config, "{}", on_disk_report.report);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
|
//! Support for future-incompatible warning reporting.
|
||||||
|
|
||||||
|
use crate::core::{PackageId, Workspace};
|
||||||
|
use crate::util::{iter_join, CargoResult, Config};
|
||||||
|
use anyhow::{bail, format_err, Context};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
/// The future incompatibility report, emitted by the compiler as a JSON message.
|
/// The future incompatibility report, emitted by the compiler as a JSON message.
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
@ -6,6 +12,13 @@ pub struct FutureIncompatReport {
|
||||||
pub future_incompat_report: Vec<FutureBreakageItem>,
|
pub future_incompat_report: Vec<FutureBreakageItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Structure used for collecting reports in-memory.
|
||||||
|
pub struct FutureIncompatReportPackage {
|
||||||
|
pub package_id: PackageId,
|
||||||
|
pub items: Vec<FutureBreakageItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single future-incompatible warning emitted by rustc.
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct FutureBreakageItem {
|
pub struct FutureBreakageItem {
|
||||||
/// The date at which this lint will become an error.
|
/// The date at which this lint will become an error.
|
||||||
|
@ -24,13 +37,166 @@ pub struct Diagnostic {
|
||||||
|
|
||||||
/// The filename in the top-level `target` directory where we store
|
/// The filename in the top-level `target` directory where we store
|
||||||
/// the report
|
/// the report
|
||||||
pub const FUTURE_INCOMPAT_FILE: &str = ".future-incompat-report.json";
|
const FUTURE_INCOMPAT_FILE: &str = ".future-incompat-report.json";
|
||||||
|
/// Max number of reports to save on disk.
|
||||||
|
const MAX_REPORTS: usize = 5;
|
||||||
|
|
||||||
|
/// The structure saved to disk containing the reports.
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct OnDiskReport {
|
pub struct OnDiskReports {
|
||||||
// A Cargo-generated id used to detect when a report has been overwritten
|
/// A schema version number, to handle older cargo's from trying to read
|
||||||
pub id: String,
|
/// something that they don't understand.
|
||||||
// Cannot be a &str, since Serde needs
|
version: u32,
|
||||||
// to be able to un-escape the JSON
|
/// The report ID to use for the next report to save.
|
||||||
pub report: String,
|
next_id: u32,
|
||||||
|
/// Available reports.
|
||||||
|
reports: Vec<OnDiskReport>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single report for a given compilation session.
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct OnDiskReport {
|
||||||
|
/// Unique reference to the report for the `--id` CLI flag.
|
||||||
|
id: u32,
|
||||||
|
/// Report, suitable for printing to the console.
|
||||||
|
report: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OnDiskReports {
|
||||||
|
fn default() -> OnDiskReports {
|
||||||
|
OnDiskReports {
|
||||||
|
version: 0,
|
||||||
|
next_id: 1,
|
||||||
|
reports: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnDiskReports {
|
||||||
|
/// Saves a new report.
|
||||||
|
pub fn save_report(
|
||||||
|
ws: &Workspace<'_>,
|
||||||
|
per_package_reports: &[FutureIncompatReportPackage],
|
||||||
|
) -> OnDiskReports {
|
||||||
|
let mut current_reports = match Self::load(ws) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!(
|
||||||
|
"saving future-incompatible reports failed to load current reports: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
OnDiskReports::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let report = OnDiskReport {
|
||||||
|
id: current_reports.next_id,
|
||||||
|
report: render_report(per_package_reports),
|
||||||
|
};
|
||||||
|
current_reports.next_id += 1;
|
||||||
|
current_reports.reports.push(report);
|
||||||
|
if current_reports.reports.len() > MAX_REPORTS {
|
||||||
|
current_reports.reports.remove(0);
|
||||||
|
}
|
||||||
|
let on_disk = serde_json::to_vec(¤t_reports).unwrap();
|
||||||
|
if let Err(e) = ws
|
||||||
|
.target_dir()
|
||||||
|
.open_rw(
|
||||||
|
FUTURE_INCOMPAT_FILE,
|
||||||
|
ws.config(),
|
||||||
|
"Future incompatibility report",
|
||||||
|
)
|
||||||
|
.and_then(|file| {
|
||||||
|
let mut file = file.file();
|
||||||
|
file.set_len(0)?;
|
||||||
|
file.write_all(&on_disk)?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
{
|
||||||
|
crate::display_warning_with_error(
|
||||||
|
"failed to write on-disk future incompatible report",
|
||||||
|
&e,
|
||||||
|
&mut ws.config().shell(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
current_reports
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads the on-disk reports.
|
||||||
|
pub fn load(ws: &Workspace<'_>) -> CargoResult<OnDiskReports> {
|
||||||
|
let report_file = match ws.target_dir().open_ro(
|
||||||
|
FUTURE_INCOMPAT_FILE,
|
||||||
|
ws.config(),
|
||||||
|
"Future incompatible report",
|
||||||
|
) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
|
||||||
|
if io_err.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
bail!("no reports are currently available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut file_contents = String::new();
|
||||||
|
report_file
|
||||||
|
.file()
|
||||||
|
.read_to_string(&mut file_contents)
|
||||||
|
.with_context(|| "failed to read report")?;
|
||||||
|
let on_disk_reports: OnDiskReports =
|
||||||
|
serde_json::from_str(&file_contents).with_context(|| "failed to load report")?;
|
||||||
|
if on_disk_reports.version != 0 {
|
||||||
|
bail!("unable to read reports; reports were saved from a future version of Cargo");
|
||||||
|
}
|
||||||
|
Ok(on_disk_reports)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the most recent report ID.
|
||||||
|
pub fn last_id(&self) -> u32 {
|
||||||
|
self.reports.last().map(|r| r.id).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_report(&self, id: u32, config: &Config) -> CargoResult<String> {
|
||||||
|
let report = self.reports.iter().find(|r| r.id == id).ok_or_else(|| {
|
||||||
|
let available = iter_join(self.reports.iter().map(|r| r.id.to_string()), ", ");
|
||||||
|
format_err!(
|
||||||
|
"could not find report with ID {}\n\
|
||||||
|
Available IDs are: {}",
|
||||||
|
id,
|
||||||
|
available
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let report = if config.shell().err_supports_color() {
|
||||||
|
report.report.clone()
|
||||||
|
} else {
|
||||||
|
strip_ansi_escapes::strip(&report.report)
|
||||||
|
.map(|v| String::from_utf8(v).expect("utf8"))
|
||||||
|
.expect("strip should never fail")
|
||||||
|
};
|
||||||
|
Ok(report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_report(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 {
|
||||||
|
rendered.push_str(&format!(
|
||||||
|
"The package `{}` currently triggers the following future \
|
||||||
|
incompatibility lints:\n",
|
||||||
|
per_package.package_id
|
||||||
|
));
|
||||||
|
for item in &per_package.items {
|
||||||
|
rendered.extend(
|
||||||
|
item.diagnostic
|
||||||
|
.rendered
|
||||||
|
.lines()
|
||||||
|
.map(|l| format!("> {}\n", l)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rendered.push('\n');
|
||||||
|
}
|
||||||
|
rendered
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,7 @@
|
||||||
//! improved.
|
//! improved.
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::marker;
|
use std::marker;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -62,8 +61,6 @@ use cargo_util::ProcessBuilder;
|
||||||
use crossbeam_utils::thread::Scope;
|
use crossbeam_utils::thread::Scope;
|
||||||
use jobserver::{Acquired, Client, HelperThread};
|
use jobserver::{Acquired, Client, HelperThread};
|
||||||
use log::{debug, info, trace};
|
use log::{debug, info, trace};
|
||||||
use rand::distributions::Alphanumeric;
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
|
|
||||||
use super::context::OutputFile;
|
use super::context::OutputFile;
|
||||||
use super::job::{
|
use super::job::{
|
||||||
|
@ -73,7 +70,7 @@ use super::job::{
|
||||||
use super::timings::Timings;
|
use super::timings::Timings;
|
||||||
use super::{BuildContext, BuildPlan, CompileMode, Context, Unit};
|
use super::{BuildContext, BuildPlan, CompileMode, Context, Unit};
|
||||||
use crate::core::compiler::future_incompat::{
|
use crate::core::compiler::future_incompat::{
|
||||||
FutureBreakageItem, OnDiskReport, FUTURE_INCOMPAT_FILE,
|
FutureBreakageItem, FutureIncompatReportPackage, OnDiskReports,
|
||||||
};
|
};
|
||||||
use crate::core::resolver::ResolveBehavior;
|
use crate::core::resolver::ResolveBehavior;
|
||||||
use crate::core::{FeatureValue, PackageId, Shell, TargetKind};
|
use crate::core::{FeatureValue, PackageId, Shell, TargetKind};
|
||||||
|
@ -161,7 +158,7 @@ struct DrainState<'cfg> {
|
||||||
|
|
||||||
/// How many jobs we've finished
|
/// How many jobs we've finished
|
||||||
finished: usize,
|
finished: usize,
|
||||||
per_crate_future_incompat_reports: Vec<FutureIncompatReportCrate>,
|
per_package_future_incompat_reports: Vec<FutureIncompatReportPackage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
@ -173,11 +170,6 @@ impl std::fmt::Display for JobId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FutureIncompatReportCrate {
|
|
||||||
package_id: PackageId,
|
|
||||||
report: Vec<FutureBreakageItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `JobState` is constructed by `JobQueue::run` and passed to `Job::run`. It includes everything
|
/// A `JobState` is constructed by `JobQueue::run` and passed to `Job::run`. It includes everything
|
||||||
/// necessary to communicate between the main thread and the execution of the job.
|
/// necessary to communicate between the main thread and the execution of the job.
|
||||||
///
|
///
|
||||||
|
@ -432,7 +424,7 @@ impl<'cfg> JobQueue<'cfg> {
|
||||||
pending_queue: Vec::new(),
|
pending_queue: Vec::new(),
|
||||||
print: DiagnosticPrinter::new(cx.bcx.config),
|
print: DiagnosticPrinter::new(cx.bcx.config),
|
||||||
finished: 0,
|
finished: 0,
|
||||||
per_crate_future_incompat_reports: Vec::new(),
|
per_package_future_incompat_reports: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a helper thread for acquiring jobserver tokens
|
// Create a helper thread for acquiring jobserver tokens
|
||||||
|
@ -615,10 +607,10 @@ impl<'cfg> DrainState<'cfg> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::FutureIncompatReport(id, report) => {
|
Message::FutureIncompatReport(id, items) => {
|
||||||
let package_id = self.active[&id].pkg.package_id();
|
let package_id = self.active[&id].pkg.package_id();
|
||||||
self.per_crate_future_incompat_reports
|
self.per_package_future_incompat_reports
|
||||||
.push(FutureIncompatReportCrate { package_id, report });
|
.push(FutureIncompatReportPackage { package_id, items });
|
||||||
}
|
}
|
||||||
Message::Token(acquired_token) => {
|
Message::Token(acquired_token) => {
|
||||||
let token = acquired_token.with_context(|| "failed to acquire jobserver token")?;
|
let token = acquired_token.with_context(|| "failed to acquire jobserver token")?;
|
||||||
|
@ -801,7 +793,7 @@ impl<'cfg> DrainState<'cfg> {
|
||||||
if !cx.bcx.build_config.build_plan {
|
if !cx.bcx.build_config.build_plan {
|
||||||
// It doesn't really matter if this fails.
|
// It doesn't really matter if this fails.
|
||||||
drop(cx.bcx.config.shell().status("Finished", message));
|
drop(cx.bcx.config.shell().status("Finished", message));
|
||||||
self.emit_future_incompat(cx);
|
self.emit_future_incompat(cx.bcx);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -811,93 +803,57 @@ impl<'cfg> DrainState<'cfg> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_future_incompat(&mut self, cx: &mut Context<'_, '_>) {
|
fn emit_future_incompat(&mut self, bcx: &BuildContext<'_, '_>) {
|
||||||
if cx.bcx.config.cli_unstable().future_incompat_report {
|
if !bcx.config.cli_unstable().future_incompat_report {
|
||||||
if self.per_crate_future_incompat_reports.is_empty() {
|
return;
|
||||||
|
}
|
||||||
|
if self.per_package_future_incompat_reports.is_empty() {
|
||||||
|
if bcx.build_config.future_incompat_report {
|
||||||
drop(
|
drop(
|
||||||
cx.bcx
|
bcx.config
|
||||||
.config
|
|
||||||
.shell()
|
.shell()
|
||||||
.note("0 dependencies had future-incompat warnings"),
|
.note("0 dependencies had future-incompatible warnings"),
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
self.per_crate_future_incompat_reports
|
return;
|
||||||
.sort_by_key(|r| r.package_id);
|
}
|
||||||
|
|
||||||
let crates_and_versions = self
|
// Get a list of unique and sorted package name/versions.
|
||||||
.per_crate_future_incompat_reports
|
let package_vers: BTreeSet<_> = self
|
||||||
.iter()
|
.per_package_future_incompat_reports
|
||||||
.map(|r| r.package_id.to_string())
|
.iter()
|
||||||
.collect::<Vec<_>>()
|
.map(|r| r.package_id)
|
||||||
.join(", ");
|
.collect();
|
||||||
|
let package_vers: Vec<_> = package_vers
|
||||||
|
.into_iter()
|
||||||
|
.map(|pid| pid.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
drop(cx.bcx.config.shell().warn(&format!(
|
drop(bcx.config.shell().warn(&format!(
|
||||||
"the following crates contain code that will be rejected by a future version of Rust: {}",
|
"the following packages contain code that will be rejected by a future \
|
||||||
crates_and_versions
|
version of Rust: {}",
|
||||||
|
package_vers.join(", ")
|
||||||
|
)));
|
||||||
|
|
||||||
|
let on_disk_reports =
|
||||||
|
OnDiskReports::save_report(bcx.ws, &self.per_package_future_incompat_reports);
|
||||||
|
let report_id = on_disk_reports.last_id();
|
||||||
|
|
||||||
|
if bcx.build_config.future_incompat_report {
|
||||||
|
let rendered = on_disk_reports.get_report(report_id, bcx.config).unwrap();
|
||||||
|
drop_eprint!(bcx.config, "{}", rendered);
|
||||||
|
drop(bcx.config.shell().note(&format!(
|
||||||
|
"this report can be shown with `cargo report \
|
||||||
|
future-incompatibilities -Z future-incompat-report --id {}`",
|
||||||
|
report_id
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
drop(bcx.config.shell().note(&format!(
|
||||||
|
"to see what the problems were, use the option \
|
||||||
|
`--future-incompat-report`, or run `cargo report \
|
||||||
|
future-incompatibilities --id {}`",
|
||||||
|
report_id
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let mut full_report = String::new();
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
|
|
||||||
// Generate a short ID to allow detecting if a report gets overwritten
|
|
||||||
let id: String = std::iter::repeat(())
|
|
||||||
.map(|()| char::from(rng.sample(Alphanumeric)))
|
|
||||||
.take(4)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for report in std::mem::take(&mut self.per_crate_future_incompat_reports) {
|
|
||||||
full_report.push_str(&format!(
|
|
||||||
"The crate `{}` currently triggers the following future incompatibility lints:\n",
|
|
||||||
report.package_id
|
|
||||||
));
|
|
||||||
for item in report.report {
|
|
||||||
let rendered = if cx.bcx.config.shell().err_supports_color() {
|
|
||||||
item.diagnostic.rendered
|
|
||||||
} else {
|
|
||||||
strip_ansi_escapes::strip(&item.diagnostic.rendered)
|
|
||||||
.map(|v| String::from_utf8(v).expect("utf8"))
|
|
||||||
.expect("strip should never fail")
|
|
||||||
};
|
|
||||||
|
|
||||||
for line in rendered.lines() {
|
|
||||||
full_report.push_str(&format!("> {}\n", line));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let report_file = cx.bcx.ws.target_dir().open_rw(
|
|
||||||
FUTURE_INCOMPAT_FILE,
|
|
||||||
cx.bcx.config,
|
|
||||||
"Future incompatibility report",
|
|
||||||
);
|
|
||||||
let err = report_file
|
|
||||||
.and_then(|report_file| {
|
|
||||||
let on_disk_report = OnDiskReport {
|
|
||||||
id: id.clone(),
|
|
||||||
report: full_report.clone(),
|
|
||||||
};
|
|
||||||
serde_json::to_writer(report_file, &on_disk_report).map_err(|e| e.into())
|
|
||||||
})
|
|
||||||
.err();
|
|
||||||
if let Some(e) = err {
|
|
||||||
crate::display_warning_with_error(
|
|
||||||
"failed to write on-disk future incompat report",
|
|
||||||
&e,
|
|
||||||
&mut cx.bcx.config.shell(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if cx.bcx.build_config.future_incompat_report {
|
|
||||||
drop_eprint!(cx.bcx.config, "{}", full_report);
|
|
||||||
drop(cx.bcx.config.shell().note(
|
|
||||||
&format!("this report can be shown with `cargo report future-incompatibilities -Z future-incompat-report --id {}`", id)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
drop(cx.bcx.config.shell().note(
|
|
||||||
&format!("to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id {}`", id)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,33 @@
|
||||||
//! Tests for future-incompat-report messages
|
//! Tests for future-incompat-report messages
|
||||||
|
|
||||||
use cargo_test_support::registry::Package;
|
use cargo_test_support::registry::Package;
|
||||||
use cargo_test_support::{basic_manifest, is_nightly, project};
|
use cargo_test_support::{basic_manifest, is_nightly, project, Project};
|
||||||
|
|
||||||
|
// An arbitrary lint (array_into_iter) that triggers a report.
|
||||||
|
const FUTURE_EXAMPLE: &'static str = "fn main() { [true].into_iter(); }";
|
||||||
|
// Some text that will be displayed when the lint fires.
|
||||||
|
const FUTURE_OUTPUT: &'static str = "[..]array_into_iter[..]";
|
||||||
|
|
||||||
|
fn simple_project() -> Project {
|
||||||
|
project()
|
||||||
|
.file("Cargo.toml", &basic_manifest("foo", "0.0.0"))
|
||||||
|
.file("src/main.rs", FUTURE_EXAMPLE)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
fn no_output_on_stable() {
|
fn no_output_on_stable() {
|
||||||
let p = project()
|
let p = simple_project();
|
||||||
.file("Cargo.toml", &basic_manifest("foo", "0.0.0"))
|
|
||||||
.file("src/main.rs", "fn main() { [true].into_iter(); }")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("build")
|
p.cargo("build")
|
||||||
.with_stderr_contains(" = note: `#[warn(array_into_iter)]` on by default")
|
.with_stderr_contains(FUTURE_OUTPUT)
|
||||||
.with_stderr_does_not_contain("[..]crates[..]")
|
.with_stderr_does_not_contain("[..]cargo report[..]")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
fn gate_future_incompat_report() {
|
fn gate_future_incompat_report() {
|
||||||
let p = project()
|
let p = simple_project();
|
||||||
.file("Cargo.toml", &basic_manifest("foo", "0.0.0"))
|
|
||||||
.file("src/main.rs", "fn main() { [true].into_iter(); }")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("build --future-incompat-report")
|
p.cargo("build --future-incompat-report")
|
||||||
.with_stderr_contains("error: the `--future-incompat-report` flag is unstable[..]")
|
.with_stderr_contains("error: the `--future-incompat-report` flag is unstable[..]")
|
||||||
|
@ -60,9 +66,25 @@ fn test_zero_future_incompat() {
|
||||||
.file("src/main.rs", "fn main() {}")
|
.file("src/main.rs", "fn main() {}")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// No note if --future-incompat-report is not specified.
|
||||||
|
p.cargo("build -Z future-incompat-report")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] foo v0.0.0 [..]
|
||||||
|
[FINISHED] [..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
|
||||||
p.cargo("build --future-incompat-report -Z unstable-options -Z future-incompat-report")
|
p.cargo("build --future-incompat-report -Z unstable-options -Z future-incompat-report")
|
||||||
.masquerade_as_nightly_cargo()
|
.masquerade_as_nightly_cargo()
|
||||||
.with_stderr_contains("note: 0 dependencies had future-incompat warnings")
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[FINISHED] [..]
|
||||||
|
note: 0 dependencies had future-incompatible warnings
|
||||||
|
",
|
||||||
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,24 +94,21 @@ fn test_single_crate() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let p = project()
|
let p = simple_project();
|
||||||
.file("Cargo.toml", &basic_manifest("foo", "0.0.0"))
|
|
||||||
.file("src/main.rs", "fn main() { [true].into_iter(); }")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
for command in &["build", "check", "rustc", "test"] {
|
for command in &["build", "check", "rustc", "test"] {
|
||||||
p.cargo(command).arg("-Zfuture-incompat-report")
|
p.cargo(command).arg("-Zfuture-incompat-report")
|
||||||
.masquerade_as_nightly_cargo()
|
.masquerade_as_nightly_cargo()
|
||||||
.with_stderr_contains(" = note: `#[warn(array_into_iter)]` on by default")
|
.with_stderr_contains(FUTURE_OUTPUT)
|
||||||
.with_stderr_contains("warning: the following crates contain code that will be rejected by a future version of Rust: foo v0.0.0 [..]")
|
.with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 [..]")
|
||||||
.with_stderr_does_not_contain("[..]incompatibility[..]")
|
.with_stderr_does_not_contain("[..]incompatibility[..]")
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
p.cargo(command).arg("-Zfuture-incompat-report").arg("-Zunstable-options").arg("--future-incompat-report")
|
p.cargo(command).arg("-Zfuture-incompat-report").arg("-Zunstable-options").arg("--future-incompat-report")
|
||||||
.masquerade_as_nightly_cargo()
|
.masquerade_as_nightly_cargo()
|
||||||
.with_stderr_contains(" = note: `#[warn(array_into_iter)]` on by default")
|
.with_stderr_contains(FUTURE_OUTPUT)
|
||||||
.with_stderr_contains("warning: the following crates contain code that will be rejected by a future version of Rust: foo v0.0.0 [..]")
|
.with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: foo v0.0.0 [..]")
|
||||||
.with_stderr_contains("The crate `foo v0.0.0 ([..])` currently triggers the following future incompatibility lints:")
|
.with_stderr_contains("The package `foo v0.0.0 ([..])` currently triggers the following future incompatibility lints:")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,10 +120,10 @@ fn test_multi_crate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Package::new("first-dep", "0.0.1")
|
Package::new("first-dep", "0.0.1")
|
||||||
.file("src/lib.rs", "fn foo() { [25].into_iter(); }")
|
.file("src/lib.rs", FUTURE_EXAMPLE)
|
||||||
.publish();
|
.publish();
|
||||||
Package::new("second-dep", "0.0.2")
|
Package::new("second-dep", "0.0.2")
|
||||||
.file("src/lib.rs", "fn foo() { ['a'].into_iter(); }")
|
.file("src/lib.rs", FUTURE_EXAMPLE)
|
||||||
.publish();
|
.publish();
|
||||||
|
|
||||||
let p = project()
|
let p = project()
|
||||||
|
@ -126,24 +145,17 @@ fn test_multi_crate() {
|
||||||
for command in &["build", "check", "rustc", "test"] {
|
for command in &["build", "check", "rustc", "test"] {
|
||||||
p.cargo(command).arg("-Zfuture-incompat-report")
|
p.cargo(command).arg("-Zfuture-incompat-report")
|
||||||
.masquerade_as_nightly_cargo()
|
.masquerade_as_nightly_cargo()
|
||||||
.with_stderr_does_not_contain("[..]array_into_iter[..]")
|
.with_stderr_does_not_contain(FUTURE_OUTPUT)
|
||||||
.with_stderr_contains("warning: the following crates contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2")
|
.with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2")
|
||||||
// Check that we don't have the 'triggers' message shown at the bottom of this loop
|
// Check that we don't have the 'triggers' message shown at the bottom of this loop
|
||||||
.with_stderr_does_not_contain("[..]triggers[..]")
|
.with_stderr_does_not_contain("[..]triggers[..]")
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
p.cargo("report future-incompatibilities -Z future-incompat-report --id bad-id")
|
|
||||||
.masquerade_as_nightly_cargo()
|
|
||||||
.with_stderr_contains("error: Expected an id of [..]")
|
|
||||||
.with_stderr_does_not_contain("[..]triggers[..]")
|
|
||||||
.with_status(101)
|
|
||||||
.run();
|
|
||||||
|
|
||||||
p.cargo(command).arg("-Zunstable-options").arg("-Zfuture-incompat-report").arg("--future-incompat-report")
|
p.cargo(command).arg("-Zunstable-options").arg("-Zfuture-incompat-report").arg("--future-incompat-report")
|
||||||
.masquerade_as_nightly_cargo()
|
.masquerade_as_nightly_cargo()
|
||||||
.with_stderr_contains("warning: the following crates contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2")
|
.with_stderr_contains("warning: the following packages contain code that will be rejected by a future version of Rust: first-dep v0.0.1, second-dep v0.0.2")
|
||||||
.with_stderr_contains("The crate `first-dep v0.0.1` currently triggers the following future incompatibility lints:")
|
.with_stderr_contains("The package `first-dep v0.0.1` currently triggers the following future incompatibility lints:")
|
||||||
.with_stderr_contains("The crate `second-dep v0.0.2` currently triggers the following future incompatibility lints:")
|
.with_stderr_contains("The package `second-dep v0.0.2` currently triggers the following future incompatibility lints:")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +184,95 @@ fn test_multi_crate() {
|
||||||
|
|
||||||
p.cargo(&format!("report future-incompatibilities -Z future-incompat-report --id {}", id))
|
p.cargo(&format!("report future-incompatibilities -Z future-incompat-report --id {}", id))
|
||||||
.masquerade_as_nightly_cargo()
|
.masquerade_as_nightly_cargo()
|
||||||
.with_stderr_contains("The crate `first-dep v0.0.1` currently triggers the following future incompatibility lints:")
|
.with_stdout_contains("The package `first-dep v0.0.1` currently triggers the following future incompatibility lints:")
|
||||||
.with_stderr_contains("The crate `second-dep v0.0.2` currently triggers the following future incompatibility lints:")
|
.with_stdout_contains("The package `second-dep v0.0.2` currently triggers the following future incompatibility lints:")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// Test without --id, and also the full output of the report.
|
||||||
|
let output = p
|
||||||
|
.cargo("report future-incompatibilities -Z future-incompat-report")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.exec_with_output()
|
||||||
|
.unwrap();
|
||||||
|
let output = std::str::from_utf8(&output.stdout).unwrap();
|
||||||
|
let mut lines = output.lines();
|
||||||
|
for expected in &["first-dep v0.0.1", "second-dep v0.0.2"] {
|
||||||
|
assert_eq!(
|
||||||
|
&format!(
|
||||||
|
"The package `{}` currently triggers the following future incompatibility lints:",
|
||||||
|
expected
|
||||||
|
),
|
||||||
|
lines.next().unwrap()
|
||||||
|
);
|
||||||
|
let mut count = 0;
|
||||||
|
while let Some(line) = lines.next() {
|
||||||
|
if line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
assert!(count > 0);
|
||||||
|
}
|
||||||
|
assert_eq!(lines.next(), Some(""));
|
||||||
|
assert_eq!(lines.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn color() {
|
||||||
|
if !is_nightly() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = simple_project();
|
||||||
|
|
||||||
|
p.cargo("check -Zfuture-incompat-report")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("report future-incompatibilities -Z future-incompat-report")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_stdout_does_not_contain("[..]\x1b[[..]")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("report future-incompatibilities -Z future-incompat-report")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.env("CARGO_TERM_COLOR", "always")
|
||||||
|
.with_stdout_contains("[..]\x1b[[..]")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn bad_ids() {
|
||||||
|
if !is_nightly() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = simple_project();
|
||||||
|
|
||||||
|
p.cargo("report future-incompatibilities -Z future-incompat-report --id 1")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr("error: no reports are currently available")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("check -Zfuture-incompat-report")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("report future-incompatibilities -Z future-incompat-report --id foo")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_status(1)
|
||||||
|
.with_stderr("error: Invalid value: could not parse `foo` as a number")
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("report future-incompatibilities -Z future-incompat-report --id 7")
|
||||||
|
.masquerade_as_nightly_cargo()
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
error: could not find report with ID 7
|
||||||
|
Available IDs are: 1
|
||||||
|
",
|
||||||
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue