]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/mismatching_type_param_order.rs
Auto merge of #8901 - Jarcho:sharing_code, r=dswij
[rust.git] / clippy_lints / src / mismatching_type_param_order.rs
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 }