about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-16 23:15:20 +0000
committerbors <bors@rust-lang.org>2022-10-16 23:15:20 +0000
commita501e6699ed979ef0540949211fc28256336a3f3 (patch)
treea876bc7366a5e4ee6029ced4131ef49699c57f4f
parentb8b5caee04116c7383eb1c6470fcf15c437a60d4 (diff)
parentdb30a2578155472212fa81fd870c2b0506c7ecf0 (diff)
downloadrust-a501e6699ed979ef0540949211fc28256336a3f3.tar.gz
rust-a501e6699ed979ef0540949211fc28256336a3f3.zip
Auto merge of #103125 - matthiaskrgr:rollup-82xttcl, r=matthiaskrgr
Rollup of 5 pull requests

Successful merges:

 - #103087 (Documentation BTreeMap::append's behavior for already existing keys)
 - #103089 (Mark derived StructuralEq as automatically derived.)
 - #103102 (Clarify the possible return values of `len_utf16`)
 - #103109 (PhantomData: inline a macro that is used only once)
 - #103120 (rustdoc: Do not expect `doc(primitive)` modules to always exist)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs16
-rw-r--r--library/alloc/src/collections/btree/map.rs9
-rw-r--r--library/core/src/char/methods.rs5
-rw-r--r--library/core/src/marker.rs112
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs10
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs3
-rw-r--r--src/test/rustdoc/intra-doc/no-doc-primitive.rs15
-rw-r--r--src/test/ui/deriving/deriving-all-codegen.stdout72
-rw-r--r--src/test/ui/single-use-lifetime/derive-eq.rs11
9 files changed, 156 insertions, 97 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs
index a65d0bad6de..ee346047a0b 100644
--- a/compiler/rustc_builtin_macros/src/deriving/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs
@@ -131,6 +131,8 @@ fn inject_impl_of_structural_trait(
     // Create generics param list for where clauses and impl headers
     let mut generics = generics.clone();
 
+    let ctxt = span.ctxt();
+
     // Create the type of `self`.
     //
     // in addition, remove defaults from generic params (impls cannot have them).
@@ -138,16 +140,18 @@ fn inject_impl_of_structural_trait(
         .params
         .iter_mut()
         .map(|param| match &mut param.kind {
-            ast::GenericParamKind::Lifetime => {
-                ast::GenericArg::Lifetime(cx.lifetime(span, param.ident))
-            }
+            ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime(
+                cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident),
+            ),
             ast::GenericParamKind::Type { default } => {
                 *default = None;
-                ast::GenericArg::Type(cx.ty_ident(span, param.ident))
+                ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
             }
             ast::GenericParamKind::Const { ty: _, kw_span: _, default } => {
                 *default = None;
-                ast::GenericArg::Const(cx.const_ident(span, param.ident))
+                ast::GenericArg::Const(
+                    cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident),
+                )
             }
         })
         .collect();
@@ -174,6 +178,8 @@ fn inject_impl_of_structural_trait(
             })
             .cloned(),
     );
