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} ;
2
2
use clippy_utils:: paths;
3
3
use clippy_utils:: ty:: { implements_trait, is_copy} ;
4
4
use clippy_utils:: { is_automatically_derived, is_lint_allowed, match_def_path} ;
5
5
use if_chain:: if_chain;
6
+ use rustc_errors:: Applicability ;
6
7
use rustc_hir:: intravisit:: { walk_expr, walk_fn, walk_item, FnKind , Visitor } ;
7
8
use rustc_hir:: {
8
9
BlockCheckMode , BodyId , Expr , ExprKind , FnDecl , HirId , Impl , Item , ItemKind , TraitRef , UnsafeSource , Unsafety ,
@@ -156,11 +157,44 @@ declare_clippy_lint! {
156
157
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
157
158
}
158
159
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
+ nursery,
189
+ "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
190
+ }
191
+
159
192
declare_lint_pass ! ( Derive => [
160
193
EXPL_IMPL_CLONE_ON_COPY ,
161
194
DERIVE_HASH_XOR_EQ ,
162
195
DERIVE_ORD_XOR_PARTIAL_ORD ,
163
- UNSAFE_DERIVE_DESERIALIZE
196
+ UNSAFE_DERIVE_DESERIALIZE ,
197
+ DERIVE_PARTIAL_EQ_WITHOUT_EQ
164
198
] ) ;
165
199
166
200
impl < ' tcx > LateLintPass < ' tcx > for Derive {
@@ -176,6 +210,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
176
210
177
211
check_hash_peq ( cx, item. span , trait_ref, ty, is_automatically_derived) ;
178
212
check_ord_partial_ord ( cx, item. span , trait_ref, ty, is_automatically_derived) ;
213
+ check_partial_eq_without_eq ( cx, item. span , trait_ref, ty, is_automatically_derived) ;
179
214
180
215
if is_automatically_derived {
181
216
check_unsafe_derive_deserialize ( cx, item, trait_ref, ty) ;
@@ -419,3 +454,43 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
419
454
self . cx . tcx . hir ( )
420
455
}
421
456
}
457
+
458
+ /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
459
+ fn check_partial_eq_without_eq < ' tcx > (
460
+ cx : & LateContext < ' tcx > ,
461
+ span : Span ,
462
+ trait_ref : & TraitRef < ' _ > ,
463
+ ty : Ty < ' tcx > ,
464
+ peq_is_automatically_derived : bool ,
465
+ ) {
466
+ if_chain ! {
467
+ if let ty:: Adt ( adt, substs) = ty. kind( ) ;
468
+ if peq_is_automatically_derived;
469
+ if let Some ( eq_trait_def_id) = cx. tcx. get_diagnostic_item( sym:: Eq ) ;
470
+ if let Some ( def_id) = trait_ref. trait_def_id( ) ;
471
+ if cx. tcx. is_diagnostic_item( sym:: PartialEq , def_id) ;
472
+ if !implements_trait( cx, ty, eq_trait_def_id, substs) ;
473
+ then {
474
+ // If all of our fields implement `Eq`, we can implement `Eq` too
475
+ for variant in adt. variants( ) {
476
+ for field in & variant. fields {
477
+ let ty = field. ty( cx. tcx, substs) ;
478
+
479
+ if !implements_trait( cx, ty, eq_trait_def_id, substs) {
480
+ return ;
481
+ }
482
+ }
483
+ }
484
+
485
+ span_lint_and_sugg(
486
+ cx,
487
+ DERIVE_PARTIAL_EQ_WITHOUT_EQ ,
488
+ span. ctxt( ) . outer_expn_data( ) . call_site,
489
+ "you are deriving `PartialEq` and can implement `Eq`" ,
490
+ "consider deriving `Eq` as well" ,
491
+ "PartialEq, Eq" . to_string( ) ,
492
+ Applicability :: MachineApplicable ,
493
+ )
494
+ }
495
+ }
496
+ }
0 commit comments