@@ -720,7 +720,7 @@ fn compare_synthetic_generics<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
720720 _trait_item_span : Option < Span > ) // FIXME necessary?
721721 -> Result < ( ) , ErrorReported > {
722722 // FIXME(chrisvittal) Clean up this function, list of FIXME items:
723- // 1. Better messages for the span lables
723+ // 1. Better messages for the span labels
724724 // 2. Explanation as to what is going on
725725 // 3. Correct the function signature for what we actually use
726726 // If we get here, we already have the same number of generics, so the zip will
@@ -751,8 +751,131 @@ fn compare_synthetic_generics<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
751751 E0643 ,
752752 "method `{}` has incompatible signature for trait" ,
753753 trait_m. name) ;
754- err. span_label ( trait_span, "annotation in trait" ) ;
755- err. span_label ( impl_span, "annotation in impl" ) ;
754+ err. span_label ( trait_span, "declaration in trait here" ) ;
755+ match ( impl_synthetic, trait_synthetic) {
756+ // The case where the impl method uses `impl Trait` but the trait method uses
757+ // explicit generics
758+ ( Some ( hir:: SyntheticTyParamKind :: ImplTrait ) , None ) => {
759+ err. span_label ( impl_span, "expected generic parameter, found `impl Trait`" ) ;
760+ ( || {
761+ // try taking the name from the trait impl
762+ // FIXME: this is obviously suboptimal since the name can already be used
763+ // as another generic argument
764+ let new_name = tcx
765+ . sess
766+ . codemap ( )
767+ . span_to_snippet ( trait_span)
768+ . ok ( ) ?;
769+ let trait_m = tcx. hir . as_local_node_id ( trait_m. def_id ) ?;
770+ let trait_m = tcx. hir . trait_item ( hir:: TraitItemId { node_id : trait_m } ) ;
771+
772+ let impl_m = tcx. hir . as_local_node_id ( impl_m. def_id ) ?;
773+ let impl_m = tcx. hir . impl_item ( hir:: ImplItemId { node_id : impl_m } ) ;
774+
775+ // in case there are no generics, take the spot between the function name
776+ // and the opening paren of the argument list
777+ let new_generics_span = tcx
778+ . sess
779+ . codemap ( )
780+ . generate_fn_name_span ( impl_m. span ) ?
781+ . shrink_to_hi ( ) ;
782+ // in case there are generics, just replace them
783+ let generics_span = impl_m
784+ . generics
785+ . span
786+ . substitute_dummy ( new_generics_span) ;
787+ // replace with the generics from the trait
788+ let new_generics = tcx
789+ . sess
790+ . codemap ( )
791+ . span_to_snippet ( trait_m. generics . span )
792+ . ok ( ) ?;
793+
794+ err. multipart_suggestion (
795+ "try changing the `impl Trait` argument to a generic parameter" ,
796+ vec ! [
797+ // replace `impl Trait` with `T`
798+ ( impl_span, new_name) ,
799+ // replace impl method generics with trait method generics
800+ // This isn't quite right, as users might have changed the names
801+ // of the generics, but it works for the common case
802+ ( generics_span, new_generics) ,
803+ ] ,
804+ ) ;
805+ Some ( ( ) )
806+ } ) ( ) ;
807+ } ,
808+ // The case where the trait method uses `impl Trait`, but the impl method uses
809+ // explicit generics.
810+ ( None , Some ( hir:: SyntheticTyParamKind :: ImplTrait ) ) => {
811+ err. span_label ( impl_span, "expected `impl Trait`, found generic parameter" ) ;
812+ ( || {
813+ let impl_m = tcx. hir . as_local_node_id ( impl_m. def_id ) ?;
814+ let impl_m = tcx. hir . impl_item ( hir:: ImplItemId { node_id : impl_m } ) ;
815+ let input_tys = match impl_m. node {
816+ hir:: ImplItemKind :: Method ( ref sig, _) => & sig. decl . inputs ,
817+ _ => unreachable ! ( ) ,
818+ } ;
819+ struct Visitor ( Option < Span > , hir:: def_id:: DefId ) ;
820+ impl < ' v > hir:: intravisit:: Visitor < ' v > for Visitor {
821+ fn visit_ty ( & mut self , ty : & ' v hir:: Ty ) {
822+ hir:: intravisit:: walk_ty ( self , ty) ;
823+ match ty. node {
824+ hir:: TyPath ( hir:: QPath :: Resolved ( None , ref path) ) => {
825+ if let hir:: def:: Def :: TyParam ( def_id) = path. def {
826+ if def_id == self . 1 {
827+ self . 0 = Some ( ty. span ) ;
828+ }
829+ }
830+ } ,
831+ _ => { }
832+ }
833+ }
834+ fn nested_visit_map < ' this > (
835+ & ' this mut self
836+ ) -> hir:: intravisit:: NestedVisitorMap < ' this , ' v > {
837+ hir:: intravisit:: NestedVisitorMap :: None
838+ }
839+ }
840+ let mut visitor = Visitor ( None , impl_def_id) ;
841+ for ty in input_tys {
842+ hir:: intravisit:: Visitor :: visit_ty ( & mut visitor, ty) ;
843+ }
844+ let span = visitor. 0 ?;
845+
846+ let param = impl_m. generics . params . iter ( ) . filter_map ( |param| {
847+ match param {
848+ hir:: GenericParam :: Type ( param) => {
849+ if param. id == impl_node_id {
850+ Some ( param)
851+ } else {
852+ None
853+ }
854+ } ,
855+ hir:: GenericParam :: Lifetime ( ..) => None ,
856+ }
857+ } ) . next ( ) ?;
858+ let bounds = param. bounds . first ( ) ?. span ( ) . to ( param. bounds . last ( ) ?. span ( ) ) ;
859+ let bounds = tcx
860+ . sess
861+ . codemap ( )
862+ . span_to_snippet ( bounds)
863+ . ok ( ) ?;
864+
865+ err. multipart_suggestion (
866+ "try removing the generic parameter and using `impl Trait` instead" ,
867+ vec ! [
868+ // delete generic parameters
869+ ( impl_m. generics. span, String :: new( ) ) ,
870+ // replace param usage with `impl Trait`
871+ ( span, format!( "impl {}" , bounds) ) ,
872+ ] ,
873+ ) ;
874+ Some ( ( ) )
875+ } ) ( ) ;
876+ } ,
877+ _ => unreachable ! ( ) ,
878+ }
756879 err. emit ( ) ;
757880 error_found = true ;
758881 }
0 commit comments