1- use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_note, span_lint_and_then} ;
1+ use clippy_utils:: diagnostics:: { span_lint_and_help, span_lint_and_note, span_lint_and_sugg , span_lint_and_then} ;
22use clippy_utils:: paths;
33use clippy_utils:: ty:: { implements_trait, is_copy} ;
44use clippy_utils:: { is_automatically_derived, is_lint_allowed, match_def_path} ;
55use if_chain:: if_chain;
6+ use rustc_errors:: Applicability ;
67use rustc_hir:: intravisit:: { walk_expr, walk_fn, walk_item, FnKind , Visitor } ;
78use rustc_hir:: {
89 BlockCheckMode , BodyId , Expr , ExprKind , FnDecl , HirId , Impl , Item , ItemKind , TraitRef , UnsafeSource , Unsafety ,
@@ -156,11 +157,44 @@ declare_clippy_lint! {
156157 "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
157158}
158159
160+ declare_clippy_lint ! {
161+ /// ### What it does
162+ /// Checks for types that derive `PartialEq` and could implement `Eq`.
163+ ///
164+ /// ### Why is this bad?
165+ /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
166+ /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
167+ /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
168+ /// `Eq` themselves.
169+ ///
170+ /// ### Example
171+ /// ```rust
172+ /// #[derive(PartialEq)]
173+ /// struct Foo {
174+ /// i_am_eq: i32,
175+ /// i_am_eq_too: Vec<String>,
176+ /// }
177+ /// ```
178+ /// Use instead:
179+ /// ```rust
180+ /// #[derive(PartialEq, Eq)]
181+ /// struct Foo {
182+ /// i_am_eq: i32,
183+ /// i_am_eq_too: Vec<String>,
184+ /// }
185+ /// ```
186+ #[ clippy:: version = "1.62.0" ]
187+ pub DERIVE_PARTIAL_EQ_WITHOUT_EQ ,
188+ style,
189+ "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
190+ }
191+
159192declare_lint_pass ! ( Derive => [
160193 EXPL_IMPL_CLONE_ON_COPY ,
161194 DERIVE_HASH_XOR_EQ ,
162195 DERIVE_ORD_XOR_PARTIAL_ORD ,
163- UNSAFE_DERIVE_DESERIALIZE
196+ UNSAFE_DERIVE_DESERIALIZE ,
197+ DERIVE_PARTIAL_EQ_WITHOUT_EQ
164198] ) ;
165199
166200impl < ' tcx > LateLintPass < ' tcx > for Derive {
@@ -179,6 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
179213
180214 if is_automatically_derived {
181215 check_unsafe_derive_deserialize ( cx, item, trait_ref, ty) ;
216+ check_partial_eq_without_eq ( cx, item. span , trait_ref, ty) ;
182217 } else {
183218 check_copy_clone ( cx, item, trait_ref, ty) ;
184219 }
@@ -419,3 +454,36 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
419454 self . cx . tcx . hir ( )
420455 }
421456}
457+
458+ /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
459+ fn check_partial_eq_without_eq < ' tcx > ( cx : & LateContext < ' tcx > , span : Span , trait_ref : & TraitRef < ' _ > , ty : Ty < ' tcx > ) {
460+ if_chain ! {
461+ if let ty:: Adt ( adt, substs) = ty. kind( ) ;
462+ if let Some ( eq_trait_def_id) = cx. tcx. get_diagnostic_item( sym:: Eq ) ;
463+ if let Some ( def_id) = trait_ref. trait_def_id( ) ;
464+ if cx. tcx. is_diagnostic_item( sym:: PartialEq , def_id) ;
465+ if !implements_trait( cx, ty, eq_trait_def_id, substs) ;
466+ then {
467+ // If all of our fields implement `Eq`, we can implement `Eq` too
468+ for variant in adt. variants( ) {
469+ for field in & variant. fields {
470+ let ty = field. ty( cx. tcx, substs) ;
471+
472+ if !implements_trait( cx, ty, eq_trait_def_id, substs) {
473+ return ;
474+ }
475+ }
476+ }
477+
478+ span_lint_and_sugg(
479+ cx,
480+ DERIVE_PARTIAL_EQ_WITHOUT_EQ ,
481+ span. ctxt( ) . outer_expn_data( ) . call_site,
482+ "you are deriving `PartialEq` and can implement `Eq`" ,
483+ "consider deriving `Eq` as well" ,
484+ "PartialEq, Eq" . to_string( ) ,
485+ Applicability :: MachineApplicable ,
486+ )
487+ }
488+ }
489+ }
0 commit comments