1
- use clippy_utils:: diagnostics:: span_lint ;
1
+ use clippy_utils:: diagnostics:: span_lint_and_help ;
2
2
use clippy_utils:: trait_ref_of_method;
3
3
use rustc_data_structures:: fx:: FxHashMap ;
4
+ use rustc_errors:: MultiSpan ;
4
5
use 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
+ } ;
6
9
use rustc_lint:: { LateContext , LateLintPass } ;
7
10
use rustc_middle:: hir:: nested_filter;
8
11
use 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 } ;
10
13
11
14
declare_clippy_lint ! {
12
15
/// ### What it does
@@ -39,32 +42,79 @@ declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
39
42
struct TypeWalker < ' cx , ' tcx > {
40
43
cx : & ' cx LateContext < ' tcx > ,
41
44
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 ,
42
49
}
43
50
44
51
impl < ' 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 ( ) ;
46
70
Self {
47
71
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 ,
56
77
}
57
78
}
58
79
59
80
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 => (
65
84
"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) ;
68
118
}
69
119
}
70
120
@@ -73,7 +123,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
73
123
74
124
fn visit_ty ( & mut self , t : & ' tcx Ty < ' tcx > ) {
75
125
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
+ }
77
129
} else if let TyKind :: OpaqueDef ( id, _, _) = t. kind {
78
130
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
79
131
// `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> {
86
138
}
87
139
88
140
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 {
92
151
walk_param_bound ( self , bound) ;
93
152
}
94
153
}
0 commit comments