| use clippy_utils::res::MaybeResPath; | |
| use rustc_hir::def::Res; | |
| use rustc_hir::{Impl, Item, ItemKind}; | |
| use rustc_lint::{LateContext, LateLintPass}; | |
| use rustc_session::declare_lint_pass; | |
| mod derive_ord_xor_partial_ord; | |
| mod derive_partial_eq_without_eq; | |
| mod derived_hash_with_manual_eq; | |
| mod expl_impl_clone_on_copy; | |
| mod unsafe_derive_deserialize; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` | |
| /// or `PartialOrd` implementation. | |
| /// | |
| /// ### Why is this bad? | |
| /// The implementation of these traits must agree (for | |
| /// example for use with `sort`) so it’s probably a bad idea to use a | |
| /// default-generated `Ord` implementation with an explicitly defined | |
| /// `PartialOrd`. In particular, the following must hold for any type | |
| /// implementing `Ord`: | |
| /// | |
| /// ```text | |
| /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap() | |
| /// ``` | |
| /// | |
| /// ### Example | |
| /// ```rust,ignore | |
| /// #[derive(Ord, PartialEq, Eq)] | |
| /// struct Foo; | |
| /// | |
| /// impl PartialOrd for Foo { | |
| /// ... | |
| /// } | |
| /// ``` | |
| /// Use instead: | |
| /// ```rust,ignore | |
| /// #[derive(PartialEq, Eq)] | |
| /// struct Foo; | |
| /// | |
| /// impl PartialOrd for Foo { | |
| /// fn partial_cmp(&self, other: &Foo) -> Option<Ordering> { | |
| /// Some(self.cmp(other)) | |
| /// } | |
| /// } | |
| /// | |
| /// impl Ord for Foo { | |
| /// ... | |
| /// } | |
| /// ``` | |
| /// or, if you don't need a custom ordering: | |
| /// ```rust,ignore | |
| /// #[derive(Ord, PartialOrd, PartialEq, Eq)] | |
| /// struct Foo; | |
| /// ``` | |
| #[clippy::version = "1.47.0"] | |
| pub DERIVE_ORD_XOR_PARTIAL_ORD, | |
| correctness, | |
| "deriving `Ord` but implementing `PartialOrd` explicitly" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks for types that derive `PartialEq` and could implement `Eq`. | |
| /// | |
| /// ### Why is this bad? | |
| /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, | |
| /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used | |
| /// in APIs that require `Eq` types. It also allows structs containing `T` to derive | |
| /// `Eq` themselves. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// #[derive(PartialEq)] | |
| /// struct Foo { | |
| /// i_am_eq: i32, | |
| /// i_am_eq_too: Vec<String>, | |
| /// } | |
| /// ``` | |
| /// Use instead: | |
| /// ```no_run | |
| /// #[derive(PartialEq, Eq)] | |
| /// struct Foo { | |
| /// i_am_eq: i32, | |
| /// i_am_eq_too: Vec<String>, | |
| /// } | |
| /// ``` | |
| #[clippy::version = "1.63.0"] | |
| pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, | |
| nursery, | |
| "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Lints against manual `PartialEq` implementations for types with a derived `Hash` | |
| /// implementation. | |
| /// | |
| /// ### Why is this bad? | |
| /// The implementation of these traits must agree (for | |
| /// example for use with `HashMap`) so it’s probably a bad idea to use a | |
| /// default-generated `Hash` implementation with an explicitly defined | |
| /// `PartialEq`. In particular, the following must hold for any type: | |
| /// | |
| /// ```text | |
| /// k1 == k2 ⇒ hash(k1) == hash(k2) | |
| /// ``` | |
| /// | |
| /// ### Example | |
| /// ```ignore | |
| /// #[derive(Hash)] | |
| /// struct Foo; | |
| /// | |
| /// impl PartialEq for Foo { | |
| /// ... | |
| /// } | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub DERIVED_HASH_WITH_MANUAL_EQ, | |
| correctness, | |
| "deriving `Hash` but implementing `PartialEq` explicitly" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks for explicit `Clone` implementations for `Copy` | |
| /// types. | |
| /// | |
| /// ### Why is this bad? | |
| /// To avoid surprising behavior, these traits should | |
| /// agree and the behavior of `Copy` cannot be overridden. In almost all | |
| /// situations a `Copy` type should have a `Clone` implementation that does | |
| /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` | |
| /// gets you. | |
| /// | |
| /// ### Example | |
| /// ```rust,ignore | |
| /// #[derive(Copy)] | |
| /// struct Foo; | |
| /// | |
| /// impl Clone for Foo { | |
| /// // .. | |
| /// } | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub EXPL_IMPL_CLONE_ON_COPY, | |
| pedantic, | |
| "implementing `Clone` explicitly on `Copy` types" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks for deriving `serde::Deserialize` on a type that | |
| /// has methods using `unsafe`. | |
| /// | |
| /// ### Why is this bad? | |
| /// Deriving `serde::Deserialize` will create a constructor | |
| /// that may violate invariants held by another constructor. | |
| /// | |
| /// ### Example | |
| /// ```rust,ignore | |
| /// use serde::Deserialize; | |
| /// | |
| /// #[derive(Deserialize)] | |
| /// pub struct Foo { | |
| /// // .. | |
| /// } | |
| /// | |
| /// impl Foo { | |
| /// pub fn new() -> Self { | |
| /// // setup here .. | |
| /// } | |
| /// | |
| /// pub unsafe fn parts() -> (&str, &str) { | |
| /// // assumes invariants hold | |
| /// } | |
| /// } | |
| /// ``` | |
| #[clippy::version = "1.45.0"] | |
| pub UNSAFE_DERIVE_DESERIALIZE, | |
| pedantic, | |
| "deriving `serde::Deserialize` on a type that has methods using `unsafe`" | |
| } | |
| declare_lint_pass!(Derive => [ | |
| DERIVED_HASH_WITH_MANUAL_EQ, | |
| DERIVE_ORD_XOR_PARTIAL_ORD, | |
| DERIVE_PARTIAL_EQ_WITHOUT_EQ, | |
| EXPL_IMPL_CLONE_ON_COPY, | |
| UNSAFE_DERIVE_DESERIALIZE, | |
| ]); | |
| impl<'tcx> LateLintPass<'tcx> for Derive { | |
| fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { | |
| if let ItemKind::Impl(Impl { | |
| of_trait: Some(of_trait), | |
| self_ty, | |
| .. | |
| }) = item.kind | |
| && let Res::Def(_, def_id) = *self_ty.basic_res() | |
| && let Some(local_def_id) = def_id.as_local() | |
| { | |
| let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); | |
| let trait_ref = &of_trait.trait_ref; | |
| let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); | |
| let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); | |
| derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); | |
| derive_ord_xor_partial_ord::check(cx, item, trait_ref, ty, adt_hir_id, is_automatically_derived); | |
| if is_automatically_derived { | |
| unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id); | |
| derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty, adt_hir_id); | |
| } else { | |
| expl_impl_clone_on_copy::check(cx, item, trait_ref, ty, adt_hir_id); | |
| } | |
| } | |
| } | |
| } |