1
1
//! Support for future-incompatible warning reporting.
2
2
3
- use crate :: core:: { PackageId , Workspace } ;
3
+ use crate :: core:: { Dependency , PackageId , Workspace } ;
4
+ use crate :: sources:: SourceConfigMap ;
4
5
use crate :: util:: { iter_join, CargoResult , Config } ;
5
6
use anyhow:: { bail, format_err, Context } ;
6
7
use serde:: { Deserialize , Serialize } ;
8
+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
9
+ use std:: fmt:: Write as _;
7
10
use std:: io:: { Read , Write } ;
8
11
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
+
9
24
/// The future incompatibility report, emitted by the compiler as a JSON message.
10
25
#[ derive( serde:: Deserialize ) ]
11
26
pub struct FutureIncompatReport {
@@ -90,7 +105,7 @@ impl OnDiskReports {
90
105
} ;
91
106
let report = OnDiskReport {
92
107
id : current_reports. next_id ,
93
- report : render_report ( per_package_reports) ,
108
+ report : render_report ( ws , per_package_reports) ,
94
109
} ;
95
110
current_reports. next_id += 1 ;
96
111
current_reports. reports . push ( report) ;
@@ -178,11 +193,14 @@ impl OnDiskReports {
178
193
}
179
194
}
180
195
181
- fn render_report ( per_package_reports : & [ FutureIncompatReportPackage ] ) -> String {
196
+ fn render_report (
197
+ ws : & Workspace < ' _ > ,
198
+ per_package_reports : & [ FutureIncompatReportPackage ] ,
199
+ ) -> String {
182
200
let mut per_package_reports: Vec < _ > = per_package_reports. iter ( ) . collect ( ) ;
183
201
per_package_reports. sort_by_key ( |r| r. package_id ) ;
184
202
let mut rendered = String :: new ( ) ;
185
- for per_package in per_package_reports {
203
+ for per_package in & per_package_reports {
186
204
rendered. push_str ( & format ! (
187
205
"The package `{}` currently triggers the following future \
188
206
incompatibility lints:\n ",
@@ -198,5 +216,75 @@ fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> String
198
216
}
199
217
rendered. push ( '\n' ) ;
200
218
}
219
+ if let Some ( s) = render_suggestions ( ws, & per_package_reports) {
220
+ rendered. push_str ( & s) ;
221
+ }
201
222
rendered
202
223
}
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