Skip to content

Commit 337ca0c

Browse files
committed
Add new lint type_param_mismatch
Add new lint for checking if type parameters are consistent between type definitions and impl blocks.
1 parent 2038084 commit 337ca0c

7 files changed

+238
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3743,6 +3743,7 @@ Released 2018-09-13
37433743
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
37443744
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
37453745
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
3746+
[`type_param_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_param_mismatch
37463747
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
37473748
[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
37483749
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ store.register_lints(&[
513513
transmute::WRONG_TRANSMUTE,
514514
transmuting_null::TRANSMUTING_NULL,
515515
try_err::TRY_ERR,
516+
type_param_mismatch::TYPE_PARAM_MISMATCH,
516517
types::BORROWED_BOX,
517518
types::BOX_COLLECTION,
518519
types::LINKEDLIST,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
8585
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
8686
LintId::of(strings::STRING_ADD_ASSIGN),
8787
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
88+
LintId::of(type_param_mismatch::TYPE_PARAM_MISMATCH),
8889
LintId::of(types::LINKEDLIST),
8990
LintId::of(types::OPTION_OPTION),
9091
LintId::of(unicode::UNICODE_NOT_NFC),

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ mod trait_bounds;
384384
mod transmute;
385385
mod transmuting_null;
386386
mod try_err;
387+
mod type_param_mismatch;
387388
mod types;
388389
mod undocumented_unsafe_blocks;
389390
mod unicode;
@@ -902,6 +903,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
902903
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
903904
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
904905
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
906+
store.register_late_pass(|| Box::new(type_param_mismatch::TypeParamMismatch));
905907
// add lints here, do not remove this comment, it's used in `new_lint`
906908
}
907909

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use rustc_errors::Applicability;
3+
use rustc_hir::def::{DefKind, Res};
4+
use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_middle::ty::GenericParamDefKind;
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Checks for type parameters which are positioned inconsistently between
12+
/// a type definition and impl block. Specifically, a paramater in an impl
13+
/// block which has the same name as a parameter in the type def, but is in
14+
/// a different place.
15+
///
16+
/// ### Why is this bad?
17+
/// Type parameters are determined by their position rather than name.
18+
/// Naming type parameters inconsistently may cause you to refer to the
19+
/// wrong type parameter.
20+
///
21+
/// ### Example
22+
/// ```rust
23+
/// struct Foo<A, B> {
24+
/// x: A,
25+
/// y: B,
26+
/// }
27+
/// // inside the impl, B refers to Foo::A
28+
/// impl<B, A> Foo<B, A> {}
29+
/// ```
30+
/// Use instead:
31+
/// ```rust
32+
/// impl<A, B> Foo<A, B> {}
33+
/// ```
34+
#[clippy::version = "1.62.0"]
35+
pub TYPE_PARAM_MISMATCH,
36+
pedantic,
37+
"type parameter positioned inconsistently between type def and impl block"
38+
}
39+
declare_lint_pass!(TypeParamMismatch => [TYPE_PARAM_MISMATCH]);
40+
41+
impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
42+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
43+
if_chain! {
44+
if !item.span.from_expansion();
45+
if let ItemKind::Impl(imp) = &item.kind;
46+
if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
47+
if let Some(segment) = path.segments.iter().next();
48+
if let Some(generic_args) = segment.args;
49+
if !generic_args.args.is_empty();
50+
then {
51+
// get the name and span of the generic parameters in the Impl
52+
let impl_params = generic_args.args.iter()
53+
.filter_map(|p|
54+
match p {
55+
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
56+
Some((path.segments[0].ident.to_string(), path.span)),
57+
_ => None,
58+
}
59+
);
60+
61+
// find the type that the Impl is for
62+
// only lint on struct/enum/union for now
63+
let defid = match path.res {
64+
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
65+
_ => return,
66+
};
67+
68+
let type_name = segment.ident;
69+
70+
// get the names of the generic parameters in the type
71+
let type_params = &cx.tcx.generics_of(defid).params;
72+
let type_param_names: Vec<_> = type_params.iter()
73+
.filter_map(|p|
74+
match p.kind {
75+
GenericParamDefKind::Type {..} => Some(p.name.to_string()),
76+
_ => None,
77+
}
78+
).collect();
79+
80+
for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
81+
if mismatch_param_name(i, &impl_param_name, &type_param_names) {
82+
let msg = format!("this type parameter is named {}, but it does not refer to {}::{}",
83+
impl_param_name, type_name, impl_param_name);
84+
span_lint_and_sugg(
85+
cx,
86+
TYPE_PARAM_MISMATCH,
87+
impl_param_span,
88+
&msg,
89+
"try",
90+
type_param_names[i].to_string(),
91+
Applicability::MaybeIncorrect
92+
);
93+
}
94+
}
95+
}
96+
}
97+
}
98+
}
99+
100+
// Checks if impl_param_name is the same as one of type_param_names,
101+
// and is in a different position
102+
fn mismatch_param_name(i: usize, impl_param_name: &str, type_param_names: &[String]) -> bool {
103+
for (j, type_param_name) in type_param_names.iter().enumerate() {
104+
if impl_param_name == type_param_name && i != j {
105+
return true;
106+
}
107+
}
108+
false
109+
}

