diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 52ac7540f6943..1e224dbf83390 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -34,7 +34,7 @@ impl MarkedAttrs {
 }
 
 pub fn is_known_lint_tool(m_item: Ident) -> bool {
-    [sym::clippy, sym::rustc].contains(&m_item.name)
+    [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item.name)
 }
 
 impl NestedMetaItem {
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index e4a702531442e..14ddb3e20793a 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -1,4 +1,5 @@
-#![deny(invalid_codeblock_attributes)]
+#![cfg_attr(bootstrap, deny(invalid_codeblock_attributes))]
+#![cfg_attr(not(bootstrap), deny(rustdoc::invalid_codeblock_attributes))]
 //! This library is used to gather all error codes into one place,
 //! the goal being to make their maintenance easier.
 
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b8db51f590d84..7d5577cdca663 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -89,6 +89,7 @@ impl SessionLintStore for LintStore {
 }
 
 /// The target of the `by_name` map, which accounts for renaming/deprecation.
+#[derive(Debug)]
 enum TargetLint {
     /// A direct lint target
     Id(LintId),
@@ -470,7 +471,10 @@ impl LintStore {
             Some(&Id(ref id)) => {
                 CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
             }
-            _ => CheckLintNameResult::NoLint(None),
+            Some(other) => {
+                tracing::debug!("got renamed lint {:?}", other);
+                CheckLintNameResult::NoLint(None)
+            }
         }
     }
 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 638b73c27a8d7..547779dd6856a 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -69,9 +69,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint::builtin::{
-    BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS,
-    EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, INVALID_HTML_TAGS,
-    MISSING_DOC_CODE_EXAMPLES, NON_AUTOLINKS, PRIVATE_DOC_TESTS,
+    BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
 };
 use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::Span;
@@ -314,17 +312,6 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
                                        // MACRO_USE_EXTERN_CRATE
     );
 
-    add_lint_group!(
-        "rustdoc",
-        NON_AUTOLINKS,
-        BROKEN_INTRA_DOC_LINKS,
-        PRIVATE_INTRA_DOC_LINKS,
-        INVALID_CODEBLOCK_ATTRIBUTES,
-        MISSING_DOC_CODE_EXAMPLES,
-        PRIVATE_DOC_TESTS,
-        INVALID_HTML_TAGS
-    );
-
     // Register renamed and removed lints.
     store.register_renamed("single_use_lifetime", "single_use_lifetimes");
     store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths");
@@ -334,8 +321,29 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
     store.register_renamed("async_idents", "keyword_idents");
     store.register_renamed("exceeding_bitshifts", "arithmetic_overflow");
     store.register_renamed("redundant_semicolon", "redundant_semicolons");
-    store.register_renamed("intra_doc_link_resolution_failure", "broken_intra_doc_links");
     store.register_renamed("overlapping_patterns", "overlapping_range_endpoints");
+
+    // These were moved to tool lints, but rustc still sees them when compiling normally, before
+    // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use
+    // `register_removed` explicitly.
+    const RUSTDOC_LINTS: &[&str] = &[
+        "broken_intra_doc_links",
+        "private_intra_doc_links",
+        "missing_crate_level_docs",
+        "missing_doc_code_examples",
+        "private_doc_tests",
+        "invalid_codeblock_attributes",
+        "invalid_html_tags",
+        "non_autolinks",
+    ];
+    for rustdoc_lint in RUSTDOC_LINTS {
+        store.register_removed(rustdoc_lint, &format!("use `rustdoc::{}` instead", rustdoc_lint));
+    }
+    store.register_removed(
+        "intra_doc_link_resolution_failure",
+        "use `rustdoc::broken_intra_doc_links` instead",
+    );
+
     store.register_removed("unknown_features", "replaced by an error");
     store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate");
     store.register_removed("negate_unsigned", "cast a signed value instead");
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 46138df07bf69..12d849e3b9466 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -1875,93 +1875,6 @@ declare_lint! {
     "detects labels that are never used"
 }
 
-declare_lint! {
-    /// The `broken_intra_doc_links` lint detects failures in resolving
-    /// intra-doc link targets. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links
-    pub BROKEN_INTRA_DOC_LINKS,
-    Warn,
-    "failures in resolving intra-doc link targets"
-}
-
-declare_lint! {
-    /// This is a subset of `broken_intra_doc_links` that warns when linking from
-    /// a public item to a private one. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#private_intra_doc_links
-    pub PRIVATE_INTRA_DOC_LINKS,
-    Warn,
-    "linking from a public item to a private one"
-}
-
-declare_lint! {
-    /// The `invalid_codeblock_attributes` lint detects code block attributes
-    /// in documentation examples that have potentially mis-typed values. This
-    /// is a `rustdoc` only lint, see the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes
-    pub INVALID_CODEBLOCK_ATTRIBUTES,
-    Warn,
-    "codeblock attribute looks a lot like a known one"
-}
-
-declare_lint! {
-    /// The `missing_crate_level_docs` lint detects if documentation is
-    /// missing at the crate root. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_crate_level_docs
-    pub MISSING_CRATE_LEVEL_DOCS,
-    Allow,
-    "detects crates with no crate-level documentation"
-}
-
-declare_lint! {
-    /// The `missing_doc_code_examples` lint detects publicly-exported items
-    /// without code samples in their documentation. This is a `rustdoc` only
-    /// lint, see the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
-    pub MISSING_DOC_CODE_EXAMPLES,
-    Allow,
-    "detects publicly-exported items without code samples in their documentation"
-}
-
-declare_lint! {
-    /// The `private_doc_tests` lint detects code samples in docs of private
-    /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see
-    /// the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests
-    pub PRIVATE_DOC_TESTS,
-    Allow,
-    "detects code samples in docs of private items not documented by rustdoc"
-}
-
-declare_lint! {
-    /// The `invalid_html_tags` lint detects invalid HTML tags. This is a
-    /// `rustdoc` only lint, see the documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags
-    pub INVALID_HTML_TAGS,
-    Allow,
-    "detects invalid HTML tags in doc comments"
-}
-
-declare_lint! {
-    /// The `non_autolinks` lint detects when a URL could be written using
-    /// only angle brackets. This is a `rustdoc` only lint, see the
-    /// documentation in the [rustdoc book].
-    ///
-    /// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks
-    pub NON_AUTOLINKS,
-    Warn,
-    "detects URLs that could be written using only angle brackets"
-}
-
 declare_lint! {
     /// The `where_clauses_object_safety` lint detects for [object safety] of
     /// [where clauses].
@@ -3020,14 +2933,6 @@ declare_lint_pass! {
         ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
         UNSTABLE_NAME_COLLISIONS,
         IRREFUTABLE_LET_PATTERNS,
-        BROKEN_INTRA_DOC_LINKS,
-        PRIVATE_INTRA_DOC_LINKS,
-        INVALID_CODEBLOCK_ATTRIBUTES,
-        MISSING_CRATE_LEVEL_DOCS,
-        MISSING_DOC_CODE_EXAMPLES,
-        INVALID_HTML_TAGS,
-        PRIVATE_DOC_TESTS,
-        NON_AUTOLINKS,
         WHERE_CLAUSES_OBJECT_SAFETY,
         PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
         MACRO_USE_EXTERN_CRATE,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 27bb45bcc8512..f9af0886a959a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1021,6 +1021,7 @@ symbols! {
         rustc_then_this_would_need,
         rustc_unsafe_specialization_marker,
         rustc_variance,
+        rustdoc,
         rustfmt,
         rvalue_static_promotion,
         sanitize,
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index c50d9507a1798..0492612372913 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -297,7 +297,8 @@ pub mod primitive;
     unused_imports,
     unsafe_op_in_unsafe_fn
 )]
-#[allow(non_autolinks)]
+#[cfg_attr(bootstrap, allow(non_autolinks))]
+#[cfg_attr(not(bootstrap), allow(rustdoc::non_autolinks))]
 // FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is
 // merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet.
 #[allow(clashing_extern_declarations)]
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 2008348ea8d8e..9317c89625d06 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -735,8 +735,15 @@ impl<'a> Builder<'a> {
             .env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler))
             .env("CFG_RELEASE_CHANNEL", &self.config.channel)
             .env("RUSTDOC_REAL", self.rustdoc(compiler))
-            .env("RUSTC_BOOTSTRAP", "1")
-            .arg("-Winvalid_codeblock_attributes");
+            .env("RUSTC_BOOTSTRAP", "1");
+
+        // cfg(bootstrap), can be removed on the next beta bump
+        if compiler.stage == 0 {
+            cmd.arg("-Winvalid_codeblock_attributes");
+        } else {
+            cmd.arg("-Wrustdoc::invalid_codeblock_attributes");
+        }
+
         if self.config.deny_warnings {
             cmd.arg("-Dwarnings");
         }
@@ -1292,7 +1299,12 @@ impl<'a> Builder<'a> {
             // fixed via better support from Cargo.
             cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" "));
 
