]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/derive.rs
drive-by: Fix path spans
[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, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
18     TraitRef, 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.owner_id);
214             let is_automatically_derived = cx.tcx.has_attr(item.owner_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 Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return };
343     let (ty_adt, ty_subs) = match *ty.kind() {
344         // Unions can't derive clone.
345         ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
346         _ => return,
347     };
348     // If the current self type doesn't implement Copy (due to generic constraints), search to see if
349     // there's a Copy impl for any instance of the adt.
350     if !is_copy(cx, ty) {
351         if ty_subs.non_erasable_generics().next().is_some() {
352             let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
353                 impls
354                     .iter()
355                     .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
356             });
357             if !has_copy_impl {
358                 return;
359             }
360         } else {
361             return;
362         }
363     }
364     // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
365     // this impl.
366     if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
367         return;
368     }
369
370     span_lint_and_note(
371         cx,
372         EXPL_IMPL_CLONE_ON_COPY,
373         item.span,
374         "you are implementing `Clone` explicitly on a `Copy` type",
375         Some(item.span),
376         "consider deriving `Clone` or removing `Copy`",
377     );
378 }
379
380 /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
381 fn check_unsafe_derive_deserialize<'tcx>(
382     cx: &LateContext<'tcx>,
383     item: &Item<'_>,
384     trait_ref: &hir::TraitRef<'_>,
385     ty: Ty<'tcx>,
386 ) {
387     fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
388         let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
389         walk_item(&mut visitor, item);
390         visitor.has_unsafe
391     }
392
393     if_chain! {
394         if let Some(trait_def_id) = trait_ref.trait_def_id();
395         if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
396         if let ty::Adt(def, _) = ty.kind();
397         if let Some(local_def_id) = def.did().as_local();
398         let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
399         if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
400         if cx.tcx.inherent_impls(def.did())
401             .iter()
402             .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
403             .any(|imp| has_unsafe(cx, imp));
404         then {
405             span_lint_and_help(
406                 cx,
407                 UNSAFE_DERIVE_DESERIALIZE,
408                 item.span,
409                 "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
410                 None,
411                 "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
412             );
413         }
414     }
415 }
416
417 struct UnsafeVisitor<'a, 'tcx> {
418     cx: &'a LateContext<'tcx>,
419     has_unsafe: bool,
420 }
421
422 impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
423     type NestedFilter = nested_filter::All;
424
425     fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: HirId) {
426         if self.has_unsafe {
427             return;
428         }
429
430         if_chain! {
431             if let Some(header) = kind.header();
432             if header.unsafety == Unsafety::Unsafe;
433             then {
434                 self.has_unsafe = true;
435             }
436         }
437
438         walk_fn(self, kind, decl, body_id, id);
439     }
440
441     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
442         if self.has_unsafe {
443             return;
444         }
445
446         if let ExprKind::Block(block, _) = expr.kind {
447             if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
448                 self.has_unsafe = true;
449             }
450         }
451
452         walk_expr(self, expr);
453     }
454
455     fn nested_visit_map(&mut self) -> Self::Map {
456         self.cx.tcx.hir()
457     }
458 }
459
460 /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
461 fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
462     if_chain! {
463         if let ty::Adt(adt, substs) = ty.kind();
464         if cx.tcx.visibility(adt.did()).is_public();
465         if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
466         if let Some(def_id) = trait_ref.trait_def_id();
467         if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
468         let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
469         if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []);
470         // If all of our fields implement `Eq`, we can implement `Eq` too
471         if adt
472             .all_fields()
473             .map(|f| f.ty(cx.tcx, substs))
474             .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []));
475         then {
476             span_lint_and_sugg(
477                 cx,
478                 DERIVE_PARTIAL_EQ_WITHOUT_EQ,
479                 span.ctxt().outer_expn_data().call_site,
480                 "you are deriving `PartialEq` and can implement `Eq`",
481                 "consider deriving `Eq` as well",
482                 "PartialEq, Eq".to_string(),
483                 Applicability::MachineApplicable,
484             )
485         }
486     }
487 }
488
489 /// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
490 fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
491     // Initial map from generic index to param def.
492     // Vec<(param_def, needs_eq)>
493     let mut params = tcx
494         .generics_of(did)
495         .params
496         .iter()
497         .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
498         .collect::<Vec<_>>();
499
500     let ty_predicates = tcx.predicates_of(did).predicates;
501     for (p, _) in ty_predicates {
502         if let PredicateKind::Clause(Clause::Trait(p)) = p.kind().skip_binder()
503             && p.trait_ref.def_id == eq_trait_id
504             && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
505             && p.constness == BoundConstness::NotConst
506         {
507             // Flag types which already have an `Eq` bound.
508             params[self_ty.index as usize].1 = false;
509         }
510     }
511
512     ParamEnv::new(
513         tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
514             params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
515                 tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate {
516                     trait_ref: TraitRef::new(
517                         eq_trait_id,
518                         tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
519                     ),
520                     constness: BoundConstness::NotConst,
521                     polarity: ImplPolarity::Positive,
522                 }))))
523             }),
524         )),
525         Reveal::UserFacing,
526         Constness::NotConst,
527     )
528 }