11//! Diagnostics related methods for `Ty`.
22
3- use std:: borrow:: Cow ;
43use std:: fmt:: Write ;
54use std:: ops:: ControlFlow ;
65
76use rustc_data_structures:: fx:: FxHashMap ;
8- 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+ } ;
910use rustc_hir:: def:: DefKind ;
1011use rustc_hir:: def_id:: DefId ;
1112use rustc_hir:: { self as hir, LangItem , PredicateOrigin , WherePredicateKind } ;
@@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
161162 true
162163}
163164
164- #[ derive( Debug ) ]
165+ #[ derive( Debug , Clone , Copy ) ]
165166enum SuggestChangingConstraintsMessage < ' a > {
166167 RestrictBoundFurther ,
167168 RestrictType { ty : & ' a str } ,
@@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> {
172173
173174fn suggest_changing_unsized_bound (
174175 generics : & hir:: Generics < ' _ > ,
175- suggestions : & mut Vec < ( Span , String , SuggestChangingConstraintsMessage < ' _ > ) > ,
176+ suggestions : & mut Vec < ( Span , String , String , SuggestChangingConstraintsMessage < ' _ > ) > ,
176177 param : & hir:: GenericParam < ' _ > ,
177178 def_id : Option < DefId > ,
178179) {
@@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound(
207208 continue ;
208209 }
209210
210- let mut push_suggestion = |sp, msg| suggestions. push ( ( sp, String :: new ( ) , msg) ) ;
211+ let mut push_suggestion =
212+ |sp, msg| suggestions. push ( ( sp, "Sized" . to_string ( ) , String :: new ( ) , msg) ) ;
211213
212214 if predicate. bounds . len ( ) == unsized_bounds. len ( ) {
213215 // All the bounds are unsized bounds, e.g.
@@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>(
278280 span_to_replace : Option < Span > ,
279281) -> bool {
280282 let mut grouped = FxHashMap :: default ( ) ;
283+ let mut unstable_suggestion = false ;
281284 param_names_and_constraints. for_each ( |( param_name, constraint, def_id) | {
282- grouped. entry ( param_name) . or_insert ( Vec :: new ( ) ) . push ( ( constraint, def_id) )
285+ let stable = match def_id {
286+ Some ( def_id) => match tcx. lookup_stability ( def_id) {
287+ Some ( s) => s. level . is_stable ( ) ,
288+ None => true ,
289+ } ,
290+ None => true ,
291+ } ;
292+ if stable || tcx. sess . is_nightly_build ( ) {
293+ grouped. entry ( param_name) . or_insert ( Vec :: new ( ) ) . push ( (
294+ constraint,
295+ def_id,
296+ if stable { "" } else { "unstable " } ,
297+ ) ) ;
298+ if !stable {
299+ unstable_suggestion = true ;
300+ }
301+ }
283302 } ) ;
284303
285304 let mut applicability = Applicability :: MachineApplicable ;
@@ -290,16 +309,21 @@ pub fn suggest_constraining_type_params<'a>(
290309 let Some ( param) = param else { return false } ;
291310
292311 {
293- let mut sized_constraints = constraints. extract_if ( |( _, def_id) | {
312+ let mut sized_constraints = constraints. extract_if ( |( _, def_id, _ ) | {
294313 def_id. is_some_and ( |def_id| tcx. is_lang_item ( def_id, LangItem :: Sized ) )
295314 } ) ;
296- if let Some ( ( _, def_id) ) = sized_constraints. next ( ) {
315+ if let Some ( ( _, def_id, _ ) ) = sized_constraints. next ( ) {
297316 applicability = Applicability :: MaybeIncorrect ;
298317
299318 err. span_label ( param. span , "this type parameter needs to be `Sized`" ) ;
300319 suggest_changing_unsized_bound ( generics, & mut suggestions, param, def_id) ;
301320 }
302321 }
322+ let bound_message = if constraints. iter ( ) . any ( |( _, def_id, _) | def_id. is_none ( ) ) {
323+ SuggestChangingConstraintsMessage :: RestrictBoundFurther
324+ } else {
325+ SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty : param_name }
326+ } ;
303327
304328 // in the scenario like impl has stricter requirements than trait,
305329 // we should not suggest restrict bound on the impl, here we double check
@@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>(
312336 . collect ( ) ;
313337
314338 constraints
315- . retain ( |( _, def_id) | def_id. map_or ( true , |def| !bound_trait_defs. contains ( & def) ) ) ;
339+ . retain ( |( _, def_id, _ ) | def_id. map_or ( true , |def| !bound_trait_defs. contains ( & def) ) ) ;
316340
317341 if constraints. is_empty ( ) {
318342 continue ;
319343 }
320344
321- let mut constraint = constraints. iter ( ) . map ( |& ( c, _) | c) . collect :: < Vec < _ > > ( ) ;
345+ let mut constraint = constraints. iter ( ) . map ( |& ( c, _, _ ) | c) . collect :: < Vec < _ > > ( ) ;
322346 constraint. sort ( ) ;
323347 constraint. dedup ( ) ;
348+ let all_known = constraints. iter ( ) . all ( |& ( _, def_id, _) | def_id. is_some ( ) ) ;
349+ let all_stable = constraints. iter ( ) . all ( |& ( _, _, stable) | stable. is_empty ( ) ) ;
350+ let all_unstable = constraints. iter ( ) . all ( |& ( _, _, stable) | !stable. is_empty ( ) ) ;
351+ let post = if all_stable || all_unstable {
352+ // Don't redundantly say "trait `X`, trait `Y`", instead "traits `X` and `Y`"
353+ let mut trait_names = constraints
354+ . iter ( )
355+ . map ( |& ( c, def_id, _) | match def_id {
356+ None => format ! ( "`{c}`" ) ,
357+ Some ( def_id) => format ! ( "`{}`" , tcx. item_name( def_id) ) ,
358+ } )
359+ . collect :: < Vec < _ > > ( ) ;
360+ trait_names. sort ( ) ;
361+ trait_names. dedup ( ) ;
362+ let n = trait_names. len ( ) ;
363+ let stable = if all_stable { "" } else { "unstable " } ;
364+ let trait_ = if all_known { format ! ( "trait{}" , pluralize!( n) ) } else { String :: new ( ) } ;
365+ format ! ( "{stable}{trait_}{}" , match & trait_names[ ..] {
366+ [ t] => format!( " {t}" ) ,
367+ [ ts @ .., last] => format!( " {} and {last}" , ts. join( ", " ) ) ,
368+ [ ] => return false ,
369+ } , )
370+ } else {
371+ // We're more explicit when there's a mix of stable and unstable traits.
372+ let mut trait_names = constraints
373+ . iter ( )
374+ . map ( |& ( c, def_id, stable) | match def_id {
375+ None => format ! ( "`{c}`" ) ,
376+ Some ( def_id) => format ! ( "{stable}trait `{}`" , tcx. item_name( def_id) ) ,
377+ } )
378+ . collect :: < Vec < _ > > ( ) ;
379+ trait_names. sort ( ) ;
380+ trait_names. dedup ( ) ;
381+ match & trait_names[ ..] {
382+ [ t] => t. to_string ( ) ,
383+ [ ts @ .., last] => format ! ( "{} and {last}" , ts. join( ", " ) ) ,
384+ [ ] => return false ,
385+ }
386+ } ;
324387 let constraint = constraint. join ( " + " ) ;
325388 let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
326389 let suggestion = if span_to_replace. is_some ( ) {
@@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>(
333396 format ! ( " {constraint}" )
334397 } ;
335398
336- use SuggestChangingConstraintsMessage :: RestrictBoundFurther ;
337-
338399 if let Some ( open_paren_sp) = open_paren_sp {
339- suggestions. push ( ( open_paren_sp, "(" . to_string ( ) , RestrictBoundFurther ) ) ;
340- suggestions. push ( ( span, format ! ( "){suggestion}" ) , RestrictBoundFurther ) ) ;
400+ suggestions. push ( ( open_paren_sp, post . clone ( ) , "(" . to_string ( ) , bound_message ) ) ;
401+ suggestions. push ( ( span, post . clone ( ) , format ! ( "){suggestion}" ) , bound_message ) ) ;
341402 } else {
342- suggestions. push ( ( span, suggestion, RestrictBoundFurther ) ) ;
403+ suggestions. push ( ( span, post . clone ( ) , suggestion, bound_message ) ) ;
343404 }
344405 } ;
345406
@@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>(
397458 // - insert: `, X: Bar`
398459 suggestions. push ( (
399460 generics. tail_span_for_predicate_suggestion ( ) ,
400- constraints. iter ( ) . fold ( String :: new ( ) , |mut string, & ( constraint, _) | {
461+ post,
462+ constraints. iter ( ) . fold ( String :: new ( ) , |mut string, & ( constraint, _, _) | {
401463 write ! ( string, ", {param_name}: {constraint}" ) . unwrap ( ) ;
402464 string
403465 } ) ,
@@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>(
426488 // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
427489 suggestions. push ( (
428490 generics. tail_span_for_predicate_suggestion ( ) ,
491+ post,
429492 format ! ( "{where_prefix} {param_name}: {constraint}" ) ,
430493 SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty : param_name } ,
431494 ) ) ;
@@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>(
439502 if let Some ( colon_span) = param. colon_span {
440503 suggestions. push ( (
441504 colon_span. shrink_to_hi ( ) ,
505+ post,
442506 format ! ( " {constraint}" ) ,
443507 SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
444508 ) ) ;
@@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>(
451515 // - help: consider restricting this type parameter with `T: Foo`
452516 suggestions. push ( (
453517 param. span . shrink_to_hi ( ) ,
518+ post,
454519 format ! ( ": {constraint}" ) ,
455520 SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
456521 ) ) ;
@@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>(
459524 // FIXME: remove the suggestions that are from derive, as the span is not correct
460525 suggestions = suggestions
461526 . into_iter ( )
462- . filter ( |( span, _, _) | !span. in_derive_expansion ( ) )
527+ . filter ( |( span, _, _, _ ) | !span. in_derive_expansion ( ) )
463528 . collect :: < Vec < _ > > ( ) ;
464-
529+ let suggested = !suggestions . is_empty ( ) ;
465530 if suggestions. len ( ) == 1 {
466- let ( span, suggestion, msg) = suggestions. pop ( ) . unwrap ( ) ;
531+ let ( span, post , suggestion, msg) = suggestions. pop ( ) . unwrap ( ) ;
467532 let msg = match msg {
468533 SuggestChangingConstraintsMessage :: RestrictBoundFurther => {
469- Cow :: from ( "consider further restricting this bound" )
534+ format ! ( "consider further restricting this bound" )
535+ }
536+ SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty }
537+ | SuggestChangingConstraintsMessage :: RestrictType { ty }
538+ if ty. starts_with ( "impl " ) =>
539+ {
540+ format ! ( "consider restricting opaque type `{ty}` with {post}" )
470541 }
471542 SuggestChangingConstraintsMessage :: RestrictType { ty } => {
472- Cow :: from ( format ! ( "consider restricting type parameter `{ty}`" ) )
543+ format ! ( "consider restricting type parameter `{ty}` with {post}" )
473544 }
474545 SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty } => {
475- Cow :: from ( format ! ( "consider further restricting type parameter `{ty}`" ) )
546+ format ! ( "consider further restricting type parameter `{ty}` with {post}" )
476547 }
477548 SuggestChangingConstraintsMessage :: RemoveMaybeUnsized => {
478- Cow :: from ( "consider removing the `?Sized` bound to make the type parameter `Sized`" )
549+ format ! ( "consider removing the `?Sized` bound to make the type parameter `Sized`" )
479550 }
480551 SuggestChangingConstraintsMessage :: ReplaceMaybeUnsizedWithSized => {
481- Cow :: from ( "consider replacing `?Sized` with `Sized`" )
552+ format ! ( "consider replacing `?Sized` with `Sized`" )
482553 }
483554 } ;
484555
485556 err. span_suggestion_verbose ( span, msg, suggestion, applicability) ;
486557 } else if suggestions. len ( ) > 1 {
558+ let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" } ;
487559 err. multipart_suggestion_verbose (
488- "consider restricting type parameters" ,
489- suggestions. into_iter ( ) . map ( |( span, suggestion, _) | ( span, suggestion) ) . collect ( ) ,
560+ format ! ( "consider restricting type parameters{post}" ) ,
561+ suggestions. into_iter ( ) . map ( |( span, _ , suggestion, _) | ( span, suggestion) ) . collect ( ) ,
490562 applicability,
491563 ) ;
492564 }
493565
494- true
566+ suggested
495567}
496568
497569/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
0 commit comments