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 parameter 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.
22 /// This lint only applies to impl blocks with simple generic params, e.g.
23 /// `A`. If there is anything more complicated, such as a tuple, it will be
28 /// struct Foo<A, B> {
32 /// // inside the impl, B refers to Foo::A
33 /// impl<B, A> Foo<B, A> {}
37 /// struct Foo<A, B> {
41 /// impl<A, B> Foo<A, B> {}
43 #[clippy::version = "1.63.0"]
44 pub MISMATCHING_TYPE_PARAM_ORDER,
46 "type parameter positioned inconsistently between type def and impl block"
48 declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
50 impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
51 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
53 if !item.span.from_expansion();
54 if let ItemKind::Impl(imp) = &item.kind;
55 if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
56 if let Some(segment) = path.segments.iter().next();
57 if let Some(generic_args) = segment.args;
58 if !generic_args.args.is_empty();
60 // get the name and span of the generic parameters in the Impl
61 let mut impl_params = Vec::new();
62 for p in generic_args.args.iter() {
64 GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
65 impl_params.push((path.segments[0].ident.to_string(), path.span)),
66 GenericArg::Type(_) => return,
71 // find the type that the Impl is for
72 // only lint on struct/enum/union for now
73 let defid = match path.res {
74 Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
78 // get the names of the generic parameters in the type
79 let type_params = &cx.tcx.generics_of(defid).params;
80 let type_param_names: Vec<_> = type_params.iter()
83 GenericParamDefKind::Type {..} => Some(p.name.to_string()),
87 // hashmap of name -> index for mismatch_param_name
88 let type_param_names_hashmap: FxHashMap<&String, usize> =
89 type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
91 let type_name = segment.ident;
92 for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() {
93 if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) {
94 let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
95 type_name, impl_param_name);
96 let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
97 type_param_names[i], type_name);
100 MISMATCHING_TYPE_PARAM_ORDER,
113 // Checks if impl_param_name is the same as one of type_param_names,
114 // and is in a different position
115 fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
116 if let Some(j) = type_param_names.get(impl_param_name) {