@@ -4,10 +4,12 @@ use rustc_data_structures::fx::FxHashMap;
4
4
use rustc_errors:: MultiSpan ;
5
5
use rustc_hir:: intravisit:: { walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor } ;
6
6
use rustc_hir:: {
7
- GenericParamKind , Generics , ImplItem , ImplItemKind , Item , ItemKind , PredicateOrigin , Ty , TyKind , WherePredicate ,
7
+ BodyId , ExprKind , GenericParamKind , Generics , ImplItem , ImplItemKind , Item , ItemKind , PredicateOrigin , Ty , TyKind ,
8
+ WherePredicate ,
8
9
} ;
9
- use rustc_lint:: { LateContext , LateLintPass } ;
10
+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
11
use rustc_middle:: hir:: nested_filter;
12
+ use rustc_middle:: lint:: in_external_macro;
11
13
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
14
use rustc_span:: { def_id:: DefId , Span } ;
13
15
@@ -55,10 +57,13 @@ struct TypeWalker<'cx, 'tcx> {
55
57
/// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
56
58
/// parameters are present, this will be set to `false`.
57
59
all_params_unused : bool ,
60
+ /// Whether or not the function has an empty body, in which case any bounded type parameters
61
+ /// will not be linted.
62
+ fn_body_empty : bool ,
58
63
}
59
64
60
65
impl < ' cx , ' tcx > TypeWalker < ' cx , ' tcx > {
61
- fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > ) -> Self {
66
+ fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > , body_id : BodyId ) -> Self {
62
67
let mut all_params_unused = true ;
63
68
let ty_params = generics
64
69
. params
@@ -74,12 +79,18 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
74
79
}
75
80
} )
76
81
. collect ( ) ;
82
+
83
+ let body = cx. tcx . hir ( ) . body ( body_id) . value ;
84
+ let fn_body_empty =
85
+ matches ! ( & body. kind, ExprKind :: Block ( block, None ) if block. stmts. is_empty( ) && block. expr. is_none( ) ) ;
86
+
77
87
Self {
78
88
cx,
79
89
ty_params,
80
90
bounds : FxHashMap :: default ( ) ,
81
91
generics,
82
92
all_params_unused,
93
+ fn_body_empty,
83
94
}
84
95
}
85
96
@@ -96,7 +107,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
96
107
) ,
97
108
} ;
98
109
99
- let source_map = self . cx . tcx . sess . source_map ( ) ;
110
+ let source_map = self . cx . sess ( ) . source_map ( ) ;
100
111
let span = if self . all_params_unused {
101
112
self . generics . span . into ( ) // Remove the entire list of generics
102
113
} else {
@@ -139,12 +150,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
139
150
140
151
fn visit_where_predicate ( & mut self , predicate : & ' tcx WherePredicate < ' tcx > ) {
141
152
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
- && let PredicateOrigin :: GenericParam = predicate. origin
146
- {
147
- self . bounds . insert ( def_id, predicate. span ) ;
153
+ // Collect spans for any bounds on type parameters. We only keep bounds that appear in
154
+ // the list of generics (not in a where-clause).
155
+ //
156
+ // Also, if the function body is empty, we don't lint the corresponding type parameters
157
+ // (See https://github.com/rust-lang/rust-clippy/issues/10319).
158
+ if let Some ( ( def_id, _) ) = predicate. bounded_ty . peel_refs ( ) . as_generic_param ( ) {
159
+ if self . fn_body_empty {
160
+ self . ty_params . remove ( & def_id) ;
161
+ } else if let PredicateOrigin :: GenericParam = predicate. origin {
162
+ self . bounds . insert ( def_id, predicate. span ) ;
163
+ }
148
164
}
149
165
// Only walk the right-hand side of where-bounds
150
166
for bound in predicate. bounds {
@@ -160,17 +176,22 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
160
176
161
177
impl < ' tcx > LateLintPass < ' tcx > for ExtraUnusedTypeParameters {
162
178
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
163
- if let ItemKind :: Fn ( _, generics, _) = item. kind {
164
- let mut walker = TypeWalker :: new ( cx, generics) ;
179
+ if let ItemKind :: Fn ( _, generics, body_id) = item. kind
180
+ && !in_external_macro ( cx. sess ( ) , item. span )
181
+ {
182
+ let mut walker = TypeWalker :: new ( cx, generics, body_id) ;
165
183
walk_item ( & mut walker, item) ;
166
184
walker. emit_lint ( ) ;
167
185
}
168
186
}
169
187
170
188
fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx ImplItem < ' tcx > ) {
171
189
// Only lint on inherent methods, not trait methods.
172
- if let ImplItemKind :: Fn ( ..) = item. kind && trait_ref_of_method ( cx, item. owner_id . def_id ) . is_none ( ) {
173
- let mut walker = TypeWalker :: new ( cx, item. generics ) ;
190
+ if let ImplItemKind :: Fn ( .., body_id) = item. kind
191
+ && trait_ref_of_method ( cx, item. owner_id . def_id ) . is_none ( )
192
+ && !in_external_macro ( cx. sess ( ) , item. span )
193
+ {
194
+ let mut walker = TypeWalker :: new ( cx, item. generics , body_id) ;
174
195
walk_impl_item ( & mut walker, item) ;
175
196
walker. emit_lint ( ) ;
176
197
}
0 commit comments