Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions clippy_lints/src/derive/derive_ord_xor_partial_ord.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir as hir;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use rustc_hir::{self as hir, HirId};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::{Span, sym};
Expand All @@ -12,6 +12,7 @@ pub(super) fn check<'tcx>(
span: Span,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
adt_hir_id: HirId,
ord_is_automatically_derived: bool,
) {
if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
Expand All @@ -38,7 +39,7 @@ pub(super) fn check<'tcx>(
"you are deriving `Ord` but have implemented `PartialOrd` explicitly"
};

span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| {
span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| {
if let Some(local_def_id) = impl_id.as_local() {
let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here");
Expand Down
13 changes: 9 additions & 4 deletions clippy_lints/src/derive/derive_partial_eq_without_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::has_non_exhaustive_attr;
use clippy_utils::ty::implements_trait_with_env;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast};
use rustc_span::{Span, sym};

use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ;

/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
adt_hir_id: HirId,
) {
if let ty::Adt(adt, args) = ty.kind()
&& cx.tcx.visibility(adt.did()).is_public()
&& let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq)
Expand All @@ -20,7 +26,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T
&& !has_non_exhaustive_attr(cx.tcx, *adt)
&& !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id)
&& let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
&& let Some(local_def_id) = adt.did().as_local()
// If all of our fields implement `Eq`, we can implement `Eq` too
&& adt
.all_fields()
Expand All @@ -30,7 +35,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::T
span_lint_hir_and_then(
cx,
DERIVE_PARTIAL_EQ_WITHOUT_EQ,
cx.tcx.local_def_id_to_hir_id(local_def_id),
adt_hir_id,
span.ctxt().outer_expn_data().call_site,
"you are deriving `PartialEq` and can implement `Eq`",
|diag| {
Expand Down
10 changes: 6 additions & 4 deletions clippy_lints/src/derive/derived_hash_with_manual_eq.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir as hir;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use rustc_hir::{HirId, TraitRef};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::{Span, sym};
Expand All @@ -10,8 +10,9 @@ use super::DERIVED_HASH_WITH_MANUAL_EQ;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
trait_ref: &hir::TraitRef<'_>,
trait_ref: &TraitRef<'_>,
ty: Ty<'tcx>,
adt_hir_id: HirId,
hash_is_automatically_derived: bool,
) {
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait()
Expand All @@ -31,9 +32,10 @@ pub(super) fn check<'tcx>(
// Only care about `impl PartialEq<Foo> for Foo`
// For `impl PartialEq<B> for A, input_types is [A, B]
if trait_ref.instantiate_identity().args.type_at(1) == ty {
span_lint_and_then(
span_lint_hir_and_then(
cx,
DERIVED_HASH_WITH_MANUAL_EQ,
adt_hir_id,
span,
"you are deriving `Hash` but have implemented `PartialEq` explicitly",
|diag| {
Expand Down
20 changes: 14 additions & 6 deletions clippy_lints/src/derive/expl_impl_clone_on_copy.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_hir::{self as hir, Item};
use rustc_hir::{self as hir, HirId, Item};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind, Ty};

use super::EXPL_IMPL_CLONE_ON_COPY;

/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
item: &Item<'_>,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
adt_hir_id: HirId,
) {
let clone_id = match cx.tcx.lang_items().clone_trait() {
Some(id) if trait_ref.trait_def_id() == Some(id) => id,
_ => return,
Expand Down Expand Up @@ -54,12 +60,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
return;
}

span_lint_and_note(
span_lint_hir_and_then(
cx,
EXPL_IMPL_CLONE_ON_COPY,
adt_hir_id,
item.span,
"you are implementing `Clone` explicitly on a `Copy` type",
Some(item.span),
"consider deriving `Clone` or removing `Copy`",
|diag| {
diag.span_help(item.span, "consider deriving `Clone` or removing `Copy`");
},
);
}
16 changes: 11 additions & 5 deletions clippy_lints/src/derive/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use clippy_utils::path_res;
use rustc_hir::def::Res;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
Expand Down Expand Up @@ -194,21 +196,25 @@ 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) = path_res(cx, self_ty)
&& 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, is_automatically_derived);
derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived);
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.span, trait_ref, ty, adt_hir_id, is_automatically_derived);

if is_automatically_derived {
unsafe_derive_deserialize::check(cx, item, trait_ref, ty);
derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty);
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);
expl_impl_clone_on_copy::check(cx, item, trait_ref, ty, adt_hir_id);
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions clippy_lints/src/derive/unsafe_derive_deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::{is_lint_allowed, paths};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource};
use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Item, UnsafeSource};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty};
Expand All @@ -13,7 +13,13 @@ use rustc_span::{Span, sym};
use super::UNSAFE_DERIVE_DESERIALIZE;

