@@ -6,9 +6,10 @@ use rustc_errors::{
6
6
use rustc_hir as hir;
7
7
use rustc_middle:: hir:: map:: fn_sig;
8
8
use rustc_middle:: middle:: resolve_lifetime:: LifetimeScopeForPath ;
9
- use rustc_middle:: ty:: { self as ty, TyCtxt } ;
9
+ use rustc_middle:: ty:: { self as ty, AssocItems , AssocKind , TyCtxt } ;
10
10
use rustc_session:: Session ;
11
11
use rustc_span:: def_id:: DefId ;
12
+ use std:: iter;
12
13
13
14
use GenericArgsInfo :: * ;
14
15
@@ -334,6 +335,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
334
335
. join ( ", " )
335
336
}
336
337
338
+ fn get_unbound_associated_types ( & self ) -> Vec < String > {
339
+ if self . tcx . is_trait ( self . def_id ) {
340
+ let items: & AssocItems < ' _ > = self . tcx . associated_items ( self . def_id ) ;
341
+ items
342
+ . in_definition_order ( )
343
+ . filter ( |item| item. kind == AssocKind :: Type )
344
+ . filter ( |item| {
345
+ !self . gen_args . bindings . iter ( ) . any ( |binding| binding. ident . name == item. name )
346
+ } )
347
+ . map ( |item| item. name . to_ident_string ( ) )
348
+ . collect ( )
349
+ } else {
350
+ Vec :: default ( )
351
+ }
352
+ }
353
+
337
354
fn create_error_message ( & self ) -> String {
338
355
let def_path = self . tcx . def_path_str ( self . def_id ) ;
339
356
let def_kind = self . tcx . def_kind ( self . def_id ) . descr ( self . def_id ) ;
@@ -618,6 +635,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
618
635
fn suggest_removing_args_or_generics ( & self , err : & mut Diagnostic ) {
619
636
let num_provided_lt_args = self . num_provided_lifetime_args ( ) ;
620
637
let num_provided_type_const_args = self . num_provided_type_or_const_args ( ) ;
638
+ let unbound_types = self . get_unbound_associated_types ( ) ;
621
639
let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
622
640
assert ! ( num_provided_args > 0 ) ;
623
641
@@ -629,6 +647,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
629
647
let redundant_type_or_const_args = num_redundant_type_or_const_args > 0 ;
630
648
631
649
let remove_entire_generics = num_redundant_args >= self . gen_args . args . len ( ) ;
650
+ let provided_args_matches_unbound_traits =
651
+ unbound_types. len ( ) == num_redundant_type_or_const_args;
632
652
633
653
let remove_lifetime_args = |err : & mut Diagnostic | {
634
654
let mut lt_arg_spans = Vec :: new ( ) ;
@@ -713,7 +733,28 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
713
733
) ;
714
734
} ;
715
735
716
- if remove_entire_generics {
736
+ // If there is a single unbound associated type and a single excess generic param
737
+ // suggest replacing the generic param with the associated type bound
738
+ if provided_args_matches_unbound_traits && !unbound_types. is_empty ( ) {
739
+ let mut suggestions = vec ! [ ] ;
740
+ let unused_generics = & self . gen_args . args [ self . num_expected_type_or_const_args ( ) ..] ;
741
+ for ( potential, name) in iter:: zip ( unused_generics, & unbound_types) {
742
+ if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( potential. span ( ) ) {
743
+ suggestions. push ( ( potential. span ( ) , format ! ( "{} = {}" , name, snippet) ) ) ;
744
+ }
745
+ }
746
+
747
+ if !suggestions. is_empty ( ) {
748
+ err. multipart_suggestion (
749
+ & format ! (
750
+ "replace the generic bound{s} with the associated type{s}" ,
751
+ s = pluralize!( unbound_types. len( ) )
752
+ ) ,
753
+ suggestions,
754
+ Applicability :: MaybeIncorrect ,
755
+ ) ;
756
+ }
757
+ } else if remove_entire_generics {
717
758
let span = self
718
759
. path_segment
719
760
. args
0 commit comments