@@ -15,7 +15,9 @@ use rustc_hir as hir;
15
15
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
16
16
use rustc_infer:: traits:: FulfillmentError ;
17
17
use 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
+ } ;
19
21
use rustc_session:: parse:: feature_err;
20
22
use rustc_span:: edit_distance:: find_best_match_for_name;
21
23
use rustc_span:: symbol:: { sym, Ident } ;
@@ -1029,12 +1031,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
1029
1031
/// Emits an error regarding forbidden type binding associations
1030
1032
pub fn prohibit_assoc_item_binding (
1031
1033
tcx : TyCtxt < ' _ > ,
1032
- span : Span ,
1033
- segment : Option < ( & hir:: PathSegment < ' _ > , Span ) > ,
1034
+ binding : & hir :: TypeBinding < ' _ > ,
1035
+ segment : Option < ( DefId , & hir:: PathSegment < ' _ > , Span ) > ,
1034
1036
) {
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
1038
1040
&& segment. args ( ) . parenthesized == hir:: GenericArgsParentheses :: ParenSugar
1039
1041
{
1040
1042
Some ( ParenthesizedFnTraitExpansion {
@@ -1045,6 +1047,110 @@ pub fn prohibit_assoc_item_binding(
1045
1047
None
1046
1048
} ,
1047
1049
} ) ;
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 ( ) ;
1048
1154
}
1049
1155
1050
1156
pub ( crate ) fn fn_trait_to_string (
0 commit comments