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};
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.
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.
23 /// struct Foo<A, B> {
27 /// // inside the impl, B refers to Foo::A
28 /// impl<B, A> Foo<B, A> {}
32 /// struct Foo<A, B> {
36 /// impl<A, B> Foo<A, B> {}
38 #[clippy::version = "1.62.0"]
39 pub MISMATCHING_TYPE_PARAM_ORDER,
41 "type parameter positioned inconsistently between type def and impl block"
43 declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
45 impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
46 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
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();
55 // get the name and span of the generic parameters in the Impl
56 let impl_params = generic_args.args.iter()
59 GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
60 Some((path.segments[0].ident.to_string(), path.span)),
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,
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()
77 GenericParamDefKind::Type {..} => Some(p.name.to_string()),
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();
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);
94 MISMATCHING_TYPE_PARAM_ORDER,
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) {