diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 53ece08ac3d92..cbb4458d1d49a 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3464,7 +3464,8 @@ declare_lint! {
     /// out an update in your own time.
     pub LONG_RUNNING_CONST_EVAL,
     Deny,
-    "detects long const eval operations"
+    "detects long const eval operations",
+    report_in_external_macro
 }
 
 declare_lint! {
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index b2bc33c7e0de0..730e4c8d30db3 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -69,8 +69,6 @@ where
         make_query: fn(Qcx, K) -> QueryStackFrame<D>,
         jobs: &mut QueryMap<D>,
     ) -> Option<()> {
-        let mut active = Vec::new();
-
         #[cfg(parallel_compiler)]
         {
             // We use try_lock_shards here since we are called from the
@@ -79,7 +77,8 @@ where
             for shard in shards.iter() {
                 for (k, v) in shard.iter() {
                     if let QueryResult::Started(ref job) = *v {
-                        active.push((*k, job.clone()));
+                        let query = make_query(qcx, *k);
+                        jobs.insert(job.id, QueryJobInfo { query, job: job.clone() });
                     }
                 }
             }
@@ -92,18 +91,12 @@ where
             // really hurt much.)
             for (k, v) in self.active.try_lock()?.iter() {
                 if let QueryResult::Started(ref job) = *v {
-                    active.push((*k, job.clone()));
+                    let query = make_query(qcx, *k);
+                    jobs.insert(job.id, QueryJobInfo { query, job: job.clone() });
                 }
             }
         }
 
-        // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call
-        // queries leading to a deadlock.
-        for (key, job) in active {
-            let query = make_query(qcx, key);
-            jobs.insert(job.id, QueryJobInfo { query, job });
-        }
-
         Some(())
     }
 }
diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index eade9e0455912..5ab1874e9ed46 100755
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -110,6 +110,9 @@
 
 * `@has-dir PATH` checks for the existence of the given directory.
 
+* `@files FOLDER_PATH [ENTRIES]`, checks that `FOLDER_PATH` contains exactly
+  `[ENTRIES]`.
+
 All conditions can be negated with `!`. `@!has foo/type.NoSuch.html`
 checks if the given file does not exist, for example.
 
@@ -321,12 +324,15 @@ def resolve_path(self, path):
         else:
             return self.last_path
 
+    def get_absolute_path(self, path):
+        return os.path.join(self.root, path)
+
     def get_file(self, path):
         path = self.resolve_path(path)
         if path in self.files:
             return self.files[path]
 
-        abspath = os.path.join(self.root, path)
+        abspath = self.get_absolute_path(path)
         if not(os.path.exists(abspath) and os.path.isfile(abspath)):
             raise FailedCheck('File does not exist {!r}'.format(path))
 
@@ -340,7 +346,7 @@ def get_tree(self, path):
         if path in self.trees:
             return self.trees[path]
 
-        abspath = os.path.join(self.root, path)
+        abspath = self.get_absolute_path(path)
         if not(os.path.exists(abspath) and os.path.isfile(abspath)):
             raise FailedCheck('File does not exist {!r}'.format(path))
 
@@ -356,7 +362,7 @@ def get_tree(self, path):
 
     def get_dir(self, path):
         path = self.resolve_path(path)
-        abspath = os.path.join(self.root, path)
+        abspath = self.get_absolute_path(path)
         if not(os.path.exists(abspath) and os.path.isdir(abspath)):
             raise FailedCheck('Directory does not exist {!r}'.format(path))
 
@@ -538,6 +544,41 @@ def get_nb_matching_elements(cache, c, regexp, stop_at_first):
         return check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first)
 
 
+def check_files_in_folder(c, cache, folder, files):
+    files = files.strip()
+    if not files.startswith('[') or not files.endswith(']'):
+        raise InvalidCheck("Expected list as second argument of @{} (ie '[]')".format(c.cmd))
+
+    folder = cache.get_absolute_path(folder)
+
+    # First we create a set of files to check if there are duplicates.
+    files = shlex.split(files[1:-1].replace(",", ""))
+    files_set = set()
+    for file in files:
+        if file in files_set:
+            raise InvalidCheck("Duplicated file `{}` in @{}".format(file, c.cmd))
+        files_set.add(file)
+    folder_set = set([f for f in os.listdir(folder) if f != "." and f != ".."])
+
+    # Then we remove entries from both sets (we clone `folder_set` so we can iterate it while
+    # removing its elements).
+    for entry in set(folder_set):
+        if entry in files_set:
+            files_set.remove(entry)
+            folder_set.remove(entry)
+
+    error = 0
+    if len(files_set) != 0:
+        print_err(c.lineno, c.context, "Entries not found in folder `{}`: `{}`".format(
+            folder, files_set))
+        error += 1
+    if len(folder_set) != 0:
+        print_err(c.lineno, c.context, "Extra entries in folder `{}`: `{}`".format(
+            folder, folder_set))
+        error += 1
+    return error == 0
+
+
 ERR_COUNT = 0
 
 
