@@ -15,7 +15,9 @@ use rustc_hir as hir;
1515use rustc_hir:: def_id:: { DefId , LocalDefId } ;
1616use rustc_infer:: traits:: FulfillmentError ;
1717use rustc_middle:: query:: Key ;
18- use rustc_middle:: ty:: { self , suggest_constraining_type_param, Ty , TyCtxt , TypeVisitableExt } ;
18+ use rustc_middle:: ty:: {
19+ self , suggest_constraining_type_param, GenericParamDefKind , Ty , TyCtxt , TypeVisitableExt ,
20+ } ;
1921use rustc_session:: parse:: feature_err;
2022use rustc_span:: edit_distance:: find_best_match_for_name;
2123use rustc_span:: symbol:: { sym, Ident } ;
@@ -1029,12 +1031,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
10291031/// Emits an error regarding forbidden type binding associations
10301032pub fn prohibit_assoc_item_binding (
10311033 tcx : TyCtxt < ' _ > ,
1032- span : Span ,
1033- segment : Option < ( & hir:: PathSegment < ' _ > , Span ) > ,
1034+ binding : & hir :: TypeBinding < ' _ > ,
1035+ segment : Option < ( DefId , & hir:: PathSegment < ' _ > , Span ) > ,
10341036) {
1035- tcx. dcx ( ) . emit_err ( AssocTypeBindingNotAllowed {
1036- span,
1037- fn_trait_expansion : if let Some ( ( segment, span) ) = segment
1037+ let mut err = tcx. dcx ( ) . create_err ( AssocTypeBindingNotAllowed {
1038+ span : binding . span ,
1039+ fn_trait_expansion : if let Some ( ( _ , segment, span) ) = segment
10381040 && segment. args ( ) . parenthesized == hir:: GenericArgsParentheses :: ParenSugar
10391041 {
10401042 Some ( ParenthesizedFnTraitExpansion {
@@ -1045,6 +1047,110 @@ pub fn prohibit_assoc_item_binding(
10451047 None
10461048 } ,
10471049 } ) ;
1050+
1051+ // Emit a suggestion to use the type arg directly
1052+ // if there's a generic param with a matching name
1053+ // otherwise suggest removal of type binding
1054+ if let Some ( ( def_id, segment, _) ) = segment
1055+ && segment. args ( ) . parenthesized == hir:: GenericArgsParentheses :: No
1056+ && let hir:: TypeBindingKind :: Equality { term } = binding. kind
1057+ {
1058+ // Suggests removal of the offending binding
1059+ let suggest_removal = |e : & mut Diag < ' _ > | {
1060+ let bindings = segment. args ( ) . bindings ;
1061+ let args = segment. args ( ) . args ;
1062+ let binding_span = binding. span ;
1063+
1064+ // Compute the span to remove based on the the position
1065+ // of the binding. We do that as follows:
1066+ // 1. Find the index of the binding in the list of bindings
1067+ // 2. Locate the spans preceding and following the binding.
1068+ // If it's the first binding the preceding span would be
1069+ // that of the last arg
1070+ // 3. Using this information work out whether the span
1071+ // to remove will start from the end of the preceding span,
1072+ // end at the start of the next span or will simply be the
1073+ // span encomassing everything within the generics brackets
1074+
1075+ let Some ( binding_index) = bindings. iter ( ) . position ( |b| b. hir_id == binding. hir_id )
1076+ else {
1077+ bug ! ( "a type binding exists but its HIR ID not found in generics" ) ;
1078+ } ;
1079+
1080+ let preceding_span = if binding_index > 0 {
1081+ Some ( bindings[ binding_index - 1 ] . span )
1082+ } else {
1083+ args. last ( ) . map ( |a| a. span ( ) )
1084+ } ;
1085+
1086+ let next_span = if binding_index < bindings. len ( ) - 1 {
1087+ Some ( bindings[ binding_index + 1 ] . span )
1088+ } else {
1089+ None
1090+ } ;
1091+
1092+ let removal_span = match ( preceding_span, next_span) {
1093+ ( Some ( prec) , _) => binding_span. with_lo ( prec. hi ( ) ) ,
1094+ ( None , Some ( next) ) => binding_span. with_hi ( next. lo ( ) ) ,
1095+ ( None , None ) => {
1096+ let Some ( generics_span) = segment. args ( ) . span_ext ( ) else {
1097+ bug ! ( "a type binding exists but generic span is empty" ) ;
1098+ } ;
1099+
1100+ generics_span
1101+ }
1102+ } ;
1103+
1104+ // Now emit the suggestion
1105+ if let Ok ( suggestion) = tcx. sess . source_map ( ) . span_to_snippet ( removal_span) {
1106+ e. span_suggestion_verbose (
1107+ removal_span,
1108+ "consider removing this type binding" ,
1109+ suggestion,
1110+ Applicability :: MaybeIncorrect ,
1111+ ) ;
1112+ }
1113+ } ;
1114+
1115+ // Suggests replacing the type binding with normal
1116+ // type argument (i.e. replacing <T = A> with <T>)
1117+ let suggest_direct_use = |e : & mut Diag < ' _ > , sp : Span , is_ty : bool | {
1118+ if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( sp) {
1119+ let ty_or_const = if is_ty { "generic" } else { "const" } ;
1120+ e. span_suggestion_verbose (
1121+ binding. span ,
1122+ format ! ( "to use `{snippet}` as a {ty_or_const} argument specify it directly" ) ,
1123+ snippet,
1124+ Applicability :: MaybeIncorrect ,
1125+ ) ;
1126+ }
1127+ } ;
1128+
1129+ // Check if the type has a generic param with the
1130+ // same name as the assoc type name in type binding
1131+ let generics = tcx. generics_of ( def_id) ;
1132+ let binding_ident_lower = binding. ident . as_str ( ) . to_lowercase ( ) ;
1133+ let matching_param =
1134+ generics. params . iter ( ) . find ( |p| p. name . as_str ( ) . to_lowercase ( ) == binding_ident_lower) ;
1135+
1136+ // Now emit the appropriate suggestion
1137+ if let Some ( matching_param) = matching_param {
1138+ match ( & matching_param. kind , term) {
1139+ ( GenericParamDefKind :: Type { .. } , hir:: Term :: Ty ( ty) ) => {
1140+ suggest_direct_use ( & mut err, ty. span , true ) ;
1141+ }
1142+ ( GenericParamDefKind :: Const { .. } , hir:: Term :: Const ( c) ) => {
1143+ let span = tcx. hir ( ) . span ( c. hir_id ) ;
1144+ suggest_direct_use ( & mut err, span, false ) ;
1145+ }
1146+ _ => suggest_removal ( & mut err) ,
1147+ }
1148+ } else {
1149+ suggest_removal ( & mut err) ;
1150+ }
1151+ }
1152+
1153+ err. emit ( ) ;
10481154}
10491155
10501156pub ( crate ) fn fn_trait_to_string (
0 commit comments