@@ -4,7 +4,9 @@ use std::fmt::Write;
4
4
use std:: ops:: ControlFlow ;
5
5
6
6
use rustc_data_structures:: fx:: FxHashMap ;
7
- use rustc_errors:: { Applicability , Diag , DiagArgValue , IntoDiagArg , into_diag_arg_using_display} ;
7
+ use rustc_errors:: {
8
+ Applicability , Diag , DiagArgValue , IntoDiagArg , into_diag_arg_using_display, pluralize,
9
+ } ;
8
10
use rustc_hir:: def:: DefKind ;
9
11
use rustc_hir:: def_id:: DefId ;
10
12
use rustc_hir:: { self as hir, LangItem , PredicateOrigin , WherePredicateKind } ;
@@ -288,7 +290,11 @@ pub fn suggest_constraining_type_params<'a>(
288
290
None => true ,
289
291
} ;
290
292
if stable || tcx. sess . is_nightly_build ( ) {
291
- grouped. entry ( param_name) . or_insert ( Vec :: new ( ) ) . push ( ( constraint, def_id) ) ;
293
+ grouped. entry ( param_name) . or_insert ( Vec :: new ( ) ) . push ( (
294
+ constraint,
295
+ def_id,
296
+ if stable { "" } else { "unstable " } ,
297
+ ) ) ;
292
298
if !stable {
293
299
unstable_suggestion = true ;
294
300
}
@@ -303,10 +309,10 @@ pub fn suggest_constraining_type_params<'a>(
303
309
let Some ( param) = param else { return false } ;
304
310
305
311
{
306
- let mut sized_constraints = constraints. extract_if ( |( _, def_id) | {
312
+ let mut sized_constraints = constraints. extract_if ( |( _, def_id, _ ) | {
307
313
def_id. is_some_and ( |def_id| tcx. is_lang_item ( def_id, LangItem :: Sized ) )
308
314
} ) ;
309
- if let Some ( ( _, def_id) ) = sized_constraints. next ( ) {
315
+ if let Some ( ( _, def_id, _ ) ) = sized_constraints. next ( ) {
310
316
applicability = Applicability :: MaybeIncorrect ;
311
317
312
318
err. span_label ( param. span , "this type parameter needs to be `Sized`" ) ;
@@ -325,15 +331,52 @@ pub fn suggest_constraining_type_params<'a>(
325
331
. collect ( ) ;
326
332
327
333
constraints
328
- . retain ( |( _, def_id) | def_id. map_or ( true , |def| !bound_trait_defs. contains ( & def) ) ) ;
334
+ . retain ( |( _, def_id, _ ) | def_id. map_or ( true , |def| !bound_trait_defs. contains ( & def) ) ) ;
329
335
330
336
if constraints. is_empty ( ) {
331
337
continue ;
332
338
}
333
339
334
- let mut constraint = constraints. iter ( ) . map ( |& ( c, _) | c) . collect :: < Vec < _ > > ( ) ;
340
+ let mut constraint = constraints. iter ( ) . map ( |& ( c, _, _ ) | c) . collect :: < Vec < _ > > ( ) ;
335
341
constraint. sort ( ) ;
336
342
constraint. dedup ( ) ;
343
+ let all_stable = constraints. iter ( ) . all ( |& ( _, _, stable) | stable. is_empty ( ) ) ;
344
+ let all_unstable = constraints. iter ( ) . all ( |& ( _, _, stable) | !stable. is_empty ( ) ) ;
345
+ let post = if all_stable || all_unstable {
346
+ // Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`"
347
+ let mut trait_names = constraints
348
+ . iter ( )
349
+ . map ( |& ( c, def_id, _) | match def_id {
350
+ None => format ! ( "`{c}`" ) ,
351
+ Some ( def_id) => format ! ( "`{}`" , tcx. item_name( def_id) ) ,
352
+ } )
353
+ . collect :: < Vec < _ > > ( ) ;
354
+ trait_names. sort ( ) ;
355
+ trait_names. dedup ( ) ;
356
+ let n = trait_names. len ( ) ;
357
+ let stable = if all_stable { "" } else { "unstable " } ;
358
+ format ! ( "{stable}trait{} {}" , pluralize!( n) , match & trait_names[ ..] {
359
+ [ t] => t. to_string( ) ,
360
+ [ ts @ .., last] => format!( "{} and {last}" , ts. join( ", " ) ) ,
361
+ [ ] => return false ,
362
+ } , )
363
+ } else {
364
+ // We're more explicit when there's a mix of stable and unstable traits.
365
+ let mut trait_names = constraints
366
+ . iter ( )
367
+ . map ( |& ( c, def_id, stable) | match def_id {
368
+ None => format ! ( "{stable}trait `{c}`" ) ,
369
+ Some ( def_id) => format ! ( "{stable}trait `{}`" , tcx. item_name( def_id) ) ,
370
+ } )
371
+ . collect :: < Vec < _ > > ( ) ;
372
+ trait_names. sort ( ) ;
373
+ trait_names. dedup ( ) ;
374
+ match & trait_names[ ..] {
375
+ [ t] => t. to_string ( ) ,
376
+ [ ts @ .., last] => format ! ( "{} and {last}" , ts. join( ", " ) ) ,
377
+ [ ] => return false ,
378
+ }
379
+ } ;
337
380
let constraint = constraint. join ( " + " ) ;
338
381
let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
339
382
let suggestion = if span_to_replace. is_some ( ) {
@@ -351,18 +394,18 @@ pub fn suggest_constraining_type_params<'a>(
351
394
if let Some ( open_paren_sp) = open_paren_sp {
352
395
suggestions. push ( (
353
396
open_paren_sp,
354
- constraint . clone ( ) ,
397
+ post . clone ( ) ,
355
398
"(" . to_string ( ) ,
356
399
RestrictBoundFurther ,
357
400
) ) ;
358
401
suggestions. push ( (
359
402
span,
360
- constraint . clone ( ) ,
403
+ post . clone ( ) ,
361
404
format ! ( "){suggestion}" ) ,
362
405
RestrictBoundFurther ,
363
406
) ) ;
364
407
} else {
365
- suggestions. push ( ( span, constraint . clone ( ) , suggestion, RestrictBoundFurther ) ) ;
408
+ suggestions. push ( ( span, post . clone ( ) , suggestion, RestrictBoundFurther ) ) ;
366
409
}
367
410
} ;
368
411
@@ -420,8 +463,8 @@ pub fn suggest_constraining_type_params<'a>(
420
463
// - insert: `, X: Bar`
421
464
suggestions. push ( (
422
465
generics. tail_span_for_predicate_suggestion ( ) ,
423
- constraint . clone ( ) ,
424
- constraints. iter ( ) . fold ( String :: new ( ) , |mut string, & ( constraint, _) | {
466
+ post ,
467
+ constraints. iter ( ) . fold ( String :: new ( ) , |mut string, & ( constraint, _, _ ) | {
425
468
write ! ( string, ", {param_name}: {constraint}" ) . unwrap ( ) ;
426
469
string
427
470
} ) ,
@@ -450,7 +493,7 @@ pub fn suggest_constraining_type_params<'a>(
450
493
// default (`<T=Foo>`), so we suggest adding `where T: Bar`.
451
494
suggestions. push ( (
452
495
generics. tail_span_for_predicate_suggestion ( ) ,
453
- constraint . clone ( ) ,
496
+ post ,
454
497
format ! ( "{where_prefix} {param_name}: {constraint}" ) ,
455
498
SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty : param_name } ,
456
499
) ) ;
@@ -464,7 +507,7 @@ pub fn suggest_constraining_type_params<'a>(
464
507
if let Some ( colon_span) = param. colon_span {
465
508
suggestions. push ( (
466
509
colon_span. shrink_to_hi ( ) ,
467
- constraint . clone ( ) ,
510
+ post ,
468
511
format ! ( " {constraint}" ) ,
469
512
SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
470
513
) ) ;
@@ -477,7 +520,7 @@ pub fn suggest_constraining_type_params<'a>(
477
520
// - help: consider restricting this type parameter with `T: Foo`
478
521
suggestions. push ( (
479
522
param. span . shrink_to_hi ( ) ,
480
- constraint . clone ( ) ,
523
+ post ,
481
524
format ! ( ": {constraint}" ) ,
482
525
SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
483
526
) ) ;
@@ -490,21 +533,16 @@ pub fn suggest_constraining_type_params<'a>(
490
533
. collect :: < Vec < _ > > ( ) ;
491
534
let suggested = !suggestions. is_empty ( ) ;
492
535
if suggestions. len ( ) == 1 {
493
- let ( span, constraint, suggestion, msg) = suggestions. pop ( ) . unwrap ( ) ;
494
- let post = format ! (
495
- " with {}trait{} `{constraint}`" ,
496
- if unstable_suggestion { "unstable " } else { "" } ,
497
- if constraint. contains( '+' ) { "s" } else { "" } ,
498
- ) ;
536
+ let ( span, post, suggestion, msg) = suggestions. pop ( ) . unwrap ( ) ;
499
537
let msg = match msg {
500
538
SuggestChangingConstraintsMessage :: RestrictBoundFurther => {
501
- format ! ( "consider further restricting this bound{post}" )
539
+ format ! ( "consider further restricting this bound with {post}" )
502
540
}
503
541
SuggestChangingConstraintsMessage :: RestrictType { ty } => {
504
- format ! ( "consider restricting type parameter `{ty}`{post}" )
542
+ format ! ( "consider restricting type parameter `{ty}` with {post}" )
505
543
}
506
544
SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty } => {
507
- format ! ( "consider further restricting type parameter `{ty}`{post}" )
545
+ format ! ( "consider further restricting type parameter `{ty}` with {post}" )
508
546
}
509
547
SuggestChangingConstraintsMessage :: RemoveMaybeUnsized => {
510
548
format ! ( "consider removing the `?Sized` bound to make the type parameter `Sized`" )
0 commit comments