@@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
523
523
if self . not_enough_args_provided ( ) {
524
524
self . suggest_adding_args ( err) ;
525
525
} else if self . too_many_args_provided ( ) {
526
+ self . suggest_moving_args_from_assoc_fn_to_trait ( err) ;
526
527
self . suggest_removing_args_or_generics ( err) ;
527
528
} else {
528
529
unreachable ! ( ) ;
@@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
653
654
}
654
655
}
655
656
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
+
656
774
/// Suggests to remove redundant argument(s):
657
775
///
658
776
/// ```text
0 commit comments