@@ -525,6 +525,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
525
525
self . suggest_adding_args ( err) ;
526
526
} else if self . too_many_args_provided ( ) {
527
527
self . suggest_removing_args_or_generics ( err) ;
528
+ self . suggest_moving_args ( err) ;
528
529
} else {
529
530
unreachable ! ( ) ;
530
531
}
@@ -654,6 +655,64 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
654
655
}
655
656
}
656
657
658
+ /// Suggests moving redundant argument(s) of an associate function to the
659
+ /// trait it belongs to.
660
+ ///
661
+ /// ```compile_fail
662
+ /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
663
+ /// ```
664
+ fn suggest_moving_args ( & self , err : & mut Diagnostic ) {
665
+ if let Some ( trait_) = self . tcx . trait_of_item ( self . def_id ) {
666
+ // HACK(hkmatsumoto): Ugly way to tell "<trait>::<assoc fn>()" from "x.<assoc fn>()";
667
+ // we don't care the latter (for now).
668
+ if self . path_segment . res == Some ( hir:: def:: Res :: Err ) {
669
+ return ;
670
+ }
671
+
672
+ // Say, if the assoc fn takes `A`, `B` and `C` as generic arguments while expecting 1
673
+ // argument, and its trait expects 2 arguments. It is hard to "split" them right as
674
+ // there are too many cases to handle: `A` `B` | `C`, `A` `B` | `C`, `A` `C` | `B`, ...
675
+ let num_assoc_fn_expected_args =
676
+ self . num_expected_type_or_const_args ( ) + self . num_expected_lifetime_args ( ) ;
677
+ if num_assoc_fn_expected_args > 0 {
678
+ return ;
679
+ }
680
+
681
+ let num_assoc_fn_excess_args =
682
+ self . num_excess_type_or_const_args ( ) + self . num_excess_lifetime_args ( ) ;
683
+
684
+ let trait_generics = self . tcx . generics_of ( trait_) ;
685
+ let num_trait_generics_except_self =
686
+ trait_generics. count ( ) - if trait_generics. has_self { 1 } else { 0 } ;
687
+
688
+ // FIXME(hkmatsumoto): RHS of this condition ideally should be
689
+ // `num_trait_generics_except_self` - "# of generic args already provided to trait"
690
+ // but unable to get that information with `self.def_id`.
691
+ if num_assoc_fn_excess_args == num_trait_generics_except_self {
692
+ if let Some ( span) = self . gen_args . span_ext ( )
693
+ && let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
694
+ let msg = format ! (
695
+ "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}" ,
696
+ these = pluralize!( "this" , num_assoc_fn_excess_args) ,
697
+ s = pluralize!( num_assoc_fn_excess_args) ,
698
+ name = self . tcx. item_name( trait_) ,
699
+ num = num_trait_generics_except_self,
700
+ ) ;
701
+ let sugg = vec ! [
702
+ ( self . path_segment. ident. span, format!( "{}::{}" , snippet, self . path_segment. ident) ) ,
703
+ ( span. with_lo( self . path_segment. ident. span. hi( ) ) , "" . to_owned( ) )
704
+ ] ;
705
+
706
+ err. multipart_suggestion (
707
+ msg,
708
+ sugg,
709
+ Applicability :: MaybeIncorrect
710
+ ) ;
711
+ }
712
+ }
713
+ }
714
+ }
715
+
657
716
/// Suggests to remove redundant argument(s):
658
717
///
659
718
/// ```text
0 commit comments