]>
Commit | Line | Data |
---|---|---|
923072b8 | 1 | use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; |
cdc7bbd5 | 2 | use clippy_utils::paths; |
923072b8 | 3 | use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; |
04454e1e | 4 | use clippy_utils::{is_lint_allowed, match_def_path}; |
f20569fa | 5 | use if_chain::if_chain; |
923072b8 FG |
6 | use rustc_errors::Applicability; |
7 | use rustc_hir::def_id::DefId; | |
5099ac24 | 8 | use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; |
f20569fa | 9 | use rustc_hir::{ |
923072b8 FG |
10 | self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, |
11 | Unsafety, | |
f20569fa XL |
12 | }; |
13 | use rustc_lint::{LateContext, LateLintPass}; | |
5099ac24 | 14 | use rustc_middle::hir::nested_filter; |
923072b8 FG |
15 | use rustc_middle::traits::Reveal; |
16 | use rustc_middle::ty::{ | |
f25598a0 FG |
17 | self, Binder, BoundConstness, Clause, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, |
18 | TraitPredicate, Ty, TyCtxt, | |
923072b8 | 19 | }; |
f20569fa | 20 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
17df50a5 | 21 | use rustc_span::source_map::Span; |
5e7ed085 | 22 | use rustc_span::sym; |
f20569fa XL |
23 | |
24 | declare_clippy_lint! { | |
94222f64 XL |
25 | /// ### What it does |
26 | /// Checks for deriving `Hash` but implementing `PartialEq` | |
f20569fa XL |
27 | /// explicitly or vice versa. |
28 | /// | |
94222f64 XL |
29 | /// ### Why is this bad? |
30 | /// The implementation of these traits must agree (for | |
f20569fa XL |
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 | /// | |
94222f64 | 39 | /// ### Example |
f20569fa XL |
40 | /// ```ignore |
41 | /// #[derive(Hash)] | |
42 | /// struct Foo; | |
43 | /// | |
44 | /// impl PartialEq for Foo { | |
45 | /// ... | |
46 | /// } | |
47 | /// ``` | |
a2a8927a | 48 | #[clippy::version = "pre 1.29.0"] |
f25598a0 | 49 | pub DERIVED_HASH_WITH_MANUAL_EQ, |
f20569fa XL |
50 | correctness, |
51 | "deriving `Hash` but implementing `PartialEq` explicitly" | |
52 | } | |
53 | ||
54 | declare_clippy_lint! { | |
94222f64 XL |
55 | /// ### What it does |
56 | /// Checks for deriving `Ord` but implementing `PartialOrd` | |
f20569fa XL |
57 | /// explicitly or vice versa. |
58 | /// | |
94222f64 XL |
59 | /// ### Why is this bad? |
60 | /// The implementation of these traits must agree (for | |
f20569fa XL |
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 | /// | |
94222f64 | 70 | /// ### Example |
f20569fa XL |
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 | /// ``` | |
a2a8927a | 99 | #[clippy::version = "1.47.0"] |
f20569fa XL |
100 | pub DERIVE_ORD_XOR_PARTIAL_ORD, |
101 | correctness, | |
102 | "deriving `Ord` but implementing `PartialOrd` explicitly" | |
103 | } | |
104 | ||
105 | declare_clippy_lint! { | |
94222f64 XL |
106 | /// ### What it does |
107 | /// Checks for explicit `Clone` implementations for `Copy` | |
f20569fa XL |
108 | /// types. |
109 | /// | |
94222f64 | 110 | /// ### Why is this bad? |
923072b8 FG |
111 | /// To avoid surprising behavior, these traits should |
112 | /// agree and the behavior of `Copy` cannot be overridden. In almost all | |
f20569fa XL |
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 | /// | |
94222f64 | 117 | /// ### Example |
f20569fa XL |
118 | /// ```rust,ignore |
119 | /// #[derive(Copy)] | |
120 | /// struct Foo; | |
121 | /// | |
122 | /// impl Clone for Foo { | |
123 | /// // .. | |
124 | /// } | |
125 | /// ``` | |
a2a8927a | 126 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
127 | pub EXPL_IMPL_CLONE_ON_COPY, |
128 | pedantic, | |
129 | "implementing `Clone` explicitly on `Copy` types" | |
130 | } | |
131 | ||
132 | declare_clippy_lint! { | |
94222f64 XL |
133 | /// ### What it does |
134 | /// Checks for deriving `serde::Deserialize` on a type that | |
f20569fa XL |
135 | /// has methods using `unsafe`. |
136 | /// | |
94222f64 XL |
137 | /// ### Why is this bad? |
138 | /// Deriving `serde::Deserialize` will create a constructor | |
f20569fa XL |
139 | /// that may violate invariants hold by another constructor. |
140 | /// | |
94222f64 | 141 | /// ### Example |
f20569fa XL |
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 | /// ``` | |
a2a8927a | 160 | #[clippy::version = "1.45.0"] |
f20569fa XL |
161 | pub UNSAFE_DERIVE_DESERIALIZE, |
162 | pedantic, | |
163 | "deriving `serde::Deserialize` on a type that has methods using `unsafe`" | |
164 | } | |
165 | ||
923072b8 FG |
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 | /// ``` | |
064997fb | 192 | #[clippy::version = "1.63.0"] |
923072b8 | 193 | pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, |
2b03887a | 194 | nursery, |
923072b8 FG |
195 | "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" |
196 | } | |
197 | ||
f20569fa XL |
198 | declare_lint_pass!(Derive => [ |
199 | EXPL_IMPL_CLONE_ON_COPY, | |
f25598a0 | 200 | DERIVED_HASH_WITH_MANUAL_EQ, |
f20569fa | 201 | DERIVE_ORD_XOR_PARTIAL_ORD, |
923072b8 FG |
202 | UNSAFE_DERIVE_DESERIALIZE, |
203 | DERIVE_PARTIAL_EQ_WITHOUT_EQ | |
f20569fa XL |
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 | { | |
2b03887a FG |
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); | |
f20569fa XL |
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); | |
923072b8 | 221 | check_partial_eq_without_eq(cx, item.span, trait_ref, ty); |
f20569fa XL |
222 | } else { |
223 | check_copy_clone(cx, item, trait_ref, ty); | |
224 | } | |
225 | } | |
226 | } | |
227 | } | |
228 | ||
f25598a0 | 229 | /// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. |
f20569fa XL |
230 | fn check_hash_peq<'tcx>( |
231 | cx: &LateContext<'tcx>, | |
232 | span: Span, | |
923072b8 | 233 | trait_ref: &hir::TraitRef<'_>, |
f20569fa XL |
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(); | |
5e7ed085 | 240 | if cx.tcx.is_diagnostic_item(sym::Hash, def_id); |
f20569fa XL |
241 | then { |
242 | // Look for the PartialEq implementations for `ty` | |
243 | cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { | |
04454e1e | 244 | let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); |
f20569fa | 245 | |
f25598a0 | 246 | if !hash_is_automatically_derived || peq_is_automatically_derived { |
f20569fa XL |
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] | |
f25598a0 | 254 | if trait_ref.subst_identity().substs.type_at(1) == ty { |
f20569fa XL |
255 | span_lint_and_then( |
256 | cx, | |
f25598a0 | 257 | DERIVED_HASH_WITH_MANUAL_EQ, |
f20569fa | 258 | span, |
f25598a0 | 259 | "you are deriving `Hash` but have implemented `PartialEq` explicitly", |
f20569fa XL |
260 | |diag| { |
261 | if let Some(local_def_id) = impl_id.as_local() { | |
262 | let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); | |
263 | diag.span_note( | |
264 | cx.tcx.hir().span(hir_id), | |
265 | "`PartialEq` implemented here" | |
266 | ); | |
267 | } | |
268 | } | |
269 | ); | |
270 | } | |
271 | }); | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. | |
277 | fn check_ord_partial_ord<'tcx>( | |
278 | cx: &LateContext<'tcx>, | |
279 | span: Span, | |
923072b8 | 280 | trait_ref: &hir::TraitRef<'_>, |
f20569fa XL |
281 | ty: Ty<'tcx>, |
282 | ord_is_automatically_derived: bool, | |
283 | ) { | |
284 | if_chain! { | |
5e7ed085 | 285 | if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord); |
f20569fa XL |
286 | if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); |
287 | if let Some(def_id) = &trait_ref.trait_def_id(); | |
288 | if *def_id == ord_trait_def_id; | |
289 | then { | |
290 | // Look for the PartialOrd implementations for `ty` | |
291 | cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { | |
04454e1e | 292 | let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); |
f20569fa XL |
293 | |
294 | if partial_ord_is_automatically_derived == ord_is_automatically_derived { | |
295 | return; | |
296 | } | |
297 | ||
298 | let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); | |
299 | ||
300 | // Only care about `impl PartialOrd<Foo> for Foo` | |
301 | // For `impl PartialOrd<B> for A, input_types is [A, B] | |
f25598a0 | 302 | if trait_ref.subst_identity().substs.type_at(1) == ty { |
f20569fa XL |
303 | let mess = if partial_ord_is_automatically_derived { |
304 | "you are implementing `Ord` explicitly but have derived `PartialOrd`" | |
305 | } else { | |
306 | "you are deriving `Ord` but have implemented `PartialOrd` explicitly" | |
307 | }; | |
308 | ||
309 | span_lint_and_then( | |
310 | cx, | |
311 | DERIVE_ORD_XOR_PARTIAL_ORD, | |
312 | span, | |
313 | mess, | |
314 | |diag| { | |
315 | if let Some(local_def_id) = impl_id.as_local() { | |
316 | let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); | |
317 | diag.span_note( | |
318 | cx.tcx.hir().span(hir_id), | |
319 | "`PartialOrd` implemented here" | |
320 | ); | |
321 | } | |
322 | } | |
323 | ); | |
324 | } | |
325 | }); | |
326 | } | |
327 | } | |
328 | } | |
329 | ||
330 | /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. | |
923072b8 | 331 | fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { |
cdc7bbd5 XL |
332 | let clone_id = match cx.tcx.lang_items().clone_trait() { |
333 | Some(id) if trait_ref.trait_def_id() == Some(id) => id, | |
334 | _ => return, | |
335 | }; | |
2b03887a | 336 | let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return }; |
cdc7bbd5 XL |
337 | let (ty_adt, ty_subs) = match *ty.kind() { |
338 | // Unions can't derive clone. | |
339 | ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), | |
340 | _ => return, | |
341 | }; | |
342 | // If the current self type doesn't implement Copy (due to generic constraints), search to see if | |
343 | // there's a Copy impl for any instance of the adt. | |
344 | if !is_copy(cx, ty) { | |
345 | if ty_subs.non_erasable_generics().next().is_some() { | |
17df50a5 XL |
346 | let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { |
347 | impls | |
348 | .iter() | |
5e7ed085 | 349 | .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did())) |
17df50a5 | 350 | }); |
cdc7bbd5 XL |
351 | if !has_copy_impl { |
352 | return; | |
353 | } | |
354 | } else { | |
f20569fa XL |
355 | return; |
356 | } | |
f20569fa | 357 | } |
cdc7bbd5 XL |
358 | // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for |
359 | // this impl. | |
360 | if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { | |
361 | return; | |
362 | } | |
f25598a0 FG |
363 | // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. |
364 | // https://github.com/rust-lang/rust-clippy/issues/10188 | |
365 | if ty_adt.repr().packed() | |
366 | && ty_subs | |
367 | .iter() | |
368 | .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) | |
369 | { | |
370 | return; | |
371 | } | |
cdc7bbd5 XL |
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 | ); | |
f20569fa XL |
381 | } |
382 | ||
383 | /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. | |
384 | fn check_unsafe_derive_deserialize<'tcx>( | |
385 | cx: &LateContext<'tcx>, | |
386 | item: &Item<'_>, | |
923072b8 | 387 | trait_ref: &hir::TraitRef<'_>, |
f20569fa XL |
388 | ty: Ty<'tcx>, |
389 | ) { | |
f20569fa XL |
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(); | |
5e7ed085 | 400 | if let Some(local_def_id) = def.did().as_local(); |
f20569fa | 401 | let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); |
136023e0 | 402 | if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); |
5e7ed085 | 403 | if cx.tcx.inherent_impls(def.did()) |
f20569fa | 404 | .iter() |
a2a8927a | 405 | .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) |
f20569fa XL |
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> { | |
5099ac24 | 426 | type NestedFilter = nested_filter::All; |
f20569fa | 427 | |
f2b60f7d | 428 | fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: HirId) { |
f20569fa XL |
429 | if self.has_unsafe { |
430 | return; | |
431 | } | |
432 | ||
433 | if_chain! { | |
434 | if let Some(header) = kind.header(); | |
c295e0f8 | 435 | if header.unsafety == Unsafety::Unsafe; |
f20569fa XL |
436 | then { |
437 | self.has_unsafe = true; | |
438 | } | |
439 | } | |
440 | ||
f2b60f7d | 441 | walk_fn(self, kind, decl, body_id, id); |
f20569fa XL |
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 { | |
c295e0f8 | 450 | if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { |
136023e0 | 451 | self.has_unsafe = true; |
f20569fa XL |
452 | } |
453 | } | |
454 | ||
455 | walk_expr(self, expr); | |
456 | } | |
457 | ||
5099ac24 FG |
458 | fn nested_visit_map(&mut self) -> Self::Map { |
459 | self.cx.tcx.hir() | |
f20569fa XL |
460 | } |
461 | } | |
923072b8 FG |
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(); | |
f2b60f7d | 467 | if cx.tcx.visibility(adt.did()).is_public(); |
923072b8 FG |
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); | |
487cf647 | 472 | if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []); |
923072b8 FG |
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)) | |
487cf647 | 477 | .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, [])); |
923072b8 FG |
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 { | |
487cf647 | 505 | if let PredicateKind::Clause(Clause::Trait(p)) = p.kind().skip_binder() |
923072b8 FG |
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, _)| { | |
487cf647 | 518 | tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate { |
f25598a0 | 519 | trait_ref: tcx.mk_trait_ref(eq_trait_id, [tcx.mk_param_from_def(param)]), |
923072b8 FG |
520 | constness: BoundConstness::NotConst, |
521 | polarity: ImplPolarity::Positive, | |
487cf647 | 522 | })))) |
923072b8 FG |
523 | }), |
524 | )), | |
525 | Reveal::UserFacing, | |
526 | Constness::NotConst, | |
527 | ) | |
528 | } |