@@ -4,7 +4,9 @@ use std::fmt::Write;
44use std:: ops:: ControlFlow ;
55
66use 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+ } ;
810use rustc_hir:: def:: DefKind ;
911use rustc_hir:: def_id:: DefId ;
1012use rustc_hir:: { self as hir, LangItem , PredicateOrigin , WherePredicateKind } ;
@@ -288,7 +290,11 @@ pub fn suggest_constraining_type_params<'a>(
288290 None => true ,
289291 } ;
290292 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+ ) ) ;
292298 if !stable {
293299 unstable_suggestion = true ;
294300 }
@@ -303,10 +309,10 @@ pub fn suggest_constraining_type_params<'a>(
303309 let Some ( param) = param else { return false } ;
304310
305311 {
306- let mut sized_constraints = constraints. extract_if ( |( _, def_id) | {
312+ let mut sized_constraints = constraints. extract_if ( |( _, def_id, _ ) | {
307313 def_id. is_some_and ( |def_id| tcx. is_lang_item ( def_id, LangItem :: Sized ) )
308314 } ) ;
309- if let Some ( ( _, def_id) ) = sized_constraints. next ( ) {
315+ if let Some ( ( _, def_id, _ ) ) = sized_constraints. next ( ) {
310316 applicability = Applicability :: MaybeIncorrect ;
311317
312318 err. span_label ( param. span , "this type parameter needs to be `Sized`" ) ;
@@ -325,15 +331,52 @@ pub fn suggest_constraining_type_params<'a>(
325331 . collect ( ) ;
326332
327333 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) ) ) ;
329335
330336 if constraints. is_empty ( ) {
331337 continue ;
332338 }
333339
334- let mut constraint = constraints. iter ( ) . map ( |& ( c, _) | c) . collect :: < Vec < _ > > ( ) ;
340+ let mut constraint = constraints. iter ( ) . map ( |& ( c, _, _ ) | c) . collect :: < Vec < _ > > ( ) ;
335341 constraint. sort ( ) ;
336342 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+ } ;
337380 let constraint = constraint. join ( " + " ) ;
338381 let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
339382 let suggestion = if span_to_replace. is_some ( ) {
@@ -351,18 +394,18 @@ pub fn suggest_constraining_type_params<'a>(
351394 if let Some ( open_paren_sp) = open_paren_sp {
352395 suggestions. push ( (
353396 open_paren_sp,
354- constraint . clone ( ) ,
397+ post . clone ( ) ,
355398 "(" . to_string ( ) ,
356399 RestrictBoundFurther ,
357400 ) ) ;
358401 suggestions. push ( (
359402 span,
360- constraint . clone ( ) ,
403+ post . clone ( ) ,
361404 format ! ( "){suggestion}" ) ,
362405 RestrictBoundFurther ,
363406 ) ) ;
364407 } else {
365- suggestions. push ( ( span, constraint . clone ( ) , suggestion, RestrictBoundFurther ) ) ;
408+ suggestions. push ( ( span, post . clone ( ) , suggestion, RestrictBoundFurther ) ) ;
366409 }
367410 } ;
368411
@@ -420,8 +463,8 @@ pub fn suggest_constraining_type_params<'a>(
420463 // - insert: `, X: Bar`
421464 suggestions. push ( (
422465 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, _, _ ) | {
425468 write ! ( string, ", {param_name}: {constraint}" ) . unwrap ( ) ;
426469 string
427470 } ) ,
@@ -450,7 +493,7 @@ pub fn suggest_constraining_type_params<'a>(
450493 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
451494 suggestions. push ( (
452495 generics. tail_span_for_predicate_suggestion ( ) ,
453- constraint . clone ( ) ,
496+ post ,
454497 format ! ( "{where_prefix} {param_name}: {constraint}" ) ,
455498 SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty : param_name } ,
456499 ) ) ;
@@ -464,7 +507,7 @@ pub fn suggest_constraining_type_params<'a>(
464507 if let Some ( colon_span) = param. colon_span {
465508 suggestions. push ( (
466509 colon_span. shrink_to_hi ( ) ,
467- constraint . clone ( ) ,
510+ post ,
468511 format ! ( " {constraint}" ) ,
469512 SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
470513 ) ) ;
@@ -477,7 +520,7 @@ pub fn suggest_constraining_type_params<'a>(
477520 // - help: consider restricting this type parameter with `T: Foo`
478521 suggestions. push ( (
479522 param. span . shrink_to_hi ( ) ,
480- constraint . clone ( ) ,
523+ post ,
481524 format ! ( ": {constraint}" ) ,
482525 SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
483526 ) ) ;
@@ -490,21 +533,16 @@ pub fn suggest_constraining_type_params<'a>(
490533 . collect :: < Vec < _ > > ( ) ;
491534 let suggested = !suggestions. is_empty ( ) ;
492535 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 ( ) ;
499537 let msg = match msg {
500538 SuggestChangingConstraintsMessage :: RestrictBoundFurther => {
501- format ! ( "consider further restricting this bound{post}" )
539+ format ! ( "consider further restricting this bound with {post}" )
502540 }
503541 SuggestChangingConstraintsMessage :: RestrictType { ty } => {
504- format ! ( "consider restricting type parameter `{ty}`{post}" )
542+ format ! ( "consider restricting type parameter `{ty}` with {post}" )
505543 }
506544 SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty } => {
507- format ! ( "consider further restricting type parameter `{ty}`{post}" )
545+ format ! ( "consider further restricting type parameter `{ty}` with {post}" )
508546 }
509547 SuggestChangingConstraintsMessage :: RemoveMaybeUnsized => {
510548 format ! ( "consider removing the `?Sized` bound to make the type parameter `Sized`" )
0 commit comments