Auto merge of #9953 - Aaron1011:nicer-incompat-report, r=ehuss

Make future-incompat-report output more user-friendly

When the user enables `--future-incompat-report`, we now display
a high-level summary of the problem, as well as several suggestions
for fixing the affected crates.

The command `cargo report future-incompatibilities` now takes
a `--crate` option, which can be used to display a report
(including the actual lint messages) for a single crate.
When this option is not used, we display the report for all
crates.

Sample output from the `actix` crate:

`> RUSTFLAGS="-Z future-incompat-test" ~/repos/cargo/target/debug/cargo build -Z future-incompat-report
`

```
    Finished dev [unoptimized + debuginfo] target(s) in 2.09s
warning: the following packages contain code that will be rejected by a future version of Rust: actix v0.11.1 (/home/aaron/repos/actix/actix), ahash v0.7.4, arc-swap v0.4.4, autocfg v1.0.0, crossbeam-utils v0.8.5, futures-macro v0.3.17, futures-util v0.3.17, lazy_static v1.4.0, libc v0.2.103, lock_api v0.4.5, log v0.4.8, mio v0.7.13, parking_lot_core v0.8.5, signal-hook-registry v1.2.0, smallvec v1.7.0, syn v1.0.77, tokio v1.12.0, tokio-util v0.6.8, unicode-xid v0.2.0, version_check v0.9.3
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 2`

```

`> RUSTFLAGS="-Z future-incompat-test" ~/repos/cargo/target/debug/cargo build -Z future-incompat-report --future-incompat-report -Z unstable-options`

```
    Finished dev [unoptimized + debuginfo] target(s) in 2.12s
warning: the following packages contain code that will be rejected by a future version of Rust: actix v0.11.1 (/home/aaron/repos/actix/actix), ahash v0.7.4, arc-swap v0.4.4, autocfg v1.0.0, crossbeam-utils v0.8.5, futures-macro v0.3.17, futures-util v0.3.17, lazy_static v1.4.0, libc v0.2.103, lock_api v0.4.5, log v0.4.8, mio v0.7.13, parking_lot_core v0.8.5, signal-hook-registry v1.2.0, smallvec v1.7.0, syn v1.0.77, tokio v1.12.0, tokio-util v0.6.8, unicode-xid v0.2.0, version_check v0.9.3
note:
To solve this problem, you can try the following approaches:

- Some affected dependencies have newer versions available.
You may want to consider updating them to a newer version to see if the issue has been fixed.

ahash v0.7.4 has the following newer versions available: 0.7.5
arc-swap v0.4.4 has the following newer versions available: 0.4.8, 1.1.0, 1.2.0, 1.3.0, 1.3.1, 1.3.2, 1.4.0
autocfg v1.0.0 has the following newer versions available: 1.0.1
log v0.4.8 has the following newer versions available: 0.4.11, 0.4.13, 0.4.14
signal-hook-registry v1.2.0 has the following newer versions available: 1.2.1, 1.2.2, 1.3.0, 1.4.0
syn v1.0.77 has the following newer versions available: 1.0.78, 1.0.79, 1.0.80
unicode-xid v0.2.0 has the following newer versions available: 0.2.1, 0.2.2

