@@ -17,6 +17,7 @@ use rustc_hir::def::{DefKind, Res};
17
17
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
18
18
use rustc_infer:: traits:: FulfillmentError ;
19
19
use rustc_middle:: query:: Key ;
20
+ use rustc_middle:: ty:: GenericParamDefKind ;
20
21
use rustc_middle:: ty:: { self , suggest_constraining_type_param} ;
21
22
use rustc_middle:: ty:: { AdtDef , Ty , TyCtxt , TypeVisitableExt } ;
22
23
use rustc_middle:: ty:: { Binder , TraitRef } ;
@@ -1200,12 +1201,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
1200
1201
/// Emits an error regarding forbidden type binding associations
1201
1202
pub fn prohibit_assoc_item_binding (
1202
1203
tcx : TyCtxt < ' _ > ,
1203
- span : Span ,
1204
- segment : Option < ( & hir:: PathSegment < ' _ > , Span ) > ,
1204
+ binding : & hir :: TypeBinding < ' _ > ,
1205
+ segment : Option < ( DefId , & hir:: PathSegment < ' _ > , Span ) > ,
1205
1206
) -> ErrorGuaranteed {
1206
- tcx. dcx ( ) . emit_err ( AssocTypeBindingNotAllowed {
1207
- span,
1208
- fn_trait_expansion : if let Some ( ( segment, span) ) = segment
1207
+ let mut err = tcx. dcx ( ) . create_err ( AssocTypeBindingNotAllowed {
1208
+ span : binding . span ,
1209
+ fn_trait_expansion : if let Some ( ( _ , segment, span) ) = segment
1209
1210
&& segment. args ( ) . parenthesized == hir:: GenericArgsParentheses :: ParenSugar
1210
1211
{
1211
1212
Some ( ParenthesizedFnTraitExpansion {
@@ -1215,7 +1216,109 @@ pub fn prohibit_assoc_item_binding(
1215
1216
} else {
1216
1217
None
1217
1218
} ,
1218
- } )
1219
+ } ) ;
1220
+
1221
+ // Emit a suggestion to turn the assoc item binding into a generic arg
1222
+ // if the relevant item has a generic param whose name matches the binding name;
1223
+ // otherwise suggest the removal of the binding.
1224
+ if let Some ( ( def_id, segment, _) ) = segment
1225
+ && segment. args ( ) . parenthesized == hir:: GenericArgsParentheses :: No
1226
+ && let hir:: TypeBindingKind :: Equality { term } = binding. kind
1227
+ {
1228
+ // Suggests removal of the offending binding
1229
+ let suggest_removal = |e : & mut Diag < ' _ > | {
1230
+ let bindings = segment. args ( ) . bindings ;
1231
+ let args = segment. args ( ) . args ;
1232
+ let binding_span = binding. span ;
1233
+
1234
+ // Compute the span to remove based on the position
1235
+ // of the binding. We do that as follows:
1236
+ // 1. Find the index of the binding in the list of bindings
1237
+ // 2. Locate the spans preceding and following the binding.
1238
+ // If it's the first binding the preceding span would be
1239
+ // that of the last arg
1240
+ // 3. Using this information work out whether the span
1241
+ // to remove will start from the end of the preceding span,
1242
+ // the start of the next span or will simply be the
1243
+ // span encomassing everything within the generics brackets
1244
+
1245
+ let Some ( binding_index) = bindings. iter ( ) . position ( |b| b. hir_id == binding. hir_id )
1246
+ else {
1247
+ bug ! ( "a type binding exists but its HIR ID not found in generics" ) ;
1248
+ } ;
1249
+
1250
+ let preceding_span = if binding_index > 0 {
1251
+ Some ( bindings[ binding_index - 1 ] . span )
1252
+ } else {
1253
+ args. last ( ) . map ( |a| a. span ( ) )
1254
+ } ;
1255
+
1256
+ let next_span = if binding_index < bindings. len ( ) - 1 {
1257
+ Some ( bindings[ binding_index + 1 ] . span )
1258
+ } else {
1259
+ None
1260
+ } ;
1261
+
1262
+ let removal_span = match ( preceding_span, next_span) {
1263
+ ( Some ( prec) , _) => binding_span. with_lo ( prec. hi ( ) ) ,
1264
+ ( None , Some ( next) ) => binding_span. with_hi ( next. lo ( ) ) ,
1265
+ ( None , None ) => {
1266
+ let Some ( generics_span) = segment. args ( ) . span_ext ( ) else {
1267
+ bug ! ( "a type binding exists but generic span is empty" ) ;
1268
+ } ;
1269
+
1270
+ generics_span
1271
+ }
1272
+ } ;
1273
+
1274
+ // Now emit the suggestion
1275
+ if let Ok ( suggestion) = tcx. sess . source_map ( ) . span_to_snippet ( removal_span) {
1276
+ e. span_suggestion_verbose (
1277
+ removal_span,
1278
+ "consider removing this type binding" ,
1279
+ suggestion,
1280
+ Applicability :: MaybeIncorrect ,
1281
+ ) ;
1282
+ }
1283
+ } ;
1284
+
1285
+ // Suggest replacing the associated item binding with a generic argument.
1286
+ // i.e., replacing `<..., T = A, ...>` with `<..., A, ...>`.
1287
+ let suggest_direct_use = |e : & mut Diag < ' _ > , sp : Span | {
1288
+ if let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( sp) {
1289
+ e. span_suggestion_verbose (
1290
+ binding. span ,
1291
+ format ! ( "to use `{snippet}` as a generic argument specify it directly" ) ,
1292
+ snippet,
1293
+ Applicability :: MaybeIncorrect ,
1294
+ ) ;
1295
+ }
1296
+ } ;
1297
+
1298
+ // Check if the type has a generic param with the
1299
+ // same name as the assoc type name in type binding
1300
+ let generics = tcx. generics_of ( def_id) ;
1301
+ let matching_param =
1302
+ generics. params . iter ( ) . find ( |p| p. name . as_str ( ) == binding. ident . as_str ( ) ) ;
1303
+
1304
+ // Now emit the appropriate suggestion
1305
+ if let Some ( matching_param) = matching_param {
1306
+ match ( & matching_param. kind , term) {
1307
+ ( GenericParamDefKind :: Type { .. } , hir:: Term :: Ty ( ty) ) => {
1308
+ suggest_direct_use ( & mut err, ty. span ) ;
1309
+ }
1310
+ ( GenericParamDefKind :: Const { .. } , hir:: Term :: Const ( c) ) => {
1311
+ let span = tcx. hir ( ) . span ( c. hir_id ) ;
1312
+ suggest_direct_use ( & mut err, span) ;
1313
+ }
1314
+ _ => suggest_removal ( & mut err) ,
1315
+ }
1316
+ } else {
1317
+ suggest_removal ( & mut err) ;
1318
+ }
1319
+ }
1320
+
1321
+ err. emit ( )
1219
1322
}
1220
1323
1221
1324
pub ( crate ) fn fn_trait_to_string (
0 commit comments