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
+ style,
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 {
@@ -179,6 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
179
213
180
214
if is_automatically_derived {
181
215
check_unsafe_derive_deserialize ( cx, item, trait_ref, ty) ;
216
+ check_partial_eq_without_eq ( cx, item. span , trait_ref, ty) ;
182
217
} else {
183
218
check_copy_clone ( cx, item, trait_ref, ty) ;
184
219
}
@@ -419,3 +454,36 @@ 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 > ( 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