- If the issue is not solved by updating the dependencies, a fix has to be
  implemented by those dependencies. You can help with that by notifying the
  maintainers of this problem (e.g. by creating a bug report) or by proposing a
  fix to the maintainers (e.g. by creating a pull request):

  - actix:0.11.1
    - Repository: https://github.com/actix/actix
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "actix:0.11.1"

  - ahash:0.7.4
    - Repository: https://github.com/tkaitchuck/ahash
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "ahash:0.7.4"

  - arc-swap:0.4.4
    - Repository: https://github.com/vorner/arc-swap
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "arc-swap:0.4.4"

  - autocfg:1.0.0
    - Repository: https://github.com/cuviper/autocfg
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "autocfg:1.0.0"

  - crossbeam-utils:0.8.5
    - Repository: https://github.com/crossbeam-rs/crossbeam
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "crossbeam-utils:0.8.5"

  - futures-macro:0.3.17
    - Repository: https://github.com/rust-lang/futures-rs
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "futures-macro:0.3.17"

  - futures-util:0.3.17
    - Repository: https://github.com/rust-lang/futures-rs
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "futures-util:0.3.17"

  - lazy_static:1.4.0
    - Repository: https://github.com/rust-lang-nursery/lazy-static.rs
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "lazy_static:1.4.0"

  - libc:0.2.103
    - Repository: https://github.com/rust-lang/libc
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "libc:0.2.103"

  - lock_api:0.4.5
    - Repository: https://github.com/Amanieu/parking_lot
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "lock_api:0.4.5"

  - log:0.4.8
    - Repository: https://github.com/rust-lang/log
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "log:0.4.8"

  - mio:0.7.13
    - Repository: https://github.com/tokio-rs/mio
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "mio:0.7.13"

  - parking_lot_core:0.8.5
    - Repository: https://github.com/Amanieu/parking_lot
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "parking_lot_core:0.8.5"

  - signal-hook-registry:1.2.0
    - Repository: https://github.com/vorner/signal-hook
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "signal-hook-registry:1.2.0"

  - smallvec:1.7.0
    - Repository: https://github.com/servo/rust-smallvec
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "smallvec:1.7.0"

  - syn:1.0.77
    - Repository: https://github.com/dtolnay/syn
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "syn:1.0.77"

  - tokio:1.12.0
    - Repository: https://github.com/tokio-rs/tokio
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "tokio:1.12.0"

  - tokio-util:0.6.8
    - Repository: https://github.com/tokio-rs/tokio
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "tokio-util:0.6.8"

  - unicode-xid:0.2.0
    - Repository: https://github.com/unicode-rs/unicode-xid
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "unicode-xid:0.2.0"

  - version_check:0.9.3
    - Repository: https://github.com/SergioBenitez/version_check
    - Detailed warning command: `cargo report future-incompatibilities --id 3 --crate "version_check:0.9.3"

- If waiting for an upstream fix is not an option, you can use the `[patch]`
  section in `Cargo.toml` to use your own version of the dependency. For more
  information, see:
  https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section

note: this report can be shown with `cargo report future-incompatibilities -Z future-incompat-report --id 3`
```

`> RUSTFLAGS="-Z future-incompat-test" ~/repos/cargo/target/debug/cargo report future-incompatibilities -Z future-incompat-report --color never | head -n 100`