/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
item: &Item<'_>,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
adt_hir_id: HirId,
) {
fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
let mut visitor = UnsafeVisitor { cx };
walk_item(&mut visitor, item).is_break()
Expand All @@ -22,8 +28,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
if let Some(trait_def_id) = trait_ref.trait_def_id()
&& paths::SERDE_DESERIALIZE.matches(cx, trait_def_id)
&& let ty::Adt(def, _) = ty.kind()
&& let Some(local_def_id) = def.did().as_local()
&& let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
&& !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id)
&& cx
.tcx
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,16 @@ fn issue14558() {
}

fn main() {}

mod issue15708 {
// Check that the lint posts on the type definition node
#[expect(clippy::expl_impl_clone_on_copy)]
#[derive(Copy)]
struct S;

impl Clone for S {
fn clone(&self) -> Self {
S
}
}
}
10 changes: 5 additions & 5 deletions tests/ui/derive.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | | fn clone(&self) -> Self {
LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
help: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:15:1
|
LL | / impl Clone for Qux {
Expand All @@ -33,7 +33,7 @@ LL | | fn clone(&self) -> Self {
LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
help: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:41:1
|
LL | / impl<'a> Clone for Lt<'a> {
Expand All @@ -55,7 +55,7 @@ LL | | fn clone(&self) -> Self {
LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
help: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:54:1
|
LL | / impl Clone for BigArray {
Expand All @@ -77,7 +77,7 @@ LL | | fn clone(&self) -> Self {
LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
help: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:67:1
|
LL | / impl Clone for FnPtr {
Expand All @@ -99,7 +99,7 @@ LL | | fn clone(&self) -> Self {
LL | | }
| |_^
|
note: consider deriving `Clone` or removing `Copy`
help: consider deriving `Clone` or removing `Copy`
--> tests/ui/derive.rs:89:1
|
LL | / impl<T: Clone> Clone for Generic2<T> {
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/derive_ord_xor_partial_ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,18 @@ mod use_ord {
}

fn main() {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

mod issue15708 {
use std::cmp::{Ord, Ordering};

// Check that the lint posts on the type definition node
#[expect(clippy::derive_ord_xor_partial_ord)]
#[derive(PartialOrd, PartialEq, Eq)]
struct DerivePartialOrdInUseOrd;

impl Ord for DerivePartialOrdInUseOrd {
fn cmp(&self, other: &Self) -> Ordering {
Ordering::Less
}
}
}
16 changes: 16 additions & 0 deletions tests/ui/derived_hash_with_manual_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,19 @@ impl std::hash::Hash for Bah {
}

fn main() {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…and here

mod issue15708 {
// Check that the lint posts on the type definition node
#[expect(clippy::derived_hash_with_manual_eq)]
#[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
start: usize,
end: usize,
}

impl PartialEq for Span {
fn eq(&self, other: &Self) -> bool {
self.start.cmp(&other.start).then(self.end.cmp(&other.end)).is_eq()
}
}
}