-            rustdocflags.arg("-Winvalid_codeblock_attributes");
+            // cfg(bootstrap), can be removed on the next beta bump
+            if compiler.stage == 0 {
+                rustdocflags.arg("-Winvalid_codeblock_attributes");
+            } else {
+                rustdocflags.arg("-Wrustdoc::invalid_codeblock_attributes");
+            }
         }
 
         if mode == Mode::Rustc {
diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md
index cce3623dc8f49..174db711bcee7 100644
--- a/src/doc/rustdoc/src/lints.md
+++ b/src/doc/rustdoc/src/lints.md
@@ -4,12 +4,13 @@
 can use them like any other lints by doing this:
 
 ```rust
-#![allow(missing_docs)] // allows the lint, no diagnostics will be reported
-#![warn(missing_docs)] // warn if there are missing docs
-#![deny(missing_docs)] // error if there are missing docs
-# //! Crate docs.
+#![allow(rustdoc::broken_intra_doc_links)] // allows the lint, no diagnostics will be reported
+#![warn(rustdoc::broken_intra_doc_links)] // warn if there are broken intra-doc links
+#![deny(rustdoc::broken_intra_doc_links)] // error if there are broken intra-doc links
 ```
 
+Note that, except for `missing_docs`, these lints are only available when running `rustdoc`, not `rustc`.
+
 Here is the list of the lints provided by `rustdoc`:
 
 ## broken_intra_doc_links
@@ -51,7 +52,7 @@ warning: `Foo` is both an enum and a function
 1 | /// [`Foo`]
   |      ^^^^^ ambiguous link
   |
-  = note: `#[warn(broken_intra_doc_links)]` on by default
+  = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
 help: to link to the enum, prefix with the item type
   |
 1 | /// [`enum@Foo`]
@@ -83,7 +84,7 @@ warning: public documentation for `public` links to private item `private`
 1 | /// [private]
   |      ^^^^^^^ this item is private
   |
-  = note: `#[warn(private_intra_doc_links)]` on by default
+  = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
   = note: this link will resolve properly if you pass `--document-private-items`
 ```
 
@@ -97,7 +98,7 @@ warning: public documentation for `public` links to private item `private`
 1 | /// [private]
   |      ^^^^^^^ this item is private
   |
-  = note: `#[warn(private_intra_doc_links)]` on by default
+  = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
   = note: this link resolves only because you passed `--document-private-items`, but will break without
 ```
 
@@ -125,13 +126,15 @@ warning: missing documentation for a function
    | ^^^^^^^^^^^^^^^^^^^^^
 ```
 
+Note that unlike other rustdoc lints, this lint is also available from `rustc` directly.
+
 ## missing_crate_level_docs
 
 This lint is **allowed by default**. It detects if there is no documentation
 at the crate root. For example:
 
 ```rust
-#![warn(missing_crate_level_docs)]
+#![warn(rustdoc::missing_crate_level_docs)]
 ```
 
 This will generate the following warning:
@@ -155,7 +158,7 @@ This lint is **allowed by default** and is **nightly-only**. It detects when a d
 is missing a code example. For example:
 
 ```rust
-#![warn(missing_doc_code_examples)]
+#![warn(rustdoc::missing_doc_code_examples)]
 
 /// There is no code example!
 pub fn no_code_example() {}
@@ -191,7 +194,7 @@ This lint is **allowed by default**. It detects documentation tests when they
 are on a private item. For example:
 
 ```rust
-#![warn(private_doc_tests)]
+#![warn(rustdoc::private_doc_tests)]
 
 mod foo {
     /// private doc test
@@ -245,7 +248,7 @@ warning: unknown attribute `should-panic`. Did you mean `should_panic`?
 5 | | /// ```
   | |_______^
   |
-  = note: `#[warn(invalid_codeblock_attributes)]` on by default
+  = note: `#[warn(rustdoc::invalid_codeblock_attributes)]` on by default
   = help: the code block will either not be tested if not marked as a rust one or won't fail if it doesn't panic when running
 ```
 
@@ -258,7 +261,7 @@ This lint is **allowed by default** and is **nightly-only**. It detects unclosed
 or invalid HTML tags. For example:
 
 ```rust
-#![warn(invalid_html_tags)]
+#![warn(rustdoc::invalid_html_tags)]
 
 /// <h1>
 /// </script>
@@ -275,7 +278,11 @@ warning: unopened HTML tag `script`
 2 | | /// </script>
   | |_____________^
   |
-  = note: `#[warn(invalid_html_tags)]` on by default
+  note: the lint level is defined here
+ --> foo.rs:1:9
+  |
+1 | #![warn(rustdoc::invalid_html_tags)]
+  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 warning: unclosed HTML tag `h1`
  --> foo.rs:1:1
@@ -310,7 +317,7 @@ warning: this URL is not a hyperlink
 1 | /// http://example.org
   |     ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<http://example.org>`
   |
-  = note: `#[warn(non_autolinks)]` on by default
+  = note: `#[warn(rustdoc::non_autolinks)]` on by default
 
 warning: unneeded long form for URL
  --> foo.rs:2:5
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index d9f5b5bfa3ae2..de6942968ea0d 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -263,6 +263,8 @@ crate struct RenderOptions {
     crate document_private: bool,
     /// Document items that have `doc(hidden)`.
     crate document_hidden: bool,
+    /// If `true`, generate a JSON file in the crate folder instead of HTML redirection files.
+    crate generate_redirect_map: bool,
     crate unstable_features: rustc_feature::UnstableFeatures,
 }
 
@@ -570,6 +572,7 @@ impl Options {
         let document_private = matches.opt_present("document-private-items");
         let document_hidden = matches.opt_present("document-hidden-items");
         let run_check = matches.opt_present("check");
+        let generate_redirect_map = matches.opt_present("generate-redirect-map");
 
         let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
@@ -627,6 +630,7 @@ impl Options {
                 generate_search_filter,
                 document_private,
                 document_hidden,
+                generate_redirect_map,
                 unstable_features: rustc_feature::UnstableFeatures::from_environment(
                     crate_name.as_deref(),
                 ),
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 6218aaf8276d5..9cb0649b314f5 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -24,9 +24,10 @@ use rustc_span::source_map;
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
 
+use std::cell::RefCell;
+use std::collections::hash_map::Entry;
 use std::mem;
 use std::rc::Rc;
-use std::{cell::RefCell, collections::hash_map::Entry};
 
 use crate::clean;
 use crate::clean::inline::build_external_trait;
@@ -227,64 +228,6 @@ crate fn new_handler(
     )
 }
 
-/// This function is used to setup the lint initialization. By default, in rustdoc, everything
-/// is "allowed". Depending if we run in test mode or not, we want some of them to be at their
-/// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTES" lint is activated in both
-/// modes.
-///
-/// A little detail easy to forget is that there is a way to set the lint level for all lints
-/// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level
-/// inside this function.
-///
-/// It returns a tuple containing:
-///  * Vector of tuples of lints' name and their associated "max" level
-///  * HashMap of lint id with their associated "max" level
-pub(crate) fn init_lints<F>(
-    mut allowed_lints: Vec<String>,
-    lint_opts: Vec<(String, lint::Level)>,
-    filter_call: F,
-) -> (Vec<(String, lint::Level)>, FxHashMap<lint::LintId, lint::Level>)
-where
-    F: Fn(&lint::Lint) -> Option<(String, lint::Level)>,
-{
-    let warnings_lint_name = lint::builtin::WARNINGS.name;
-
-    allowed_lints.push(warnings_lint_name.to_owned());
-    allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
-
-    let lints = || {
-        lint::builtin::HardwiredLints::get_lints()
-            .into_iter()
-            .chain(rustc_lint::SoftLints::get_lints().into_iter())
-    };
-
-    let lint_opts = lints()
-        .filter_map(|lint| {
-            // Permit feature-gated lints to avoid feature errors when trying to
-            // allow all lints.
-            if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) {
-                None
-            } else {
-                filter_call(lint)
-            }
-        })
-        .chain(lint_opts.into_iter())
-        .collect::<Vec<_>>();
-
-    let lint_caps = lints()
-        .filter_map(|lint| {
-            // We don't want to allow *all* lints so let's ignore
-            // those ones.
-            if allowed_lints.iter().any(|l| lint.name == l) {
-                None
-            } else {
-                Some((lint::LintId::of(lint), lint::Allow))
-            }
-        })
-        .collect();
-    (lint_opts, lint_caps)
-}
-
 /// Parse, resolve, and typecheck the given crate.
 crate fn create_config(
     RustdocOptions {
@@ -313,37 +256,22 @@ crate fn create_config(
     let cpath = Some(input.clone());
     let input = Input::File(input);
 
-    let broken_intra_doc_links = lint::builtin::BROKEN_INTRA_DOC_LINKS.name;
-    let private_intra_doc_links = lint::builtin::PRIVATE_INTRA_DOC_LINKS.name;
-    let missing_docs = rustc_lint::builtin::MISSING_DOCS.name;
-    let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name;
-    let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name;
-    let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name;
-    let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
-    let invalid_html_tags = rustc_lint::builtin::INVALID_HTML_TAGS.name;
-    let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name;
-    let non_autolinks = rustc_lint::builtin::NON_AUTOLINKS.name;
-    let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name;
-
-    // In addition to those specific lints, we also need to allow those given through
-    // command line, otherwise they'll get ignored and we don't want that.
-    let lints_to_show = vec![
-        broken_intra_doc_links.to_owned(),
-        private_intra_doc_links.to_owned(),
-        missing_docs.to_owned(),
-        missing_doc_example.to_owned(),
-        private_doc_tests.to_owned(),
-        no_crate_level_docs.to_owned(),
-        invalid_codeblock_attributes_name.to_owned(),
-        invalid_html_tags.to_owned(),
-        renamed_and_removed_lints.to_owned(),
-        unknown_lints.to_owned(),
-        non_autolinks.to_owned(),
+    // By default, rustdoc ignores all lints.
+    // Specifically unblock lints relevant to documentation or the lint machinery itself.
+    let mut lints_to_show = vec![
+        // it's unclear whether this should be part of rustdoc directly (#77364)
+        rustc_lint::builtin::MISSING_DOCS.name.to_string(),
+        // these are definitely not part of rustdoc, but we want to warn on them anyway.
+        rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name.to_string(),
+        rustc_lint::builtin::UNKNOWN_LINTS.name.to_string(),
     ];
+    lints_to_show.extend(crate::lint::RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string()));
 
-    let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| {
+    let (lint_opts, lint_caps) = crate::lint::init_lints(lints_to_show, lint_opts, |lint| {
         // FIXME: why is this necessary?
-        if lint.name == broken_intra_doc_links || lint.name == invalid_codeblock_attributes_name {
+        if lint.name == crate::lint::BROKEN_INTRA_DOC_LINKS.name
+            || lint.name == crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name
+        {
             None
         } else {
             Some((lint.name_lower(), lint::Allow))
@@ -383,7 +311,7 @@ crate fn create_config(
         diagnostic_output: DiagnosticOutput::Default,
         stderr: None,
         lint_caps,
-        register_lints: None,
+        register_lints: Some(box crate::lint::register_lints),
         override_queries: Some(|_sess, providers, _external_providers| {
             // Most lints will require typechecking, so just don't run them.
             providers.lint_mod = |_, _| {};
@@ -550,7 +478,7 @@ crate fn run_global_ctxt(
             let help = "The following guide may be of use:\n\
                 https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html";
             tcx.struct_lint_node(
-                rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS,
+                crate::lint::MISSING_CRATE_LEVEL_DOCS,
                 ctxt.as_local_hir_id(m.def_id).unwrap(),
                 |lint| {
                     let mut diag =
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index c3b9fd5a1dd42..27ce064669d2c 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -26,8 +26,8 @@ use std::str;
 
 use crate::clean::Attributes;
 use crate::config::Options;
-use crate::core::init_lints;
 use crate::html::markdown::{self, ErrorCodes, Ignore, LangString};
+use crate::lint::init_lints;
 use crate::passes::span_of_attrs;
 
 #[derive(Clone, Default)]
@@ -44,11 +44,14 @@ crate struct TestOptions {
 crate fn run(options: Options) -> Result<(), ErrorReported> {
     let input = config::Input::File(options.input.clone());
 
-    let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name;
+    let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
 
-    // In addition to those specific lints, we also need to allow those given through
-    // command line, otherwise they'll get ignored and we don't want that.
-    let allowed_lints = vec![invalid_codeblock_attributes_name.to_owned()];
+    // See core::create_config for what's going on here.
+    let allowed_lints = vec![
+        invalid_codeblock_attributes_name.to_owned(),
+        lint::builtin::UNKNOWN_LINTS.name.to_owned(),
+        lint::builtin::RENAMED_AND_REMOVED_LINTS.name.to_owned(),
+    ];
 
     let (lint_opts, lint_caps) = init_lints(allowed_lints, options.lint_opts.clone(), |lint| {
         if lint.name == invalid_codeblock_attributes_name {
@@ -92,7 +95,7 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
         diagnostic_output: DiagnosticOutput::Default,
         stderr: None,
         lint_caps,
-        register_lints: None,
+        register_lints: Some(box crate::lint::register_lints),
         override_queries: None,
         make_codegen_backend: None,
         registry: rustc_driver::diagnostics_registry(),
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 9a054e29dd3b5..f8ca259fb9ab5 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -21,7 +21,6 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
 use rustc_hir::HirId;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::lint;
 use rustc_span::edition::Edition;
 use rustc_span::Span;
 use std::borrow::Cow;
@@ -721,7 +720,7 @@ impl<'tcx> ExtraInfo<'tcx> {
             (None, None) => return,
         };
         self.tcx.struct_span_lint_hir(
-            lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES,
+            crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
             hir_id,
             self.sp,
             |lint| {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 4e762a40f0849..394c57c7214e7 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -111,6 +111,10 @@ crate struct Context<'tcx> {
     /// real location of an item. This is used to allow external links to
     /// publicly reused items to redirect to the right location.
     crate render_redirect_pages: bool,
+    /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set
+    /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
+    /// the crate.
+    crate redirections: Option<Rc<RefCell<FxHashMap<String, String>>>>,
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     /// Tracks section IDs for `Deref` targets so they match in both the main
@@ -404,6 +408,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             static_root_path,
             generate_search_filter,
             unstable_features,
+            generate_redirect_map,
             ..
         } = options;
 
@@ -509,6 +514,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             all: Rc::new(RefCell::new(AllTypes::new())),
             errors: Rc::new(receiver),
             cache: Rc::new(cache),
+            redirections: if generate_redirect_map { Some(Default::default()) } else { None },
         };
 
         CURRENT_DEPTH.with(|s| s.set(0));
@@ -587,6 +593,15 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             &style_files,
         );
         self.shared.fs.write(&settings_file, v.as_bytes())?;
+        if let Some(redirections) = self.redirections.take() {
+            if !redirections.borrow().is_empty() {
+                let redirect_map_path =
+                    self.dst.join(&*krate.name.as_str()).join("redirect-map.json");
+                let paths = serde_json::to_string(&*redirections.borrow()).unwrap();
+                self.shared.ensure_dir(&self.dst.join(&*krate.name.as_str()))?;
+                self.shared.fs.write(&redirect_map_path, paths.as_bytes())?;
+            }
+        }
 
         // Flush pending errors.
         Arc::get_mut(&mut self.shared).unwrap().fs.close();
@@ -675,9 +690,17 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             // to the new one (without).
             if item_type == ItemType::Macro {
                 let redir_name = format!("{}.{}!.html", item_type, name);
-                let redir_dst = self.dst.join(redir_name);
-                let v = layout::redirect(file_name);
-                self.shared.fs.write(&redir_dst, v.as_bytes())?;
+                if let Some(ref redirections) = self.redirections {
+                    let crate_name = &self.shared.layout.krate;
+                    redirections.borrow_mut().insert(
+                        format!("{}/{}", crate_name, redir_name),
+                        format!("{}/{}", crate_name, file_name),
+                    );
+                } else {
+                    let v = layout::redirect(file_name);
+                    let redir_dst = self.dst.join(redir_name);
+                    self.shared.fs.write(&redir_dst, v.as_bytes())?;
+                }
             }
         }
         Ok(())
@@ -1588,17 +1611,27 @@ impl Context<'_> {
                 &self.shared.style_files,
             )
         } else {
-            let mut url = self.root_path();
             if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) {
+                let mut path = String::new();
                 for name in &names[..names.len() - 1] {
-                    url.push_str(name);
-                    url.push('/');
+                    path.push_str(name);
+                    path.push('/');
+                }
+                path.push_str(&item_path(ty, names.last().unwrap()));
+                match self.redirections {
+                    Some(ref redirections) => {
+                        let mut current_path = String::new();
+                        for name in &self.current {
+                            current_path.push_str(name);
+                            current_path.push('/');
+                        }
+                        current_path.push_str(&item_path(ty, names.last().unwrap()));
+                        redirections.borrow_mut().insert(current_path, path);
+                    }
+                    None => return layout::redirect(&format!("{}{}", self.root_path(), path)),
                 }
-                url.push_str(&item_path(ty, names.last().unwrap()));
-                layout::redirect(&url)
-            } else {
-                String::new()
             }
+            String::new()
         }
     }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 6b37643a39580..e447b49c11a77 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -45,6 +45,7 @@ extern crate rustc_infer;
 extern crate rustc_interface;
 extern crate rustc_lexer;
 extern crate rustc_lint;
+extern crate rustc_lint_defs;
 extern crate rustc_metadata;
 extern crate rustc_middle;
 extern crate rustc_mir;
@@ -86,6 +87,7 @@ mod formats;
 // used by the error-index generator, so it needs to be public
 pub mod html;
 mod json;
+crate mod lint;
 mod markdown;
 mod passes;
 mod theme;
@@ -497,6 +499,13 @@ fn opts() -> Vec<RustcOptGroup> {
             o.optopt("", "test-builder", "The rustc-like binary to use as the test builder", "PATH")
         }),
         unstable("check", |o| o.optflag("", "check", "Run rustdoc checks")),
+        unstable("generate-redirect-map", |o| {
+            o.optflag(
+                "",
+                "generate-redirect-map",
+                "Generate JSON file at the top level instead of generating HTML redirection files",
+            )
+        }),
     ]
 }
 
diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs
new file mode 100644
index 0000000000000..e8806c1b6d787
--- /dev/null
+++ b/src/librustdoc/lint.rs
@@ -0,0 +1,188 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_lint::LintStore;
+use rustc_lint_defs::{declare_tool_lint, Lint, LintId};
+use rustc_session::{lint, Session};
+
+use std::lazy::SyncLazy as Lazy;
+
+/// This function is used to setup the lint initialization. By default, in rustdoc, everything
+/// is "allowed". Depending if we run in test mode or not, we want some of them to be at their
+/// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTES" lint is activated in both
+/// modes.
+///
+/// A little detail easy to forget is that there is a way to set the lint level for all lints
+/// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level
+/// inside this function.
+///
+/// It returns a tuple containing:
+///  * Vector of tuples of lints' name and their associated "max" level
+///  * HashMap of lint id with their associated "max" level
+pub(crate) fn init_lints<F>(
+    mut allowed_lints: Vec<String>,
+    lint_opts: Vec<(String, lint::Level)>,
+    filter_call: F,
+) -> (Vec<(String, lint::Level)>, FxHashMap<lint::LintId, lint::Level>)
+where
+    F: Fn(&lint::Lint) -> Option<(String, lint::Level)>,
+{
+    let warnings_lint_name = lint::builtin::WARNINGS.name;
+
+    allowed_lints.push(warnings_lint_name.to_owned());
+    allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
+
+    let lints = || {
+        lint::builtin::HardwiredLints::get_lints()
+            .into_iter()
+            .chain(rustc_lint::SoftLints::get_lints().into_iter())
+    };
+
+    let lint_opts = lints()
+        .filter_map(|lint| {
+            // Permit feature-gated lints to avoid feature errors when trying to
+            // allow all lints.
+            if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) {
+                None
+            } else {
+                filter_call(lint)
+            }
+        })
+        .chain(lint_opts.into_iter())
+        .collect::<Vec<_>>();
+
+    let lint_caps = lints()
+        .filter_map(|lint| {
+            // We don't want to allow *all* lints so let's ignore
+            // those ones.
+            if allowed_lints.iter().any(|l| lint.name == l) {
+                None
+            } else {
+                Some((lint::LintId::of(lint), lint::Allow))
+            }
+        })
+        .collect();
+    (lint_opts, lint_caps)
+}
+
+macro_rules! declare_rustdoc_lint {
+    ($(#[$attr:meta])* $name: ident, $level: ident, $descr: literal $(,)?) => {
+        declare_tool_lint! {
+            $(#[$attr])* pub rustdoc::$name, $level, $descr
+        }
+    }
+}
+
+declare_rustdoc_lint! {
+    /// The `broken_intra_doc_links` lint detects failures in resolving
+    /// intra-doc link targets. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links
+    BROKEN_INTRA_DOC_LINKS,
+    Warn,
+    "failures in resolving intra-doc link targets"
+}
+
+declare_rustdoc_lint! {
+    /// This is a subset of `broken_intra_doc_links` that warns when linking from
+    /// a public item to a private one. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#private_intra_doc_links
+    PRIVATE_INTRA_DOC_LINKS,
+    Warn,
+    "linking from a public item to a private one"
+}
+
+declare_rustdoc_lint! {
+    /// The `invalid_codeblock_attributes` lint detects code block attributes
+    /// in documentation examples that have potentially mis-typed values. This
+    /// is a `rustdoc` only lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes
+    INVALID_CODEBLOCK_ATTRIBUTES,
+    Warn,
+    "codeblock attribute looks a lot like a known one"
+}
+
+declare_rustdoc_lint! {
+    /// The `missing_crate_level_docs` lint detects if documentation is
+    /// missing at the crate root. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_crate_level_docs
+    MISSING_CRATE_LEVEL_DOCS,
+    Allow,
+    "detects crates with no crate-level documentation"
+}
+
+declare_rustdoc_lint! {
+    /// The `missing_doc_code_examples` lint detects publicly-exported items
+    /// without code samples in their documentation. This is a `rustdoc` only
+    /// lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
+    MISSING_DOC_CODE_EXAMPLES,
+    Allow,
+    "detects publicly-exported items without code samples in their documentation"
+}
+
+declare_rustdoc_lint! {
+    /// The `private_doc_tests` lint detects code samples in docs of private
+    /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see
+    /// the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests
+    PRIVATE_DOC_TESTS,
+    Allow,
+    "detects code samples in docs of private items not documented by rustdoc"
+}
+
+declare_rustdoc_lint! {
+    /// The `invalid_html_tags` lint detects invalid HTML tags. This is a
+    /// `rustdoc` only lint, see the documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags
+    INVALID_HTML_TAGS,
+    Allow,
+    "detects invalid HTML tags in doc comments"
+}
+
+declare_rustdoc_lint! {
+    /// The `non_autolinks` lint detects when a URL could be written using
+    /// only angle brackets. This is a `rustdoc` only lint, see the
+    /// documentation in the [rustdoc book].
+    ///
+    /// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks
+    NON_AUTOLINKS,
+    Warn,
+    "detects URLs that could be written using only angle brackets"
+}
+
+crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
+    vec![
+        BROKEN_INTRA_DOC_LINKS,
+        PRIVATE_INTRA_DOC_LINKS,
+        MISSING_DOC_CODE_EXAMPLES,
+        PRIVATE_DOC_TESTS,
+        INVALID_CODEBLOCK_ATTRIBUTES,
+        INVALID_HTML_TAGS,
+        NON_AUTOLINKS,
+        MISSING_CRATE_LEVEL_DOCS,
+    ]
+});
+
+crate fn register_lints(_sess: &Session, lint_store: &mut LintStore) {
+    lint_store.register_lints(&**RUSTDOC_LINTS);
+    lint_store.register_group(
+        true,
+        "rustdoc",
+        None,
+        RUSTDOC_LINTS.iter().map(|&lint| LintId::of(lint)).collect(),
+    );
+    for lint in &*RUSTDOC_LINTS {
+        let name = lint.name_lower();
+        lint_store.register_renamed(&name.replace("rustdoc::", ""), &name);
+    }
+    lint_store
+        .register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links");
+}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index c047f8729adc1..38efecb393b79 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -16,10 +16,7 @@ use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::{bug, ty};
 use rustc_resolve::ParentScope;
-use rustc_session::lint::{
-    builtin::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS},
-    Lint,
-};
+use rustc_session::lint::Lint;
 use rustc_span::hygiene::{MacroKind, SyntaxContext};
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::DUMMY_SP;
@@ -37,6 +34,7 @@ use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLin
 use crate::core::DocContext;
 use crate::fold::DocFolder;
 use crate::html::markdown::{markdown_links, MarkdownLink};
+use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
 use crate::passes::Pass;
 
 use super::span_of_attrs;
diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs
index 3b1508c134857..81104236314d9 100644
--- a/src/librustdoc/passes/doc_test_lints.rs
+++ b/src/librustdoc/passes/doc_test_lints.rs
@@ -68,8 +68,7 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo
         return false;
     }
     let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local());
-    let (level, source) =
-        cx.tcx.lint_level_at_node(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id);
+    let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
     level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
 }
 
@@ -91,7 +90,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
             let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
             cx.tcx.struct_span_lint_hir(
-                lint::builtin::MISSING_DOC_CODE_EXAMPLES,
+                crate::lint::MISSING_DOC_CODE_EXAMPLES,
                 hir_id,
                 sp,
                 |lint| lint.build("missing code example in this documentation").emit(),
@@ -99,7 +98,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
         }
     } else if tests.found_tests > 0 && !cx.cache.access_levels.is_public(item.def_id) {
         cx.tcx.struct_span_lint_hir(
-            lint::builtin::PRIVATE_DOC_TESTS,
+            crate::lint::PRIVATE_DOC_TESTS,
             hir_id,
             span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
             |lint| lint.build("documentation test in private item").emit(),
diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs
index a6fe7e228d7e8..27e669aa44fc0 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/html_tags.rs
@@ -5,7 +5,6 @@ use crate::fold::DocFolder;
 use crate::html::markdown::opts;
 use core::ops::Range;
 use pulldown_cmark::{Event, Parser, Tag};
-use rustc_session::lint;
 use std::iter::Peekable;
 use std::str::CharIndices;
 
@@ -183,7 +182,7 @@ impl<'a, 'tcx> DocFolder for InvalidHtmlTagsLinter<'a, 'tcx> {
                     Some(sp) => sp,
                     None => span_of_attrs(&item.attrs).unwrap_or(item.source.span()),
                 };
-                cx.tcx.struct_span_lint_hir(lint::builtin::INVALID_HTML_TAGS, hir_id, sp, |lint| {
+                cx.tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
                     lint.build(msg).emit()
                 });
             };
diff --git a/src/librustdoc/passes/non_autolinks.rs b/src/librustdoc/passes/non_autolinks.rs
index 9d4539a9769ca..09a1959fa113c 100644
--- a/src/librustdoc/passes/non_autolinks.rs
+++ b/src/librustdoc/passes/non_autolinks.rs
@@ -7,7 +7,6 @@ use core::ops::Range;
 use pulldown_cmark::{Event, LinkType, Parser, Tag};
 use regex::Regex;
 use rustc_errors::Applicability;
-use rustc_session::lint;
 
 crate const CHECK_NON_AUTOLINKS: Pass = Pass {
     name: "check-non-autolinks",
@@ -74,7 +73,7 @@ impl<'a, 'tcx> DocFolder for NonAutolinksLinter<'a, 'tcx> {
                 let sp = super::source_span_for_markdown_range(cx, &dox, &range, &item.attrs)
                     .or_else(|| span_of_attrs(&item.attrs))
                     .unwrap_or(item.source.span());
-                cx.tcx.struct_span_lint_hir(lint::builtin::NON_AUTOLINKS, hir_id, sp, |lint| {
+                cx.tcx.struct_span_lint_hir(crate::lint::NON_AUTOLINKS, hir_id, sp, |lint| {
                     lint.build(msg)
                         .span_suggestion(
                             sp,
diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/Makefile b/src/test/run-make-fulldeps/rustdoc-map-file/Makefile
new file mode 100644
index 0000000000000..ce977fa0cea55
--- /dev/null
+++ b/src/test/run-make-fulldeps/rustdoc-map-file/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all:
+	$(RUSTDOC) -Z unstable-options --generate-redirect-map foo.rs -o "$(TMPDIR)/out"
+	"$(PYTHON)" validate_json.py "$(TMPDIR)/out"
diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/expected.json b/src/test/run-make-fulldeps/rustdoc-map-file/expected.json
new file mode 100644
index 0000000000000..6b1ccbeac3010
--- /dev/null
+++ b/src/test/run-make-fulldeps/rustdoc-map-file/expected.json
@@ -0,0 +1,5 @@
+{
+  "foo/macro.foo!.html": "foo/macro.foo.html",
+  "foo/private/struct.Quz.html": "foo/struct.Quz.html",
+  "foo/hidden/struct.Bar.html": "foo/struct.Bar.html"
+}
diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/foo.rs b/src/test/run-make-fulldeps/rustdoc-map-file/foo.rs
new file mode 100644
index 0000000000000..e12b9d2292c51
--- /dev/null
+++ b/src/test/run-make-fulldeps/rustdoc-map-file/foo.rs
@@ -0,0 +1,16 @@
+pub use private::Quz;
+pub use hidden::Bar;
+
+mod private {
+    pub struct Quz;
+}
+
+#[doc(hidden)]
+pub mod hidden {
+    pub struct Bar;
+}
+
+#[macro_export]
+macro_rules! foo {
+    () => {}
+}
diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py b/src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py
new file mode 100755
index 0000000000000..5c14c90b70d37
--- /dev/null
+++ b/src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import json
+
+
+def find_redirect_map_file(folder, errors):
+    for root, dirs, files in os.walk(folder):
+        for name in files:
+            if not name.endswith("redirect-map.json"):
+                continue
+            with open(os.path.join(root, name)) as f:
+                data = json.load(f)
+            with open("expected.json") as f:
+                expected = json.load(f)
+            for key in expected:
+                if expected[key] != data.get(key):
+                    errors.append("Expected `{}` for key `{}`, found: `{}`".format(
+                        expected[key], key, data.get(key)))
+                else:
+                    del data[key]
+            for key in data:
+                errors.append("Extra data not expected: key: `{}`, data: `{}`".format(
+                    key, data[key]))
+            return True
+    return False
+
+
+if len(sys.argv) != 2:
+    print("Expected doc directory to check!")
+    sys.exit(1)
+
+errors = []
+if not find_redirect_map_file(sys.argv[1], errors):
+    print("Didn't find the map file in `{}`...".format(sys.argv[1]))
+    sys.exit(1)
+for err in errors:
+    print("=> {}".format(err))
+if len(errors) != 0:
+    sys.exit(1)
diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.rs b/src/test/rustdoc-ui/assoc-item-not-in-scope.rs
index c5bb4305db7ec..0976515f4a426 100644
--- a/src/test/rustdoc-ui/assoc-item-not-in-scope.rs
+++ b/src/test/rustdoc-ui/assoc-item-not-in-scope.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 #[derive(Debug)]
 /// Link to [`S::fmt`]
diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr
index 92d27179e8c3f..358871b532313 100644
--- a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr
+++ b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr
@@ -7,8 +7,8 @@ LL | /// Link to [`S::fmt`]
 note: the lint level is defined here
   --> $DIR/assoc-item-not-in-scope.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/rustdoc-ui/check-attr-test.rs b/src/test/rustdoc-ui/check-attr-test.rs
index 665f330e34ea5..023d620bea222 100644
--- a/src/test/rustdoc-ui/check-attr-test.rs
+++ b/src/test/rustdoc-ui/check-attr-test.rs
@@ -1,6 +1,6 @@
 // compile-flags:--test
 
-#![deny(invalid_codeblock_attributes)]
+#![deny(rustdoc::invalid_codeblock_attributes)]
 
 /// foo
 ///
diff --git a/src/test/rustdoc-ui/check-attr-test.stderr b/src/test/rustdoc-ui/check-attr-test.stderr
index 1e067a5d21c44..affd0372a1f5e 100644
--- a/src/test/rustdoc-ui/check-attr-test.stderr
+++ b/src/test/rustdoc-ui/check-attr-test.stderr
@@ -11,8 +11,8 @@ error: unknown attribute `compile-fail`. Did you mean `compile_fail`?
 note: the lint level is defined here
  --> $DIR/check-attr-test.rs:3:9
   |
-3 | #![deny(invalid_codeblock_attributes)]
-  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+3 | #![deny(rustdoc::invalid_codeblock_attributes)]
+  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
 
 error: unknown attribute `compilefail`. Did you mean `compile_fail`?
diff --git a/src/test/rustdoc-ui/check-attr.rs b/src/test/rustdoc-ui/check-attr.rs
index 9e02eab753e26..763bc4c6cddb9 100644
--- a/src/test/rustdoc-ui/check-attr.rs
+++ b/src/test/rustdoc-ui/check-attr.rs
@@ -1,4 +1,4 @@
-#![deny(invalid_codeblock_attributes)]
+#![deny(rustdoc::invalid_codeblock_attributes)]
 
 /// foo
 //~^ ERROR
diff --git a/src/test/rustdoc-ui/check-attr.stderr b/src/test/rustdoc-ui/check-attr.stderr
index 919eb047eefb5..9312cfb76f35f 100644
--- a/src/test/rustdoc-ui/check-attr.stderr
+++ b/src/test/rustdoc-ui/check-attr.stderr
@@ -13,8 +13,8 @@ LL | | /// ```
 note: the lint level is defined here
   --> $DIR/check-attr.rs:1:9
    |
-LL | #![deny(invalid_codeblock_attributes)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::invalid_codeblock_attributes)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: the code block will either not be tested if not marked as a rust one or won't fail if it compiles successfully
 
 error: unknown attribute `compilefail`. Did you mean `compile_fail`?
diff --git a/src/test/rustdoc-ui/check-fail.stderr b/src/test/rustdoc-ui/check-fail.stderr
index b4f255642da53..9f5ccbc6687c2 100644
--- a/src/test/rustdoc-ui/check-fail.stderr
+++ b/src/test/rustdoc-ui/check-fail.stderr
@@ -21,7 +21,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rustdoc)]
    |         ^^^^^^^
-   = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]`
+   = note: `#[deny(rustdoc::missing_doc_code_examples)]` implied by `#[deny(rustdoc)]`
 
 error: unknown attribute `testharness`. Did you mean `test_harness`?
   --> $DIR/check-fail.rs:6:1
@@ -37,7 +37,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rustdoc)]
    |         ^^^^^^^
-   = note: `#[deny(invalid_codeblock_attributes)]` implied by `#[deny(rustdoc)]`
+   = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(rustdoc)]`
    = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
 
 error: unknown attribute `testharness`. Did you mean `test_harness`?
diff --git a/src/test/rustdoc-ui/check.stderr b/src/test/rustdoc-ui/check.stderr
index 27e5a736148e1..e6ba9df9b0555 100644
--- a/src/test/rustdoc-ui/check.stderr
+++ b/src/test/rustdoc-ui/check.stderr
@@ -21,6 +21,17 @@ warning: missing documentation for a function
 LL | pub fn foo() {}
    | ^^^^^^^^^^^^
 
+warning: no documentation found for this crate's top-level module
+   |
+note: the lint level is defined here
+  --> $DIR/check.rs:7:9
+   |
+LL | #![warn(rustdoc)]
+   |         ^^^^^^^
+   = note: `#[warn(rustdoc::missing_crate_level_docs)]` implied by `#[warn(rustdoc)]`
+   = help: The following guide may be of use:
+           https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html
+
 warning: missing code example in this documentation
   --> $DIR/check.rs:4:1
    |
@@ -37,7 +48,7 @@ note: the lint level is defined here
    |
 LL | #![warn(rustdoc)]
    |         ^^^^^^^
-   = note: `#[warn(missing_doc_code_examples)]` implied by `#[warn(rustdoc)]`
+   = note: `#[warn(rustdoc::missing_doc_code_examples)]` implied by `#[warn(rustdoc)]`
 
 warning: missing code example in this documentation
   --> $DIR/check.rs:9:1
@@ -45,5 +56,5 @@ warning: missing code example in this documentation
 LL | pub fn foo() {}
    | ^^^^^^^^^^^^^^^
 
-warning: 4 warnings emitted
+warning: 5 warnings emitted
 
diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs
index 54e7689f3163f..09da124b16206 100644
--- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs
+++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 /// [v2] //~ ERROR
 pub fn foo() {}
diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
index 9ec9dd4bc9ab7..67d9c3989f5ae 100644
--- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
+++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr
@@ -7,8 +7,8 @@ LL | /// [v2]
 note: the lint level is defined here
   --> $DIR/deny-intra-link-resolution-failure.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: aborting due to previous error
diff --git a/src/test/rustdoc-ui/doc-without-codeblock.rs b/src/test/rustdoc-ui/doc-without-codeblock.rs
index 5ad8e8a826f05..6812a454157b9 100644
--- a/src/test/rustdoc-ui/doc-without-codeblock.rs
+++ b/src/test/rustdoc-ui/doc-without-codeblock.rs
@@ -1,4 +1,4 @@
-#![deny(missing_doc_code_examples)] //~ ERROR missing code example in this documentation
+#![deny(rustdoc::missing_doc_code_examples)] //~ ERROR missing code example in this documentation
 
 /// Some docs.
 //~^ ERROR missing code example in this documentation
diff --git a/src/test/rustdoc-ui/doc-without-codeblock.stderr b/src/test/rustdoc-ui/doc-without-codeblock.stderr
index 3372304f44a3d..aac537e9783cd 100644
--- a/src/test/rustdoc-ui/doc-without-codeblock.stderr
+++ b/src/test/rustdoc-ui/doc-without-codeblock.stderr
@@ -1,7 +1,7 @@
 error: missing code example in this documentation
   --> $DIR/doc-without-codeblock.rs:1:1
    |
-LL | / #![deny(missing_doc_code_examples)]
+LL | / #![deny(rustdoc::missing_doc_code_examples)]
 LL | |
 LL | | /// Some docs.
 LL | |
@@ -13,8 +13,8 @@ LL | | }
 note: the lint level is defined here
   --> $DIR/doc-without-codeblock.rs:1:9
    |
-LL | #![deny(missing_doc_code_examples)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::missing_doc_code_examples)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: missing code example in this documentation
   --> $DIR/doc-without-codeblock.rs:7:1
diff --git a/src/test/rustdoc-ui/intra-doc/alias-ice.rs b/src/test/rustdoc-ui/intra-doc/alias-ice.rs
index c053e378e7147..51922caeb2543 100644
--- a/src/test/rustdoc-ui/intra-doc/alias-ice.rs
+++ b/src/test/rustdoc-ui/intra-doc/alias-ice.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 pub type TypeAlias = usize;
 
diff --git a/src/test/rustdoc-ui/intra-doc/alias-ice.stderr b/src/test/rustdoc-ui/intra-doc/alias-ice.stderr
index 3db5fad4cfbdc..5e7ffeeb8a219 100644
--- a/src/test/rustdoc-ui/intra-doc/alias-ice.stderr
+++ b/src/test/rustdoc-ui/intra-doc/alias-ice.stderr
@@ -7,8 +7,8 @@ LL | /// [broken cross-reference](TypeAlias::hoge)
 note: the lint level is defined here
   --> $DIR/alias-ice.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/rustdoc-ui/intra-doc/ambiguity.rs b/src/test/rustdoc-ui/intra-doc/ambiguity.rs
index f63435337cfbc..1f3dc722eff8d 100644
--- a/src/test/rustdoc-ui/intra-doc/ambiguity.rs
+++ b/src/test/rustdoc-ui/intra-doc/ambiguity.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 #![allow(non_camel_case_types)]
 #![allow(non_upper_case_globals)]
 
diff --git a/src/test/rustdoc-ui/intra-doc/ambiguity.stderr b/src/test/rustdoc-ui/intra-doc/ambiguity.stderr
index 7e967dc88bcdd..0f23b9b8adf67 100644
--- a/src/test/rustdoc-ui/intra-doc/ambiguity.stderr
+++ b/src/test/rustdoc-ui/intra-doc/ambiguity.stderr
@@ -7,8 +7,8 @@ LL | /// [true]
 note: the lint level is defined here
   --> $DIR/ambiguity.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: to link to the module, prefix with `mod@`
    |
 LL | /// [mod@true]
diff --git a/src/test/rustdoc-ui/intra-doc/anchors.rs b/src/test/rustdoc-ui/intra-doc/anchors.rs
index ccefd2e6fabb5..009b291be1f08 100644
--- a/src/test/rustdoc-ui/intra-doc/anchors.rs
+++ b/src/test/rustdoc-ui/intra-doc/anchors.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 // A few tests on anchors.
 
diff --git a/src/test/rustdoc-ui/intra-doc/anchors.stderr b/src/test/rustdoc-ui/intra-doc/anchors.stderr
index 5b272d960d50f..97b0cea0c1e4d 100644
--- a/src/test/rustdoc-ui/intra-doc/anchors.stderr
+++ b/src/test/rustdoc-ui/intra-doc/anchors.stderr
@@ -7,8 +7,8 @@ LL | /// Or maybe [Foo::f#hola].
 note: the lint level is defined here
   --> $DIR/anchors.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `hello#people#!` contains multiple anchors
   --> $DIR/anchors.rs:31:28
diff --git a/src/test/rustdoc-ui/intra-doc/broken-reexport.rs b/src/test/rustdoc-ui/intra-doc/broken-reexport.rs
index ef261359ebd9e..862faa50b4ddd 100644
--- a/src/test/rustdoc-ui/intra-doc/broken-reexport.rs
+++ b/src/test/rustdoc-ui/intra-doc/broken-reexport.rs
@@ -1,7 +1,7 @@
 // aux-build:intra-doc-broken.rs
 // check-pass
 
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 extern crate intra_doc_broken;
 
diff --git a/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.rs b/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.rs
index b9c8e033b1b21..596623190a33f 100644
--- a/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.rs
+++ b/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 //~^ NOTE lint level is defined
 pub enum S {}
 
diff --git a/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr b/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr
index 2f5f3daa29785..5d4d4a699e4f3 100644
--- a/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr
+++ b/src/test/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr
@@ -7,8 +7,8 @@ LL | /// Link to [struct@S]
 note: the lint level is defined here
   --> $DIR/disambiguator-mismatch.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this link resolved to an enum, which is not a struct
 
 error: incompatible link kind for `S`
diff --git a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr
index 1cd9231eded4d..c0241b98b78c1 100644
--- a/src/test/rustdoc-ui/intra-doc/double-anchor.stderr
+++ b/src/test/rustdoc-ui/intra-doc/double-anchor.stderr
@@ -4,7 +4,7 @@ warning: `with#anchor#error` contains multiple anchors
 LL | /// docs [label][with#anchor#error]
    |                  ^^^^^^^^^^^^^^^^^ contains invalid anchor
    |
-   = note: `#[warn(broken_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
 
 warning: 1 warning emitted
 
diff --git a/src/test/rustdoc-ui/intra-doc/errors.rs b/src/test/rustdoc-ui/intra-doc/errors.rs
index 81e42643ae8fc..b29f7c29b5d86 100644
--- a/src/test/rustdoc-ui/intra-doc/errors.rs
+++ b/src/test/rustdoc-ui/intra-doc/errors.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 //~^ NOTE lint level is defined
 
 // FIXME: this should say that it was skipped (maybe an allowed by default lint?)
diff --git a/src/test/rustdoc-ui/intra-doc/errors.stderr b/src/test/rustdoc-ui/intra-doc/errors.stderr
index 21c806108e3af..061151720578b 100644
--- a/src/test/rustdoc-ui/intra-doc/errors.stderr
+++ b/src/test/rustdoc-ui/intra-doc/errors.stderr
@@ -7,8 +7,8 @@ LL | /// [path::to::nonexistent::module]
 note: the lint level is defined here
   --> $DIR/errors.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unresolved link to `path::to::nonexistent::macro`
   --> $DIR/errors.rs:11:6
diff --git a/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs
index 0d1d5d1134b7b..3088bcd46531e 100644
--- a/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs
+++ b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.rs
@@ -1,3 +1,3 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 //! [static@u8::MIN]
 //~^ ERROR incompatible link kind
diff --git a/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr
index ed1c10f9e0cb8..d4dcc493c8b6a 100644
--- a/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr
+++ b/src/test/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr
@@ -7,8 +7,8 @@ LL | //! [static@u8::MIN]
 note: the lint level is defined here
   --> $DIR/incompatible-primitive-disambiguator.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this link resolved to an associated constant, which is not a static
 
 error: aborting due to previous error
diff --git a/src/test/rustdoc-ui/intra-doc/malformed-generics.rs b/src/test/rustdoc-ui/intra-doc/malformed-generics.rs
index 9c54092146fef..15e02925ed90d 100644
--- a/src/test/rustdoc-ui/intra-doc/malformed-generics.rs
+++ b/src/test/rustdoc-ui/intra-doc/malformed-generics.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 //! [Vec<] //~ ERROR
 //! [Vec<Box<T] //~ ERROR
diff --git a/src/test/rustdoc-ui/intra-doc/malformed-generics.stderr b/src/test/rustdoc-ui/intra-doc/malformed-generics.stderr
index 2e1b22807bc39..5bc0f84e24d13 100644
--- a/src/test/rustdoc-ui/intra-doc/malformed-generics.stderr
+++ b/src/test/rustdoc-ui/intra-doc/malformed-generics.stderr
@@ -7,8 +7,8 @@ LL | //! [Vec<]
 note: the lint level is defined here
   --> $DIR/malformed-generics.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unresolved link to `Vec<Box<T`
   --> $DIR/malformed-generics.rs:4:6
diff --git a/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs b/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs
index 6785c4c43f541..75159979e8890 100644
--- a/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs
+++ b/src/test/rustdoc-ui/intra-doc/non-path-primitives.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 #![feature(intra_doc_pointers)]
 // These are links that could reasonably expected to work, but don't.
 
diff --git a/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr b/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr
index 174758504ae22..610c830560527 100644
--- a/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr
+++ b/src/test/rustdoc-ui/intra-doc/non-path-primitives.stderr
@@ -7,8 +7,8 @@ LL | //! [[T]::rotate_left]
 note: the lint level is defined here
   --> $DIR/non-path-primitives.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: unresolved link to `Z`
diff --git a/src/test/rustdoc-ui/intra-doc/prim-conflict.rs b/src/test/rustdoc-ui/intra-doc/prim-conflict.rs
index 85738ceae8e61..2c1a8b5357aa1 100644
--- a/src/test/rustdoc-ui/intra-doc/prim-conflict.rs
+++ b/src/test/rustdoc-ui/intra-doc/prim-conflict.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 //~^ NOTE lint level is defined
 
 /// [char]
diff --git a/src/test/rustdoc-ui/intra-doc/prim-conflict.stderr b/src/test/rustdoc-ui/intra-doc/prim-conflict.stderr
index 01275f8d9afb0..e4bd9fd4b8f1a 100644
--- a/src/test/rustdoc-ui/intra-doc/prim-conflict.stderr
+++ b/src/test/rustdoc-ui/intra-doc/prim-conflict.stderr
@@ -7,8 +7,8 @@ LL | /// [char]
 note: the lint level is defined here
   --> $DIR/prim-conflict.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: to link to the module, prefix with `mod@`
    |
 LL | /// [mod@char]
diff --git a/src/test/rustdoc-ui/intra-doc/private.private.stderr b/src/test/rustdoc-ui/intra-doc/private.private.stderr
index 94a833fcc1a15..cae5b1f20e6c3 100644
--- a/src/test/rustdoc-ui/intra-doc/private.private.stderr
+++ b/src/test/rustdoc-ui/intra-doc/private.private.stderr
@@ -4,7 +4,7 @@ warning: public documentation for `DocMe` links to private item `DontDocMe`
 LL | /// docs [DontDocMe] [DontDocMe::f]
    |           ^^^^^^^^^ this item is private
    |
-   = note: `#[warn(private_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
    = note: this link resolves only because you passed `--document-private-items`, but will break without
 
 warning: public documentation for `DocMe` links to private item `DontDocMe::f`
diff --git a/src/test/rustdoc-ui/intra-doc/private.public.stderr b/src/test/rustdoc-ui/intra-doc/private.public.stderr
index 21a60638d5efc..05b202e37fbcb 100644
--- a/src/test/rustdoc-ui/intra-doc/private.public.stderr
+++ b/src/test/rustdoc-ui/intra-doc/private.public.stderr
@@ -4,7 +4,7 @@ warning: public documentation for `DocMe` links to private item `DontDocMe`
 LL | /// docs [DontDocMe] [DontDocMe::f]
    |           ^^^^^^^^^ this item is private
    |
-   = note: `#[warn(private_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
    = note: this link will resolve properly if you pass `--document-private-items`
 
 warning: public documentation for `DocMe` links to private item `DontDocMe::f`
diff --git a/src/test/rustdoc-ui/intra-doc/span-ice-55723.rs b/src/test/rustdoc-ui/intra-doc/span-ice-55723.rs
index 7764a6df6ee77..041ec29325923 100644
--- a/src/test/rustdoc-ui/intra-doc/span-ice-55723.rs
+++ b/src/test/rustdoc-ui/intra-doc/span-ice-55723.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 // An error in calculating spans while reporting intra-doc link resolution errors caused rustdoc to
 // attempt to slice in the middle of a multibyte character. See
diff --git a/src/test/rustdoc-ui/intra-doc/span-ice-55723.stderr b/src/test/rustdoc-ui/intra-doc/span-ice-55723.stderr
index 10ca14e850fd7..bf4ab9fdd18b1 100644
--- a/src/test/rustdoc-ui/intra-doc/span-ice-55723.stderr
+++ b/src/test/rustdoc-ui/intra-doc/span-ice-55723.stderr
@@ -7,8 +7,8 @@ LL | /// (arr[i])
 note: the lint level is defined here
   --> $DIR/span-ice-55723.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: aborting due to previous error
diff --git a/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs
index 186503cf69d3f..9565830930f41 100644
--- a/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs
+++ b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.rs
@@ -1,5 +1,5 @@
 // compile-flags: --extern zip=whatever.rlib
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 /// See [zip] crate.
 //~^ ERROR unresolved
 pub struct ArrayZip;
diff --git a/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr
index b3b57fd131838..5c0df1d1b9e4f 100644
--- a/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr
+++ b/src/test/rustdoc-ui/intra-doc/unused-extern-crate.stderr
@@ -7,8 +7,8 @@ LL | /// See [zip] crate.
 note: the lint level is defined here
   --> $DIR/unused-extern-crate.rs:2:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: aborting due to previous error
diff --git a/src/test/rustdoc-ui/intra-doc/warning-crlf.stderr b/src/test/rustdoc-ui/intra-doc/warning-crlf.stderr
index 01e4282003766..d46df92649d17 100644
--- a/src/test/rustdoc-ui/intra-doc/warning-crlf.stderr
+++ b/src/test/rustdoc-ui/intra-doc/warning-crlf.stderr
@@ -4,7 +4,7 @@ warning: unresolved link to `error`
 LL | /// [error]
    |      ^^^^^ no item named `error` in scope
    |
-   = note: `#[warn(broken_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 warning: unresolved link to `error1`
diff --git a/src/test/rustdoc-ui/intra-doc/warning.stderr b/src/test/rustdoc-ui/intra-doc/warning.stderr
index 430d18165a005..135c432e05342 100644
--- a/src/test/rustdoc-ui/intra-doc/warning.stderr
+++ b/src/test/rustdoc-ui/intra-doc/warning.stderr
@@ -4,7 +4,7 @@ warning: unresolved link to `Foo::baz`
 LL |        //! Test with [Foo::baz], [Bar::foo], ...
    |                       ^^^^^^^^ the struct `Foo` has no field or associated item named `baz`
    |
-   = note: `#[warn(broken_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
 
 warning: unresolved link to `Bar::foo`
   --> $DIR/warning.rs:3:35
diff --git a/src/test/rustdoc-ui/invalid-html-tags.rs b/src/test/rustdoc-ui/invalid-html-tags.rs
index 9c2fc4beb5eb1..cec44b6d2ca62 100644
--- a/src/test/rustdoc-ui/invalid-html-tags.rs
+++ b/src/test/rustdoc-ui/invalid-html-tags.rs
@@ -1,4 +1,4 @@
-#![deny(invalid_html_tags)]
+#![deny(rustdoc::invalid_html_tags)]
 
 //! <p>💩<p>
 //~^ ERROR unclosed HTML tag `p`
diff --git a/src/test/rustdoc-ui/invalid-html-tags.stderr b/src/test/rustdoc-ui/invalid-html-tags.stderr
index aa9ace006bd1a..335e100c89d89 100644
--- a/src/test/rustdoc-ui/invalid-html-tags.stderr
+++ b/src/test/rustdoc-ui/invalid-html-tags.stderr
@@ -7,8 +7,8 @@ LL | //! <p>💩<p>
 note: the lint level is defined here
   --> $DIR/invalid-html-tags.rs:1:9
    |
-LL | #![deny(invalid_html_tags)]
-   |         ^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::invalid_html_tags)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: unclosed HTML tag `p`
   --> $DIR/invalid-html-tags.rs:3:9
diff --git a/src/test/rustdoc-ui/issue-58473-2.rs b/src/test/rustdoc-ui/issue-58473-2.rs
index e5f3b4daf5729..000b6a329c1a7 100644
--- a/src/test/rustdoc-ui/issue-58473-2.rs
+++ b/src/test/rustdoc-ui/issue-58473-2.rs
@@ -1,6 +1,6 @@
 // check-pass
 
-#![deny(private_doc_tests)]
+#![deny(rustdoc::private_doc_tests)]
 
 mod foo {
     /**
diff --git a/src/test/rustdoc-ui/issue-74134.private.stderr b/src/test/rustdoc-ui/issue-74134.private.stderr
index b802d7e12523a..457987e207496 100644
--- a/src/test/rustdoc-ui/issue-74134.private.stderr
+++ b/src/test/rustdoc-ui/issue-74134.private.stderr
@@ -4,7 +4,7 @@ warning: public documentation for `public_item` links to private item `PrivateTy
 LL |     /// [`PrivateType`]
    |          ^^^^^^^^^^^^^ this item is private
    |
-   = note: `#[warn(private_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
    = note: this link resolves only because you passed `--document-private-items`, but will break without
 
 warning: 1 warning emitted
diff --git a/src/test/rustdoc-ui/issue-74134.public.stderr b/src/test/rustdoc-ui/issue-74134.public.stderr
index 40aa2ece1a373..07aebc3541fe3 100644
--- a/src/test/rustdoc-ui/issue-74134.public.stderr
+++ b/src/test/rustdoc-ui/issue-74134.public.stderr
@@ -4,7 +4,7 @@ warning: public documentation for `public_item` links to private item `PrivateTy
 LL |     /// [`PrivateType`]
    |          ^^^^^^^^^^^^^ this item is private
    |
-   = note: `#[warn(private_intra_doc_links)]` on by default
+   = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
    = note: this link will resolve properly if you pass `--document-private-items`
 
 warning: 1 warning emitted
diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr
index 0c111a33b6564..6f8a20f7d53f0 100644
--- a/src/test/rustdoc-ui/lint-group.stderr
+++ b/src/test/rustdoc-ui/lint-group.stderr
@@ -9,7 +9,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rustdoc)]
    |         ^^^^^^^
-   = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]`
+   = note: `#[deny(rustdoc::missing_doc_code_examples)]` implied by `#[deny(rustdoc)]`
 
 error: documentation test in private item
   --> $DIR/lint-group.rs:19:1
@@ -26,7 +26,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rustdoc)]
    |         ^^^^^^^
-   = note: `#[deny(private_doc_tests)]` implied by `#[deny(rustdoc)]`
+   = note: `#[deny(rustdoc::private_doc_tests)]` implied by `#[deny(rustdoc)]`
 
 error: missing code example in this documentation
   --> $DIR/lint-group.rs:26:1
@@ -45,7 +45,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rustdoc)]
    |         ^^^^^^^
