1
1
//! Diagnostics related methods for `Ty`.
2
2
3
- use std:: borrow:: Cow ;
4
3
use std:: fmt:: Write ;
5
4
use std:: ops:: ControlFlow ;
6
5
7
6
use 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
+ } ;
9
10
use rustc_hir:: def:: DefKind ;
10
11
use rustc_hir:: def_id:: DefId ;
11
12
use rustc_hir:: { self as hir, LangItem , PredicateOrigin , WherePredicateKind } ;
@@ -161,7 +162,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>(
161
162
true
162
163
}
163
164
164
- #[ derive( Debug ) ]
165
+ #[ derive( Debug , Clone , Copy ) ]
165
166
enum SuggestChangingConstraintsMessage < ' a > {
166
167
RestrictBoundFurther ,
167
168
RestrictType { ty : & ' a str } ,
@@ -172,7 +173,7 @@ enum SuggestChangingConstraintsMessage<'a> {
172
173
173
174
fn suggest_changing_unsized_bound (
174
175
generics : & hir:: Generics < ' _ > ,
175
- suggestions : & mut Vec < ( Span , String , SuggestChangingConstraintsMessage < ' _ > ) > ,
176
+ suggestions : & mut Vec < ( Span , String , String , SuggestChangingConstraintsMessage < ' _ > ) > ,
176
177
param : & hir:: GenericParam < ' _ > ,
177
178
def_id : Option < DefId > ,
178
179
) {
@@ -207,7 +208,8 @@ fn suggest_changing_unsized_bound(
207
208
continue ;
208
209
}
209
210
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) ) ;
211
213
212
214
if predicate. bounds . len ( ) == unsized_bounds. len ( ) {
213
215
// All the bounds are unsized bounds, e.g.
@@ -278,8 +280,25 @@ pub fn suggest_constraining_type_params<'a>(
278
280
span_to_replace : Option < Span > ,
279
281
) -> bool {
280
282
let mut grouped = FxHashMap :: default ( ) ;
283
+ let mut unstable_suggestion = false ;
281
284
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
+ }
283
302
} ) ;
284
303
285
304
let mut applicability = Applicability :: MachineApplicable ;
@@ -290,16 +309,21 @@ pub fn suggest_constraining_type_params<'a>(
290
309
let Some ( param) = param else { return false } ;
291
310
292
311
{
293
- let mut sized_constraints = constraints. extract_if ( |( _, def_id) | {
312
+ let mut sized_constraints = constraints. extract_if ( |( _, def_id, _ ) | {
294
313
def_id. is_some_and ( |def_id| tcx. is_lang_item ( def_id, LangItem :: Sized ) )
295
314
} ) ;
296
- if let Some ( ( _, def_id) ) = sized_constraints. next ( ) {
315
+ if let Some ( ( _, def_id, _ ) ) = sized_constraints. next ( ) {
297
316
applicability = Applicability :: MaybeIncorrect ;
298
317
299
318
err. span_label ( param. span , "this type parameter needs to be `Sized`" ) ;
300
319
suggest_changing_unsized_bound ( generics, & mut suggestions, param, def_id) ;
301
320
}
302
321
}
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
+ } ;
303
327
304
328
// in the scenario like impl has stricter requirements than trait,
305
329
// we should not suggest restrict bound on the impl, here we double check
@@ -312,15 +336,54 @@ pub fn suggest_constraining_type_params<'a>(
312
336
. collect ( ) ;
313
337
314
338
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) ) ) ;
316
340
317
341
if constraints. is_empty ( ) {
318
342
continue ;
319
343
}
320
344
321
- let mut constraint = constraints. iter ( ) . map ( |& ( c, _) | c) . collect :: < Vec < _ > > ( ) ;
345
+ let mut constraint = constraints. iter ( ) . map ( |& ( c, _, _ ) | c) . collect :: < Vec < _ > > ( ) ;
322
346
constraint. sort ( ) ;
323
347
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
+ } ;
324
387
let constraint = constraint. join ( " + " ) ;
325
388
let mut suggest_restrict = |span, bound_list_non_empty, open_paren_sp| {
326
389
let suggestion = if span_to_replace. is_some ( ) {
@@ -333,13 +396,11 @@ pub fn suggest_constraining_type_params<'a>(
333
396
format ! ( " {constraint}" )
334
397
} ;
335
398
336
- use SuggestChangingConstraintsMessage :: RestrictBoundFurther ;
337
-
338
399
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 ) ) ;
341
402
} else {
342
- suggestions. push ( ( span, suggestion, RestrictBoundFurther ) ) ;
403
+ suggestions. push ( ( span, post . clone ( ) , suggestion, bound_message ) ) ;
343
404
}
344
405
} ;
345
406
@@ -397,7 +458,8 @@ pub fn suggest_constraining_type_params<'a>(
397
458
// - insert: `, X: Bar`
398
459
suggestions. push ( (
399
460
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, _, _) | {
401
463
write ! ( string, ", {param_name}: {constraint}" ) . unwrap ( ) ;
402
464
string
403
465
} ) ,
@@ -426,6 +488,7 @@ pub fn suggest_constraining_type_params<'a>(
426
488
// default (`<T=Foo>`), so we suggest adding `where T: Bar`.
427
489
suggestions. push ( (
428
490
generics. tail_span_for_predicate_suggestion ( ) ,
491
+ post,
429
492
format ! ( "{where_prefix} {param_name}: {constraint}" ) ,
430
493
SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty : param_name } ,
431
494
) ) ;
@@ -439,6 +502,7 @@ pub fn suggest_constraining_type_params<'a>(
439
502
if let Some ( colon_span) = param. colon_span {
440
503
suggestions. push ( (
441
504
colon_span. shrink_to_hi ( ) ,
505
+ post,
442
506
format ! ( " {constraint}" ) ,
443
507
SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
444
508
) ) ;
@@ -451,6 +515,7 @@ pub fn suggest_constraining_type_params<'a>(
451
515
// - help: consider restricting this type parameter with `T: Foo`
452
516
suggestions. push ( (
453
517
param. span . shrink_to_hi ( ) ,
518
+ post,
454
519
format ! ( ": {constraint}" ) ,
455
520
SuggestChangingConstraintsMessage :: RestrictType { ty : param_name } ,
456
521
) ) ;
@@ -459,39 +524,46 @@ pub fn suggest_constraining_type_params<'a>(
459
524
// FIXME: remove the suggestions that are from derive, as the span is not correct
460
525
suggestions = suggestions
461
526
. into_iter ( )
462
- . filter ( |( span, _, _) | !span. in_derive_expansion ( ) )
527
+ . filter ( |( span, _, _, _ ) | !span. in_derive_expansion ( ) )
463
528
. collect :: < Vec < _ > > ( ) ;
464
-
529
+ let suggested = !suggestions . is_empty ( ) ;
465
530
if suggestions. len ( ) == 1 {
466
- let ( span, suggestion, msg) = suggestions. pop ( ) . unwrap ( ) ;
531
+ let ( span, post , suggestion, msg) = suggestions. pop ( ) . unwrap ( ) ;
467
532
let msg = match msg {
468
533
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}" )
470
541
}
471
542
SuggestChangingConstraintsMessage :: RestrictType { ty } => {
472
- Cow :: from ( format ! ( "consider restricting type parameter `{ty}`" ) )
543
+ format ! ( "consider restricting type parameter `{ty}` with {post}" )
473
544
}
474
545
SuggestChangingConstraintsMessage :: RestrictTypeFurther { ty } => {
475
- Cow :: from ( format ! ( "consider further restricting type parameter `{ty}`" ) )
546
+ format ! ( "consider further restricting type parameter `{ty}` with {post}" )
476
547
}
477
548
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`" )
479
550
}
480
551
SuggestChangingConstraintsMessage :: ReplaceMaybeUnsizedWithSized => {
481
- Cow :: from ( "consider replacing `?Sized` with `Sized`" )
552
+ format ! ( "consider replacing `?Sized` with `Sized`" )
482
553
}
483
554
} ;
484
555
485
556
err. span_suggestion_verbose ( span, msg, suggestion, applicability) ;
486
557
} else if suggestions. len ( ) > 1 {
558
+ let post = if unstable_suggestion { " (some of them are unstable traits)" } else { "" } ;
487
559
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 ( ) ,
490
562
applicability,
491
563
) ;
492
564
}
493
565
494
- true
566
+ suggested
495
567
}
496
568
497
569
/// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for.
0 commit comments