+    // Mark as `automatically_derived` to avoid some silly lints.
+    attrs.push(cx.attribute(cx.meta_word(span, sym::automatically_derived)));
 
     let newitem = cx.item(
         span,
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 73bc1c21d55..c4c75e46a2a 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -1093,6 +1093,9 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
 
     /// Moves all elements from `other` into `self`, leaving `other` empty.
     ///
+    /// If a key from `other` is already present in `self`, the respective
+    /// value from `self` will be overwritten with the respective value from `other`.
+    ///
     /// # Examples
     ///
     /// ```
@@ -1101,10 +1104,10 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     /// let mut a = BTreeMap::new();
     /// a.insert(1, "a");
     /// a.insert(2, "b");
-    /// a.insert(3, "c");
+    /// a.insert(3, "c"); // Note: Key (3) also present in b.
     ///
     /// let mut b = BTreeMap::new();
-    /// b.insert(3, "d");
+    /// b.insert(3, "d"); // Note: Key (3) also present in a.
     /// b.insert(4, "e");
     /// b.insert(5, "f");
     ///
@@ -1115,7 +1118,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> {
     ///
     /// assert_eq!(a[&1], "a");
     /// assert_eq!(a[&2], "b");
-    /// assert_eq!(a[&3], "d");
+    /// assert_eq!(a[&3], "d"); // Note: "c" has been overwritten.
     /// assert_eq!(a[&4], "e");
     /// assert_eq!(a[&5], "f");
     /// ```
diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs
index 224bc9effe6..bb83599369c 100644
--- a/library/core/src/char/methods.rs
+++ b/library/core/src/char/methods.rs
@@ -597,9 +597,14 @@ impl char {
     /// Returns the number of 16-bit code units this `char` would need if
     /// encoded in UTF-16.
     ///
+    /// That number of code units is always either 1 or 2, for unicode scalar values in
+    /// the [basic multilingual plane] or [supplementary planes] respectively.
+    ///
     /// See the documentation for [`len_utf8()`] for more explanation of this
     /// concept. This function is a mirror, but for UTF-16 instead of UTF-8.
     ///
+    /// [basic multilingual plane]: http://www.unicode.org/glossary/#basic_multilingual_plane
+    /// [supplementary planes]: http://www.unicode.org/glossary/#supplementary_planes
     /// [`len_utf8()`]: #method.len_utf8
     ///
     /// # Examples
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index d5ed52124e2..c43c4fff6ae 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -483,64 +483,6 @@ impl<T: ?Sized> !Sync for *const T {}
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> !Sync for *mut T {}
 
-macro_rules! impls {
-    ($t: ident) => {
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> Hash for $t<T> {
-            #[inline]
-            fn hash<H: Hasher>(&self, _: &mut H) {}
-        }
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> cmp::PartialEq for $t<T> {
-            fn eq(&self, _other: &$t<T>) -> bool {
-                true
-            }
-        }
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> cmp::Eq for $t<T> {}
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> cmp::PartialOrd for $t<T> {
-            fn partial_cmp(&self, _other: &$t<T>) -> Option<cmp::Ordering> {
-                Option::Some(cmp::Ordering::Equal)
-            }
-        }
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> cmp::Ord for $t<T> {
-            fn cmp(&self, _other: &$t<T>) -> cmp::Ordering {
-                cmp::Ordering::Equal
-            }
-        }
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> Copy for $t<T> {}
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        impl<T: ?Sized> Clone for $t<T> {
-            fn clone(&self) -> Self {
-                Self
-            }
-        }
-
-        #[stable(feature = "rust1", since = "1.0.0")]
-        #[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
-        impl<T: ?Sized> const Default for $t<T> {
-            fn default() -> Self {
-                Self
-            }
-        }
-
-        #[unstable(feature = "structural_match", issue = "31434")]
-        impl<T: ?Sized> StructuralPartialEq for $t<T> {}
-
-        #[unstable(feature = "structural_match", issue = "31434")]
-        impl<T: ?Sized> StructuralEq for $t<T> {}
-    };
-}
-
 /// Zero-sized type used to mark things that "act like" they own a `T`.
 ///
 /// Adding a `PhantomData<T>` field to your type tells the compiler that your
@@ -678,7 +620,59 @@ macro_rules! impls {
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct PhantomData<T: ?Sized>;
 
-impls! { PhantomData }
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Hash for PhantomData<T> {
+    #[inline]
+    fn hash<H: Hasher>(&self, _: &mut H) {}
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> cmp::PartialEq for PhantomData<T> {
+    fn eq(&self, _other: &PhantomData<T>) -> bool {
+        true
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> cmp::Eq for PhantomData<T> {}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> cmp::PartialOrd for PhantomData<T> {
+    fn partial_cmp(&self, _other: &PhantomData<T>) -> Option<cmp::Ordering> {
+        Option::Some(cmp::Ordering::Equal)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> cmp::Ord for PhantomData<T> {
+    fn cmp(&self, _other: &PhantomData<T>) -> cmp::Ordering {
+        cmp::Ordering::Equal
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Copy for PhantomData<T> {}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: ?Sized> Clone for PhantomData<T> {
+    fn clone(&self) -> Self {
+        Self
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
+impl<T: ?Sized> const Default for PhantomData<T> {
+    fn default() -> Self {
+        Self
+    }
+}
+
+#[unstable(feature = "structural_match", issue = "31434")]
+impl<T: ?Sized> StructuralPartialEq for PhantomData<T> {}
+
+#[unstable(feature = "structural_match", issue = "31434")]
+impl<T: ?Sized> StructuralEq for PhantomData<T> {}
 
 mod impls {
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 3beda708bf2..3513c13d522 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -80,10 +80,10 @@ impl Res {
         }
     }
 
-    fn def_id(self, tcx: TyCtxt<'_>) -> DefId {
+    fn def_id(self, tcx: TyCtxt<'_>) -> Option<DefId> {
         match self {
-            Res::Def(_, id) => id,
-            Res::Primitive(prim) => *PrimitiveType::primitive_locations(tcx).get(&prim).unwrap(),
+            Res::Def(_, id) => Some(id),
+            Res::Primitive(prim) => PrimitiveType::primitive_locations(tcx).get(&prim).copied(),
         }
     }
 
@@ -1127,10 +1127,10 @@ impl LinkCollector<'_, '_> {
                     }
                 }
 
-                Some(ItemLink {
+                res.def_id(self.cx.tcx).map(|page_id| ItemLink {
                     link: ori_link.link.clone(),
                     link_text: link_text.clone(),
-                    page_id: res.def_id(self.cx.tcx),
+                    page_id,
                     fragment,
                 })
             }
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index 38cfd7a27dd..50dc26d768c 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -48,7 +48,6 @@ pub(crate) fn early_resolve_intra_doc_links(
     link_resolver.resolve_doc_links_local(&krate.attrs);
     link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
     visit::walk_crate(&mut link_resolver, krate);
-    link_resolver.process_extern_impls();
 
     // FIXME: somehow rustdoc is still missing crates even though we loaded all
     // the known necessary crates. Load them all unconditionally until we find a way to fix this.
@@ -58,6 +57,8 @@ pub(crate) fn early_resolve_intra_doc_links(
         link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
     }
 
+    link_resolver.process_extern_impls();
+
     ResolverCaches {
         markdown_links: Some(link_resolver.markdown_links),
         doc_link_resolutions: link_resolver.doc_link_resolutions,
diff --git a/src/test/rustdoc/intra-doc/no-doc-primitive.rs b/src/test/rustdoc/intra-doc/no-doc-primitive.rs
new file mode 100644
index 00000000000..e5eba1d8d48
--- /dev/null
+++ b/src/test/rustdoc/intra-doc/no-doc-primitive.rs
@@ -0,0 +1,15 @@
+// Crate tree without a `doc(primitive)` module for primitive type linked to by a doc link.
+
+#![deny(rustdoc::broken_intra_doc_links)]
+#![feature(no_core, lang_items, rustc_attrs)]
+#![no_core]
+#![rustc_coherence_is_core]
+#![crate_type = "rlib"]
+
+// @has no_doc_primitive/index.html
+//! A [`char`] and its [`char::len_utf8`].
+impl char {
+    pub fn len_utf8(self) -> usize {
+        42
+    }
+}
diff --git a/src/test/ui/deriving/deriving-all-codegen.stdout b/src/test/ui/deriving/deriving-all-codegen.stdout
index 65f7dec8408..258ef8ba91d 100644
--- a/src/test/ui/deriving/deriving-all-codegen.stdout
+++ b/src/test/ui/deriving/deriving-all-codegen.stdout
@@ -46,13 +46,15 @@ impl ::core::default::Default for Empty {
 impl ::core::hash::Hash for Empty {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
 }
-impl ::core::marker::StructuralPartialEq for Empty {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Empty { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Empty {
     #[inline]
     fn eq(&self, other: &Empty) -> bool { true }
 }
-impl ::core::marker::StructuralEq for Empty {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Empty { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Empty {
     #[inline]
@@ -115,7 +117,8 @@ impl ::core::hash::Hash for Point {
         ::core::hash::Hash::hash(&self.y, state)
     }
 }
-impl ::core::marker::StructuralPartialEq for Point {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Point { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Point {
     #[inline]
@@ -123,7 +126,8 @@ impl ::core::cmp::PartialEq for Point {
         self.x == other.x && self.y == other.y
     }
 }
-impl ::core::marker::StructuralEq for Point {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Point { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Point {
     #[inline]
@@ -225,7 +229,8 @@ impl ::core::hash::Hash for Big {
         ::core::hash::Hash::hash(&self.b8, state)
     }
 }
-impl ::core::marker::StructuralPartialEq for Big {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Big { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Big {
     #[inline]
@@ -236,7 +241,8 @@ impl ::core::cmp::PartialEq for Big {
             self.b8 == other.b8
     }
 }
-impl ::core::marker::StructuralEq for Big {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Big { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Big {
     #[inline]
@@ -345,13 +351,15 @@ impl ::core::hash::Hash for Unsized {
         ::core::hash::Hash::hash(&self.0, state)
     }
 }
-impl ::core::marker::StructuralPartialEq for Unsized {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Unsized { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Unsized {
     #[inline]
     fn eq(&self, other: &Unsized) -> bool { self.0 == other.0 }
 }
-impl ::core::marker::StructuralEq for Unsized {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Unsized { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Unsized {
     #[inline]
@@ -410,13 +418,15 @@ impl ::core::hash::Hash for PackedCopy {
         ::core::hash::Hash::hash(&{ self.0 }, state)
     }
 }
-impl ::core::marker::StructuralPartialEq for PackedCopy {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for PackedCopy { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for PackedCopy {
     #[inline]
     fn eq(&self, other: &PackedCopy) -> bool { { self.0 } == { other.0 } }
 }
-impl ::core::marker::StructuralEq for PackedCopy {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for PackedCopy { }
 #[automatically_derived]
 impl ::core::cmp::Eq for PackedCopy {
     #[inline]
@@ -479,7 +489,8 @@ impl ::core::hash::Hash for PackedNonCopy {
         ::core::hash::Hash::hash(__self_0_0, state)
     }
 }
-impl ::core::marker::StructuralPartialEq for PackedNonCopy {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for PackedNonCopy { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for PackedNonCopy {
     #[inline]
@@ -489,7 +500,8 @@ impl ::core::cmp::PartialEq for PackedNonCopy {
         *__self_0_0 == *__self_1_0
     }
 }
-impl ::core::marker::StructuralEq for PackedNonCopy {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for PackedNonCopy { }
 #[automatically_derived]
 impl ::core::cmp::Eq for PackedNonCopy {
     #[inline]
@@ -540,7 +552,8 @@ impl ::core::hash::Hash for Enum0 {
         unsafe { ::core::intrinsics::unreachable() }
     }
 }
-impl ::core::marker::StructuralPartialEq for Enum0 {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Enum0 { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Enum0 {
     #[inline]
@@ -548,7 +561,8 @@ impl ::core::cmp::PartialEq for Enum0 {
         unsafe { ::core::intrinsics::unreachable() }
     }
 }
-impl ::core::marker::StructuralEq for Enum0 {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Enum0 { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Enum0 {
     #[inline]
@@ -607,7 +621,8 @@ impl ::core::hash::Hash for Enum1 {
         }
     }
 }
-impl ::core::marker::StructuralPartialEq for Enum1 {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Enum1 { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Enum1 {
     #[inline]
@@ -618,7 +633,8 @@ impl ::core::cmp::PartialEq for Enum1 {
         }
     }
 }
-impl ::core::marker::StructuralEq for Enum1 {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Enum1 { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Enum1 {
     #[inline]
@@ -676,13 +692,15 @@ impl ::core::default::Default for Fieldless1 {
 impl ::core::hash::Hash for Fieldless1 {
     fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
 }
-impl ::core::marker::StructuralPartialEq for Fieldless1 {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Fieldless1 { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Fieldless1 {
     #[inline]
     fn eq(&self, other: &Fieldless1) -> bool { true }
 }
-impl ::core::marker::StructuralEq for Fieldless1 {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Fieldless1 { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Fieldless1 {
     #[inline]
@@ -743,7 +761,8 @@ impl ::core::hash::Hash for Fieldless {
         ::core::hash::Hash::hash(&__self_tag, state)
     }
 }
-impl ::core::marker::StructuralPartialEq for Fieldless {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Fieldless { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Fieldless {
     #[inline]
@@ -753,7 +772,8 @@ impl ::core::cmp::PartialEq for Fieldless {
         __self_tag == __arg1_tag
     }
 }
-impl ::core::marker::StructuralEq for Fieldless {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Fieldless { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Fieldless {
     #[inline]
@@ -838,7 +858,8 @@ impl ::core::hash::Hash for Mixed {
         }
     }
 }
-impl ::core::marker::StructuralPartialEq for Mixed {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Mixed { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Mixed {
     #[inline]
@@ -856,7 +877,8 @@ impl ::core::cmp::PartialEq for Mixed {
             }
     }
 }
-impl ::core::marker::StructuralEq for Mixed {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Mixed { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Mixed {
     #[inline]
@@ -963,7 +985,8 @@ impl ::core::hash::Hash for Fielded {
         }
     }
 }
-impl ::core::marker::StructuralPartialEq for Fielded {}
+#[automatically_derived]
+impl ::core::marker::StructuralPartialEq for Fielded { }
 #[automatically_derived]
 impl ::core::cmp::PartialEq for Fielded {
     #[inline]
@@ -982,7 +1005,8 @@ impl ::core::cmp::PartialEq for Fielded {
             }
     }
 }
-impl ::core::marker::StructuralEq for Fielded {}
+#[automatically_derived]
+impl ::core::marker::StructuralEq for Fielded { }
 #[automatically_derived]
 impl ::core::cmp::Eq for Fielded {
     #[inline]
diff --git a/src/test/ui/single-use-lifetime/derive-eq.rs b/src/test/ui/single-use-lifetime/derive-eq.rs
new file mode 100644
index 00000000000..e5bdfc55dd6
--- /dev/null
+++ b/src/test/ui/single-use-lifetime/derive-eq.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+#![deny(single_use_lifetimes)]
+
+#[derive(PartialEq, Eq)]
+struct Foo<'a, T> {
+    /// a reference to the underlying secret data that will be derefed
+    pub data: &'a mut T,
+}
+
+fn main() {}