@@ -13,9 +13,14 @@ use std::assert_matches::debug_assert_matches;
13
13
use min_specialization:: check_min_specialization;
14
14
use rustc_data_structures:: fx:: FxHashSet ;
15
15
use rustc_errors:: codes:: * ;
16
+ use rustc_errors:: { Applicability , Diag } ;
16
17
use rustc_hir:: def:: DefKind ;
17
18
use rustc_hir:: def_id:: LocalDefId ;
18
- use rustc_middle:: ty:: { self , TyCtxt , TypeVisitableExt } ;
19
+ use rustc_hir:: {
20
+ GenericParam , GenericParamKind , LifetimeParamKind , Path , PredicateOrigin , QPath , Ty , TyKind ,
21
+ WhereBoundPredicate , WherePredicateKind ,
22
+ } ;
23
+ use rustc_middle:: ty:: { self , GenericParamDef , TyCtxt , TypeVisitableExt } ;
19
24
use rustc_span:: ErrorGuaranteed ;
20
25
21
26
use crate :: constrained_generic_params as cgp;
@@ -140,6 +145,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
140
145
const_param_note2 : false ,
141
146
} ) ;
142
147
diag. code ( E0207 ) ;
148
+ suggest_to_remove_or_use_generics ( tcx, & mut diag, impl_def_id, param) ;
143
149
res = Err ( diag. emit ( ) ) ;
144
150
}
145
151
// (*) This is a horrible concession to reality. I think it'd be
@@ -229,8 +235,99 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
229
235
const_param_note2 : const_param_note,
230
236
} ) ;
231
237
diag. code ( E0207 ) ;
238
+ suggest_to_remove_or_use_generics ( tcx, & mut diag, impl_def_id, & param) ;
232
239
res = Err ( diag. emit ( ) ) ;
233
240
}
234
241
}
235
242
res
236
243
}
244
+
245
+ fn suggest_to_remove_or_use_generics (
246
+ tcx : TyCtxt < ' _ > ,
247
+ diag : & mut Diag < ' _ > ,
248
+ impl_def_id : LocalDefId ,
249
+ param : & GenericParamDef ,
250
+ ) {
251
+ let node = tcx. hir ( ) . get_if_local ( impl_def_id. into ( ) ) . expect ( "cannot get `Node`" ) ;
252
+ let hir_impl = if let rustc_hir:: Node :: Item ( item) = node {
253
+ if let rustc_hir:: ItemKind :: Impl ( imp) = item. kind { Some ( imp) } else { None }
254
+ } else {
255
+ None
256
+ }
257
+ . expect ( "cannot take `Impl` in a impl block" ) ;
258
+
259
+ let ( index, hir_param) = hir_impl
260
+ . generics
261
+ . params
262
+ . iter ( )
263
+ . enumerate ( )
264
+ . find ( |( _, par) | par. name . ident ( ) . name == param. name )
265
+ . unwrap ( ) ;
266
+ let mut suggestions = vec ! [ ] ;
267
+
268
+ let is_impl_generic = |par : & & GenericParam < ' _ > | match par. kind {
269
+ GenericParamKind :: Type { .. }
270
+ | GenericParamKind :: Const { .. }
271
+ | GenericParamKind :: Lifetime { kind : LifetimeParamKind :: Explicit } => true ,
272
+ _ => false ,
273
+ } ;
274
+ // Suggestion for removing the type parameter.
275
+ suggestions. push ( vec ! [ (
276
+ // Find the span of the type parameter.
277
+ if let Some ( prev) = hir_impl. generics. params[ ..index] . iter( ) . rfind( is_impl_generic) {
278
+ let mut span = prev. span;
279
+
280
+ // Consider the span of the bounds with the generic parameter when there is.
281
+ if let Some ( predicate) = hir_impl. generics. predicates. iter( ) . find( |pred| {
282
+ if let WherePredicateKind :: BoundPredicate ( WhereBoundPredicate {
283
+ origin: PredicateOrigin :: GenericParam ,
284
+ bounded_ty,
285
+ ..
286
+ } ) = pred. kind
287
+ {
288
+ bounded_ty. span == prev. span
289
+ } else {
290
+ false
291
+ }
292
+ } ) {
293
+ span = span. to( predicate. span)
294
+ } ;
295
+
296
+ span. shrink_to_hi( ) . to( hir_param. span)
297
+ } else if let Some ( next) =
298
+ hir_impl. generics. params[ index + 1 ..] . iter( ) . find( is_impl_generic)
299
+ {
300
+ hir_param. span. until( next. span)
301
+ } else {
302
+ // Remove also angle brackets <> when there is just ONE generic parameter.
303
+ hir_impl. generics. span
304
+ } ,
305
+ String :: new( ) ,
306
+ ) ] ) ;
307
+
308
+ // Suggestion for making use of the type parameter.
309
+ if let Some ( path) = extract_ty_as_path ( hir_impl. self_ty ) {
310
+ let seg = path. segments . last ( ) . unwrap ( ) ;
311
+ if let Some ( args) = seg. args {
312
+ suggestions
313
+ . push ( vec ! [ ( args. span( ) . unwrap( ) . shrink_to_hi( ) , format!( ", {}" , param. name) ) ] ) ;
314
+ } else {
315
+ suggestions. push ( vec ! [ ( seg. ident. span. shrink_to_hi( ) , format!( "<{}>" , param. name) ) ] ) ;
316
+ }
317
+ }
318
+
319
+ diag. multipart_suggestions (
320
+ format ! ( "either remove the type parameter {}, or make use of it, for example" , param. name) ,
321
+ suggestions,
322
+ Applicability :: MaybeIncorrect ,
323
+ ) ;
324
+ }
325
+
326
+ fn extract_ty_as_path < ' hir > ( ty : & Ty < ' hir > ) -> Option < & ' hir Path < ' hir > > {
327
+ match ty. kind {
328
+ TyKind :: Path ( QPath :: Resolved ( _, path) ) => Some ( path) ,
329
+ TyKind :: Slice ( ty) | TyKind :: Array ( ty, _) => extract_ty_as_path ( ty) ,
330
+ TyKind :: Ptr ( ty) | TyKind :: Ref ( _, ty) => extract_ty_as_path ( ty. ty ) ,
331
+ _ => None ,
332
+ }
333
+ }
0 commit comments