diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index dddc087d124df..2984f3ab50eea 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -289,8 +289,26 @@ impl SerializedSearchIndex {
(Some(self_type_data), None) => Some(self_type_data),
(None, Some(other_type_data)) => Some(TypeData {
search_unbox: other_type_data.search_unbox,
- inverted_function_signature_index: other_type_data
- .inverted_function_signature_index
+ inverted_function_inputs_index: other_type_data
+ .inverted_function_inputs_index
+ .iter()
+ .cloned()
+ .map(|mut list: Vec| {
+ for fnid in &mut list {
+ assert!(
+ other.function_data
+ [usize::try_from(*fnid).unwrap()]
+ .is_some(),
+ );
+ // this is valid because we call `self.push()` once, exactly, for every entry,
+ // even if we're just pushing a tombstone
+ *fnid += u32::try_from(other_entryid_offset).unwrap();
+ }
+ list
+ })
+ .collect(),
+ inverted_function_output_index: other_type_data
+ .inverted_function_output_index
.iter()
.cloned()
.map(|mut list: Vec| {
@@ -310,18 +328,42 @@ impl SerializedSearchIndex {
}),
(Some(mut self_type_data), Some(other_type_data)) => {
for (size, other_list) in other_type_data
- .inverted_function_signature_index
+ .inverted_function_inputs_index
+ .iter()
+ .enumerate()
+ {
+ while self_type_data.inverted_function_inputs_index.len()
+ <= size
+ {
+ self_type_data
+ .inverted_function_inputs_index
+ .push(Vec::new());
+ }
+ self_type_data.inverted_function_inputs_index[size].extend(
+ other_list.iter().copied().map(|fnid| {
+ assert!(
+ other.function_data[usize::try_from(fnid).unwrap()]
+ .is_some(),
+ );
+ // this is valid because we call `self.push()` once, exactly, for every entry,
+ // even if we're just pushing a tombstone
+ fnid + u32::try_from(other_entryid_offset).unwrap()
+ }),
+ )
+ }
+ for (size, other_list) in other_type_data
+ .inverted_function_output_index
.iter()
.enumerate()
{
- while self_type_data.inverted_function_signature_index.len()
+ while self_type_data.inverted_function_output_index.len()
<= size
{
self_type_data
- .inverted_function_signature_index
+ .inverted_function_output_index
.push(Vec::new());
}
- self_type_data.inverted_function_signature_index[size].extend(
+ self_type_data.inverted_function_output_index[size].extend(
other_list.iter().copied().map(|fnid| {
assert!(
other.function_data[usize::try_from(fnid).unwrap()]
@@ -443,8 +485,25 @@ impl SerializedSearchIndex {
param_names: function_data.param_names.clone(),
}),
other.type_data[other_entryid].as_ref().map(|type_data| TypeData {
- inverted_function_signature_index: type_data
- .inverted_function_signature_index
+ inverted_function_inputs_index: type_data
+ .inverted_function_inputs_index
+ .iter()
+ .cloned()
+ .map(|mut list| {
+ for fnid in &mut list {
+ assert!(
+ other.function_data[usize::try_from(*fnid).unwrap()]
+ .is_some(),
+ );
+ // this is valid because we call `self.push()` once, exactly, for every entry,
+ // even if we're just pushing a tombstone
+ *fnid += u32::try_from(other_entryid_offset).unwrap();
+ }
+ list
+ })
+ .collect(),
+ inverted_function_output_index: type_data
+ .inverted_function_output_index
.iter()
.cloned()
.map(|mut list| {
@@ -599,9 +658,13 @@ impl SerializedSearchIndex {
},
),
self.type_data[id].as_ref().map(
- |TypeData { search_unbox, inverted_function_signature_index }| {
- let inverted_function_signature_index: Vec> =
- inverted_function_signature_index
+ |TypeData {
+ search_unbox,
+ inverted_function_inputs_index,
+ inverted_function_output_index,
+ }| {
+ let inverted_function_inputs_index: Vec> =
+ inverted_function_inputs_index
.iter()
.cloned()
.map(|mut list| {
@@ -615,7 +678,26 @@ impl SerializedSearchIndex {
list
})
.collect();
- TypeData { search_unbox: *search_unbox, inverted_function_signature_index }
+ let inverted_function_output_index: Vec> =
+ inverted_function_output_index
+ .iter()
+ .cloned()
+ .map(|mut list| {
+ for id in &mut list {
+ *id = u32::try_from(
+ *map.get(&usize::try_from(*id).unwrap()).unwrap(),
+ )
+ .unwrap();
+ }
+ list.sort();
+ list
+ })
+ .collect();
+ TypeData {
+ search_unbox: *search_unbox,
+ inverted_function_inputs_index,
+ inverted_function_output_index,
+ }
},
),
self.alias_pointers[id].and_then(|alias| map.get(&alias).copied()),
@@ -934,18 +1016,20 @@ struct TypeData {
/// | `Unboxable` | yes | no | no |
/// | `Inner` | no | no | yes |
search_unbox: bool,
- /// List of functions that mention this type in their type signature.
+ /// List of functions that mention this type in their type signature,
+ /// on the left side of the `->` arrow.
///
- /// - The outermost list has one entry per alpha-normalized generic.
- ///
- /// - The second layer is sorted by number of types that appear in the
+ /// - The outer layer is sorted by number of types that appear in the
/// type signature. The search engine iterates over these in order from
/// smallest to largest. Functions with less stuff in their type
/// signature are more likely to be what the user wants, because we never
/// show functions that are *missing* parts of the query, so removing..
///
- /// - The final layer is the list of functions.
- inverted_function_signature_index: Vec>,
+ /// - The inner layer is the list of functions.
+ inverted_function_inputs_index: Vec>,
+ /// List of functions that mention this type in their type signature,
+ /// on the right side of the `->` arrow.
+ inverted_function_output_index: Vec>,
}
impl Serialize for TypeData {
@@ -953,15 +1037,21 @@ impl Serialize for TypeData {
where
S: Serializer,
{
- if self.search_unbox || !self.inverted_function_signature_index.is_empty() {
+ if self.search_unbox
+ || !self.inverted_function_inputs_index.is_empty()
+ || !self.inverted_function_output_index.is_empty()
+ {
let mut seq = serializer.serialize_seq(None)?;
- if !self.inverted_function_signature_index.is_empty() {
- let mut buf = Vec::new();
- encode::write_postings_to_string(&self.inverted_function_signature_index, &mut buf);
- let mut serialized_result = Vec::new();
- stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
- seq.serialize_element(&String::from_utf8(serialized_result).unwrap())?;
- }
+ let mut buf = Vec::new();
+ encode::write_postings_to_string(&self.inverted_function_inputs_index, &mut buf);
+ let mut serialized_result = Vec::new();
+ stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
+ seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?;
+ buf.clear();
+ serialized_result.clear();
+ encode::write_postings_to_string(&self.inverted_function_output_index, &mut buf);
+ stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result);
+ seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?;
if self.search_unbox {
seq.serialize_element(&1)?;
}
@@ -984,21 +1074,39 @@ impl<'de> Deserialize<'de> for TypeData {
write!(formatter, "type data")
}
fn visit_none(self) -> Result {
- Ok(TypeData { inverted_function_signature_index: vec![], search_unbox: false })
+ Ok(TypeData {
+ inverted_function_inputs_index: vec![],
+ inverted_function_output_index: vec![],
+ search_unbox: false,
+ })
}
fn visit_seq>(self, mut v: A) -> Result {
- let inverted_function_signature_index: String =
+ let inverted_function_inputs_index: String =
+ v.next_element()?.unwrap_or(String::new());
+ let inverted_function_output_index: String =
v.next_element()?.unwrap_or(String::new());
let search_unbox: u32 = v.next_element()?.unwrap_or(0);
let mut idx: Vec = Vec::new();
stringdex_internals::decode::read_base64_from_bytes(
- inverted_function_signature_index.as_bytes(),
+ inverted_function_inputs_index.as_bytes(),
&mut idx,
)
.unwrap();
- let mut inverted_function_signature_index = Vec::new();
- encode::read_postings_from_string(&mut inverted_function_signature_index, &idx);
- Ok(TypeData { inverted_function_signature_index, search_unbox: search_unbox == 1 })
+ let mut inverted_function_inputs_index = Vec::new();
+ encode::read_postings_from_string(&mut inverted_function_inputs_index, &idx);
+ idx.clear();
+ stringdex_internals::decode::read_base64_from_bytes(
+ inverted_function_output_index.as_bytes(),
+ &mut idx,
+ )
+ .unwrap();
+ let mut inverted_function_output_index = Vec::new();
+ encode::read_postings_from_string(&mut inverted_function_output_index, &idx);
+ Ok(TypeData {
+ inverted_function_inputs_index,
+ inverted_function_output_index,
+ search_unbox: search_unbox == 1,
+ })
}
}
deserializer.deserialize_any(TypeDataVisitor)
@@ -1222,8 +1330,16 @@ pub(crate) fn build_index(
let index = *index.get();
serialized_index.descs[index] = crate_doc;
for type_data in serialized_index.type_data.iter_mut() {
- if let Some(TypeData { inverted_function_signature_index, .. }) = type_data {
- for list in &mut inverted_function_signature_index[..] {
+ if let Some(TypeData {
+ inverted_function_inputs_index,
+ inverted_function_output_index,
+ ..
+ }) = type_data
+ {
+ for list in inverted_function_inputs_index
+ .iter_mut()
+ .chain(inverted_function_output_index.iter_mut())
+ {
list.retain(|fnid| {
serialized_index.entry_data[usize::try_from(*fnid).unwrap()]
.as_ref()
@@ -1449,7 +1565,8 @@ pub(crate) fn build_index(
if serialized_index.type_data[id].as_mut().is_none() {
serialized_index.type_data[id] = Some(TypeData {
search_unbox,
- inverted_function_signature_index: Vec::new(),
+ inverted_function_inputs_index: Vec::new(),
+ inverted_function_output_index: Vec::new(),
});
} else if search_unbox {
serialized_index.type_data[id].as_mut().unwrap().search_unbox = true;
@@ -1473,7 +1590,11 @@ pub(crate) fn build_index(
None
},
},
- TypeData { search_unbox, inverted_function_signature_index: Vec::new() },
+ TypeData {
+ inverted_function_inputs_index: Vec::new(),
+ inverted_function_output_index: Vec::new(),
+ search_unbox,
+ },
);
pathid
}
@@ -1695,13 +1816,14 @@ pub(crate) fn build_index(
}
}
if let Some(search_type) = &mut item.search_type {
- let mut used_in_function_signature = BTreeSet::new();
+ let mut used_in_function_inputs = BTreeSet::new();
+ let mut used_in_function_output = BTreeSet::new();
for item in &mut search_type.inputs {
convert_render_type(
item,
cache,
&mut serialized_index,
- &mut used_in_function_signature,
+ &mut used_in_function_inputs,
tcx,
);
}
@@ -1710,20 +1832,44 @@ pub(crate) fn build_index(
item,
cache,
&mut serialized_index,
- &mut used_in_function_signature,
+ &mut used_in_function_output,
tcx,
);
}
+ let mut used_in_constraints = Vec::new();
for constraint in &mut search_type.where_clause {
+ let mut used_in_constraint = BTreeSet::new();
for trait_ in &mut constraint[..] {
convert_render_type(
trait_,
cache,
&mut serialized_index,
- &mut used_in_function_signature,
+ &mut used_in_constraint,
tcx,
);
}
+ used_in_constraints.push(used_in_constraint);
+ }
+ loop {
+ let mut inserted_any = false;
+ for (i, used_in_constraint) in used_in_constraints.iter().enumerate() {
+ let id = !(i as isize);
+ if used_in_function_inputs.contains(&id)
+ && !used_in_function_inputs.is_superset(&used_in_constraint)
+ {
+ used_in_function_inputs.extend(used_in_constraint.iter().copied());
+ inserted_any = true;
+ }
+ if used_in_function_output.contains(&id)
+ && !used_in_function_output.is_superset(&used_in_constraint)
+ {
+ used_in_function_output.extend(used_in_constraint.iter().copied());
+ inserted_any = true;
+ }
+ }
+ if !inserted_any {
+ break;
+ }
}
let search_type_size = search_type.size() +
// Artificially give struct fields a size of 8 instead of their real
@@ -1746,13 +1892,34 @@ pub(crate) fn build_index(
.map(|sym| sym.map(|sym| sym.to_string()).unwrap_or(String::new()))
.collect::>(),
});
- for index in used_in_function_signature {
+ for index in used_in_function_inputs {
+ let postings = if index >= 0 {
+ assert!(serialized_index.path_data[index as usize].is_some());
+ &mut serialized_index.type_data[index as usize]
+ .as_mut()
+ .unwrap()
+ .inverted_function_inputs_index
+ } else {
+ let generic_id = usize::try_from(-index).unwrap() - 1;
+ for _ in serialized_index.generic_inverted_index.len()..=generic_id {
+ serialized_index.generic_inverted_index.push(Vec::new());
+ }
+ &mut serialized_index.generic_inverted_index[generic_id]
+ };
+ while postings.len() <= search_type_size {
+ postings.push(Vec::new());
+ }
+ if postings[search_type_size].last() != Some(&(new_entry_id as u32)) {
+ postings[search_type_size].push(new_entry_id as u32);
+ }
+ }
+ for index in used_in_function_output {
let postings = if index >= 0 {
assert!(serialized_index.path_data[index as usize].is_some());
&mut serialized_index.type_data[index as usize]
.as_mut()
.unwrap()
- .inverted_function_signature_index
+ .inverted_function_output_index
} else {
let generic_id = usize::try_from(-index).unwrap() - 1;
for _ in serialized_index.generic_inverted_index.len()..=generic_id {
@@ -1763,7 +1930,9 @@ pub(crate) fn build_index(
while postings.len() <= search_type_size {
postings.push(Vec::new());
}
- postings[search_type_size].push(new_entry_id as u32);
+ if postings[search_type_size].last() != Some(&(new_entry_id as u32)) {
+ postings[search_type_size].push(new_entry_id as u32);
+ }
}
}
}
diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index 3ac10742e4152..74f646008ebd7 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -270,9 +270,12 @@ declare namespace rustdoc {
*/
interface TypeData {
searchUnbox: boolean,
- invertedFunctionSignatureIndex: RoaringBitmap[],
+ invertedFunctionInputsIndex: RoaringBitmap[],
+ invertedFunctionOutputIndex: RoaringBitmap[],
}
+ type TypeInvertedIndexPolarity = "invertedFunctionInputsIndex" | "invertedFunctionOutputIndex";
+
/**
* A search entry of some sort.
*/
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index b003bcc7bf9c8..fa812a2b67b5a 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1696,7 +1696,7 @@ class DocSearch {
}
/**
* function_signature, param_names
- * @type {[string, number] | [number] | [string] | [] | null}
+ * @type {[string, string, number] | [string, string] | [] | null}
*/
const raw = JSON.parse(encoded);
@@ -1705,32 +1705,46 @@ class DocSearch {
}
let searchUnbox = false;
- const invertedFunctionSignatureIndex = [];
+ const invertedFunctionInputsIndex = [];
+ const invertedFunctionOutputIndex = [];
if (typeof raw[0] === "string") {
- if (raw[1]) {
+ if (raw[2]) {
searchUnbox = true;
}
// the inverted function signature index is a list of bitmaps,
// by number of types that appear in the function
let i = 0;
- const pb = makeUint8ArrayFromBase64(raw[0]);
- const l = pb.length;
+ let pb = makeUint8ArrayFromBase64(raw[0]);
+ let l = pb.length;
while (i < l) {
if (pb[i] === 0) {
- invertedFunctionSignatureIndex.push(RoaringBitmap.empty());
+ invertedFunctionInputsIndex.push(RoaringBitmap.empty());
i += 1;
} else {
const bitmap = new RoaringBitmap(pb, i);
i += bitmap.consumed_len_bytes;
- invertedFunctionSignatureIndex.push(bitmap);
+ invertedFunctionInputsIndex.push(bitmap);
+ }
+ }
+ i = 0;
+ pb = makeUint8ArrayFromBase64(raw[1]);
+ l = pb.length;
+ while (i < l) {
+ if (pb[i] === 0) {
+ invertedFunctionOutputIndex.push(RoaringBitmap.empty());
+ i += 1;
+ } else {
+ const bitmap = new RoaringBitmap(pb, i);
+ i += bitmap.consumed_len_bytes;
+ invertedFunctionOutputIndex.push(bitmap);
}
}
} else if (raw[0]) {
searchUnbox = true;
}
- return { searchUnbox, invertedFunctionSignatureIndex };
+ return { searchUnbox, invertedFunctionInputsIndex, invertedFunctionOutputIndex };
}
/**
@@ -4009,14 +4023,19 @@ class DocSearch {
* or anything else. This function returns all possible permutations.
*
* @param {rustdoc.ParserQueryElement|null} elem
+ * @param {rustdoc.TypeInvertedIndexPolarity} polarity
* @returns {Promise[]>}
*/
- const unpackPostingsList = async elem => {
+ const unpackPostingsList = async(elem, polarity) => {
if (!elem) {
return empty_postings_list;
}
const typeFilter = itemTypeFromName(elem.typeFilter);
- const searchResults = await index.search(elem.normalizedPathLast);
+ const [searchResults, upla, uplb] = await Promise.all([
+ index.search(elem.normalizedPathLast),
+ unpackPostingsListAll(elem.generics, polarity),
+ unpackPostingsListBindings(elem.bindings, polarity),
+ ]);
/**
* @type {Promise<[
* number,
@@ -4039,7 +4058,7 @@ class DocSearch {
const types = (await Promise.all(typePromises))
.filter(([_id, name, ty, path]) =>
name !== null && name.toLowerCase() === elem.pathLast &&
- ty && !ty.invertedFunctionSignatureIndex.every(bitmap => {
+ ty && !ty[polarity].every(bitmap => {
return bitmap.isEmpty();
}) &&
path && path.ty !== TY_ASSOCTYPE &&
@@ -4078,7 +4097,7 @@ class DocSearch {
this.getPathData(id),
]);
if (name !== null && ty !== null && path !== null &&
- !ty.invertedFunctionSignatureIndex.every(bitmap => {
+ !ty[polarity].every(bitmap => {
return bitmap.isEmpty();
}) &&
path.ty !== TY_ASSOCTYPE
@@ -4176,18 +4195,16 @@ class DocSearch {
/** @type {PostingsList[]} */
const results = [];
for (const [id, _name, typeData] of types) {
- if (!typeData || typeData.invertedFunctionSignatureIndex.every(bitmap => {
+ if (!typeData || typeData[polarity].every(bitmap => {
return bitmap.isEmpty();
})) {
continue;
}
- const upla = await unpackPostingsListAll(elem.generics);
- const uplb = await unpackPostingsListBindings(elem.bindings);
for (const {invertedIndex: genericsIdx, queryElem: generics} of upla) {
for (const {invertedIndex: bindingsIdx, queryElem: bindings} of uplb) {
results.push({
invertedIndex: intersectInvertedIndexes(
- typeData.invertedFunctionSignatureIndex,
+ typeData[polarity],
genericsIdx,
bindingsIdx,
),
@@ -4219,15 +4236,16 @@ class DocSearch {
* take the intersection of this bitmap.
*
* @param {(rustdoc.ParserQueryElement|null)[]|null} elems
+ * @param {rustdoc.TypeInvertedIndexPolarity} polarity
* @returns {Promise[]>}
*/
- const unpackPostingsListAll = async elems => {
+ const unpackPostingsListAll = async(elems, polarity) => {
if (!elems || elems.length === 0) {
return nested_everything_postings_list;
}
const [firstPostingsList, remainingAll] = await Promise.all([
- unpackPostingsList(elems[0]),
- unpackPostingsListAll(elems.slice(1)),
+ unpackPostingsList(elems[0], polarity),
+ unpackPostingsListAll(elems.slice(1), polarity),
]);
/** @type {PostingsList[]} */
const results = [];
@@ -4261,11 +4279,12 @@ class DocSearch {
* Before passing an actual parser item to it, make sure to clone the map.
*
* @param {Map} elems
+ * @param {rustdoc.TypeInvertedIndexPolarity} polarity
* @returns {Promise,
* >[]>}
*/
- const unpackPostingsListBindings = async elems => {
+ const unpackPostingsListBindings = async(elems, polarity) => {
if (!elems) {
return [{
invertedIndex: everything_inverted_index,
@@ -4286,19 +4305,23 @@ class DocSearch {
queryElem: new Map(),
}];
}
- const firstKeyIds = await index.search(firstKey);
+ // HEADS UP!
+ // We must put this map back the way we found it before returning,
+ // otherwise things break.
+ elems.delete(firstKey);
+ const [firstKeyIds, firstPostingsList, remainingAll] = await Promise.all([
+ index.search(firstKey),
+ unpackPostingsListAll(firstList, polarity),
+ unpackPostingsListBindings(elems, polarity),
+ ]);
if (!firstKeyIds) {
+ elems.set(firstKey, firstList);
// User specified a non-existent key.
return [{
invertedIndex: empty_inverted_index,
queryElem: new Map(),
}];
}
- elems.delete(firstKey);
- const [firstPostingsList, remainingAll] = await Promise.all([
- unpackPostingsListAll(firstList),
- unpackPostingsListBindings(elems),
- ]);
/** @type {PostingsList