1 use clippy_utils
::diagnostics
::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}
;
2 use clippy_utils
::ty
::{implements_trait, implements_trait_with_env, is_copy}
;
3 use clippy_utils
::{is_lint_allowed, match_def_path, paths}
;
4 use if_chain
::if_chain
;
5 use rustc_errors
::Applicability
;
6 use rustc_hir
::def_id
::DefId
;
7 use rustc_hir
::intravisit
::{walk_expr, walk_fn, walk_item, FnKind, Visitor}
;
9 self as hir
, BlockCheckMode
, BodyId
, Expr
, ExprKind
, FnDecl
, Impl
, Item
, ItemKind
, UnsafeSource
, Unsafety
,
11 use rustc_lint
::{LateContext, LateLintPass}
;
12 use rustc_middle
::hir
::nested_filter
;
13 use rustc_middle
::traits
::Reveal
;
14 use rustc_middle
::ty
::{
15 self, ClauseKind
, GenericArgKind
, GenericParamDefKind
, ImplPolarity
, ParamEnv
, ToPredicate
, TraitPredicate
, Ty
,
18 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
19 use rustc_span
::def_id
::LocalDefId
;
20 use rustc_span
::source_map
::Span
;
23 declare_clippy_lint
! {
25 /// Lints against manual `PartialEq` implementations for types with a derived `Hash`
28 /// ### Why is this bad?
29 /// The implementation of these traits must agree (for
30 /// example for use with `HashMap`) so it’s probably a bad idea to use a
31 /// default-generated `Hash` implementation with an explicitly defined
32 /// `PartialEq`. In particular, the following must hold for any type:
35 /// k1 == k2 ⇒ hash(k1) == hash(k2)
43 /// impl PartialEq for Foo {
47 #[clippy::version = "pre 1.29.0"]
48 pub DERIVED_HASH_WITH_MANUAL_EQ
,
50 "deriving `Hash` but implementing `PartialEq` explicitly"
53 declare_clippy_lint
! {
55 /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
56 /// or `PartialOrd` implementation.
58 /// ### Why is this bad?
59 /// The implementation of these traits must agree (for
60 /// example for use with `sort`) so it’s probably a bad idea to use a
61 /// default-generated `Ord` implementation with an explicitly defined
62 /// `PartialOrd`. In particular, the following must hold for any type
63 /// implementing `Ord`:
66 /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
71 /// #[derive(Ord, PartialEq, Eq)]
74 /// impl PartialOrd for Foo {
80 /// #[derive(PartialEq, Eq)]
83 /// impl PartialOrd for Foo {
84 /// fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
85 /// Some(self.cmp(other))
89 /// impl Ord for Foo {
93 /// or, if you don't need a custom ordering:
95 /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
98 #[clippy::version = "1.47.0"]
99 pub DERIVE_ORD_XOR_PARTIAL_ORD
,
101 "deriving `Ord` but implementing `PartialOrd` explicitly"
104 declare_clippy_lint
! {
106 /// Checks for explicit `Clone` implementations for `Copy`
109 /// ### Why is this bad?
110 /// To avoid surprising behavior, these traits should
111 /// agree and the behavior of `Copy` cannot be overridden. In almost all
112 /// situations a `Copy` type should have a `Clone` implementation that does
113 /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
121 /// impl Clone for Foo {
125 #[clippy::version = "pre 1.29.0"]
126 pub EXPL_IMPL_CLONE_ON_COPY
,
128 "implementing `Clone` explicitly on `Copy` types"
131 declare_clippy_lint
! {
133 /// Checks for deriving `serde::Deserialize` on a type that
134 /// has methods using `unsafe`.
136 /// ### Why is this bad?
137 /// Deriving `serde::Deserialize` will create a constructor
138 /// that may violate invariants hold by another constructor.
142 /// use serde::Deserialize;
144 /// #[derive(Deserialize)]
150 /// pub fn new() -> Self {
154 /// pub unsafe fn parts() -> (&str, &str) {
155 /// // assumes invariants hold
159 #[clippy::version = "1.45.0"]
160 pub UNSAFE_DERIVE_DESERIALIZE
,
162 "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
165 declare_clippy_lint
! {
167 /// Checks for types that derive `PartialEq` and could implement `Eq`.
169 /// ### Why is this bad?
170 /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
171 /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
172 /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
177 /// #[derive(PartialEq)]
180 /// i_am_eq_too: Vec<String>,
185 /// #[derive(PartialEq, Eq)]
188 /// i_am_eq_too: Vec<String>,
191 #[clippy::version = "1.63.0"]
192 pub DERIVE_PARTIAL_EQ_WITHOUT_EQ
,
194 "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
197 declare_lint_pass
!(Derive
=> [
198 EXPL_IMPL_CLONE_ON_COPY
,
199 DERIVED_HASH_WITH_MANUAL_EQ
,
200 DERIVE_ORD_XOR_PARTIAL_ORD
,
201 UNSAFE_DERIVE_DESERIALIZE
,
202 DERIVE_PARTIAL_EQ_WITHOUT_EQ
205 impl<'tcx
> LateLintPass
<'tcx
> for Derive
{
206 fn check_item(&mut self, cx
: &LateContext
<'tcx
>, item
: &'tcx Item
<'_
>) {
207 if let ItemKind
::Impl(Impl
{
208 of_trait
: Some(ref trait_ref
),
212 let ty
= cx
.tcx
.type_of(item
.owner_id
).instantiate_identity();
213 let is_automatically_derived
= cx
.tcx
.has_attr(item
.owner_id
, sym
::automatically_derived
);
215 check_hash_peq(cx
, item
.span
, trait_ref
, ty
, is_automatically_derived
);
216 check_ord_partial_ord(cx
, item
.span
, trait_ref
, ty
, is_automatically_derived
);
218 if is_automatically_derived
{
219 check_unsafe_derive_deserialize(cx
, item
, trait_ref
, ty
);
220 check_partial_eq_without_eq(cx
, item
.span
, trait_ref
, ty
);
222 check_copy_clone(cx
, item
, trait_ref
, ty
);
228 /// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint.
229 fn check_hash_peq
<'tcx
>(
230 cx
: &LateContext
<'tcx
>,
232 trait_ref
: &hir
::TraitRef
<'_
>,
234 hash_is_automatically_derived
: bool
,
237 if let Some(peq_trait_def_id
) = cx
.tcx
.lang_items().eq_trait();
238 if let Some(def_id
) = trait_ref
.trait_def_id();
239 if cx
.tcx
.is_diagnostic_item(sym
::Hash
, def_id
);
241 // Look for the PartialEq implementations for `ty`
242 cx
.tcx
.for_each_relevant_impl(peq_trait_def_id
, ty
, |impl_id
| {
243 let peq_is_automatically_derived
= cx
.tcx
.has_attr(impl_id
, sym
::automatically_derived
);
245 if !hash_is_automatically_derived
|| peq_is_automatically_derived
{
249 let trait_ref
= cx
.tcx
.impl_trait_ref(impl_id
).expect("must be a trait implementation");
251 // Only care about `impl PartialEq<Foo> for Foo`
252 // For `impl PartialEq<B> for A, input_types is [A, B]
253 if trait_ref
.instantiate_identity().args
.type_at(1) == ty
{
256 DERIVED_HASH_WITH_MANUAL_EQ
,
258 "you are deriving `Hash` but have implemented `PartialEq` explicitly",
260 if let Some(local_def_id
) = impl_id
.as_local() {
261 let hir_id
= cx
.tcx
.hir().local_def_id_to_hir_id(local_def_id
);
263 cx
.tcx
.hir().span(hir_id
),
264 "`PartialEq` implemented here"
275 /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
276 fn check_ord_partial_ord
<'tcx
>(
277 cx
: &LateContext
<'tcx
>,
279 trait_ref
: &hir
::TraitRef
<'_
>,
281 ord_is_automatically_derived
: bool
,
284 if let Some(ord_trait_def_id
) = cx
.tcx
.get_diagnostic_item(sym
::Ord
);
285 if let Some(partial_ord_trait_def_id
) = cx
.tcx
.lang_items().partial_ord_trait();
286 if let Some(def_id
) = &trait_ref
.trait_def_id();
287 if *def_id
== ord_trait_def_id
;
289 // Look for the PartialOrd implementations for `ty`
290 cx
.tcx
.for_each_relevant_impl(partial_ord_trait_def_id
, ty
, |impl_id
| {
291 let partial_ord_is_automatically_derived
= cx
.tcx
.has_attr(impl_id
, sym
::automatically_derived
);
293 if partial_ord_is_automatically_derived
== ord_is_automatically_derived
{
297 let trait_ref
= cx
.tcx
.impl_trait_ref(impl_id
).expect("must be a trait implementation");
299 // Only care about `impl PartialOrd<Foo> for Foo`
300 // For `impl PartialOrd<B> for A, input_types is [A, B]
301 if trait_ref
.instantiate_identity().args
.type_at(1) == ty
{
302 let mess
= if partial_ord_is_automatically_derived
{
303 "you are implementing `Ord` explicitly but have derived `PartialOrd`"
305 "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
310 DERIVE_ORD_XOR_PARTIAL_ORD
,
314 if let Some(local_def_id
) = impl_id
.as_local() {
315 let hir_id
= cx
.tcx
.hir().local_def_id_to_hir_id(local_def_id
);
317 cx
.tcx
.hir().span(hir_id
),
318 "`PartialOrd` implemented here"
329 /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
330 fn check_copy_clone
<'tcx
>(cx
: &LateContext
<'tcx
>, item
: &Item
<'_
>, trait_ref
: &hir
::TraitRef
<'_
>, ty
: Ty
<'tcx
>) {
331 let clone_id
= match cx
.tcx
.lang_items().clone_trait() {
332 Some(id
) if trait_ref
.trait_def_id() == Some(id
) => id
,
335 let Some(copy_id
) = cx
.tcx
.lang_items().copy_trait() else {
338 let (ty_adt
, ty_subs
) = match *ty
.kind() {
339 // Unions can't derive clone.
340 ty
::Adt(adt
, subs
) if !adt
.is_union() => (adt
, subs
),
343 // If the current self type doesn't implement Copy (due to generic constraints), search to see if
344 // there's a Copy impl for any instance of the adt.
345 if !is_copy(cx
, ty
) {
346 if ty_subs
.non_erasable_generics(cx
.tcx
, ty_adt
.did()).next().is_some() {
347 let has_copy_impl
= cx
.tcx
.all_local_trait_impls(()).get(©_id
).map_or(false, |impls
| {
348 impls
.iter().any(|&id
| {
349 matches
!(cx
.tcx
.type_of(id
).instantiate_identity().kind(), ty
::Adt(adt
, _
)
350 if ty_adt
.did() == adt
.did())
360 // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
362 if ty_subs
.types().any(|ty
| !implements_trait(cx
, ty
, clone_id
, &[])) {
365 // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`.
366 // https://github.com/rust-lang/rust-clippy/issues/10188
367 if ty_adt
.repr().packed()
370 .any(|arg
| matches
!(arg
.unpack(), GenericArgKind
::Type(_
) | GenericArgKind
::Const(_
)))
377 EXPL_IMPL_CLONE_ON_COPY
,
379 "you are implementing `Clone` explicitly on a `Copy` type",
381 "consider deriving `Clone` or removing `Copy`",
385 /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
386 fn check_unsafe_derive_deserialize
<'tcx
>(
387 cx
: &LateContext
<'tcx
>,
389 trait_ref
: &hir
::TraitRef
<'_
>,
392 fn has_unsafe
<'tcx
>(cx
: &LateContext
<'tcx
>, item
: &'tcx Item
<'_
>) -> bool
{
393 let mut visitor
= UnsafeVisitor { cx, has_unsafe: false }
;
394 walk_item(&mut visitor
, item
);
399 if let Some(trait_def_id
) = trait_ref
.trait_def_id();
400 if match_def_path(cx
, trait_def_id
, &paths
::SERDE_DESERIALIZE
);
401 if let ty
::Adt(def
, _
) = ty
.kind();
402 if let Some(local_def_id
) = def
.did().as_local();
403 let adt_hir_id
= cx
.tcx
.hir().local_def_id_to_hir_id(local_def_id
);
404 if !is_lint_allowed(cx
, UNSAFE_DERIVE_DESERIALIZE
, adt_hir_id
);
405 if cx
.tcx
.inherent_impls(def
.did())
407 .map(|imp_did
| cx
.tcx
.hir().expect_item(imp_did
.expect_local()))
408 .any(|imp
| has_unsafe(cx
, imp
));
412 UNSAFE_DERIVE_DESERIALIZE
,
414 "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
416 "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
422 struct UnsafeVisitor
<'a
, 'tcx
> {
423 cx
: &'a LateContext
<'tcx
>,
427 impl<'tcx
> Visitor
<'tcx
> for UnsafeVisitor
<'_
, 'tcx
> {
428 type NestedFilter
= nested_filter
::All
;
430 fn visit_fn(&mut self, kind
: FnKind
<'tcx
>, decl
: &'tcx FnDecl
<'_
>, body_id
: BodyId
, _
: Span
, id
: LocalDefId
) {
436 if let Some(header
) = kind
.header();
437 if header
.unsafety
== Unsafety
::Unsafe
;
439 self.has_unsafe
= true;
443 walk_fn(self, kind
, decl
, body_id
, id
);
446 fn visit_expr(&mut self, expr
: &'tcx Expr
<'_
>) {
451 if let ExprKind
::Block(block
, _
) = expr
.kind
{
452 if block
.rules
== BlockCheckMode
::UnsafeBlock(UnsafeSource
::UserProvided
) {
453 self.has_unsafe
= true;
457 walk_expr(self, expr
);
460 fn nested_visit_map(&mut self) -> Self::Map
{
465 /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
466 fn check_partial_eq_without_eq
<'tcx
>(cx
: &LateContext
<'tcx
>, span
: Span
, trait_ref
: &hir
::TraitRef
<'_
>, ty
: Ty
<'tcx
>) {
468 if let ty
::Adt(adt
, args
) = ty
.kind();
469 if cx
.tcx
.visibility(adt
.did()).is_public();
470 if let Some(eq_trait_def_id
) = cx
.tcx
.get_diagnostic_item(sym
::Eq
);
471 if let Some(def_id
) = trait_ref
.trait_def_id();
472 if cx
.tcx
.is_diagnostic_item(sym
::PartialEq
, def_id
);
473 let param_env
= param_env_for_derived_eq(cx
.tcx
, adt
.did(), eq_trait_def_id
);
474 if !implements_trait_with_env(cx
.tcx
, param_env
, ty
, eq_trait_def_id
, &[]);
475 // If all of our fields implement `Eq`, we can implement `Eq` too
478 .map(|f
| f
.ty(cx
.tcx
, args
))
479 .all(|ty
| implements_trait_with_env(cx
.tcx
, param_env
, ty
, eq_trait_def_id
, &[]));
483 DERIVE_PARTIAL_EQ_WITHOUT_EQ
,
484 span
.ctxt().outer_expn_data().call_site
,
485 "you are deriving `PartialEq` and can implement `Eq`",
486 "consider deriving `Eq` as well",
487 "PartialEq, Eq".to_string(),
488 Applicability
::MachineApplicable
,
494 /// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
495 fn param_env_for_derived_eq(tcx
: TyCtxt
<'_
>, did
: DefId
, eq_trait_id
: DefId
) -> ParamEnv
<'_
> {
496 // Initial map from generic index to param def.
497 // Vec<(param_def, needs_eq)>
502 .map(|p
| (p
, matches
!(p
.kind
, GenericParamDefKind
::Type { .. }
)))
503 .collect
::<Vec
<_
>>();
505 let ty_predicates
= tcx
.predicates_of(did
).predicates
;
506 for (p
, _
) in ty_predicates
{
507 if let ClauseKind
::Trait(p
) = p
.kind().skip_binder()
508 && p
.trait_ref
.def_id
== eq_trait_id
509 && let ty
::Param(self_ty
) = p
.trait_ref
.self_ty().kind()
511 // Flag types which already have an `Eq` bound.
512 params
[self_ty
.index
as usize].1 = false;
517 tcx
.mk_clauses_from_iter(ty_predicates
.iter().map(|&(p
, _
)| p
).chain(
518 params
.iter().filter(|&&(_
, needs_eq
)| needs_eq
).map(|&(param
, _
)| {
519 ClauseKind
::Trait(TraitPredicate
{
520 trait_ref
: ty
::TraitRef
::new(tcx
, eq_trait_id
, [tcx
.mk_param_from_def(param
)]),
521 polarity
: ImplPolarity
::Positive
,