diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index a9e4434581163..a694d3b8c2857 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -326,6 +326,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     let main_attr = ecx.attr_word(sym::rustc_main, sp);
     // #[coverage(off)]
     let coverage_attr = ecx.attr_nested_word(sym::coverage, sym::off, sp);
+    // #[allow(missing_docs)]
+    let missing_docs_attr = ecx.attr_nested_word(sym::allow, sym::missing_docs, sp);
 
     // pub fn main() { ... }
     let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(ThinVec::new()));
@@ -355,7 +357,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
 
     let main = P(ast::Item {
         ident: main_id,
-        attrs: thin_vec![main_attr, coverage_attr],
+        attrs: thin_vec![main_attr, coverage_attr, missing_docs_attr],
         id: ast::DUMMY_NODE_ID,
         kind: main,
         vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None },
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index d8482567bbe5b..5cb5195367402 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -426,12 +426,6 @@ impl MissingDoc {
         article: &'static str,
         desc: &'static str,
     ) {
-        // If we're building a test harness, then warning about
-        // documentation is probably not really relevant right now.
-        if cx.sess().opts.test {
-            return;
-        }
-
         // Only check publicly-visible items, using the result from the privacy pass.
         // It's an option so the crate root can also use this function (it doesn't
         // have a `NodeId`).
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 36b31d10c4d29..db4f44ebcbc33 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1235,6 +1235,7 @@ symbols! {
         mir_unwind_unreachable,
         mir_variant,
         miri,
+        missing_docs,
         mmx_reg,
         modifiers,
         module,
diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs
index 9d70487032699..8cdba166c9dff 100644
--- a/library/alloc/src/slice.rs
+++ b/library/alloc/src/slice.rs
@@ -96,6 +96,7 @@ pub(crate) mod hack {
     // We shouldn't add inline attribute to this since this is used in
     // `vec!` macro mostly and causes perf regression. See #71204 for
     // discussion and perf results.
+    #[allow(missing_docs)]
     pub fn into_vec<T, A: Allocator>(b: Box<[T], A>) -> Vec<T, A> {
         unsafe {
             let len = b.len();
@@ -105,6 +106,7 @@ pub(crate) mod hack {
     }
 
     #[cfg(not(no_global_oom_handling))]
+    #[allow(missing_docs)]
     #[inline]
     pub fn to_vec<T: ConvertVec, A: Allocator>(s: &[T], alloc: A) -> Vec<T, A> {
         T::to_vec(s, alloc)
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index bc8b7e24bf12b..05617669ed231 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -508,6 +508,7 @@ impl String {
     // NB see the slice::hack module in slice.rs for more information
     #[inline]
     #[cfg(test)]
+    #[allow(missing_docs)]
     pub fn from_str(_: &str) -> String {
         panic!("not available with cfg(test)");
     }
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index cf226bd28d005..035afbb8368b4 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -267,6 +267,7 @@ impl<R: ?Sized> BufReader<R> {
 // This is only used by a test which asserts that the initialization-tracking is correct.
 #[cfg(test)]
 impl<R: ?Sized> BufReader<R> {
+    #[allow(missing_docs)]
     pub fn initialized(&self) -> usize {
         self.buf.initialized()
     }
diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp
index 816cd5a5c072c..a4b15dde4530e 100644
--- a/tests/pretty/tests-are-sorted.pp
+++ b/tests/pretty/tests-are-sorted.pp
@@ -83,6 +83,7 @@
 fn a_test() {}
 #[rustc_main]
 #[coverage(off)]
+#[allow(missing_docs)]
 pub fn main() -> () {
     extern crate test;
     test::test_main_static(&[&a_test, &m_test, &z_test])
diff --git a/tests/ui/lint/lint-missing-doc-crate.rs b/tests/ui/lint/lint-missing-doc-crate.rs
new file mode 100644
index 0000000000000..afda73cbc603a
--- /dev/null
+++ b/tests/ui/lint/lint-missing-doc-crate.rs
@@ -0,0 +1,4 @@
+// This test checks that we lint on the crate when it's missing a documentation.
+//
+//@ compile-flags: -Dmissing-docs --crate-type=lib
+//~ ERROR missing documentation for the crate
diff --git a/tests/ui/lint/lint-missing-doc-crate.stderr b/tests/ui/lint/lint-missing-doc-crate.stderr
new file mode 100644
index 0000000000000..8efd3a17263fe
--- /dev/null
+++ b/tests/ui/lint/lint-missing-doc-crate.stderr
@@ -0,0 +1,10 @@
+error: missing documentation for the crate
+  --> $DIR/lint-missing-doc-crate.rs:4:47
+   |
+LL |
+   |                                              ^
+   |
+   = note: requested on the command line with `-D missing-docs`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/lint-missing-doc-expect.rs b/tests/ui/lint/lint-missing-doc-expect.rs
new file mode 100644
index 0000000000000..991f65003dc26
--- /dev/null
+++ b/tests/ui/lint/lint-missing-doc-expect.rs
@@ -0,0 +1,13 @@
+// Make sure that `#[expect(missing_docs)]` is always correctly fulfilled.
+
+//@ check-pass
+//@ revisions: lib bin test
+//@ [lib]compile-flags: --crate-type lib
+//@ [bin]compile-flags: --crate-type bin
+//@ [test]compile-flags: --test
+
+#[expect(missing_docs)]
+pub fn foo() {}
+
+#[cfg(bin)]
+fn main() {}
diff --git a/tests/ui/lint/lint-missing-doc-test.rs b/tests/ui/lint/lint-missing-doc-test.rs
new file mode 100644
index 0000000000000..93d4e4a44e928
--- /dev/null
+++ b/tests/ui/lint/lint-missing-doc-test.rs
@@ -0,0 +1,5 @@
+//! This test checks that denying the missing_docs lint does not trigger
+//! on the generated test harness.
+
+//@ check-pass
+//@ compile-flags: --test -Dmissing_docs