@@ -4,14 +4,17 @@ 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
- BodyId , ExprKind , GenericParamKind , Generics , ImplItem , ImplItemKind , Item , ItemKind , PredicateOrigin , Ty , TyKind ,
8
- WherePredicate ,
7
+ BodyId , ExprKind , GenericBound , GenericParamKind , Generics , ImplItem , ImplItemKind , Item , ItemKind ,
8
+ PredicateOrigin , Ty , TyKind , WherePredicate ,
9
9
} ;
10
10
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
11
11
use rustc_middle:: hir:: nested_filter;
12
12
use rustc_middle:: lint:: in_external_macro;
13
- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
14
- use rustc_span:: { def_id:: DefId , Span } ;
13
+ use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
14
+ use rustc_span:: {
15
+ def_id:: { DefId , LocalDefId } ,
16
+ Span ,
17
+ } ;
15
18
16
19
declare_clippy_lint ! {
17
20
/// ### What it does
@@ -38,7 +41,29 @@ declare_clippy_lint! {
38
41
complexity,
39
42
"unused type parameters in function definitions"
40
43
}
41
- declare_lint_pass ! ( ExtraUnusedTypeParameters => [ EXTRA_UNUSED_TYPE_PARAMETERS ] ) ;
44
+
45
+ pub struct ExtraUnusedTypeParameters {
46
+ avoid_breaking_exported_api : bool ,
47
+ }
48
+
49
+ impl ExtraUnusedTypeParameters {
50
+ pub fn new ( avoid_breaking_exported_api : bool ) -> Self {
51
+ Self {
52
+ avoid_breaking_exported_api,
53
+ }
54
+ }
55
+
56
+ /// Don't lint external macros or functions with empty bodies. Also, don't lint public items if
57
+ /// the `avoid_breaking_exported_api` config option is set.
58
+ fn check_false_positive ( & self , cx : & LateContext < ' _ > , span : Span , def_id : LocalDefId , body_id : BodyId ) -> bool {
59
+ let body = cx. tcx . hir ( ) . body ( body_id) . value ;
60
+ let fn_empty = matches ! ( & body. kind, ExprKind :: Block ( blk, None ) if blk. stmts. is_empty( ) && blk. expr. is_none( ) ) ;
61
+ let is_exported = cx. effective_visibilities . is_exported ( def_id) ;
62
+ in_external_macro ( cx. sess ( ) , span) || ( self . avoid_breaking_exported_api && is_exported) || fn_empty
63
+ }
64
+ }
65
+
66
+ impl_lint_pass ! ( ExtraUnusedTypeParameters => [ EXTRA_UNUSED_TYPE_PARAMETERS ] ) ;
42
67
43
68
/// A visitor struct that walks a given function and gathers generic type parameters, plus any
44
69
/// trait bounds those parameters have.
@@ -56,13 +81,10 @@ struct TypeWalker<'cx, 'tcx> {
56
81
/// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
57
82
/// parameters are present, this will be set to `false`.
58
83
all_params_unused : bool ,
59
- /// Whether or not the function has an empty body, in which case any bounded type parameters
60
- /// will not be linted.
61
- fn_body_empty : bool ,
62
84
}
63
85
64
86
impl < ' cx , ' tcx > TypeWalker < ' cx , ' tcx > {
65
- fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > , body_id : BodyId ) -> Self {
87
+ fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > ) -> Self {
66
88
let mut all_params_unused = true ;
67
89
let ty_params = generics
68
90
. params
@@ -79,17 +101,18 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
79
101
} )
80
102
. collect ( ) ;
81
103
82
- let body = cx. tcx . hir ( ) . body ( body_id) . value ;
83
- let fn_body_empty =
84
- matches ! ( & body. kind, ExprKind :: Block ( block, None ) if block. stmts. is_empty( ) && block. expr. is_none( ) ) ;
85
-
86
104
Self {
87
105
cx,
88
106
ty_params,
89
107
bounds : FxHashMap :: default ( ) ,
90
108
generics,
91
109
all_params_unused,
92
- fn_body_empty,
110
+ }
111
+ }
112
+
113
+ fn mark_param_used ( & mut self , def_id : DefId ) {
114
+ if self . ty_params . remove ( & def_id) . is_some ( ) {
115
+ self . all_params_unused = false ;
93
116
}
94
117
}
95
118
@@ -128,14 +151,18 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
128
151
}
129
152
}
130
153
154
+ /// Given a generic bound, if the bound is for a trait that's not a `LangItem`, return the
155
+ /// `LocalDefId` for that trait.
156
+ fn bound_to_trait_def_id ( bound : & GenericBound < ' _ > ) -> Option < LocalDefId > {
157
+ bound. trait_ref ( ) ?. trait_def_id ( ) ?. as_local ( )
158
+ }
159
+
131
160
impl < ' cx , ' tcx > Visitor < ' tcx > for TypeWalker < ' cx , ' tcx > {
132
161
type NestedFilter = nested_filter:: OnlyBodies ;
133
162
134
163
fn visit_ty ( & mut self , t : & ' tcx Ty < ' tcx > ) {
135
164
if let Some ( ( def_id, _) ) = t. peel_refs ( ) . as_generic_param ( ) {
136
- if self . ty_params . remove ( & def_id) . is_some ( ) {
137
- self . all_params_unused = false ;
138
- }
165
+ self . mark_param_used ( def_id) ;
139
166
} else if let TyKind :: OpaqueDef ( id, _, _) = t. kind {
140
167
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
141
168
// `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@@ -151,12 +178,16 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
151
178
if let WherePredicate :: BoundPredicate ( predicate) = predicate {
152
179
// Collect spans for any bounds on type parameters. We only keep bounds that appear in
153
180
// the list of generics (not in a where-clause).
154
- //
155
- // Also, if the function body is empty, we don't lint the corresponding type parameters
156
- // (See https://github.com/rust-lang/rust-clippy/issues/10319).
157
181
if let Some ( ( def_id, _) ) = predicate. bounded_ty . peel_refs ( ) . as_generic_param ( ) {
158
- if self . fn_body_empty {
159
- self . ty_params . remove ( & def_id) ;
182
+ // If the bound contains non-public traits, err on the safe side and don't lint the
183
+ // corresponding parameter.
184
+ if !predicate
185
+ . bounds
186
+ . iter ( )
187
+ . filter_map ( bound_to_trait_def_id)
188
+ . all ( |id| self . cx . effective_visibilities . is_exported ( id) )
189
+ {
190
+ self . mark_param_used ( def_id) ;
160
191
} else if let PredicateOrigin :: GenericParam = predicate. origin {
161
192
self . bounds . insert ( def_id, predicate. span ) ;
162
193
}
@@ -176,9 +207,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
176
207
impl < ' tcx > LateLintPass < ' tcx > for ExtraUnusedTypeParameters {
177
208
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
178
209
if let ItemKind :: Fn ( _, generics, body_id) = item. kind
179
- && !in_external_macro ( cx . sess ( ) , item. span )
210
+ && !self . check_false_positive ( cx , item. span , item . owner_id . def_id , body_id )
180
211
{
181
- let mut walker = TypeWalker :: new ( cx, generics, body_id ) ;
212
+ let mut walker = TypeWalker :: new ( cx, generics) ;
182
213
walk_item ( & mut walker, item) ;
183
214
walker. emit_lint ( ) ;
184
215
}
@@ -188,9 +219,9 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
188
219
// Only lint on inherent methods, not trait methods.
189
220
if let ImplItemKind :: Fn ( .., body_id) = item. kind
190
221
&& trait_ref_of_method ( cx, item. owner_id . def_id ) . is_none ( )
191
- && !in_external_macro ( cx . sess ( ) , item. span )
222
+ && !self . check_false_positive ( cx , item. span , item . owner_id . def_id , body_id )
192
223
{
193
- let mut walker = TypeWalker :: new ( cx, item. generics , body_id ) ;
224
+ let mut walker = TypeWalker :: new ( cx, item. generics ) ;
194
225
walk_impl_item ( & mut walker, item) ;
195
226
walker. emit_lint ( ) ;
196
227
}
0 commit comments