Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc-search: avoid infinite where clause unbox #118251

Merged
merged 1 commit into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 24 additions & 8 deletions src/librustdoc/html/static/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
}
Expand All @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions tests/rustdoc-js/assoc-type-loop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Crash reduction of
// https://github.com/rust-lang/rust/issues/118242

const EXPECTED = [
{
'query': 't',
'correction': null,
},
];
35 changes: 35 additions & 0 deletions tests/rustdoc-js/assoc-type-loop.rs
Original file line number Diff line number Diff line change
@@ -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;
}