@@ -13,9 +13,14 @@ use std::assert_matches::debug_assert_matches;
1313use min_specialization:: check_min_specialization;
1414use rustc_data_structures:: fx:: FxHashSet ;
1515use rustc_errors:: codes:: * ;
16+ use rustc_errors:: { Applicability , Diag } ;
1617use rustc_hir:: def:: DefKind ;
1718use 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 } ;
1924use rustc_span:: ErrorGuaranteed ;
2025
2126use crate :: constrained_generic_params as cgp;
@@ -140,6 +145,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
140145 const_param_note2 : false ,
141146 } ) ;
142147 diag. code ( E0207 ) ;
148+ suggest_to_remove_or_use_generics ( tcx, & mut diag, impl_def_id, param) ;
143149 res = Err ( diag. emit ( ) ) ;
144150 }
145151 // (*) 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(
229235 const_param_note2 : const_param_note,
230236 } ) ;
231237 diag. code ( E0207 ) ;
238+ suggest_to_remove_or_use_generics ( tcx, & mut diag, impl_def_id, & param) ;
232239 res = Err ( diag. emit ( ) ) ;
233240 }
234241 }
235242 res
236243}
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