@@ -566,6 +607,13 @@ def check_command(c, cache):
             else:
                 raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
 
+        elif c.cmd == 'files': # check files in given folder
+            if len(c.args) != 2: # @files <folder path> <file list>
+                raise InvalidCheck("Invalid number of @{} arguments".format(c.cmd))
+            elif c.negated:
+                raise InvalidCheck("@{} doesn't support negative check".format(c.cmd))
+            ret = check_files_in_folder(c, cache, c.args[0], c.args[1])
+
         elif c.cmd == 'count':  # count test
             if len(c.args) == 3:  # @count <path> <pat> <count> = count test
                 expected = int(c.args[2])
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 294de12cea8e2..f375f0efbd17b 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -21,6 +21,7 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::symbol::{kw, sym, Symbol};
 use std::fmt::Write as _;
 use std::mem;
+use std::sync::LazyLock as Lazy;
 use thin_vec::{thin_vec, ThinVec};
 
 #[cfg(test)]
@@ -582,6 +583,8 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool {
 ///
 /// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable.
 pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL");
+pub(crate) static DOC_CHANNEL: Lazy<&'static str> =
+    Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit("/").filter(|c| !c.is_empty()).next().unwrap());
 
 /// Render a sequence of macro arms in a format suitable for displaying to the user
 /// as part of an item declaration.
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 6ab849c92a076..8c5871d912647 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -55,6 +55,7 @@ struct PageLayout<'a> {
     sidebar: String,
     content: String,
     krate_with_trailing_slash: String,
+    rust_channel: &'static str,
     pub(crate) rustdoc_version: &'a str,
 }
 