```
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.

- Some affected dependencies have newer versions available.
You may want to consider updating them to a newer version to see if the issue has been fixed.

ahash v0.7.4 has the following newer versions available: 0.7.5
arc-swap v0.4.4 has the following newer versions available: 0.4.8, 1.1.0, 1.2.0, 1.3.0, 1.3.1, 1.3.2, 1.4.0
autocfg v1.0.0 has the following newer versions available: 1.0.1
log v0.4.8 has the following newer versions available: 0.4.11, 0.4.13, 0.4.14
signal-hook-registry v1.2.0 has the following newer versions available: 1.2.1, 1.2.2, 1.3.0, 1.4.0
syn v1.0.77 has the following newer versions available: 1.0.78, 1.0.79, 1.0.80
unicode-xid v0.2.0 has the following newer versions available: 0.2.1, 0.2.2

The package `actix v0.11.1 (/home/aaron/repos/actix/actix)` currently triggers the following future incompatibility lints:
> warning: use of deprecated struct `utils::Condition`: Please use tokio::sync::oneshot::Sender instead.
>   --> actix/src/utils.rs:25:9
>    |
> 25 | impl<T> Condition<T>
>    |         ^^^^^^^^^
>    |
> note: the lint level is defined here
>   --> actix/src/lib.rs:30:10
>    |
> 30 | #![allow(deprecated)]
>    |          ^^^^^^^^^^
>
> warning: use of deprecated struct `utils::Condition`: Please use tokio::sync::oneshot::Sender instead.
>   --> actix/src/utils.rs:42:21
>    |
> 42 | impl<T> Default for Condition<T>
>    |                     ^^^^^^^^^
>
> warning: use of deprecated struct `utils::Condition`: Please use tokio::sync::oneshot::Sender instead.
>   --> actix/src/utils.rs:47:9
>    |
> 47 |         Condition {
>    |         ^^^^^^^^^
>
> warning: use of deprecated struct `utils::Condition`: Please use tokio::sync::oneshot::Sender instead.
>    --> actix/src/lib.rs:120:28
>     |
> 120 |     pub use crate::utils::{Condition, IntervalFunc, TimerFunc};
>     |                            ^^^^^^^^^
>
> warning: use of deprecated associated function `std::sync::atomic::AtomicUsize::compare_and_swap`: Use `compare_exchange` or `compare_exchange_weak` instead
>    --> actix/src/address/channel.rs:512:49
>     |
> 512 |             let actual = self.inner.num_senders.compare_and_swap(curr, next, SeqCst);
>     |                                                 ^^^^^^^^^^^^^^^^
>
> warning: use of deprecated associated function `std::sync::atomic::AtomicUsize::compare_and_swap`: Use `compare_exchange` or `compare_exchange_weak` instead
>    --> actix/src/address/channel.rs:636:49
>     |
> 636 |             let actual = self.inner.num_senders.compare_and_swap(curr, next, SeqCst);
>     |                                                 ^^^^^^^^^^^^^^^^
>
> warning: use of deprecated associated function `std::sync::atomic::AtomicUsize::compare_and_swap`: Use `compare_exchange` or `compare_exchange_weak` instead
>    --> actix/src/address/channel.rs:697:49
>     |
> 697 |             let actual = self.inner.num_senders.compare_and_swap(curr, next, SeqCst);
>     |                                                 ^^^^^^^^^^^^^^^^
>
> warning: use of deprecated field `utils::Condition::waiters`: Please use tokio::sync::oneshot::Sender instead.
>   --> actix/src/utils.rs:31:9
>    |
> 31 |         self.waiters.push(tx);
>    |         ^^^^^^^^^^^^
>
> warning: use of deprecated field `utils::Condition::waiters`: Please use tokio::sync::oneshot::Sender instead.
>   --> actix/src/utils.rs:36:23
>    |
> 36 |         for waiter in self.waiters {
>    |                       ^^^^^^^^^^^^
>
> warning: use of deprecated field `utils::Condition::waiters`: Please use tokio::sync::oneshot::Sender instead.
>   --> actix/src/utils.rs:48:13
>    |
> 48 |             waiters: Vec::new(),
>    |             ^^^^^^^^^^^^^^^^^^^
>
> warning: unused variable: `ctx`
>   --> actix/src/actor.rs:78:27
>    |
> 78 |     fn started(&mut self, ctx: &mut Self::Context) {}
>    |                           ^^^ help: if this is intentional, prefix it with an underscore: `_ctx`
>    |
> note: the lint level is defined here
>   --> actix/src/actor.rs:72:9
>    |
> 72 | #[allow(unused_variables)]
>    |         ^^^^^^^^^^^^^^^^
```
This commit is contained in:
bors 2021-10-19 02:16:48 +00:00
commit 7fbbf4e8f2
4 changed files with 280 additions and 158 deletions

View file

