Skip to content

Commit cf09da7

Browse files
committed
Add new lint shadow_type_generic
Add a new restriction lint `shadow_type_generic` that checks for generic parameters that shadow types in scope. Signed-off-by: Alexandre Barone <abalexandrebarone@gmail.com>
1 parent 76118ec commit cf09da7

File tree

6 files changed

+274
-0
lines changed

6 files changed

+274
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6269,6 +6269,7 @@ Released 2018-09-13
62696269
[`set_contains_or_insert`]: https://rust-lang.github.io/rust-clippy/master/index.html#set_contains_or_insert
62706270
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
62716271
[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
6272+
[`shadow_type_generic`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_type_generic
62726273
[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
62736274
[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
62746275
[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
671671
crate::shadow::SHADOW_REUSE_INFO,
672672
crate::shadow::SHADOW_SAME_INFO,
673673
crate::shadow::SHADOW_UNRELATED_INFO,
674+
crate::shadow_type::SHADOW_TYPE_GENERIC_INFO,
674675
crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO,
675676
crate::single_call_fn::SINGLE_CALL_FN_INFO,
676677
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ mod semicolon_if_nothing_returned;
337337
mod serde_api;
338338
mod set_contains_or_insert;
339339
mod shadow;
340+
mod shadow_type;
340341
mod significant_drop_tightening;
341342
mod single_call_fn;
342343
mod single_char_lifetime_names;
@@ -830,5 +831,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
830831
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
831832
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
832833
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
834+
store.register_late_pass(|_| Box::<shadow_type::ShadowTypeGeneric>::default());
833835
// add lints here, do not remove this comment, it's used in `new_lint`
834836
}

clippy_lints/src/shadow_type.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_data_structures::fx::FxHashMap;
3+
use rustc_hir::{GenericParamKind, Generics, HirId, Item, ItemKind, Mod};
4+
use rustc_lint::{LateContext, LateLintPass, LintContext};
5+
use rustc_session::impl_lint_pass;
6+
use rustc_span::{Ident, Span, Symbol};
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks for generic parameters that shadow types in scope.
11+
///
12+
/// ### Why restrict this?
13+
/// To avoid confusion and potential bugs, as it can lead to
14+
/// misunderstandings about which type is being referred to.
15+
///
16+
/// ### Example
17+
/// ```no_run
18+
/// struct Foo;
19+
/// struct Bar<Foo> { f: Foo }
20+
/// ```
21+
///
22+
/// Use instead:
23+
/// ```no_run
24+
/// struct Foo;
25+
/// struct Bar<F> { f: F } // use different generic parameter name
26+
/// ```
27+
#[clippy::version = "1.89.0"]
28+
pub SHADOW_TYPE_GENERIC,
29+
restriction,
30+
"shadowing of type in scope by generic parameter"
31+
}
32+
33+
#[derive(Default)]
34+
pub(crate) struct ShadowTypeGeneric {
35+
module_types: FxHashMap<Symbol, Span>,
36+
}
37+
38+
impl_lint_pass!(ShadowTypeGeneric => [SHADOW_TYPE_GENERIC]);
39+
40+
impl<'tcx> LateLintPass<'tcx> for ShadowTypeGeneric {
41+
fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: HirId) {
42+
self.module_types.clear();
43+
let items = module.item_ids.iter().map(|&id| cx.tcx.hir_item(id));
44+
for item in items {
45+
if item.span.in_external_macro(cx.sess().source_map()) || item.span.from_expansion() {
46+
continue;
47+
}
48+
49+
if let ItemKind::Enum(ident, _, _) | ItemKind::Struct(ident, _, _) = item.kind {
50+
self.module_types.insert(ident.name, ident.span);
51+
}
52+
}
53+
}
54+
55+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
56+
if item.span.in_external_macro(cx.sess().source_map()) || item.span.from_expansion() {
57+
return;
58+
}
59+
if let ItemKind::Enum(ident, generics, _) | ItemKind::Struct(ident, generics, _) = item.kind {
60+
self.check(cx, ident, generics);
61+
}
62+
}
63+
}
64+
65+
impl ShadowTypeGeneric {
66+
fn check(&self, cx: &LateContext<'_>, ident: Ident, generics: &Generics<'_>) {
67+
// Look for generic parameters such as `T`.
68+
let generic_params = generics
69+
.params
70+
.iter()
71+
.filter(|gen_param| matches!(gen_param.kind, GenericParamKind::Type { .. }));
72+
73+
// Match generic parameters with module types and split into spans lists.
74+
let (gen_param_spans, type_spans): (Vec<_>, Vec<_>) = generic_params
75+
.filter_map(|gen_param| {
76+
self.module_types
77+
.get(&gen_param.name.ident().name)
78+
.map(|type_span| (gen_param.span, type_span))
79+
})
80+
.unzip();
81+
82+
let (msg, help) = match gen_param_spans.len() {
83+
0 => {
84+
// No generic parameters shadowing types in scope
85+
return;
86+
},
87+
1 => (
88+
format!("generic parameter in `{ident}` shadows type in scope"),
89+
"consider using a different name for the generic parameter",
90+
),
91+
_ => (
92+
format!("generic parameters in `{ident}` shadow types in scope"),
93+
"consider using different names for the generic parameters",
94+
),
95+
};
96+
97+
span_lint_and_then(cx, SHADOW_TYPE_GENERIC, gen_param_spans, msg, |diag| {
98+
diag.span_labels(
99+
type_spans,
100+
&format!("this type is being shadowed by a generic parameter in `{ident}`"),
101+
);
102+
diag.help(help);
103+
});
104+
}
105+
}

tests/ui/shadow_type.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#![warn(clippy::shadow_type_generic)]
2+
3+
pub mod structs {
4+
struct Foo;
5+
enum Bar {}
6+
7+
//~v shadow_type_generic
8+
struct Struct1<Foo> {
9+
foo: Foo,
10+
}
11+
//~v shadow_type_generic
12+
struct Struct2<Bar> {
13+
bar: Bar,
14+
}
15+
//~v shadow_type_generic
16+
struct Struct3<Foo, Bar> {
17+
foo: Foo,
18+
bar: Bar,
19+
}
20+
//~v shadow_type_generic
21+
struct Struct4<Foo, B, Bar> {
22+
foo: Foo,
23+
b: B,
24+
bar: Bar,
25+
}
26+
struct Struct5 {
27+
foo: Foo,
28+
}
29+
struct Struct6 {
30+
bar: Bar,
31+
}
32+
}
33+
34+
pub mod enums {
35+
struct Foo;
36+
enum Bar {}
37+
38+
//~v shadow_type_generic
39+
enum Enum1<Foo> {
40+
Foo(Foo),
41+
}
42+
//~v shadow_type_generic
43+
enum Enum2<Bar> {
44+
Bar(Bar),
45+
}
46+
//~v shadow_type_generic
47+
enum Enum3<Foo, Bar> {
48+
Foo(Foo),
49+
Bar(Bar),
50+
}
51+
//~v shadow_type_generic
52+
enum Enum4<Foo, B, Bar> {
53+
Foo(Foo),
54+
B(B),
55+
Bar(Bar),
56+
}
57+
enum Enum5 {
58+
Foo(Foo),
59+
}
60+
enum Enum6 {
61+
Bar(Bar),
62+
}
63+
}
64+
65+
fn main() {}

tests/ui/shadow_type.stderr

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
error: generic parameter in `Struct1` shadows type in scope
2+
--> tests/ui/shadow_type.rs:8:20
3+
|
4+
LL | struct Foo;
5+
| --- this type is being shadowed by a generic parameter in `Struct1`
6+
...
7+
LL | struct Struct1<Foo> {
8+
| ^^^
9+
|
10+
= help: consider using a different name for the generic parameter
11+
= note: `-D clippy::shadow-type-generic` implied by `-D warnings`
12+
= help: to override `-D warnings` add `#[allow(clippy::shadow_type_generic)]`
13+
14+
error: generic parameter in `Struct2` shadows type in scope
15+
--> tests/ui/shadow_type.rs:12:20
16+
|
17+
LL | enum Bar {}
18+
| --- this type is being shadowed by a generic parameter in `Struct2`
19+
...
20+
LL | struct Struct2<Bar> {
21+
| ^^^
22+
|
23+
= help: consider using a different name for the generic parameter
24+
25+
error: generic parameters in `Struct3` shadow types in scope
26+
--> tests/ui/shadow_type.rs:16:20
27+
|
28+
LL | struct Foo;
29+
| --- this type is being shadowed by a generic parameter in `Struct3`
30+
LL | enum Bar {}
31+
| --- this type is being shadowed by a generic parameter in `Struct3`
32+
...
33+
LL | struct Struct3<Foo, Bar> {
34+
| ^^^ ^^^
35+
|
36+
= help: consider using different names for the generic parameters
37+
38+
error: generic parameters in `Struct4` shadow types in scope
39+
--> tests/ui/shadow_type.rs:21:20
40+
|
41+
LL | struct Foo;
42+
| --- this type is being shadowed by a generic parameter in `Struct4`
43+
LL | enum Bar {}
44+
| --- this type is being shadowed by a generic parameter in `Struct4`
45+
...
46+
LL | struct Struct4<Foo, B, Bar> {
47+
| ^^^ ^^^
48+
|
49+
= help: consider using different names for the generic parameters
50+
51+
error: generic parameter in `Enum1` shadows type in scope
52+
--> tests/ui/shadow_type.rs:39:16
53+
|
54+
LL | struct Foo;
55+
| --- this type is being shadowed by a generic parameter in `Enum1`
56+
...
57+
LL | enum Enum1<Foo> {
58+
| ^^^
59+
|
60+
= help: consider using a different name for the generic parameter
61+
62+
error: generic parameter in `Enum2` shadows type in scope
63+
--> tests/ui/shadow_type.rs:43:16
64+
|
65+
LL | enum Bar {}
66+
| --- this type is being shadowed by a generic parameter in `Enum2`
67+
...
68+
LL | enum Enum2<Bar> {
69+
| ^^^
70+
|
71+
= help: consider using a different name for the generic parameter
72+
73+
error: generic parameters in `Enum3` shadow types in scope
74+
--> tests/ui/shadow_type.rs:47:16
75+
|
76+
LL | struct Foo;
77+
| --- this type is being shadowed by a generic parameter in `Enum3`
78+
LL | enum Bar {}
79+
| --- this type is being shadowed by a generic parameter in `Enum3`
80+
...
81+
LL | enum Enum3<Foo, Bar> {
82+
| ^^^ ^^^
83+
|
84+
= help: consider using different names for the generic parameters
85+
86+
error: generic parameters in `Enum4` shadow types in scope
87+
--> tests/ui/shadow_type.rs:52:16
88+
|
89+
LL | struct Foo;
90+
| --- this type is being shadowed by a generic parameter in `Enum4`
91+
LL | enum Bar {}
92+
| --- this type is being shadowed by a generic parameter in `Enum4`
93+
...
94+
LL | enum Enum4<Foo, B, Bar> {
95+
| ^^^ ^^^
96+
|
97+
= help: consider using different names for the generic parameters
98+
99+
error: aborting due to 8 previous errors
100+

0 commit comments

Comments
 (0)