tests/ui/type_param_mismatch.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![warn(clippy::type_param_mismatch)]
2+
#![allow(clippy::blacklisted_name)]
3+
4+
fn main() {
5+
struct Foo<A, B> {
6+
x: A,
7+
y: B,
8+
}
9+
10+
// lint on both params
11+
impl<B, A> Foo<B, A> {}
12+
13+
// lint on the 2nd param
14+
impl<C, A> Foo<C, A> {}
15+
16+
// should not lint
17+
impl<A, B> Foo<A, B> {}
18+
19+
struct FooLifetime<'l, 'm, A, B> {
20+
x: &'l A,
21+
y: &'m B,
22+
}
23+
24+
// should not lint on lifetimes
25+
impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
26+
27+
struct Bar {
28+
x: i32,
29+
}
30+
31+
// should not lint
32+
impl Bar {}
33+
34+
// also works for enums
35+
enum FooEnum<A, B, C> {
36+
X(A),
37+
Y(B),
38+
Z(C),
39+
}
40+
41+
impl<C, A, B> FooEnum<C, A, B> {}
42+
43+
// also works for unions
44+
union FooUnion<A: Copy, B>
45+
where
46+
B: Copy,
47+
{
48+
x: A,
49+
y: B,
50+
}
51+
52+
impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
53+
54+
impl<A, B> FooUnion<A, B>
55+
where
56+
A: Copy,
57+
B: Copy,
58+
{
59+
}
60+
}

tests/ui/type_param_mismatch.stderr

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error: this type parameter is named B, but it does not refer to Foo::B
2+
--> $DIR/type_param_mismatch.rs:11:20
3+
|
4+
LL | impl<B, A> Foo<B, A> {}
5+
| ^ help: try: `A`
6+
|
7+
= note: `-D clippy::type-param-mismatch` implied by `-D warnings`
8+
9+
error: this type parameter is named A, but it does not refer to Foo::A
10+
--> $DIR/type_param_mismatch.rs:11:23
11+
|
12+
LL | impl<B, A> Foo<B, A> {}
13+
| ^ help: try: `B`
14+
15+
error: this type parameter is named A, but it does not refer to Foo::A
16+
--> $DIR/type_param_mismatch.rs:14:23
17+
|
18+
LL | impl<C, A> Foo<C, A> {}
19+
| ^ help: try: `B`
20+
21+
error: this type parameter is named B, but it does not refer to FooLifetime::B
22+
--> $DIR/type_param_mismatch.rs:25:44
23+
|
24+
LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
25+
| ^ help: try: `A`
26+
27+
error: this type parameter is named A, but it does not refer to FooLifetime::A
28+
--> $DIR/type_param_mismatch.rs:25:47
29+
|
30+
LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
31+
| ^ help: try: `B`
32+
33+
error: this type parameter is named C, but it does not refer to FooEnum::C
34+
--> $DIR/type_param_mismatch.rs:41:27
35+
|
36+
LL | impl<C, A, B> FooEnum<C, A, B> {}
37+
| ^ help: try: `A`
38+
39+
error: this type parameter is named A, but it does not refer to FooEnum::A
40+
--> $DIR/type_param_mismatch.rs:41:30
41+
|
42+
LL | impl<C, A, B> FooEnum<C, A, B> {}
43+
| ^ help: try: `B`
44+
45+
error: this type parameter is named B, but it does not refer to FooEnum::B
46+
--> $DIR/type_param_mismatch.rs:41:33
47+
|
48+
LL | impl<C, A, B> FooEnum<C, A, B> {}
49+
| ^ help: try: `C`
50+
51+
error: this type parameter is named B, but it does not refer to FooUnion::B
52+
--> $DIR/type_param_mismatch.rs:52:31
53+
|
54+
LL | impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
55+
| ^ help: try: `A`
56+
57+
error: this type parameter is named A, but it does not refer to FooUnion::A
58+
--> $DIR/type_param_mismatch.rs:52:34
59+
|
60+
LL | impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
61+
| ^ help: try: `B`
62+
63+
error: aborting due to 10 previous errors
64+

0 commit comments

Comments
 (0)