rustdoc-search: avoid infinite where clause unbox

Fixes #118242
This commit is contained in:
Michael Howell 2023-11-24 09:47:55 -07:00
parent b06258cde4
commit 1b7b9540fe
3 changed files with 68 additions and 8 deletions

View File

@ -1755,17 +1755,26 @@ function initSearch(rawSearchIndex) {
if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) { if (mgens && mgens.has(fnType.id) && mgens.get(fnType.id) !== 0) {
return false; 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 // This is only a potential unbox if the search query appears in the where clause
// for example, searching `Read -> usize` should find // for example, searching `Read -> usize` should find
// `fn read_all<R: Read>(R) -> Result<usize>` // `fn read_all<R: Read>(R) -> Result<usize>`
// generic `R` is considered "unboxed" // 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) { } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) {
const simplifiedGenerics = [ const simplifiedGenerics = [
...fnType.generics, ...fnType.generics,
...Array.from(fnType.bindings.values()).flat(), ...Array.from(fnType.bindings.values()).flat(),
]; ];
return checkIfInList(simplifiedGenerics, queryElem, whereClause); return checkIfInList(simplifiedGenerics, queryElem, whereClause, mgens);
} }
return false; return false;
} }
@ -1777,12 +1786,13 @@ function initSearch(rawSearchIndex) {
* @param {Array<FunctionType>} list * @param {Array<FunctionType>} list
* @param {QueryElement} elem - The element from the parsed query. * @param {QueryElement} elem - The element from the parsed query.
* @param {[FunctionType]} whereClause - Trait bounds for generic items. * @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. * @return {boolean} - Returns true if found, false otherwise.
*/ */
function checkIfInList(list, elem, whereClause) { function checkIfInList(list, elem, whereClause, mgens) {
for (const entry of list) { for (const entry of list) {
if (checkType(entry, elem, whereClause)) { if (checkType(entry, elem, whereClause, mgens)) {
return true; return true;
} }
} }
@ -1796,23 +1806,29 @@ function initSearch(rawSearchIndex) {
* @param {Row} row * @param {Row} row
* @param {QueryElement} elem - The element from the parsed query. * @param {QueryElement} elem - The element from the parsed query.
* @param {[FunctionType]} whereClause - Trait bounds for generic items. * @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. * @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 (row.bindings.size === 0 && elem.bindings.size === 0) {
if (elem.id < 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 && if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 &&
typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 &&
// special case // special case
elem.id !== typeNameIdOfArrayOrSlice 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) { function checkPath(contains, ty, maxEditDistance) {

View File

@ -0,0 +1,9 @@
// Crash reduction of
// https://github.com/rust-lang/rust/issues/118242
const EXPECTED = [
{
'query': 't',
'correction': null,
},
];

View File

@ -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;
}