Skip to content

Commit 7572b6b

Browse files
committed
Auto merge of #8869 - Jarcho:derive_partial_eq_without_eq, r=flip1995
Set correct `ParamEnv` for `derive_partial_eq_without_eq` fixes #8867 changelog: Handle differing predicates applied by `#[derive(PartialEq)]` and `#[derive(Eq)]` in `derive_partial_eq_without_eq`
2 parents c4c413b + 5cf07c6 commit 7572b6b

File tree

4 files changed

+67
-14
lines changed

4 files changed

+67
-14
lines changed

clippy_lints/src/derive.rs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
22
use clippy_utils::paths;
3-
use clippy_utils::ty::{implements_trait, is_copy};
3+
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
44
use clippy_utils::{is_lint_allowed, match_def_path};
55
use if_chain::if_chain;
66
use rustc_errors::Applicability;
77
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
88
use rustc_hir::{
9-
BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
9+
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety,
1010
};
1111
use rustc_lint::{LateContext, LateLintPass};
1212
use rustc_middle::hir::nested_filter;
13-
use rustc_middle::ty::{self, Ty};
13+
use rustc_middle::ty::subst::GenericArg;
14+
use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty};
1415
use rustc_session::{declare_lint_pass, declare_tool_lint};
1516
use rustc_span::source_map::Span;
1617
use rustc_span::sym;
@@ -224,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
224225
fn check_hash_peq<'tcx>(
225226
cx: &LateContext<'tcx>,
226227
span: Span,
227-
trait_ref: &TraitRef<'_>,
228+
trait_ref: &hir::TraitRef<'_>,
228229
ty: Ty<'tcx>,
229230
hash_is_automatically_derived: bool,
230231
) {
@@ -277,7 +278,7 @@ fn check_hash_peq<'tcx>(
277278
fn check_ord_partial_ord<'tcx>(
278279
cx: &LateContext<'tcx>,
279280
span: Span,
280-
trait_ref: &TraitRef<'_>,
281+
trait_ref: &hir::TraitRef<'_>,
281282
ty: Ty<'tcx>,
282283
ord_is_automatically_derived: bool,
283284
) {
@@ -328,7 +329,7 @@ fn check_ord_partial_ord<'tcx>(
328329
}
329330

330331
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
331-
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
332+
fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
332333
let clone_id = match cx.tcx.lang_items().clone_trait() {
333334
Some(id) if trait_ref.trait_def_id() == Some(id) => id,
334335
_ => return,
@@ -378,7 +379,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T
378379
fn check_unsafe_derive_deserialize<'tcx>(
379380
cx: &LateContext<'tcx>,
380381
item: &Item<'_>,
381-
trait_ref: &TraitRef<'_>,
382+
trait_ref: &hir::TraitRef<'_>,
382383
ty: Ty<'tcx>,
383384
) {
384385
fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
@@ -455,13 +456,41 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
455456
}
456457

457458
/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
458-
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
459+
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
459460
if_chain! {
460461
if let ty::Adt(adt, substs) = ty.kind();
461462
if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
463+
if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq);
462464
if let Some(def_id) = trait_ref.trait_def_id();
463465
if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
464-
if !implements_trait(cx, ty, eq_trait_def_id, substs);
466+
// New `ParamEnv` replacing `T: PartialEq` with `T: Eq`
467+
let param_env = ParamEnv::new(
468+
cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| {
469+
let kind = p.kind();
470+
match kind.skip_binder() {
471+
PredicateKind::Trait(p)
472+
if p.trait_ref.def_id == peq_trait_def_id
473+
&& p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1)
474+
&& matches!(p.trait_ref.self_ty().kind(), ty::Param(_))
475+
&& p.constness == BoundConstness::NotConst
476+
&& p.polarity == ImplPolarity::Positive =>
477+
{
478+
cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate {
479+
trait_ref: TraitRef::new(
480+
eq_trait_def_id,
481+
cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()),
482+
),
483+
constness: BoundConstness::NotConst,
484+
polarity: ImplPolarity::Positive,
485+
})))
486+
},
487+
_ => p,
488+
}
489+
})),
490+
cx.param_env.reveal(),
491+
cx.param_env.constness(),
492+
);
493+
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs);
465494
then {
466495
// If all of our fields implement `Eq`, we can implement `Eq` too
467496
for variant in adt.variants() {

clippy_utils/src/ty.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use rustc_lint::LateContext;
1313
use rustc_middle::mir::interpret::{ConstValue, Scalar};
1414
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
1515
use rustc_middle::ty::{
16-
self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr,
16+
self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
17+
VariantDiscr,
1718
};
1819
use rustc_span::symbol::Ident;
1920
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
@@ -151,18 +152,29 @@ pub fn implements_trait<'tcx>(
151152
ty: Ty<'tcx>,
152153
trait_id: DefId,
153154
ty_params: &[GenericArg<'tcx>],
155+
) -> bool {
156+
implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params)
157+
}
158+
159+
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
160+
pub fn implements_trait_with_env<'tcx>(
161+
tcx: TyCtxt<'tcx>,
162+
param_env: ParamEnv<'tcx>,
163+
ty: Ty<'tcx>,
164+
trait_id: DefId,
165+
ty_params: &[GenericArg<'tcx>],
154166
) -> bool {
155167
// Clippy shouldn't have infer types
156168
assert!(!ty.needs_infer());
157169

158-
let ty = cx.tcx.erase_regions(ty);
170+
let ty = tcx.erase_regions(ty);
159171
if ty.has_escaping_bound_vars() {
160172
return false;
161173
}
162-
let ty_params = cx.tcx.mk_substs(ty_params.iter());
163-
cx.tcx.infer_ctxt().enter(|infcx| {
174+
let ty_params = tcx.mk_substs(ty_params.iter());
175+
tcx.infer_ctxt().enter(|infcx| {
164176
infcx
165-
.type_implements_trait(trait_id, ty, ty_params, cx.param_env)
177+
.type_implements_trait(trait_id, ty, ty_params, param_env)
166178
.must_apply_modulo_regions()
167179
})
168180
}

tests/ui/derive_partial_eq_without_eq.fixed

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,10 @@ enum EnumNotEq {
9595
#[derive(Debug, PartialEq, Eq, Clone)]
9696
struct RustFixWithOtherDerives;
9797

98+
#[derive(PartialEq)]
99+
struct Generic<T>(T);
100+
101+
#[derive(PartialEq, Eq)]
102+
struct GenericPhantom<T>(core::marker::PhantomData<T>);
103+
98104
fn main() {}

tests/ui/derive_partial_eq_without_eq.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,10 @@ enum EnumNotEq {
9595
#[derive(Debug, PartialEq, Clone)]
9696
struct RustFixWithOtherDerives;
9797

98+
#[derive(PartialEq)]
99+
struct Generic<T>(T);
100+
101+
#[derive(PartialEq, Eq)]
102+
struct GenericPhantom<T>(core::marker::PhantomData<T>);
103+
98104
fn main() {}

0 commit comments

Comments
 (0)