@@ -4,14 +4,17 @@ use rustc_data_structures::fx::FxHashMap;
44use rustc_errors:: MultiSpan ;
55use rustc_hir:: intravisit:: { walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor } ;
66use 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 ,
99} ;
1010use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1111use rustc_middle:: hir:: nested_filter;
1212use 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+ } ;
1518
1619declare_clippy_lint ! {
1720 /// ### What it does
@@ -38,7 +41,30 @@ declare_clippy_lint! {
3841 complexity,
3942 "unused type parameters in function definitions"
4043}
41- declare_lint_pass ! ( ExtraUnusedTypeParameters => [ EXTRA_UNUSED_TYPE_PARAMETERS ] ) ;
44+
45+ #[ derive( Default ) ]
46+ pub struct ExtraUnusedTypeParameters {
47+ avoid_breaking_exported_api : bool ,
48+ }
49+
50+ impl ExtraUnusedTypeParameters {
51+ pub fn new ( avoid_breaking_exported_api : bool ) -> Self {
52+ Self {
53+ avoid_breaking_exported_api,
54+ }
55+ }
56+
57+ /// Don't lint external macros or functions with empty bodies. Also, don't lint public items if
58+ /// the `avoid_breaking_exported_api` config option is set.
59+ fn check_false_positive ( & self , cx : & LateContext < ' _ > , span : Span , def_id : LocalDefId , body_id : BodyId ) -> bool {
60+ let body = cx. tcx . hir ( ) . body ( body_id) . value ;
61+ let fn_empty = matches ! ( & body. kind, ExprKind :: Block ( blk, None ) if blk. stmts. is_empty( ) && blk. expr. is_none( ) ) ;
62+ let is_exported = cx. effective_visibilities . is_exported ( def_id) ;
63+ in_external_macro ( cx. sess ( ) , span) || ( self . avoid_breaking_exported_api && is_exported) || fn_empty
64+ }
65+ }
66+
67+ impl_lint_pass ! ( ExtraUnusedTypeParameters => [ EXTRA_UNUSED_TYPE_PARAMETERS ] ) ;
4268
4369/// A visitor struct that walks a given function and gathers generic type parameters, plus any
4470/// trait bounds those parameters have.
@@ -56,13 +82,10 @@ struct TypeWalker<'cx, 'tcx> {
5682 /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
5783 /// parameters are present, this will be set to `false`.
5884 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 ,
6285}
6386
6487impl < ' cx , ' tcx > TypeWalker < ' cx , ' tcx > {
65- fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > , body_id : BodyId ) -> Self {
88+ fn new ( cx : & ' cx LateContext < ' tcx > , generics : & ' tcx Generics < ' tcx > ) -> Self {
6689 let mut all_params_unused = true ;
6790 let ty_params = generics
6891 . params
@@ -79,17 +102,12 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
79102 } )
80103 . collect ( ) ;
81104
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-
86105 Self {
87106 cx,
88107 ty_params,
89108 bounds : FxHashMap :: default ( ) ,
90109 generics,
91110 all_params_unused,
92- fn_body_empty,
93111 }
94112 }
95113
@@ -128,6 +146,12 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
128146 }
129147}
130148
149+ /// Given a generic bound, if the bound is for a trait that's not a `LangItem`, return the
150+ /// `LocalDefId` for that trait.
151+ fn bound_to_trait_def_id ( bound : & GenericBound < ' _ > ) -> Option < LocalDefId > {
152+ bound. trait_ref ( ) ?. trait_def_id ( ) ?. as_local ( )
153+ }
154+
131155impl < ' cx , ' tcx > Visitor < ' tcx > for TypeWalker < ' cx , ' tcx > {
132156 type NestedFilter = nested_filter:: OnlyBodies ;
133157
@@ -151,11 +175,15 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
151175 if let WherePredicate :: BoundPredicate ( predicate) = predicate {
152176 // Collect spans for any bounds on type parameters. We only keep bounds that appear in
153177 // 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).
157178 if let Some ( ( def_id, _) ) = predicate. bounded_ty . peel_refs ( ) . as_generic_param ( ) {
158- if self . fn_body_empty {
179+ // If the bound contains non-public traits, err on the safe side and don't lint the
180+ // corresponding parameter.
181+ if !predicate
182+ . bounds
183+ . iter ( )
184+ . filter_map ( bound_to_trait_def_id)
185+ . all ( |def_id| self . cx . effective_visibilities . is_exported ( def_id) )
186+ {
159187 self . ty_params . remove ( & def_id) ;
160188 } else if let PredicateOrigin :: GenericParam = predicate. origin {
161189 self . bounds . insert ( def_id, predicate. span ) ;
@@ -176,9 +204,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
176204impl < ' tcx > LateLintPass < ' tcx > for ExtraUnusedTypeParameters {
177205 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
178206 if let ItemKind :: Fn ( _, generics, body_id) = item. kind
179- && !in_external_macro ( cx . sess ( ) , item. span )
207+ && !self . check_false_positive ( cx , item. span , item . owner_id . def_id , body_id )
180208 {
181- let mut walker = TypeWalker :: new ( cx, generics, body_id ) ;
209+ let mut walker = TypeWalker :: new ( cx, generics) ;
182210 walk_item ( & mut walker, item) ;
183211 walker. emit_lint ( ) ;
184212 }
@@ -188,9 +216,9 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
188216 // Only lint on inherent methods, not trait methods.
189217 if let ImplItemKind :: Fn ( .., body_id) = item. kind
190218 && trait_ref_of_method ( cx, item. owner_id . def_id ) . is_none ( )
191- && !in_external_macro ( cx . sess ( ) , item. span )
219+ && !self . check_false_positive ( cx , item. span , item . owner_id . def_id , body_id )
192220 {
193- let mut walker = TypeWalker :: new ( cx, item. generics , body_id ) ;
221+ let mut walker = TypeWalker :: new ( cx, item. generics ) ;
194222 walk_impl_item ( & mut walker, item) ;
195223 walker. emit_lint ( ) ;
196224 }
0 commit comments