-   = note: `#[deny(broken_intra_doc_links)]` implied by `#[deny(rustdoc)]`
+   = note: `#[deny(rustdoc::broken_intra_doc_links)]` implied by `#[deny(rustdoc)]`
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: unclosed HTML tag `unknown`
@@ -59,7 +59,7 @@ note: the lint level is defined here
    |
 LL | #![deny(rustdoc)]
    |         ^^^^^^^
-   = note: `#[deny(invalid_html_tags)]` implied by `#[deny(rustdoc)]`
+   = note: `#[deny(rustdoc::invalid_html_tags)]` implied by `#[deny(rustdoc)]`
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs
index ebe7a242211bf..8d727b0d0b550 100644
--- a/src/test/rustdoc-ui/lint-missing-doc-code-example.rs
+++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.rs
@@ -1,5 +1,5 @@
 #![deny(missing_docs)]
-#![deny(missing_doc_code_examples)]
+#![deny(rustdoc::missing_doc_code_examples)]
 
 //! crate level doc
 //! ```
@@ -19,7 +19,7 @@ fn test() {
 mod module1 { //~ ERROR
 }
 
-#[allow(missing_doc_code_examples)]
+#[allow(rustdoc::missing_doc_code_examples)]
 /// doc
 mod module2 {
 
diff --git a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr
index e02ed4a056c12..370c577f85d8f 100644
--- a/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr
+++ b/src/test/rustdoc-ui/lint-missing-doc-code-example.stderr
@@ -8,8 +8,8 @@ LL | | }
 note: the lint level is defined here
   --> $DIR/lint-missing-doc-code-example.rs:2:9
    |
-LL | #![deny(missing_doc_code_examples)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::missing_doc_code_examples)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: missing code example in this documentation
   --> $DIR/lint-missing-doc-code-example.rs:37:3
diff --git a/src/test/rustdoc-ui/no-crate-level-doc-lint.rs b/src/test/rustdoc-ui/no-crate-level-doc-lint.rs
index 152a7cd88bcb1..3939ec6827ade 100644
--- a/src/test/rustdoc-ui/no-crate-level-doc-lint.rs
+++ b/src/test/rustdoc-ui/no-crate-level-doc-lint.rs
@@ -1,3 +1,5 @@
-#![deny(missing_crate_level_docs)]
+// error-pattern: no documentation found
+#![deny(rustdoc::missing_crate_level_docs)]
+//^~ NOTE defined here
 
 pub fn foo() {}
diff --git a/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr b/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr
index 6e7e2fb3eb73f..55ead1a55cfcd 100644
--- a/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr
+++ b/src/test/rustdoc-ui/no-crate-level-doc-lint.stderr
@@ -1,10 +1,10 @@
 error: no documentation found for this crate's top-level module
    |
 note: the lint level is defined here
-  --> $DIR/no-crate-level-doc-lint.rs:1:9
+  --> $DIR/no-crate-level-doc-lint.rs:2:9
    |
-LL | #![deny(missing_crate_level_docs)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::missing_crate_level_docs)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: The following guide may be of use:
            https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html
 
diff --git a/src/test/rustdoc-ui/private-doc-test.rs b/src/test/rustdoc-ui/private-doc-test.rs
index 379fa45f9fa3e..a1f9f8dca083e 100644
--- a/src/test/rustdoc-ui/private-doc-test.rs
+++ b/src/test/rustdoc-ui/private-doc-test.rs
@@ -1,6 +1,6 @@
 // check-pass
 
-#![deny(private_doc_tests)]
+#![deny(rustdoc::private_doc_tests)]
 
 mod foo {
     /// private doc test
diff --git a/src/test/rustdoc-ui/private-item-doc-test.rs b/src/test/rustdoc-ui/private-item-doc-test.rs
index 2f1bddc7c75cc..1a3d6cc636d31 100644
--- a/src/test/rustdoc-ui/private-item-doc-test.rs
+++ b/src/test/rustdoc-ui/private-item-doc-test.rs
@@ -1,4 +1,4 @@
-#![deny(private_doc_tests)]
+#![deny(rustdoc::private_doc_tests)]
 
 mod foo {
     /// private doc test
diff --git a/src/test/rustdoc-ui/private-item-doc-test.stderr b/src/test/rustdoc-ui/private-item-doc-test.stderr
index 70b6638b23711..5df6132987c9f 100644
--- a/src/test/rustdoc-ui/private-item-doc-test.stderr
+++ b/src/test/rustdoc-ui/private-item-doc-test.stderr
@@ -11,8 +11,8 @@ LL | |     /// ```
 note: the lint level is defined here
   --> $DIR/private-item-doc-test.rs:1:9
    |
-LL | #![deny(private_doc_tests)]
-   |         ^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::private_doc_tests)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/rustdoc-ui/pub-export-lint.rs b/src/test/rustdoc-ui/pub-export-lint.rs
index 3fd3f77400978..f2e66b77bf3ea 100644
--- a/src/test/rustdoc-ui/pub-export-lint.rs
+++ b/src/test/rustdoc-ui/pub-export-lint.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 /// [aloha]
 //~^ ERROR unresolved link to `aloha`
diff --git a/src/test/rustdoc-ui/pub-export-lint.stderr b/src/test/rustdoc-ui/pub-export-lint.stderr
index c345def794c08..c6be9c6a9f504 100644
--- a/src/test/rustdoc-ui/pub-export-lint.stderr
+++ b/src/test/rustdoc-ui/pub-export-lint.stderr
@@ -7,8 +7,8 @@ LL | /// [aloha]
 note: the lint level is defined here
   --> $DIR/pub-export-lint.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: aborting due to previous error
diff --git a/src/test/rustdoc-ui/reference-link-reports-error-once.rs b/src/test/rustdoc-ui/reference-link-reports-error-once.rs
index 7957ee373c49e..71bd2c522ff86 100644
--- a/src/test/rustdoc-ui/reference-link-reports-error-once.rs
+++ b/src/test/rustdoc-ui/reference-link-reports-error-once.rs
@@ -1,4 +1,4 @@
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 /// Links to [a] [link][a]
 /// And also a [third link][a]
diff --git a/src/test/rustdoc-ui/reference-link-reports-error-once.stderr b/src/test/rustdoc-ui/reference-link-reports-error-once.stderr
index 218eb334a6fc3..b46a51e93fb8b 100644
--- a/src/test/rustdoc-ui/reference-link-reports-error-once.stderr
+++ b/src/test/rustdoc-ui/reference-link-reports-error-once.stderr
@@ -7,8 +7,8 @@ LL | /// [a]: ref
 note: the lint level is defined here
   --> $DIR/reference-link-reports-error-once.rs:1:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
 
 error: unresolved link to `ref2`
diff --git a/src/test/rustdoc-ui/reference-links.rs b/src/test/rustdoc-ui/reference-links.rs
index 6e00b9f0fa1a9..e81e034465d7f 100644
--- a/src/test/rustdoc-ui/reference-links.rs
+++ b/src/test/rustdoc-ui/reference-links.rs
@@ -1,5 +1,5 @@
 // Test that errors point to the reference, not to the title text.
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 //! Links to [a] [link][a]
 //!
 //! [a]: std::process::Comman
diff --git a/src/test/rustdoc-ui/reference-links.stderr b/src/test/rustdoc-ui/reference-links.stderr
index 3df89df21b4c6..c98a2fd7ce690 100644
--- a/src/test/rustdoc-ui/reference-links.stderr
+++ b/src/test/rustdoc-ui/reference-links.stderr
@@ -7,8 +7,8 @@ LL | //! [a]: std::process::Comman
 note: the lint level is defined here
   --> $DIR/reference-links.rs:2:9
    |
-LL | #![deny(broken_intra_doc_links)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/rustdoc-ui/renamed-lint-still-applies.rs b/src/test/rustdoc-ui/renamed-lint-still-applies.rs
new file mode 100644
index 0000000000000..6d4bad16aadc2
--- /dev/null
+++ b/src/test/rustdoc-ui/renamed-lint-still-applies.rs
@@ -0,0 +1,5 @@
+// compile-args: --crate-type lib
+#![deny(broken_intra_doc_links)]
+//~^ WARNING renamed
+//! [x]
+//~^ ERROR unresolved link
diff --git a/src/test/rustdoc-ui/renamed-lint-still-applies.stderr b/src/test/rustdoc-ui/renamed-lint-still-applies.stderr
new file mode 100644
index 0000000000000..e14cbfa1726c3
--- /dev/null
+++ b/src/test/rustdoc-ui/renamed-lint-still-applies.stderr
@@ -0,0 +1,23 @@
+warning: lint `broken_intra_doc_links` has been renamed to `rustdoc::broken_intra_doc_links`
+  --> $DIR/renamed-lint-still-applies.rs:2:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::broken_intra_doc_links`
+   |
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
+
+error: unresolved link to `x`
+  --> $DIR/renamed-lint-still-applies.rs:4:6
+   |
+LL | //! [x]
+   |      ^ no item named `x` in scope
+   |
+note: the lint level is defined here
+  --> $DIR/renamed-lint-still-applies.rs:2:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.rs b/src/test/rustdoc-ui/unknown-renamed-lints.rs
index 7faa82ea429c2..d2c78bc477410 100644
--- a/src/test/rustdoc-ui/unknown-renamed-lints.rs
+++ b/src/test/rustdoc-ui/unknown-renamed-lints.rs
@@ -4,5 +4,14 @@
 //~^ NOTE lint level is defined
 #![deny(x)]
 //~^ ERROR unknown lint
+#![deny(rustdoc::x)]
+//~^ ERROR unknown lint: `rustdoc::x`
 #![deny(intra_doc_link_resolution_failure)]
-//~^ ERROR lint `intra_doc_link_resolution_failure` has been renamed
+//~^ ERROR renamed to `rustdoc::broken_intra_doc_links`
+
+#![deny(non_autolinks)]
+//~^ ERROR renamed to `rustdoc::non_autolinks`
+
+// Explicitly don't try to handle this case, it was never valid
+#![deny(rustdoc::intra_doc_link_resolution_failure)]
+//~^ ERROR unknown lint
diff --git a/src/test/rustdoc-ui/unknown-renamed-lints.stderr b/src/test/rustdoc-ui/unknown-renamed-lints.stderr
index f0917f194bb08..0f31673fb47f2 100644
--- a/src/test/rustdoc-ui/unknown-renamed-lints.stderr
+++ b/src/test/rustdoc-ui/unknown-renamed-lints.stderr
@@ -10,11 +10,17 @@ note: the lint level is defined here
 LL | #![deny(unknown_lints)]
    |         ^^^^^^^^^^^^^
 
-error: lint `intra_doc_link_resolution_failure` has been renamed to `broken_intra_doc_links`
+error: unknown lint: `rustdoc::x`
   --> $DIR/unknown-renamed-lints.rs:7:9
    |
+LL | #![deny(rustdoc::x)]
+   |         ^^^^^^^^^^
+
+error: lint `intra_doc_link_resolution_failure` has been renamed to `rustdoc::broken_intra_doc_links`
+  --> $DIR/unknown-renamed-lints.rs:9:9
+   |
 LL | #![deny(intra_doc_link_resolution_failure)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `broken_intra_doc_links`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rustdoc::broken_intra_doc_links`
    |
 note: the lint level is defined here
   --> $DIR/unknown-renamed-lints.rs:3:9
@@ -22,7 +28,19 @@ note: the lint level is defined here
 LL | #![deny(renamed_and_removed_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: lint `non_autolinks` has been renamed to `rustdoc::non_autolinks`
+  --> $DIR/unknown-renamed-lints.rs:12:9
+   |
+LL | #![deny(non_autolinks)]
+   |         ^^^^^^^^^^^^^ help: use the new name: `rustdoc::non_autolinks`
+
+error: unknown lint: `rustdoc::intra_doc_link_resolution_failure`
+  --> $DIR/unknown-renamed-lints.rs:16:9
+   |
+LL | #![deny(rustdoc::intra_doc_link_resolution_failure)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 error: Compilation failed, aborting rustdoc
 
-error: aborting due to 3 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/src/test/rustdoc-ui/url-improvements.rs b/src/test/rustdoc-ui/url-improvements.rs
index 8531583d38a65..d0b43de2f0e0e 100644
--- a/src/test/rustdoc-ui/url-improvements.rs
+++ b/src/test/rustdoc-ui/url-improvements.rs
@@ -1,4 +1,4 @@
-#![deny(non_autolinks)]
+#![deny(rustdoc::non_autolinks)]
 
 /// [http://aa.com](http://aa.com)
 //~^ ERROR unneeded long form for URL
@@ -59,7 +59,7 @@ pub fn c() {}
 /// [should_not.lint](should_not.lint)
 pub fn everything_is_fine_here() {}
 
-#[allow(non_autolinks)]
+#[allow(rustdoc::non_autolinks)]
 pub mod foo {
     /// https://somewhere.com/a?hello=12&bye=11#xyz
     pub fn bar() {}
diff --git a/src/test/rustdoc-ui/url-improvements.stderr b/src/test/rustdoc-ui/url-improvements.stderr
index 70ad4b06a515d..f377973656a83 100644
--- a/src/test/rustdoc-ui/url-improvements.stderr
+++ b/src/test/rustdoc-ui/url-improvements.stderr
@@ -7,8 +7,8 @@ LL | /// [http://aa.com](http://aa.com)
 note: the lint level is defined here
   --> $DIR/url-improvements.rs:1:9
    |
-LL | #![deny(non_autolinks)]
-   |         ^^^^^^^^^^^^^
+LL | #![deny(rustdoc::non_autolinks)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: unneeded long form for URL
   --> $DIR/url-improvements.rs:5:5
diff --git a/src/test/rustdoc/intra-doc/non-path-primitives.rs b/src/test/rustdoc/intra-doc/non-path-primitives.rs
index 48c667ef2d18e..ffa02b0c635b3 100644
--- a/src/test/rustdoc/intra-doc/non-path-primitives.rs
+++ b/src/test/rustdoc/intra-doc/non-path-primitives.rs
@@ -1,7 +1,7 @@
 // ignore-tidy-linelength
 #![crate_name = "foo"]
 #![feature(intra_doc_pointers)]
-#![deny(broken_intra_doc_links)]
+#![deny(rustdoc::broken_intra_doc_links)]
 
 // @has foo/index.html '//a[@href="https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.rotate_left"]' 'slice::rotate_left'
 //! [slice::rotate_left]
diff --git a/src/test/rustdoc/redirect-map-empty.rs b/src/test/rustdoc/redirect-map-empty.rs
new file mode 100644
index 0000000000000..e9d021e0fa862
--- /dev/null
+++ b/src/test/rustdoc/redirect-map-empty.rs
@@ -0,0 +1,6 @@
+// compile-flags: -Z unstable-options --generate-redirect-map
+
+#![crate_name = "foo"]
+
+// @!has foo/redirect-map.json
+pub struct Foo;
diff --git a/src/test/rustdoc/redirect-map.rs b/src/test/rustdoc/redirect-map.rs
new file mode 100644
index 0000000000000..b7f16b64e3850
--- /dev/null
+++ b/src/test/rustdoc/redirect-map.rs
@@ -0,0 +1,23 @@
+// compile-flags: -Z unstable-options --generate-redirect-map
+
+#![crate_name = "foo"]
+
+// @!has foo/private/struct.Quz.html
+// @!has foo/hidden/struct.Bar.html
+// @has foo/redirect-map.json
+pub use private::Quz;
+pub use hidden::Bar;
+
+mod private {
+    pub struct Quz;
+}
+
+#[doc(hidden)]
+pub mod hidden {
+    pub struct Bar;
+}
+
+#[macro_export]
+macro_rules! foo {
+  () => {}
+}
diff --git a/src/test/ui/lint/rustdoc-renamed.rs b/src/test/ui/lint/rustdoc-renamed.rs
new file mode 100644
index 0000000000000..71e88bd7f54a5
--- /dev/null
+++ b/src/test/ui/lint/rustdoc-renamed.rs
@@ -0,0 +1,14 @@
+#![crate_type = "lib"]
+
+#![deny(unknown_lints)]
+#![deny(renamed_and_removed_lints)]
+//~^ NOTE lint level is defined
+
+// both allowed, since the compiler doesn't yet know what rustdoc lints are valid
+#![deny(rustdoc::x)]
+#![deny(rustdoc::intra_doc_link_resolution_failure)]
+
+#![deny(intra_doc_link_resolution_failure)]
+//~^ ERROR removed: use `rustdoc::broken_intra_doc_links`
+#![deny(non_autolinks)]
+//~^ ERROR removed: use `rustdoc::non_autolinks`
diff --git a/src/test/ui/lint/rustdoc-renamed.stderr b/src/test/ui/lint/rustdoc-renamed.stderr
new file mode 100644
index 0000000000000..a7fe3e29d5be0
--- /dev/null
+++ b/src/test/ui/lint/rustdoc-renamed.stderr
@@ -0,0 +1,20 @@
+error: lint `intra_doc_link_resolution_failure` has been removed: use `rustdoc::broken_intra_doc_links` instead
+  --> $DIR/rustdoc-renamed.rs:11:9
+   |
+LL | #![deny(intra_doc_link_resolution_failure)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/rustdoc-renamed.rs:4:9
+   |
+LL | #![deny(renamed_and_removed_lints)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: lint `non_autolinks` has been removed: use `rustdoc::non_autolinks` instead
+  --> $DIR/rustdoc-renamed.rs:13:9
+   |
+LL | #![deny(non_autolinks)]
+   |         ^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+