Skip to content

Commit 7c0d649

Browse files
committed
Auto merge of rust-lang#8831 - arieluy:type_params, r=dswij
Add new lint `mismatching_type_param_order` changelog: Add new lint [`mismatching_type_param_order`] for checking if type parameters are consistent between type definitions and impl blocks. fixes rust-lang#7147
2 parents 1194c63 + 58cd01c commit 7c0d649

7 files changed

+264
-0
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3560,6 +3560,7 @@ Released 2018-09-13
35603560
[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
35613561
[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
35623562
[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
3563+
[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
35633564
[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
35643565
[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
35653566
[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items

Diff for: clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ store.register_lints(&[
380380
misc_early::UNNEEDED_WILDCARD_PATTERN,
381381
misc_early::UNSEPARATED_LITERAL_SUFFIX,
382382
misc_early::ZERO_PREFIXED_LITERAL,
383+
mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
383384
missing_const_for_fn::MISSING_CONST_FOR_FN,
384385
missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
385386
missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,

Diff for: clippy_lints/src/lib.register_pedantic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
6767
LintId::of(methods::UNNECESSARY_JOIN),
6868
LintId::of(misc::FLOAT_CMP),
6969
LintId::of(misc::USED_UNDERSCORE_BINDING),
70+
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
7071
LintId::of(mut_mut::MUT_MUT),
7172
LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
7273
LintId::of(needless_continue::NEEDLESS_CONTINUE),

Diff for: clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ mod methods;
298298
mod minmax;
299299
mod misc;
300300
mod misc_early;
301+
mod mismatching_type_param_order;
301302
mod missing_const_for_fn;
302303
mod missing_doc;
303304
mod missing_enforced_import_rename;
@@ -917,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
917918
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
918919
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
919920
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
921+
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
920922
// add lints here, do not remove this comment, it's used in `new_lint`
921923
}
922924

Diff for: clippy_lints/src/mismatching_type_param_order.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use rustc_data_structures::fx::FxHashMap;
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+
/// struct Foo<A, B> {
33+
/// x: A,
34+
/// y: B,
35+
/// }
36+
/// impl<A, B> Foo<A, B> {}
37+
/// ```
38+
#[clippy::version = "1.62.0"]
39+
pub MISMATCHING_TYPE_PARAM_ORDER,
40+
pedantic,
41+
"type parameter positioned inconsistently between type def and impl block"
42+
}
43+
declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
44+
45+
impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
46+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
47+
if_chain! {
48+
if !item.span.from_expansion();
49+
if let ItemKind::Impl(imp) = &item.kind;
50+
if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
51+
if let Some(segment) = path.segments.iter().next();
52+
if let Some(generic_args) = segment.args;
53+
if !generic_args.args.is_empty();
54+
then {
55+
// get the name and span of the generic parameters in the Impl
56+
let impl_params = generic_args.args.iter()
57+
.filter_map(|p|
58+
match p {
59+
GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
60+
Some((path.segments[0].ident.to_string(), path.span)),
61+
_ => None,
62+
}
63+
);
64+
65+
// find the type that the Impl is for
66+
// only lint on struct/enum/union for now
67+
let defid = match path.res {
68+
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
69+
_ => return,
70+
};
71+
72+
// get the names of the generic parameters in the type
73+
let type_params = &cx.tcx.generics_of(defid).params;
74+
let type_param_names: Vec<_> = type_params.iter()
75+
.filter_map(|p|
76+
match p.kind {
77+
GenericParamDefKind::Type {..} => Some(p.name.to_string()),
78+
_ => None,
79+
}
80+
).collect();
81+
// hashmap of name -> index for mismatch_param_name
82+
let type_param_names_hashmap: FxHashMap<&String, usize> =
83+
type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
84+
85+
let type_name = segment.ident;
86+
for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
87+
if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) {
88+
let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
89+
type_name, impl_param_name);
90+
let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
91+
type_param_names[i], type_name);
92+
span_lint_and_help(
93+
cx,
94+
MISMATCHING_TYPE_PARAM_ORDER,
95+
impl_param_span,
96+
&msg,
97+
None,
98+
&help
99+
);
100+
}
101+
}
102+
}
103+
}
104+
}
105+
}
106+
107+
// Checks if impl_param_name is the same as one of type_param_names,
108+
// and is in a different position
109+
fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
110+
if let Some(j) = type_param_names.get(impl_param_name) {
111+
if i != *j {
112+
return true;
113+
}
114+
}
115+
false
116+
}

Diff for: tests/ui/mismatching_type_param_order.rs

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#![warn(clippy::mismatching_type_param_order)]
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+
}

Diff for: tests/ui/mismatching_type_param_order.stderr

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order
2+
--> $DIR/mismatching_type_param_order.rs:11:20
3+
|
4+
LL | impl<B, A> Foo<B, A> {}
5+
| ^
6+
|
7+
= note: `-D clippy::mismatching-type-param-order` implied by `-D warnings`
8+
= help: try `A`, or a name that does not conflict with `Foo`'s generic params
9+
10+
error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order
11+
--> $DIR/mismatching_type_param_order.rs:11:23
12+
|
13+
LL | impl<B, A> Foo<B, A> {}
14+
| ^
15+
|
16+
= help: try `B`, or a name that does not conflict with `Foo`'s generic params
17+
18+
error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order
19+
--> $DIR/mismatching_type_param_order.rs:14:23
20+
|
21+
LL | impl<C, A> Foo<C, A> {}
22+
| ^
23+
|
24+
= help: try `B`, or a name that does not conflict with `Foo`'s generic params
25+
26+
error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order
27+
--> $DIR/mismatching_type_param_order.rs:25:44
28+
|
29+
LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
30+
| ^
31+
|
32+
= help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params
33+
34+
error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order
35+
--> $DIR/mismatching_type_param_order.rs:25:47
36+
|
37+
LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
38+
| ^
39+
|
40+
= help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params
41+
42+
error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order
43+
--> $DIR/mismatching_type_param_order.rs:41:27
44+
|
45+
LL | impl<C, A, B> FooEnum<C, A, B> {}
46+
| ^
47+
|
48+
= help: try `A`, or a name that does not conflict with `FooEnum`'s generic params
49+
50+
error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order
51+
--> $DIR/mismatching_type_param_order.rs:41:30
52+
|
53+
LL | impl<C, A, B> FooEnum<C, A, B> {}
54+
| ^
55+
|
56+
= help: try `B`, or a name that does not conflict with `FooEnum`'s generic params
57+
58+
error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order
59+
--> $DIR/mismatching_type_param_order.rs:41:33
60+
|
61+
LL | impl<C, A, B> FooEnum<C, A, B> {}
62+
| ^
63+
|
64+
= help: try `C`, or a name that does not conflict with `FooEnum`'s generic params
65+
66+
error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order
67+
--> $DIR/mismatching_type_param_order.rs:52:31
68+
|
69+
LL | impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
70+
| ^
71+
|
72+
= help: try `A`, or a name that does not conflict with `FooUnion`'s generic params
73+
74+
error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order
75+
--> $DIR/mismatching_type_param_order.rs:52:34
76+
|
77+
LL | impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
78+
| ^
79+
|
80+
= help: try `B`, or a name that does not conflict with `FooUnion`'s generic params
81+
82+
error: aborting due to 10 previous errors
83+

0 commit comments

Comments
 (0)