@@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
523523 if self . not_enough_args_provided ( ) {
524524 self . suggest_adding_args ( err) ;
525525 } else if self . too_many_args_provided ( ) {
526+ self . suggest_moving_args_from_assoc_fn_to_trait ( err) ;
526527 self . suggest_removing_args_or_generics ( err) ;
527528 } else {
528529 unreachable ! ( ) ;
@@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
653654 }
654655 }
655656
657+ /// Suggests moving redundant argument(s) of an associate function to the
658+ /// trait it belongs to.
659+ ///
660+ /// ```compile_fail
661+ /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
662+ /// ```
663+ fn suggest_moving_args_from_assoc_fn_to_trait ( & self , err : & mut Diagnostic ) {
664+ let trait_ = match self . tcx . trait_of_item ( self . def_id ) {
665+ Some ( def_id) => def_id,
666+ None => return ,
667+ } ;
668+
669+ // Skip suggestion when the associated function is itself generic, it is unclear
670+ // how to split the provided parameters between those to suggest to the trait and
671+ // those to remain on the associated type.
672+ let num_assoc_fn_expected_args =
673+ self . num_expected_type_or_const_args ( ) + self . num_expected_lifetime_args ( ) ;
674+ if num_assoc_fn_expected_args > 0 {
675+ return ;
676+ }
677+
678+ let num_assoc_fn_excess_args =
679+ self . num_excess_type_or_const_args ( ) + self . num_excess_lifetime_args ( ) ;
680+
681+ let trait_generics = self . tcx . generics_of ( trait_) ;
682+ let num_trait_generics_except_self =
683+ trait_generics. count ( ) - if trait_generics. has_self { 1 } else { 0 } ;
684+
685+ let msg = format ! (
686+ "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}" ,
687+ these = pluralize!( "this" , num_assoc_fn_excess_args) ,
688+ s = pluralize!( num_assoc_fn_excess_args) ,
689+ name = self . tcx. item_name( trait_) ,
690+ num = num_trait_generics_except_self,
691+ ) ;
692+
693+ if let Some ( hir_id) = self . path_segment . hir_id
694+ && let Some ( parent_node) = self . tcx . hir ( ) . find_parent_node ( hir_id)
695+ && let Some ( parent_node) = self . tcx . hir ( ) . find ( parent_node)
696+ && let hir:: Node :: Expr ( expr) = parent_node {
697+ match expr. kind {
698+ hir:: ExprKind :: Path ( ref qpath) => {
699+ self . suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path (
700+ err,
701+ qpath,
702+ msg,
703+ num_assoc_fn_excess_args,
704+ num_trait_generics_except_self
705+ )
706+ } ,
707+ hir:: ExprKind :: MethodCall ( ..) => {
708+ self . suggest_moving_args_from_assoc_fn_to_trait_for_method_call (
709+ err,
710+ trait_,
711+ expr,
712+ msg,
713+ num_assoc_fn_excess_args,
714+ num_trait_generics_except_self
715+ )
716+ } ,
717+ _ => return ,
718+ }
719+ }
720+ }
721+
722+ fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path (
723+ & self ,
724+ err : & mut Diagnostic ,
725+ qpath : & ' tcx hir:: QPath < ' tcx > ,
726+ msg : String ,
727+ num_assoc_fn_excess_args : usize ,
728+ num_trait_generics_except_self : usize ,
729+ ) {
730+ if let hir:: QPath :: Resolved ( _, path) = qpath
731+ && let Some ( trait_path_segment) = path. segments . get ( 0 ) {
732+ let num_generic_args_supplied_to_trait = trait_path_segment. args ( ) . num_generic_params ( ) ;
733+
734+ if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
735+ if let Some ( span) = self . gen_args . span_ext ( )
736+ && let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
737+ let sugg = vec ! [
738+ ( self . path_segment. ident. span, format!( "{}::{}" , snippet, self . path_segment. ident) ) ,
739+ ( span. with_lo( self . path_segment. ident. span. hi( ) ) , "" . to_owned( ) )
740+ ] ;
741+
742+ err. multipart_suggestion (
743+ msg,
744+ sugg,
745+ Applicability :: MaybeIncorrect
746+ ) ;
747+ }
748+ }
749+ }
750+ }
751+
752+ fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call (
753+ & self ,
754+ err : & mut Diagnostic ,
755+ trait_ : DefId ,
756+ expr : & ' tcx hir:: Expr < ' tcx > ,
757+ msg : String ,
758+ num_assoc_fn_excess_args : usize ,
759+ num_trait_generics_except_self : usize ,
760+ ) {
761+ if let hir:: ExprKind :: MethodCall ( _, args, _) = expr. kind {
762+ assert_eq ! ( args. len( ) , 1 ) ;
763+ if num_assoc_fn_excess_args == num_trait_generics_except_self {
764+ if let Some ( gen_args) = self . gen_args . span_ext ( )
765+ && let Ok ( gen_args) = self . tcx . sess . source_map ( ) . span_to_snippet ( gen_args)
766+ && let Ok ( args) = self . tcx . sess . source_map ( ) . span_to_snippet ( args[ 0 ] . span ) {
767+ let sugg = format ! ( "{}::{}::{}({})" , self . tcx. item_name( trait_) , gen_args, self . tcx. item_name( self . def_id) , args) ;
768+ err. span_suggestion ( expr. span , msg, sugg, Applicability :: MaybeIncorrect ) ;
769+ }
770+ }
771+ }
772+ }
773+
656774 /// Suggests to remove redundant argument(s):
657775 ///
658776 /// ```text
0 commit comments