From 1b7b9540fe15c8c7681a47a701e57b9de22b8aba Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 24 Nov 2023 09:47:55 -0700 Subject: [PATCH] rustdoc-search: avoid infinite where clause unbox Fixes #118242 --- src/librustdoc/html/static/js/search.js | 32 ++++++++++++++++------ tests/rustdoc-js/assoc-type-loop.js | 9 +++++++ tests/rustdoc-js/assoc-type-loop.rs | 35 +++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 tests/rustdoc-js/assoc-type-loop.js create mode 100644 tests/rustdoc-js/assoc-type-loop.rs diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 4822690fd04b6..0bb56cd971013 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) -> Result` // 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} list * @param {QueryElement} elem - The element from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Map|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|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 0000000000000..f0192371ab41a --- /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 0000000000000..f123c83f50fdf --- /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::Connection> + DerefMut + Send; +} +pub trait Database { + type Connection: Connection; +} +pub trait Connection { + type Database: Database; + type Options: ConnectionOptions; + fn begin( + &mut self + ) -> Pin, 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 ::Connection: Send +{ + type Database = DB; + type Connection = &'t mut ::Connection; +}