diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs')
-rw-r--r-- | src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs new file mode 100644 index 000000000..f763e0d24 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters which are positioned inconsistently between + /// a type definition and impl block. Specifically, a parameter in an impl + /// block which has the same name as a parameter in the type def, but is in + /// a different place. + /// + /// ### Why is this bad? + /// Type parameters are determined by their position rather than name. + /// Naming type parameters inconsistently may cause you to refer to the + /// wrong type parameter. + /// + /// ### Limitations + /// This lint only applies to impl blocks with simple generic params, e.g. + /// `A`. If there is anything more complicated, such as a tuple, it will be + /// ignored. + /// + /// ### Example + /// ```rust + /// struct Foo<A, B> { + /// x: A, + /// y: B, + /// } + /// // inside the impl, B refers to Foo::A + /// impl<B, A> Foo<B, A> {} + /// ``` + /// Use instead: + /// ```rust + /// struct Foo<A, B> { + /// x: A, + /// y: B, + /// } + /// impl<A, B> Foo<A, B> {} + /// ``` + #[clippy::version = "1.62.0"] + pub MISMATCHING_TYPE_PARAM_ORDER, + pedantic, + "type parameter positioned inconsistently between type def and impl block" +} +declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); + +impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if_chain! { + if !item.span.from_expansion(); + if let ItemKind::Impl(imp) = &item.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind; + if let Some(segment) = path.segments.iter().next(); + if let Some(generic_args) = segment.args; + if !generic_args.args.is_empty(); + then { + // get the name and span of the generic parameters in the Impl + let mut impl_params = Vec::new(); + for p in generic_args.args.iter() { + match p { + GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => + impl_params.push((path.segments[0].ident.to_string(), path.span)), + GenericArg::Type(_) => return, + _ => (), + }; + } + + // find the type that the Impl is for + // only lint on struct/enum/union for now + let defid = match path.res { + Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, + _ => return, + }; + + // get the names of the generic parameters in the type + let type_params = &cx.tcx.generics_of(defid).params; + let type_param_names: Vec<_> = type_params.iter() + .filter_map(|p| + match p.kind { + GenericParamDefKind::Type {..} => Some(p.name.to_string()), + _ => None, + } + ).collect(); + // hashmap of name -> index for mismatch_param_name + let type_param_names_hashmap: FxHashMap<&String, usize> = + type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); + + let type_name = segment.ident; + for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() { + if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) { + let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", + type_name, impl_param_name); + let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", + type_param_names[i], type_name); + span_lint_and_help( + cx, + MISMATCHING_TYPE_PARAM_ORDER, + *impl_param_span, + &msg, + None, + &help + ); + } + } + } + } + } +} + +// Checks if impl_param_name is the same as one of type_param_names, +// and is in a different position +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { + if let Some(j) = type_param_names.get(impl_param_name) { + if i != *j { + return true; + } + } + false +} |