Skip to content

Commit 8789b37

Browse files
committed
Fix false positives for extra_unused_type_parameters
1 parent 0f75581 commit 8789b37

5 files changed

+61
-26
lines changed

clippy_lints/src/extra_unused_type_parameters.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ use rustc_data_structures::fx::FxHashMap;
44
use rustc_errors::MultiSpan;
55
use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
66
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,
89
};
9-
use rustc_lint::{LateContext, LateLintPass};
10+
use rustc_lint::{LateContext, LateLintPass, LintContext};
1011
use rustc_middle::hir::nested_filter;
12+
use rustc_middle::lint::in_external_macro;
1113
use rustc_session::{declare_lint_pass, declare_tool_lint};
1214
use rustc_span::{def_id::DefId, Span};
1315

@@ -55,10 +57,13 @@ struct TypeWalker<'cx, 'tcx> {
5557
/// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
5658
/// parameters are present, this will be set to `false`.
5759
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,
5863
}
5964

6065
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 {
6267
let mut all_params_unused = true;
6368
let ty_params = generics
6469
.params
@@ -74,12 +79,18 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
7479
}
7580
})
7681
.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+
7787
Self {
7888
cx,
7989
ty_params,
8090
bounds: FxHashMap::default(),
8191
generics,
8292
all_params_unused,
93+
fn_body_empty,
8394
}
8495
}
8596

@@ -96,7 +107,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
96107
),
97108
};
98109

99-
let source_map = self.cx.tcx.sess.source_map();
110+
let source_map = self.cx.sess().source_map();
100111
let span = if self.all_params_unused {
101112
self.generics.span.into() // Remove the entire list of generics
102113
} else {
@@ -139,12 +150,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
139150

140151
fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
141152
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+
}
148164
}
149165
// Only walk the right-hand side of where-bounds
150166
for bound in predicate.bounds {
@@ -160,17 +176,22 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
160176

161177
impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
162178
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);
165183
walk_item(&mut walker, item);
166184
walker.emit_lint();
167185
}
168186
}
169187

170188
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
171189
// 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);
174195
walk_impl_item(&mut walker, item);
175196
walker.emit_lint();
176197
}

tests/ui/extra_unused_type_parameters.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@ fn used_ret<T: Default>(x: u8) -> T {
1515
T::default()
1616
}
1717

18-
fn unused_bounded<T: Default, U>(x: U) {}
18+
fn unused_bounded<T: Default, U>(x: U) {
19+
unimplemented!();
20+
}
1921

2022
fn unused_where_clause<T, U>(x: U)
2123
where
2224
T: Default,
2325
{
26+
unimplemented!();
2427
}
2528

26-
fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {}
29+
fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
30+
unimplemented!();
31+
}
2732

2833
fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize {
2934
iter.count()
@@ -66,4 +71,14 @@ where
6671
.filter_map(move |(i, a)| if i == index { None } else { Some(a) })
6772
}
6873

74+
mod issue10319 {
75+
fn assert_send<T: Send>() {}
76+
77+
fn assert_send_where<T>()
78+
where
79+
T: Send,
80+
{
81+
}
82+
}
83+
6984
fn main() {}

tests/ui/extra_unused_type_parameters.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,29 @@ LL | fn unused_with_lt<'a, T>(x: &'a u8) {}
2626
error: type parameter goes unused in function definition
2727
--> $DIR/extra_unused_type_parameters.rs:18:19
2828
|
29-
LL | fn unused_bounded<T: Default, U>(x: U) {}
29+
LL | fn unused_bounded<T: Default, U>(x: U) {
3030
| ^^^^^^^^^^^
3131
|
3232
= help: consider removing the parameter
3333

3434
error: type parameter goes unused in function definition
35-
--> $DIR/extra_unused_type_parameters.rs:20:24
35+
--> $DIR/extra_unused_type_parameters.rs:22:24
3636
|
3737
LL | fn unused_where_clause<T, U>(x: U)
3838
| ^^
3939
|
4040
= help: consider removing the parameter
4141

4242
error: type parameters go unused in function definition
43-
--> $DIR/extra_unused_type_parameters.rs:26:16
43+
--> $DIR/extra_unused_type_parameters.rs:29:16
4444
|
45-
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {}
45+
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
4646
| ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
4747
|
4848
= help: consider removing the parameters
4949

5050
error: type parameter goes unused in function definition
51-
--> $DIR/extra_unused_type_parameters.rs:49:22
51+
--> $DIR/extra_unused_type_parameters.rs:54:22
5252
|
5353
LL | fn unused_ty_impl<T>(&self) {}
5454
| ^^^

tests/ui/type_repetition_in_bounds.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#![deny(clippy::type_repetition_in_bounds)]
2-
#![allow(clippy::extra_unused_type_parameters)]
32

43
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
54

tests/ui/type_repetition_in_bounds.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this type has already been used as a bound predicate
2-
--> $DIR/type_repetition_in_bounds.rs:9:5
2+
--> $DIR/type_repetition_in_bounds.rs:8:5
33
|
44
LL | T: Clone,
55
| ^^^^^^^^
@@ -12,23 +12,23 @@ LL | #![deny(clippy::type_repetition_in_bounds)]
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1313

1414
error: this type has already been used as a bound predicate
15-
--> $DIR/type_repetition_in_bounds.rs:26:5
15+
--> $DIR/type_repetition_in_bounds.rs:25:5
1616
|
1717
LL | Self: Copy + Default + Ord,
1818
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1919
|
2020
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
2121

2222
error: this type has already been used as a bound predicate
23-
--> $DIR/type_repetition_in_bounds.rs:86:5
23+
--> $DIR/type_repetition_in_bounds.rs:85:5
2424
|
2525
LL | T: Clone,
2626
| ^^^^^^^^
2727
|
2828
= help: consider combining the bounds: `T: ?Sized + Clone`
2929

3030
error: this type has already been used as a bound predicate
31-
--> $DIR/type_repetition_in_bounds.rs:91:5
31+
--> $DIR/type_repetition_in_bounds.rs:90:5
3232
|
3333
LL | T: ?Sized,
3434
| ^^^^^^^^^

0 commit comments

Comments
 (0)