1- use clippy_utils:: diagnostics:: span_lint ;
1+ use clippy_utils:: diagnostics:: span_lint_and_help ;
22use clippy_utils:: trait_ref_of_method;
33use rustc_data_structures:: fx:: FxHashMap ;
4+ use rustc_errors:: MultiSpan ;
45use rustc_hir:: intravisit:: { walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor } ;
5- use rustc_hir:: { GenericParamKind , Generics , ImplItem , ImplItemKind , Item , ItemKind , Ty , TyKind , WherePredicate } ;
6+ use rustc_hir:: {
7+ GenericParamKind , Generics , ImplItem , ImplItemKind , Item , ItemKind , LifetimeParamKind , Ty , TyKind , WherePredicate ,
8+ } ;
69use rustc_lint:: { LateContext , LateLintPass } ;
710use rustc_middle:: hir:: nested_filter;
811use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9- use rustc_span:: { def_id:: DefId , Span } ;
12+ use rustc_span:: { def_id:: DefId , BytePos , Span } ;
1013
1114declare_clippy_lint ! {
1215 /// ### What it does
@@ -39,32 +42,79 @@ declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
3942struct TypeWalker < ' cx , ' tcx > {
4043 cx : & ' cx LateContext < ' tcx > ,
4144 map : FxHashMap < DefId , Span > ,
45+ bound_map : FxHashMap < DefId , Span > ,
46+ generics : & ' tcx Generics < ' tcx > ,
47+ some_params_used : bool ,
48+ has_non_ty_params : bool ,
4249}
4350
4451impl < ' cx , ' tcx > TypeWalker < ' cx , ' tcx > {
45- fn new ( cx : & ' cx LateContext < ' tcx > , generics : & Generics < ' tcx > ) -> Self {
52+ fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > ) -> Self {
53+ let mut has_non_ty_params = false ;
54+ let map = generics
55+ . params
56+ . iter ( )
57+ . filter_map ( |param| match & param. kind {
58+ GenericParamKind :: Type { .. } => Some ( ( param. def_id . into ( ) , param. span ) ) ,
59+ non_ty => {
60+ if let GenericParamKind :: Lifetime { kind } = non_ty {
61+ match kind {
62+ LifetimeParamKind :: Elided => ( ) ,
63+ _ => has_non_ty_params = true ,
64+ }
65+ }
66+ None
67+ } ,
68+ } )
69+ . collect ( ) ;
4670 Self {
4771 cx,
48- map : generics
49- . params
50- . iter ( )
51- . filter_map ( |param| match param. kind {
52- GenericParamKind :: Type { .. } => Some ( ( param. def_id . into ( ) , param. span ) ) ,
53- _ => None ,
54- } )
55- . collect ( ) ,
72+ map,
73+ generics,
74+ has_non_ty_params,
75+ bound_map : FxHashMap :: default ( ) ,
76+ some_params_used : false ,
5677 }
5778 }
5879
5980 fn emit_lint ( & self ) {
60- for span in self . map . values ( ) {
61- span_lint (
62- self . cx ,
63- EXTRA_UNUSED_TYPE_PARAMETERS ,
64- * span,
81+ let ( msg, help) = match self . map . len ( ) {
82+ 0 => return ,
83+ 1 => (
6584 "type parameter goes unused in function definition" ,
66- ) ;
67- }
85+ "consider removing the parameter" ,
86+ ) ,
87+ _ => (
88+ "type parameters go unused in function definition" ,
89+ "consider removing the parameters" ,
90+ ) ,
91+ } ;
92+
93+ let source_map = self . cx . tcx . sess . source_map ( ) ;
94+ let span = if self . some_params_used || self . has_non_ty_params {
95+ MultiSpan :: from_spans (
96+ self . map
97+ . iter ( )
98+ . map ( |( def_id, & ( mut span) ) | {
99+ if let Some ( bound_span) = self . bound_map . get ( def_id) {
100+ span = span. with_hi ( bound_span. hi ( ) ) ;
101+ }
102+ span = source_map. span_extend_to_next_char ( span, ',' , false ) ;
103+ span = span. with_hi ( BytePos ( span. hi ( ) . 0 + 1 ) ) ;
104+
105+ let max_hi = self . generics . span . hi ( ) ;
106+ if span. hi ( ) >= max_hi {
107+ span = span. with_hi ( BytePos ( max_hi. 0 - 1 ) ) ;
108+ }
109+ span
110+ } )
111+ . collect ( ) ,
112+ )
113+ } else {
114+ self . generics . span . into ( )
115+ } ;
116+
117+ span_lint_and_help ( self . cx , EXTRA_UNUSED_TYPE_PARAMETERS , span, msg, None , help) ;
68118 }
69119}
70120
@@ -73,7 +123,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
73123
74124 fn visit_ty ( & mut self , t : & ' tcx Ty < ' tcx > ) {
75125 if let Some ( ( def_id, _) ) = t. peel_refs ( ) . as_generic_param ( ) {
76- self . map . remove ( & def_id) ;
126+ if self . map . remove ( & def_id) . is_some ( ) {
127+ self . some_params_used = true ;
128+ }
77129 } else if let TyKind :: OpaqueDef ( id, _, _) = t. kind {
78130 // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
79131 // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@@ -86,9 +138,16 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
86138 }
87139
88140 fn visit_where_predicate ( & mut self , predicate : & ' tcx WherePredicate < ' tcx > ) {
89- if let WherePredicate :: BoundPredicate ( where_bound_predicate) = predicate {
90- // Only check the right-hand side of where-bounds
91- for bound in where_bound_predicate. bounds {
141+ if let WherePredicate :: BoundPredicate ( predicate) = predicate {
142+ // Collect spans for bounds that appear in the list of generics (not in a where-clause)
143+ // for use in forming the help message
144+ if let Some ( ( def_id, _) ) = predicate. bounded_ty . peel_refs ( ) . as_generic_param ( )
145+ && predicate. span < self . generics . where_clause_span
146+ {
147+ self . bound_map . insert ( def_id, predicate. span ) ;
148+ }
149+ // Only walk the right-hand side of where-bounds
150+ for bound in predicate. bounds {
92151 walk_param_bound ( self , bound) ;
93152 }
94153 }
0 commit comments