about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2023-11-25 10:21:06 +0100
committerGitHub <noreply@github.com>2023-11-25 10:21:06 +0100
commit0304aac0f3f740449f373971cdfa51a14576eab8 (patch)
tree876f49543a38d084c9c8f1b1374a34b3f75130b5
parentfb2c49894722e5f04af45e4fd3858b0ce0869b2b (diff)
parent1b7b9540fe15c8c7681a47a701e57b9de22b8aba (diff)
downloadrust-0304aac0f3f740449f373971cdfa51a14576eab8.tar.gz
rust-0304aac0f3f740449f373971cdfa51a14576eab8.zip
Rollup merge of #118251 - notriddle:notriddle/less-recursion, r=GuillaumeGomez
rustdoc-search: avoid infinite where clause unbox

Fixes #118242
-rw-r--r--src/librustdoc/html/static/js/search.js32
-rw-r--r--tests/rustdoc-js/assoc-type-loop.js9
-rw-r--r--tests/rustdoc-js/assoc-type-loop.rs35
3 files changed, 68 insertions, 8 deletions
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 4822690fd04..0bb56cd9710 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1755,17 +1755,26 @@ function initSearch(rawSearchIndex) {
                 if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
                     return false;
                 }
+                // Where clauses can represent cyclical data.
+                // `null` prevents it from trying to unbox in an infinite loop
+                const mgensTmp = new Map(mgens);
+                mgensTmp.set(fnType.id, null);
                 // This is only a potential unbox if the search query appears in the where clause
                 // for example, searching `Read -> usize` should find
                 // `fn read_all<R: Read>(R) -> Result<usize>`
                 // generic `R` is considered "unboxed"
-                return checkIfInList(whereClause[(-fnType.id) - 1], queryElem, whereClause);
+                return checkIfInList(
+                    whereClause[(-fnType.id) - 1],
+                    queryElem,
+                    whereClause,
+                    mgensTmp
+                );
             } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) {
                 const simplifiedGenerics = [
                     ...fnType.generics,
                     ...Array.from(fnType.bindings.values()).flat(),
                 ];
-                return checkIfInList(simplifiedGenerics, queryElem, whereClause);
+                return checkIfInList(simplifiedGenerics, queryElem, whereClause, mgens);
             }
             return false;
         }
@@ -1777,12 +1786,13 @@ function initSearch(rawSearchIndex) {
           * @param {Array<FunctionType>} list
           * @param {QueryElement} elem          - The element from the parsed query.
           * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgens - Map functions generics to query generics.
           *
           * @return {boolean} - Returns true if found, false otherwise.
           */
-        function checkIfInList(list, elem, whereClause) {
+        function checkIfInList(list, elem, whereClause, mgens) {
             for (const entry of list) {
-                if (checkType(entry, elem, whereClause)) {
+                if (checkType(entry, elem, whereClause, mgens)) {
                     return true;
                 }
             }
@@ -1796,23 +1806,29 @@ function initSearch(rawSearchIndex) {
           * @param {Row} row
           * @param {QueryElement} elem          - The element from the parsed query.
           * @param {[FunctionType]} whereClause - Trait bounds for generic items.
+         * @param {Map<number,number>|null} mgens - Map functions generics to query generics.
           *
           * @return {boolean} - Returns true if the type matches, false otherwise.
           */
-        function checkType(row, elem, whereClause) {
+        function checkType(row, elem, whereClause, mgens) {
             if (row.bindings.size === 0 && elem.bindings.size === 0) {
                 if (elem.id < 0) {
-                    return row.id < 0 || checkIfInList(row.generics, elem, whereClause);
+                    return row.id < 0 || checkIfInList(row.generics, elem, whereClause, mgens);
                 }
                 if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 &&
                     typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 &&
                     // special case
                     elem.id !== typeNameIdOfArrayOrSlice
                 ) {
-                    return row.id === elem.id || checkIfInList(row.generics, elem, whereClause);
+                    return row.id === elem.id || checkIfInList(
+                        row.generics,
+                        elem,
+                        whereClause,
+                        mgens
+                    );
                 }
             }
-            return unifyFunctionTypes([row], [elem], whereClause);
+            return unifyFunctionTypes([row], [elem], whereClause, mgens);
         }
 
         function checkPath(contains, ty, maxEditDistance) {
diff --git a/tests/rustdoc-js/assoc-type-loop.js b/tests/rustdoc-js/assoc-type-loop.js
new file mode 100644
index 00000000000..f0192371ab4
--- /dev/null
+++ b/tests/rustdoc-js/assoc-type-loop.js
@@ -0,0 +1,9 @@
+// Crash reduction of
+// https://github.com/rust-lang/rust/issues/118242
+
+const EXPECTED = [
+    {
+        'query': 't',
+        'correction': null,
+    },
+];
diff --git a/tests/rustdoc-js/assoc-type-loop.rs b/tests/rustdoc-js/assoc-type-loop.rs
new file mode 100644
index 00000000000..f123c83f50f
--- /dev/null
+++ b/tests/rustdoc-js/assoc-type-loop.rs
@@ -0,0 +1,35 @@
+#![crate_name="foo"]
+
+// reduced from sqlx 0.7.3
+use std::future::Future;
+use std::pin::Pin;
+use std::ops::{Deref, DerefMut};
+pub enum Error {}
+pub trait Acquire<'c> {
+    type Database: Database;
+    type Connection: Deref<Target = <Self::Database as Database>::Connection> + DerefMut + Send;
+}
+pub trait Database {
+    type Connection: Connection<Database = Self>;
+}
+pub trait Connection {
+    type Database: Database;
+    type Options: ConnectionOptions<Connection = Self>;
+    fn begin(
+        &mut self
+    ) -> Pin<Box<dyn Future<Output = Result<Transaction<'_, Self::Database>, Error>> + Send + '_>>
+    where
+        Self: Sized;
+}
+pub trait ConnectionOptions {
+    type Connection: Connection;
+}
+pub struct Transaction<'c, DB: Database> {
+    _db: &'c DB,
+}
+impl<'t, 'c, DB: Database> Acquire<'t> for &'t mut Transaction<'c, DB>
+    where <DB as Database>::Connection: Send
+{
+    type Database = DB;
+    type Connection = &'t mut <DB as Database>::Connection;
+}