@@ -82,6 +83,7 @@ pub(crate) fn render<T: Print, S: Print>(
         sidebar,
         content,
         krate_with_trailing_slash,
+        rust_channel: *crate::clean::utils::DOC_CHANNEL,
         rustdoc_version,
     }
     .render()
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index f5296abaee661..254b0d8bf5a43 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1050,9 +1050,10 @@ function preLoadCss(cssUrl) {
 
     function buildHelpMenu() {
         const book_info = document.createElement("span");
+        const channel = getVar("channel");
         book_info.className = "top";
-        book_info.innerHTML = "You can find more information in \
-            <a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>.";
+        book_info.innerHTML = `You can find more information in \
+<a href="https://doc.rust-lang.org/${channel}/rustdoc/">the rustdoc book</a>.`;
 
         const shortcuts = [
             ["?", "Show this help dialog"],
@@ -1072,6 +1073,9 @@ function preLoadCss(cssUrl) {
         div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
 
         const infos = [
+            `For a full list of all search features, take a look <a \
+href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
+#the-search-interface">here</a>.`,
             "Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
              restrict the search to a given item kind.",
             "Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html
index 9133f899af60d..759c4fd601226 100644
--- a/src/librustdoc/html/templates/page.html
+++ b/src/librustdoc/html/templates/page.html
@@ -31,6 +31,7 @@
          data-themes="{{themes|join(",") }}" {#+ #}
          data-resource-suffix="{{page.resource_suffix}}" {#+ #}
          data-rustdoc-version="{{rustdoc_version}}" {#+ #}
+         data-channel="{{rust_channel}}" {#+ #}
          data-search-js="{{files.search_js}}" {#+ #}
          data-settings-js="{{files.settings_js}}" {#+ #}
          data-settings-css="{{files.settings_css}}" {#+ #}
diff --git a/tests/rustdoc/files-creation-hidden.rs b/tests/rustdoc/files-creation-hidden.rs
index bcabbfc91e869..498d9cdaef1f0 100644
--- a/tests/rustdoc/files-creation-hidden.rs
+++ b/tests/rustdoc/files-creation-hidden.rs
@@ -1,5 +1,6 @@
 #![crate_name="foo"]
 
+// @files foo '["index.html", "all.html", "sidebar-items.js"]'
 // @!has "foo/struct.Foo.html"
 #[doc(hidden)]
 pub struct Foo;
diff --git a/tests/rustdoc/files-creation-private.rs b/tests/rustdoc/files-creation-private.rs
index ca2327e0f911a..e2fdbc068f8aa 100644
--- a/tests/rustdoc/files-creation-private.rs
+++ b/tests/rustdoc/files-creation-private.rs
@@ -1,5 +1,9 @@
 #![crate_name="foo"]
 
+// @files "foo" \
+// '["index.html", "all.html", "sidebar-items.js", "foo", "bar", "private", "struct.Bar.html"]'
+// @files "foo/bar" '["index.html", "sidebar-items.js"]'
+
 // @!has "foo/priv/index.html"
 // @!has "foo/priv/struct.Foo.html"
 mod private {
diff --git a/tests/rustdoc/issue-111064-reexport-trait-from-hidden-2.rs b/tests/rustdoc/issue-111064-reexport-trait-from-hidden-2.rs
index d6832bb7a0977..65c26d6a83751 100644
--- a/tests/rustdoc/issue-111064-reexport-trait-from-hidden-2.rs
+++ b/tests/rustdoc/issue-111064-reexport-trait-from-hidden-2.rs
@@ -2,6 +2,12 @@
 #![no_core]
 #![crate_name = "foo"]
 
+// @files "foo" "['sidebar-items.js', 'all.html', 'hidden', 'index.html', 'struct.Bar.html', \
+//        'visible']"
+// @files "foo/hidden" "['inner']"
+// @files "foo/hidden/inner" "['trait.Foo.html']"
+// @files "foo/visible" "['index.html', 'sidebar-items.js', 'trait.Foo.html']"
+
 // @!has 'foo/hidden/index.html'
 // @!has 'foo/hidden/inner/index.html'
 // FIXME: Should be `@!has`: https://github.com/rust-lang/rust/issues/111249
diff --git a/tests/rustdoc/issue-111249-file-creation.rs b/tests/rustdoc/issue-111249-file-creation.rs
index d2042b231e469..afd65ddaf941f 100644
--- a/tests/rustdoc/issue-111249-file-creation.rs
+++ b/tests/rustdoc/issue-111249-file-creation.rs
@@ -2,6 +2,12 @@
 #![feature(no_core)]
 #![no_core]
 
+// @files "foo" "['all.html', 'visible', 'index.html', 'sidebar-items.js', 'hidden', \
+//        'struct.Bar.html']"
+// @files "foo/visible" "['trait.Foo.html', 'index.html', 'sidebar-items.js']"
+// @files "foo/hidden" "['inner']"
+// @files "foo/hidden/inner" "['trait.Foo.html']"
+
 // The following five should not fail!
 // @!has 'foo/hidden/index.html'
 // @!has 'foo/hidden/inner/index.html'
diff --git a/tests/ui/consts/timeout.rs b/tests/ui/consts/timeout.rs
new file mode 100644
index 0000000000000..c9094999ee276
--- /dev/null
+++ b/tests/ui/consts/timeout.rs
@@ -0,0 +1,25 @@
+//! This test checks that external macros don't hide
+//! the const eval timeout lint and then subsequently
+//! ICE.
+
+// compile-flags: --crate-type=lib -Ztiny-const-eval-limit
+// error-pattern: constant evaluation is taking a long time
+
+static ROOK_ATTACKS_TABLE: () = {
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+    0_u64.count_ones();
+};
diff --git a/tests/ui/consts/timeout.stderr b/tests/ui/consts/timeout.stderr
new file mode 100644
index 0000000000000..799b5ec8dd9f1
--- /dev/null
+++ b/tests/ui/consts/timeout.stderr
@@ -0,0 +1,15 @@
+error: constant evaluation is taking a long time
+  --> $SRC_DIR/core/src/num/mod.rs:LL:COL
+   |
+   = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
+           If your compilation actually takes a long time, you can safely allow the lint.
+help: the constant being evaluated
+  --> $DIR/timeout.rs:8:1
+   |
+LL | static ROOK_ATTACKS_TABLE: () = {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[deny(long_running_const_eval)]` on by default
+   = note: this error originates in the macro `uint_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs
new file mode 100644
index 0000000000000..2b6825089bde0
--- /dev/null
+++ b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.rs
@@ -0,0 +1,11 @@
+// compile-flags: -Ztreat-err-as-bug
+// dont-check-failure-status
+// error-pattern: aborting due to `-Z treat-err-as-bug=1`
+// normalize-stderr-test "note: .*\n\n" -> ""
+// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
+// rustc-env:RUST_BACKTRACE=0
+
+fn main() {
+    #[deny(while_true)]
+    while true {}
+}
diff --git a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr
new file mode 100644
index 0000000000000..120b127ea44d7
--- /dev/null
+++ b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr
@@ -0,0 +1,16 @@
+error: denote infinite loops with `loop { ... }`
+  --> $DIR/panic-causes-oom-112708.rs:10:5
+   |
+LL |     while true {}
+   |     ^^^^^^^^^^ help: use `loop`
+   |
+note: the lint level is defined here
+  --> $DIR/panic-causes-oom-112708.rs:9:12
+   |
+LL |     #[deny(while_true)]
+   |            ^^^^^^^^^^
+
+error: the compiler unexpectedly panicked. this is a bug.
+
+query stack during panic:
+thread panicked while processing panic. aborting.