@@ -525,6 +525,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
525525 self . suggest_adding_args ( err) ;
526526 } else if self . too_many_args_provided ( ) {
527527 self . suggest_removing_args_or_generics ( err) ;
528+ self . suggest_moving_args ( err) ;
528529 } else {
529530 unreachable ! ( ) ;
530531 }
@@ -654,6 +655,64 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
654655 }
655656 }
656657
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+
657716 /// Suggests to remove redundant argument(s):
658717 ///
659718 /// ```text
0 commit comments