@ -18,7 +18,8 @@ pub fn cli() -> App {
"identifier of the report generated by a Cargo command invocation",
)
.value_name("id"),
),
)
.arg_package("Package to display a report for"),
)
}
@ -38,7 +39,8 @@ fn report_future_incompatibilies(config: &Config, args: &ArgMatches<'_>) -> CliR
let id = args
.value_of_u32("id")?
.unwrap_or_else(|| reports.last_id());
let report = reports.get_report(id, config)?;
let krate = args.value_of("package");
let report = reports.get_report(id, config, krate)?;
drop_println!(config, "{}", REPORT_PREAMBLE);
drop(config.shell().print_ansi_stdout(report.as_bytes()));
Ok(())

View file

@ -1,11 +1,12 @@
//! Support for future-incompatible warning reporting.
use crate::core::compiler::BuildContext;
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::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::Write as _;
use std::io::{Read, Write};
@ -77,8 +78,14 @@ pub struct OnDiskReports {
struct OnDiskReport {
/// Unique reference to the report for the `--id` CLI flag.
id: u32,
/// A message describing suggestions for fixing the
/// reported issues
suggestion_message: String,
/// Report, suitable for printing to the console.
report: String,
/// Maps package names to the corresponding report
/// We use a `BTreeMap` so that the iteration order
/// is stable across multiple runs of `cargo`
per_package: BTreeMap<String, String>,
}
impl Default for OnDiskReports {
@ -94,29 +101,22 @@ impl Default for OnDiskReports {
impl OnDiskReports {
/// Saves a new report.
pub fn save_report(
mut self,
ws: &Workspace<'_>,
suggestion_message: String,
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(ws, per_package_reports),
id: self.next_id,
suggestion_message,
per_package: 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);
self.next_id += 1;
self.reports.push(report);
if self.reports.len() > MAX_REPORTS {
self.reports.remove(0);
}
let on_disk = serde_json::to_vec(&current_reports).unwrap();
let on_disk = serde_json::to_vec(&self).unwrap();
if let Err(e) = ws
.target_dir()
.open_rw(
@ -137,7 +137,6 @@ impl OnDiskReports {
&mut ws.config().shell(),
);
}
current_reports
}
/// Loads the on-disk reports.
@ -176,7 +175,12 @@ impl OnDiskReports {
self.reports.last().map(|r| r.id).unwrap()
}
pub fn get_report(&self, id: u32, config: &Config) -> CargoResult<String> {
pub fn get_report(
&self,
id: u32,
config: &Config,
package: Option<&str>,
) -> 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!(
@ -186,28 +190,56 @@ impl OnDiskReports {
available
)
})?;
let report = if config.shell().err_supports_color() {
report.report.clone()
let mut to_display = report.suggestion_message.clone();
to_display += "\n";
let package_report = if let Some(package) = package {
report
.per_package
.get(package)
.ok_or_else(|| {
format_err!(
"could not find package with ID `{}`\n
Available packages are: {}\n
Omit the `--package` flag to display a report for all packages",
package,
iter_join(report.per_package.keys(), ", ")
)
})?
.to_string()
} else {
strip_ansi_escapes::strip(&report.report)
report
.per_package
.values()
.cloned()
.collect::<Vec<_>>()
.join("\n")
};
to_display += &package_report;
let to_display = if config.shell().err_supports_color() {
to_display
} else {
strip_ansi_escapes::strip(&to_display)
.map(|v| String::from_utf8(v).expect("utf8"))
.expect("strip should never fail")
};
Ok(report)
Ok(to_display)
}
}
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 {
fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> BTreeMap<String, String> {
let mut report: BTreeMap<String, String> = BTreeMap::new();
for per_package in per_package_reports {
let package_spec = format!(
"{}:{}",
per_package.package_id.name(),
per_package.package_id.version()
);
let rendered = report.entry(package_spec).or_default();
rendered.push_str(&format!(
"The package `{}` currently triggers the following future \
incompatibility lints:\n",
"The package `{}` currently triggers the following future incompatibility lints:\n",
per_package.package_id
));
for item in &per_package.items {
@ -218,25 +250,20 @@ fn render_report(
.map(|l| format!("> {}\n", l)),
);
}
rendered.push('\n');
}
if let Some(s) = render_suggestions(ws, &per_package_reports) {
rendered.push_str(&s);
}
rendered
report
}
fn render_suggestions(
ws: &Workspace<'_>,
per_package_reports: &[&FutureIncompatReportPackage],
) -> Option<String> {
/// Returns a user-readable message explaining which of
/// the packages in `package_ids` have updates available.
/// This is best-effort - if an error occurs, `None` will be returned.
fn get_updates(ws: &Workspace<'_>, package_ids: &BTreeSet<PackageId>) -> 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
let package_ids: BTreeSet<_> = package_ids
.iter()
.map(|r| r.package_id)
.filter(|pkg_id| pkg_id.source_id().is_registry())
.collect();
let source_ids: HashSet<_> = package_ids
@ -251,7 +278,7 @@ fn render_suggestions(
})
.collect();
// Query the sources for new versions.
let mut suggestions = String::new();
let mut updates = String::new();
for pkg_id in package_ids {
let source = match sources.get_mut(&pkg_id.source_id()) {
Some(s) => s,
@ -259,31 +286,169 @@ fn render_suggestions(
};
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 mut updated_versions: Vec<_> = summaries
.iter()
.map(|summary| summary.version())
.filter(|version| *version > pkg_id.version())
.collect();
updated_versions.sort();
let updated_versions = iter_join(
updated_versions
.into_iter()
.map(|version| version.to_string()),
", ",
);
let versions = versions.map(|version| version.to_string());
let versions = iter_join(versions, ", ");
if !versions.is_empty() {
if !updated_versions.is_empty() {
writeln!(
suggestions,
updates,
"{} has the following newer versions available: {}",
pkg_id, versions
pkg_id, updated_versions
)
.unwrap();
}
}
if suggestions.is_empty() {
None
Some(updates)
}
/// Writes a future-incompat report to disk, using the per-package
/// reports gathered during the build. If requested by the user,
/// a message is also displayed in the build output.
pub fn save_and_display_report(
bcx: &BuildContext<'_, '_>,
per_package_future_incompat_reports: &[FutureIncompatReportPackage],
) {
if !bcx.config.cli_unstable().future_incompat_report {
return;
}
let should_display_message = match bcx.config.future_incompat_config() {
Ok(config) => config.should_display_message(),
Err(e) => {
crate::display_warning_with_error(
"failed to read future-incompat config from disk",
&e,
&mut bcx.config.shell(),
);
true
}
};
if per_package_future_incompat_reports.is_empty() {
// Explicitly passing a command-line flag overrides
// `should_display_message` from the config file
if bcx.build_config.future_incompat_report {
drop(
bcx.config
.shell()
.note("0 dependencies had future-incompatible warnings"),
);
}
return;
}
let current_reports = match OnDiskReports::load(bcx.ws) {
Ok(r) => r,
Err(e) => {
log::debug!(
"saving future-incompatible reports failed to load current reports: {:?}",
e
);
OnDiskReports::default()
}
};
let report_id = current_reports.next_id;
// Get a list of unique and sorted package name/versions.
let package_ids: BTreeSet<_> = per_package_future_incompat_reports
.iter()
.map(|r| r.package_id)
.collect();
let package_vers: Vec<_> = package_ids.iter().map(|pid| pid.to_string()).collect();
if should_display_message || bcx.build_config.future_incompat_report {
drop(bcx.config.shell().warn(&format!(
"the following packages contain code that will be rejected by a future \
version of Rust: {}",
package_vers.join(", ")
)));
}
let updated_versions = get_updates(bcx.ws, &package_ids).unwrap_or(String::new());
let update_message = if !updated_versions.is_empty() {
format!(
"
- Some affected dependencies have newer versions available.
You may want to consider updating them to a newer version to see if the issue has been fixed.
{updated_versions}\n",
updated_versions = updated_versions
)
} 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
))
String::new()
};
let upstream_info = package_ids
.iter()
.map(|package_id| {
let manifest = bcx.packages.get_one(*package_id).unwrap().manifest();
format!(
"
- {name}
- Repository: {url}
- Detailed warning command: `cargo report future-incompatibilities --id {id} --package {name}`",
name = format!("{}:{}", package_id.name(), package_id.version()),
url = manifest
.metadata()
.repository
.as_deref()
.unwrap_or("<not found>"),
id = report_id,
)
})
.collect::<Vec<_>>()
.join("\n");
let suggestion_message = format!(
"
To solve this problem, you can try the following approaches:
{update_message}
- If the issue is not solved by updating the dependencies, a fix has to be
implemented by those dependencies. You can help with that by notifying the
maintainers of this problem (e.g. by creating a bug report) or by proposing a
fix to the maintainers (e.g. by creating a pull request):
{upstream_info}
- If waiting for an upstream fix is not an option, you can use the `[patch]`
section in `Cargo.toml` to use your own version of the dependency. For more
information, see:
https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section
",
upstream_info = upstream_info,
update_message = update_message,
);
current_reports.save_report(
bcx.ws,
suggestion_message.clone(),
per_package_future_incompat_reports,
);
if bcx.build_config.future_incompat_report {
drop(bcx.config.shell().note(&suggestion_message));
drop(bcx.config.shell().note(&format!(
"this report can be shown with `cargo report \
future-incompatibilities -Z future-incompat-report --id {}`",
report_id
)));
} else if should_display_message {
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
)));
}
}

View file

@ -50,7 +50,7 @@
//! improved.
use std::cell::{Cell, RefCell};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::Write as _;
use std::io;
use std::marker;
@ -72,7 +72,7 @@ use super::job::{
use super::timings::Timings;
use super::{BuildContext, BuildPlan, CompileMode, Context, Unit};
use crate::core::compiler::future_incompat::{
FutureBreakageItem, FutureIncompatReportPackage, OnDiskReports,
self, FutureBreakageItem, FutureIncompatReportPackage,
};
use crate::core::resolver::ResolveBehavior;
use crate::core::{PackageId, Shell, TargetKind};
@ -871,7 +871,10 @@ impl<'cfg> DrainState<'cfg> {
if !cx.bcx.build_config.build_plan {
// It doesn't really matter if this fails.
drop(cx.bcx.config.shell().status("Finished", message));
self.emit_future_incompat(cx.bcx);
future_incompat::save_and_display_report(
cx.bcx,
&self.per_package_future_incompat_reports,
);
}
None
@ -881,76 +884,6 @@ impl<'cfg> DrainState<'cfg> {
}
}
fn emit_future_incompat(&mut self, bcx: &BuildContext<'_, '_>) {
if !bcx.config.cli_unstable().future_incompat_report {
return;
}
let should_display_message = match bcx.config.future_incompat_config() {
Ok(config) => config.should_display_message(),
Err(e) => {
crate::display_warning_with_error(
"failed to read future-incompat config from disk",
&e,
&mut bcx.config.shell(),
);
true
}
};
if self.per_package_future_incompat_reports.is_empty() {
// Explicitly passing a command-line flag overrides
// `should_display_message` from the config file
if bcx.build_config.future_incompat_report {
drop(
bcx.config
.shell()
.note("0 dependencies had future-incompatible warnings"),
);
}
return;
}
// Get a list of unique and sorted package name/versions.
let package_vers: BTreeSet<_> = self
.per_package_future_incompat_reports
.iter()
.map(|r| r.package_id)
.collect();
let package_vers: Vec<_> = package_vers
.into_iter()
.map(|pid| pid.to_string())
.collect();
if should_display_message || bcx.build_config.future_incompat_report {
drop(bcx.config.shell().warn(&format!(
"the following packages contain code that will be rejected by a future \
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(bcx.config.shell().print_ansi_stderr(rendered.as_bytes()));
drop(bcx.config.shell().note(&format!(
"this report can be shown with `cargo report \
future-incompatibilities -Z future-incompat-report --id {}`",
report_id
)));
} else if should_display_message {
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
)));
}
}
fn handle_error(
&self,
shell: &mut Shell,

View file

@ -161,7 +161,7 @@ frequency = 'never'
.env("RUSTFLAGS", "-Zfuture-incompat-test")
.with_stderr_contains(FUTURE_OUTPUT)
.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 package `foo v0.0.0 ([..])` currently triggers the following future incompatibility lints:")
.with_stderr_contains(" - foo:0.0.0[..]")
.run();
}
}
@ -202,16 +202,33 @@ fn test_multi_crate() {
.env("RUSTFLAGS", "-Zfuture-incompat-test")
.with_stderr_does_not_contain(FUTURE_OUTPUT)
.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,
// and that we don't explain how to show a per-package report
.with_stderr_does_not_contain("[..]triggers[..]")
.with_stderr_does_not_contain("[..]--package[..]")
.with_stderr_does_not_contain("[..]-p[..]")
.run();
p.cargo(command).arg("-Zunstable-options").arg("-Zfuture-incompat-report").arg("--future-incompat-report")
.masquerade_as_nightly_cargo()
.env("RUSTFLAGS", "-Zfuture-incompat-test")
.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 package `first-dep v0.0.1` currently triggers the following future incompatibility lints:")
.with_stderr_contains("The package `second-dep v0.0.2` currently triggers the following future incompatibility lints:")
.with_stderr_contains(" - first-dep:0.0.1")
.with_stderr_contains(" - second-dep:0.0.2")
.run();
p.cargo("report future-incompatibilities").arg("--package").arg("first-dep:0.0.1").arg("-Zunstable-options").arg("-Zfuture-incompat-report")
.masquerade_as_nightly_cargo()
.with_stdout_contains("The package `first-dep v0.0.1` currently triggers the following future incompatibility lints:")
.with_stdout_contains(FUTURE_OUTPUT)
.with_stdout_does_not_contain("[..]second-dep-0.0.2/src[..]")
.run();
p.cargo("report future-incompatibilities").arg("--package").arg("second-dep:0.0.2").arg("-Zunstable-options").arg("-Zfuture-incompat-report")
.masquerade_as_nightly_cargo()
.with_stdout_contains("The package `second-dep v0.0.2` currently triggers the following future incompatibility lints:")
.with_stdout_contains(FUTURE_OUTPUT)
.with_stdout_does_not_contain("[..]first-dep-0.0.1/src[..]")
.run();
}
@ -263,7 +280,9 @@ fn test_multi_crate() {
"The package `{}` currently triggers the following future incompatibility lints:",
expected
),
lines.next().unwrap()
lines.next().unwrap(),
"Bad output:\n{}",
output
);
let mut count = 0;
while let Some(line) = lines.next() {
@ -383,6 +402,9 @@ fn suggestions_for_updates() {
Package::new("with_updates", "1.0.2")
.file("src/lib.rs", "")
.publish();
Package::new("with_updates", "3.0.1")
.file("src/lib.rs", "")
.publish();
Package::new("big_update", "2.0.0")
.file("src/lib.rs", "")
.publish();
@ -396,22 +418,22 @@ fn suggestions_for_updates() {
// in a long while?).
p.cargo("update -p without_updates").run();
p.cargo("check -Zfuture-incompat-report")
let update_message = "\
- Some affected dependencies 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, 3.0.1
";
p.cargo("check -Zfuture-incompat-report -Zunstable-options --future-incompat-report")
.masquerade_as_nightly_cargo()
.env("RUSTFLAGS", "-Zfuture-incompat-test")
.with_stderr_contains("[..]cargo report future-incompatibilities --id 1[..]")
.with_stderr_contains(update_message)
.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();
.with_stdout_contains(update_message)
.run()
}