]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/derive.rs
Merge commit 'ac0e10aa68325235069a842f47499852b2dee79e' into clippyup
[rust.git] / src / tools / clippy / clippy_lints / src / derive.rs
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::paths;
3 use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
4 use clippy_utils::{is_lint_allowed, match_def_path};
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::def_id::DefId;
8 use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
9 use rustc_hir::{
10     self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
11     Unsafety,
12 };
13 use rustc_lint::{LateContext, LateLintPass};
14 use rustc_middle::hir::nested_filter;
15 use rustc_middle::traits::Reveal;
16 use rustc_middle::ty::{
17     self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
18     Ty, TyCtxt,
19 };
20 use rustc_session::{declare_lint_pass, declare_tool_lint};
21 use rustc_span::source_map::Span;
22 use rustc_span::sym;
23
24 declare_clippy_lint! {
25     /// ### What it does
26     /// Checks for deriving `Hash` but implementing `PartialEq`
27     /// explicitly or vice versa.
28     ///
29     /// ### Why is this bad?
30     /// The implementation of these traits must agree (for
31     /// example for use with `HashMap`) so it’s probably a bad idea to use a
32     /// default-generated `Hash` implementation with an explicitly defined
33     /// `PartialEq`. In particular, the following must hold for any type:
34     ///
35     /// ```text
36     /// k1 == k2 ⇒ hash(k1) == hash(k2)
37     /// ```
38     ///
39     /// ### Example
40     /// ```ignore
41     /// #[derive(Hash)]
42     /// struct Foo;
43     ///
44     /// impl PartialEq for Foo {
45     ///     ...
46     /// }
47     /// ```
48     #[clippy::version = "pre 1.29.0"]
49     pub DERIVE_HASH_XOR_EQ,
50     correctness,
51     "deriving `Hash` but implementing `PartialEq` explicitly"
52 }
53
54 declare_clippy_lint! {
55     /// ### What it does
56     /// Checks for deriving `Ord` but implementing `PartialOrd`
57     /// explicitly or vice versa.
58     ///
59     /// ### Why is this bad?
60     /// The implementation of these traits must agree (for
61     /// example for use with `sort`) so it’s probably a bad idea to use a
62     /// default-generated `Ord` implementation with an explicitly defined
63     /// `PartialOrd`. In particular, the following must hold for any type
64     /// implementing `Ord`:
65     ///
66     /// ```text
67     /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
68     /// ```
69     ///
70     /// ### Example
71     /// ```rust,ignore
72     /// #[derive(Ord, PartialEq, Eq)]
73     /// struct Foo;
74     ///
75     /// impl PartialOrd for Foo {
76     ///     ...
77     /// }
78     /// ```
79     /// Use instead:
80     /// ```rust,ignore
81     /// #[derive(PartialEq, Eq)]
82     /// struct Foo;
83     ///
84     /// impl PartialOrd for Foo {
85     ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
86     ///        Some(self.cmp(other))
87     ///     }
88     /// }
89     ///
90     /// impl Ord for Foo {
91     ///     ...
92     /// }
93     /// ```
94     /// or, if you don't need a custom ordering:
95     /// ```rust,ignore
96     /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
97     /// struct Foo;
98     /// ```
99     #[clippy::version = "1.47.0"]
100     pub DERIVE_ORD_XOR_PARTIAL_ORD,
101     correctness,
102     "deriving `Ord` but implementing `PartialOrd` explicitly"
103 }
104
105 declare_clippy_lint! {
106     /// ### What it does
107     /// Checks for explicit `Clone` implementations for `Copy`
108     /// types.
109     ///
110     /// ### Why is this bad?
111     /// To avoid surprising behavior, these traits should
112     /// agree and the behavior of `Copy` cannot be overridden. In almost all
113     /// situations a `Copy` type should have a `Clone` implementation that does
114     /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
115     /// gets you.
116     ///
117     /// ### Example
118     /// ```rust,ignore
119     /// #[derive(Copy)]
120     /// struct Foo;
121     ///
122     /// impl Clone for Foo {
123     ///     // ..
124     /// }
125     /// ```
126     #[clippy::version = "pre 1.29.0"]
127     pub EXPL_IMPL_CLONE_ON_COPY,
128     pedantic,
129     "implementing `Clone` explicitly on `Copy` types"
130 }
131
132 declare_clippy_lint! {
133     /// ### What it does
134     /// Checks for deriving `serde::Deserialize` on a type that
135     /// has methods using `unsafe`.
136     ///
137     /// ### Why is this bad?
138     /// Deriving `serde::Deserialize` will create a constructor
139     /// that may violate invariants hold by another constructor.
140     ///
141     /// ### Example
142     /// ```rust,ignore
143     /// use serde::Deserialize;
144     ///
145     /// #[derive(Deserialize)]
146     /// pub struct Foo {
147     ///     // ..
148     /// }
149     ///
150     /// impl Foo {
151     ///     pub fn new() -> Self {
152     ///         // setup here ..
153     ///     }
154     ///
155     ///     pub unsafe fn parts() -> (&str, &str) {
156     ///         // assumes invariants hold
157     ///     }
158     /// }
159     /// ```
160     #[clippy::version = "1.45.0"]
161     pub UNSAFE_DERIVE_DESERIALIZE,
162     pedantic,
163     "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
164 }
165
166 declare_clippy_lint! {
167     /// ### What it does
168     /// Checks for types that derive `PartialEq` and could implement `Eq`.
169     ///
170     /// ### Why is this bad?
171     /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
172     /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
173     /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
174     /// `Eq` themselves.
175     ///
176     /// ### Example
177     /// ```rust
178     /// #[derive(PartialEq)]
179     /// struct Foo {
180     ///     i_am_eq: i32,
181     ///     i_am_eq_too: Vec<String>,
182     /// }
183     /// ```
184     /// Use instead:
185     /// ```rust
186     /// #[derive(PartialEq, Eq)]
187     /// struct Foo {
188     ///     i_am_eq: i32,
189     ///     i_am_eq_too: Vec<String>,
190     /// }
191     /// ```
192     #[clippy::version = "1.63.0"]
193     pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
194     nursery,
195     "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
196 }
197
198 declare_lint_pass!(Derive => [
199     EXPL_IMPL_CLONE_ON_COPY,
200     DERIVE_HASH_XOR_EQ,
201     DERIVE_ORD_XOR_PARTIAL_ORD,
202     UNSAFE_DERIVE_DESERIALIZE,
203     DERIVE_PARTIAL_EQ_WITHOUT_EQ
204 ]);
205
206 impl<'tcx> LateLintPass<'tcx> for Derive {
207     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
208         if let ItemKind::Impl(Impl {
209             of_trait: Some(ref trait_ref),
210             ..
211         }) = item.kind
212         {
213             let ty = cx.tcx.type_of(item.def_id);
214             let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
215
216             check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
217             check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
218
219             if is_automatically_derived {
220                 check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
221                 check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
222             } else {
223                 check_copy_clone(cx, item, trait_ref, ty);
224             }
225         }
226     }
227 }
228
229 /// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
230 fn check_hash_peq<'tcx>(
231     cx: &LateContext<'tcx>,
232     span: Span,
233     trait_ref: &hir::TraitRef<'_>,
234     ty: Ty<'tcx>,
235     hash_is_automatically_derived: bool,
236 ) {
237     if_chain! {
238         if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
239         if let Some(def_id) = trait_ref.trait_def_id();
240         if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
241         then {
242             // Look for the PartialEq implementations for `ty`
243             cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
244                 let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
245
246                 if peq_is_automatically_derived == hash_is_automatically_derived {
247                     return;
248                 }
249
250                 let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
251
252                 // Only care about `impl PartialEq<Foo> for Foo`
253                 // For `impl PartialEq<B> for A, input_types is [A, B]
254                 if trait_ref.substs.type_at(1) == ty {
255                     let mess = if peq_is_automatically_derived {
256                         "you are implementing `Hash` explicitly but have derived `PartialEq`"
257                     } else {
258                         "you are deriving `Hash` but have implemented `PartialEq` explicitly"
259                     };
260
261                     span_lint_and_then(
262                         cx,
263                         DERIVE_HASH_XOR_EQ,
264                         span,
265                         mess,
266                         |diag| {
267                             if let Some(local_def_id) = impl_id.as_local() {
268                                 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
269                                 diag.span_note(
270                                     cx.tcx.hir().span(hir_id),
271                                     "`PartialEq` implemented here"
272                                 );
273                             }
274                         }
275                     );
276                 }
277             });
278         }
279     }
280 }
281
282 /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
283 fn check_ord_partial_ord<'tcx>(
284     cx: &LateContext<'tcx>,
285     span: Span,
286     trait_ref: &hir::TraitRef<'_>,
287     ty: Ty<'tcx>,
288     ord_is_automatically_derived: bool,
289 ) {
290     if_chain! {
291         if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
292         if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
293         if let Some(def_id) = &trait_ref.trait_def_id();
294         if *def_id == ord_trait_def_id;
295         then {
296             // Look for the PartialOrd implementations for `ty`
297             cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
298                 let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
299
300                 if partial_ord_is_automatically_derived == ord_is_automatically_derived {
301                     return;
302                 }
303
304                 let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
305
306                 // Only care about `impl PartialOrd<Foo> for Foo`
307                 // For `impl PartialOrd<B> for A, input_types is [A, B]
308                 if trait_ref.substs.type_at(1) == ty {
309                     let mess = if partial_ord_is_automatically_derived {
310                         "you are implementing `Ord` explicitly but have derived `PartialOrd`"
311                     } else {
312                         "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
313                     };
314
315                     span_lint_and_then(
316                         cx,
317                         DERIVE_ORD_XOR_PARTIAL_ORD,
318                         span,
319                         mess,
320                         |diag| {
321                             if let Some(local_def_id) = impl_id.as_local() {
322                                 let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
323                                 diag.span_note(
324                                     cx.tcx.hir().span(hir_id),
325                                     "`PartialOrd` implemented here"
326                                 );
327                             }
328                         }
329                     );
330                 }
331             });
332         }
333     }
334 }
335
336 /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
337 fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
338     let clone_id = match cx.tcx.lang_items().clone_trait() {
339         Some(id) if trait_ref.trait_def_id() == Some(id) => id,
340         _ => return,
341     };
342     let copy_id = match cx.tcx.lang_items().copy_trait() {
343         Some(id) => id,
344         None => return,
345     };
346     let (ty_adt, ty_subs) = match *ty.kind() {
347         // Unions can't derive clone.
348         ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
349         _ => return,
350     };
351     // If the current self type doesn't implement Copy (due to generic constraints), search to see if
352     // there's a Copy impl for any instance of the adt.
353     if !is_copy(cx, ty) {
354         if ty_subs.non_erasable_generics().next().is_some() {
355             let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
356                 impls
357                     .iter()
358                     .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
359             });
360             if !has_copy_impl {
361                 return;
362             }
363         } else {
364             return;
365         }
366     }
367     // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
368     // this impl.
369     if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
370         return;
371     }
372
373     span_lint_and_note(
374         cx,
375         EXPL_IMPL_CLONE_ON_COPY,
376         item.span,
377         "you are implementing `Clone` explicitly on a `Copy` type",
378         Some(item.span),
379         "consider deriving `Clone` or removing `Copy`",
380     );
381 }
382
383 /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
384 fn check_unsafe_derive_deserialize<'tcx>(
385     cx: &LateContext<'tcx>,
386     item: &Item<'_>,
387     trait_ref: &hir::TraitRef<'_>,
388     ty: Ty<'tcx>,
389 ) {
390     fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
391         let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
392         walk_item(&mut visitor, item);
393         visitor.has_unsafe
394     }
395
396     if_chain! {
397         if let Some(trait_def_id) = trait_ref.trait_def_id();
398         if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
399         if let ty::Adt(def, _) = ty.kind();
400         if let Some(local_def_id) = def.did().as_local();
401         let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
402         if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
403         if cx.tcx.inherent_impls(def.did())
404             .iter()
405             .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
406             .any(|imp| has_unsafe(cx, imp));
407         then {
408             span_lint_and_help(
409                 cx,
410                 UNSAFE_DERIVE_DESERIALIZE,
411                 item.span,
412                 "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
413                 None,
414                 "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
415             );
416         }
417     }
418 }
419
420 struct UnsafeVisitor<'a, 'tcx> {
421     cx: &'a LateContext<'tcx>,
422     has_unsafe: bool,
423 }
424
425 impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
426     type NestedFilter = nested_filter::All;
427
428     fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: HirId) {
429         if self.has_unsafe {
430             return;
431         }
432
433         if_chain! {
434             if let Some(header) = kind.header();
435             if header.unsafety == Unsafety::Unsafe;
436             then {
437                 self.has_unsafe = true;
438             }
439         }
440
441         walk_fn(self, kind, decl, body_id, id);
442     }
443
444     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
445         if self.has_unsafe {
446             return;
447         }
448
449         if let ExprKind::Block(block, _) = expr.kind {
450             if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
451                 self.has_unsafe = true;
452             }
453         }
454
455         walk_expr(self, expr);
456     }
457
458     fn nested_visit_map(&mut self) -> Self::Map {
459         self.cx.tcx.hir()
460     }
461 }
462
463 /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
464 fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
465     if_chain! {
466         if let ty::Adt(adt, substs) = ty.kind();
467         if cx.tcx.visibility(adt.did()).is_public();
468         if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
469         if let Some(def_id) = trait_ref.trait_def_id();
470         if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
471         let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
472         if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
473         // If all of our fields implement `Eq`, we can implement `Eq` too
474         if adt
475             .all_fields()
476             .map(|f| f.ty(cx.tcx, substs))
477             .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
478         then {
479             span_lint_and_sugg(
480                 cx,
481                 DERIVE_PARTIAL_EQ_WITHOUT_EQ,
482                 span.ctxt().outer_expn_data().call_site,
483                 "you are deriving `PartialEq` and can implement `Eq`",
484                 "consider deriving `Eq` as well",
485                 "PartialEq, Eq".to_string(),
486                 Applicability::MachineApplicable,
487             )
488         }
489     }
490 }
491
492 /// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
493 fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
494     // Initial map from generic index to param def.
495     // Vec<(param_def, needs_eq)>
496     let mut params = tcx
497         .generics_of(did)
498         .params
499         .iter()
500         .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
501         .collect::<Vec<_>>();
502
503     let ty_predicates = tcx.predicates_of(did).predicates;
504     for (p, _) in ty_predicates {
505         if let PredicateKind::Trait(p) = p.kind().skip_binder()
506             && p.trait_ref.def_id == eq_trait_id
507             && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
508             && p.constness == BoundConstness::NotConst
509         {
510             // Flag types which already have an `Eq` bound.
511             params[self_ty.index as usize].1 = false;
512         }
513     }
514
515     ParamEnv::new(
516         tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
517             params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
518                 tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
519                     trait_ref: TraitRef::new(
520                         eq_trait_id,
521                         tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
522                     ),
523                     constness: BoundConstness::NotConst,
524                     polarity: ImplPolarity::Positive,
525                 })))
526             }),
527         )),
528         Reveal::UserFacing,
529         Constness::NotConst,
530     )
531 }