@@ -17,6 +17,7 @@ use rustc_hir::def::{DefKind, Res};
1717use rustc_hir:: def_id:: { DefId , LocalDefId } ;
1818use rustc_infer:: traits:: FulfillmentError ;
1919use rustc_middle:: query:: Key ;
20+ use rustc_middle:: ty:: GenericParamDefKind ;
2021use rustc_middle:: ty:: { self , suggest_constraining_type_param} ;
2122use rustc_middle:: ty:: { AdtDef , Ty , TyCtxt , TypeVisitableExt } ;
2223use rustc_middle:: ty:: { Binder , TraitRef } ;
@@ -1200,12 +1201,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
12001201/// Emits an error regarding forbidden type binding associations
12011202pub fn prohibit_assoc_item_binding (
12021203 tcx : TyCtxt < ' _ > ,
1203- span : Span ,
1204- segment : Option < ( & hir:: PathSegment < ' _ > , Span ) > ,
1204+ binding : & hir :: TypeBinding < ' _ > ,
1205+ segment : Option < ( DefId , & hir:: PathSegment < ' _ > , Span ) > ,
12051206) -> 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
12091210 && segment. args ( ) . parenthesized == hir:: GenericArgsParentheses :: ParenSugar
12101211 {
12111212 Some ( ParenthesizedFnTraitExpansion {
@@ -1215,7 +1216,109 @@ pub fn prohibit_assoc_item_binding(
12151216 } else {
12161217 None
12171218 } ,
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 ( )
12191322}
12201323
12211324pub ( crate ) fn fn_trait_to_string (
0 commit comments