11//! Support for future-incompatible warning reporting.
22
3- use crate :: core:: { PackageId , Workspace } ;
3+ use crate :: core:: { Dependency , PackageId , Workspace } ;
4+ use crate :: sources:: SourceConfigMap ;
45use crate :: util:: { iter_join, CargoResult , Config } ;
56use anyhow:: { bail, format_err, Context } ;
67use serde:: { Deserialize , Serialize } ;
8+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
9+ use std:: fmt:: Write as _;
710use std:: io:: { Read , Write } ;
811
12+ pub const REPORT_PREAMBLE : & str = "\
13+ The following warnings were discovered during the build. These warnings are an
14+ indication that the packages contain code that will become an error in a
15+ future release of Rust. These warnings typically cover changes to close
16+ soundness problems, unintended or undocumented behavior, or critical problems
17+ that cannot be fixed in a backwards-compatible fashion, and are not expected
18+ to be in wide use.
19+
20+ Each warning should contain a link for more information on what the warning
21+ means and how to resolve it.
22+ " ;
23+
924/// The future incompatibility report, emitted by the compiler as a JSON message.
1025#[ derive( serde:: Deserialize ) ]
1126pub struct FutureIncompatReport {
@@ -90,7 +105,7 @@ impl OnDiskReports {
90105 } ;
91106 let report = OnDiskReport {
92107 id : current_reports. next_id ,
93- report : render_report ( per_package_reports) ,
108+ report : render_report ( ws , per_package_reports) ,
94109 } ;
95110 current_reports. next_id += 1 ;
96111 current_reports. reports . push ( report) ;
@@ -178,11 +193,14 @@ impl OnDiskReports {
178193 }
179194}
180195
181- fn render_report ( per_package_reports : & [ FutureIncompatReportPackage ] ) -> String {
196+ fn render_report (
197+ ws : & Workspace < ' _ > ,
198+ per_package_reports : & [ FutureIncompatReportPackage ] ,
199+ ) -> String {
182200 let mut per_package_reports: Vec < _ > = per_package_reports. iter ( ) . collect ( ) ;
183201 per_package_reports. sort_by_key ( |r| r. package_id ) ;
184202 let mut rendered = String :: new ( ) ;
185- for per_package in per_package_reports {
203+ for per_package in & per_package_reports {
186204 rendered. push_str ( & format ! (
187205 "The package `{}` currently triggers the following future \
188206 incompatibility lints:\n ",
@@ -198,5 +216,75 @@ fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> String
198216 }
199217 rendered. push ( '\n' ) ;
200218 }
219+ if let Some ( s) = render_suggestions ( ws, & per_package_reports) {
220+ rendered. push_str ( & s) ;
221+ }
201222 rendered
202223}
224+
225+ fn render_suggestions (
226+ ws : & Workspace < ' _ > ,
227+ per_package_reports : & [ & FutureIncompatReportPackage ] ,
228+ ) -> Option < String > {
229+ // This in general ignores all errors since this is opportunistic.
230+ let _lock = ws. config ( ) . acquire_package_cache_lock ( ) . ok ( ) ?;
231+ // Create a set of updated registry sources.
232+ let map = SourceConfigMap :: new ( ws. config ( ) ) . ok ( ) ?;
233+ let package_ids: BTreeSet < _ > = per_package_reports
234+ . iter ( )
235+ . map ( |r| r. package_id )
236+ . filter ( |pkg_id| pkg_id. source_id ( ) . is_registry ( ) )
237+ . collect ( ) ;
238+ let source_ids: HashSet < _ > = package_ids
239+ . iter ( )
240+ . map ( |pkg_id| pkg_id. source_id ( ) )
241+ . collect ( ) ;
242+ let mut sources: HashMap < _ , _ > = source_ids
243+ . into_iter ( )
244+ . filter_map ( |sid| {
245+ let unlocked = sid. clone ( ) . with_precise ( None ) ;
246+ let mut source = map. load ( unlocked, & HashSet :: new ( ) ) . ok ( ) ?;
247+ // Ignore errors updating.
248+ if let Err ( e) = source. update ( ) {
249+ log:: debug!( "failed to update source: {:?}" , e) ;
250+ }
251+ Some ( ( sid, source) )
252+ } )
253+ . collect ( ) ;
254+ // Query the sources for new versions.
255+ let mut suggestions = String :: new ( ) ;
256+ for pkg_id in package_ids {
257+ let source = match sources. get_mut ( & pkg_id. source_id ( ) ) {
258+ Some ( s) => s,
259+ None => continue ,
260+ } ;
261+ let dep = Dependency :: parse ( pkg_id. name ( ) , None , pkg_id. source_id ( ) ) . ok ( ) ?;
262+ let summaries = source. query_vec ( & dep) . ok ( ) ?;
263+ let versions = itertools:: sorted (
264+ summaries
265+ . iter ( )
266+ . map ( |summary| summary. version ( ) )
267+ . filter ( |version| * version > pkg_id. version ( ) ) ,
268+ ) ;
269+ let versions = versions. map ( |version| version. to_string ( ) ) ;
270+ let versions = iter_join ( versions, ", " ) ;
271+ if !versions. is_empty ( ) {
272+ writeln ! (
273+ suggestions,
274+ "{} has the following newer versions available: {}" ,
275+ pkg_id, versions
276+ )
277+ . unwrap ( ) ;
278+ }
279+ }
280+ if suggestions. is_empty ( ) {
281+ None
282+ } else {
283+ Some ( format ! (
284+ "The following packages appear to have newer versions available.\n \
285+ You may want to consider updating them to a newer version to see if the \
286+ issue has been fixed.\n \n {}",
287+ suggestions
288+ ) )
289+ }
290+ }
0 commit comments