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,77 @@ 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
+ GenericParamKind :: Lifetime {
60
+ kind : LifetimeParamKind :: Elided ,
61
+ } => None ,
62
+ _ => {
63
+ has_non_ty_params = true ;
64
+ None
65
+ } ,
66
+ } )
67
+ . collect ( ) ;
46
68
Self {
47
69
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 ( ) ,
70
+ map,
71
+ generics,
72
+ has_non_ty_params,
73
+ bound_map : FxHashMap :: default ( ) ,
74
+ some_params_used : false ,
56
75
}
57
76
}
58
77
59
78
fn emit_lint ( & self ) {
60
- for span in self . map . values ( ) {
61
- span_lint (
62
- self . cx ,
63
- EXTRA_UNUSED_TYPE_PARAMETERS ,
64
- * span,
79
+ let ( msg, help) = match self . map . len ( ) {
80
+ 0 => return ,
81
+ 1 => (
65
82
"type parameter goes unused in function definition" ,
66
- ) ;
67
- }
83
+ "consider removing the parameter" ,
84
+ ) ,
85
+ _ => (
86
+ "type parameters go unused in function definition" ,
87
+ "consider removing the parameters" ,
88
+ ) ,
89
+ } ;
90
+
91
+ let source_map = self . cx . tcx . sess . source_map ( ) ;
92
+ let span = if self . some_params_used || self . has_non_ty_params {
93
+ MultiSpan :: from_spans (
94
+ self . map
95
+ . iter ( )
96
+ . map ( |( def_id, & ( mut span) ) | {
97
+ if let Some ( bound_span) = self . bound_map . get ( def_id) {
98
+ span = span. with_hi ( bound_span. hi ( ) ) ;
99
+ }
100
+ span = source_map. span_extend_to_next_char ( span, ',' , false ) ;
101
+ span = span. with_hi ( BytePos ( span. hi ( ) . 0 + 1 ) ) ;
102
+
103
+ let max_hi = self . generics . span . hi ( ) ;
104
+ if span. hi ( ) >= max_hi {
105
+ span = span. with_hi ( BytePos ( max_hi. 0 - 1 ) ) ;
106
+ }
107
+ span
108
+ } )
109
+ . collect ( ) ,
110
+ )
111
+ } else {
112
+ self . generics . span . into ( )
113
+ } ;
114
+
115
+ span_lint_and_help ( self . cx , EXTRA_UNUSED_TYPE_PARAMETERS , span, msg, None , help) ;
68
116
}
69
117
}
70
118
@@ -73,7 +121,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
73
121
74
122
fn visit_ty ( & mut self , t : & ' tcx Ty < ' tcx > ) {
75
123
if let Some ( ( def_id, _) ) = t. peel_refs ( ) . as_generic_param ( ) {
76
- self . map . remove ( & def_id) ;
124
+ if self . map . remove ( & def_id) . is_some ( ) {
125
+ self . some_params_used = true ;
126
+ }
77
127
} else if let TyKind :: OpaqueDef ( id, _, _) = t. kind {
78
128
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
79
129
// `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@@ -86,9 +136,16 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
86
136
}
87
137
88
138
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 {
139
+ if let WherePredicate :: BoundPredicate ( predicate) = predicate {
140
+ // Collect spans for bounds that appear in the list of generics (not in a where-clause)
141
+ // for use in forming the help message
142
+ if let Some ( ( def_id, _) ) = predicate. bounded_ty . peel_refs ( ) . as_generic_param ( )
143
+ && predicate. span < self . generics . where_clause_span
144
+ {
145
+ self . bound_map . insert ( def_id, predicate. span ) ;
146
+ }
147
+ // Only walk the right-hand side of where-bounds
148
+ for bound in predicate. bounds {
92
149
walk_param_bound ( self , bound) ;
93
150
}
94
151
}
0 commit comments