Skip to content

Commit 45a3cd5

Browse files
authored
Rollup merge of #86659 - notriddle:notriddle/generics-rustdoc, r=GuillaumeGomez
fix(rustdoc): generics search This commit adds a test case for generics, re-adds generics data to the search index, and tweaks function indexing to use less space in JSON. This partially reverts commit 14ca894.
2 parents ab4d16f + cedd242 commit 45a3cd5

File tree

8 files changed

+191
-19
lines changed

8 files changed

+191
-19
lines changed

src/librustdoc/html/render/cache.rs

+18
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ crate fn get_index_search_type<'tcx>(
219219
fn get_index_type(clean_type: &clean::Type) -> RenderType {
220220
RenderType {
221221
name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
222+
generics: get_generics(clean_type),
222223
}
223224
}
224225

@@ -251,6 +252,23 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option
251252
}
252253
}
253254

255+
/// Return a list of generic parameters for use in the search index.
256+
///
257+
/// This function replaces bounds with types, so that `T where T: Debug` just becomes `Debug`.
258+
/// It does return duplicates, and that's intentional, since search queries like `Result<usize, usize>`
259+
/// are supposed to match only results where both parameters are `usize`.
260+
fn get_generics(clean_type: &clean::Type) -> Option<Vec<String>> {
261+
clean_type.generics().and_then(|types| {
262+
let r = types
263+
.iter()
264+
.filter_map(|t| {
265+
get_index_type_name(t, false).map(|name| name.as_str().to_ascii_lowercase())
266+
})
267+
.collect::<Vec<_>>();
268+
if r.is_empty() { None } else { Some(r) }
269+
})
270+
}
271+
254272
/// The point of this function is to replace bounds with types.
255273
///
256274
/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return

src/librustdoc/html/render/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ crate struct IndexItem {
9696
#[derive(Debug)]
9797
crate struct RenderType {
9898
name: Option<String>,
99+
generics: Option<Vec<String>>,
99100
}
100101

101102
/// Full type of functions/methods in the search index.
@@ -149,7 +150,13 @@ impl Serialize for TypeWithKind {
149150
where
150151
S: Serializer,
151152
{
152-
(&self.ty.name, self.kind).serialize(serializer)
153+
let mut seq = serializer.serialize_seq(None)?;
154+
seq.serialize_element(&self.ty.name)?;
155+
seq.serialize_element(&self.kind)?;
156+
if let Some(generics) = &self.ty.generics {
157+
seq.serialize_element(generics)?;
158+
}
159+
seq.end()
153160
}
154161
}
155162

src/librustdoc/html/static/search.js

+62-18
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function levenshtein(s1, s2) {
106106
window.initSearch = function(rawSearchIndex) {
107107
var MAX_LEV_DISTANCE = 3;
108108
var MAX_RESULTS = 200;
109-
var GENERICS_DATA = 1;
109+
var GENERICS_DATA = 2;
110110
var NAME = 0;
111111
var INPUTS_DATA = 0;
112112
var OUTPUT_DATA = 1;
@@ -306,6 +306,9 @@ window.initSearch = function(rawSearchIndex) {
306306
var elems = Object.create(null);
307307
var elength = obj[GENERICS_DATA].length;
308308
for (var x = 0; x < elength; ++x) {
309+
if (!elems[getObjectNameFromId(obj[GENERICS_DATA][x])]) {
310+
elems[getObjectNameFromId(obj[GENERICS_DATA][x])] = 0;
311+
}
309312
elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
310313
}
311314
var total = 0;
@@ -354,10 +357,13 @@ window.initSearch = function(rawSearchIndex) {
354357
if (literalSearch) {
355358
if (val.generics && val.generics.length !== 0) {
356359
if (obj.length > GENERICS_DATA &&
357-
obj[GENERICS_DATA].length >= val.generics.length) {
360+
obj[GENERICS_DATA].length > 0) {
358361
var elems = Object.create(null);
359362
len = obj[GENERICS_DATA].length;
360363
for (x = 0; x < len; ++x) {
364+
if (!elems[getObjectNameFromId(obj[GENERICS_DATA][x])]) {
365+
elems[getObjectNameFromId(obj[GENERICS_DATA][x])] = 0;
366+
}
361367
elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
362368
}
363369

@@ -375,26 +381,23 @@ window.initSearch = function(rawSearchIndex) {
375381
if (allFound) {
376382
return true;
377383
}
378-
} else {
379-
return false;
380384
}
385+
return false;
381386
}
382387
return true;
383-
}
384-
// If the type has generics but don't match, then it won't return at this point.
385-
// Otherwise, `checkGenerics` will return 0 and it'll return.
386-
if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
387-
var tmp_lev = checkGenerics(obj, val);
388-
if (tmp_lev <= MAX_LEV_DISTANCE) {
389-
return tmp_lev;
390-
}
391388
} else {
392-
return 0;
389+
// If the type has generics but don't match, then it won't return at this point.
390+
// Otherwise, `checkGenerics` will return 0 and it'll return.
391+
if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
392+
var tmp_lev = checkGenerics(obj, val);
393+
if (tmp_lev <= MAX_LEV_DISTANCE) {
394+
return tmp_lev;
395+
}
396+
}
393397
}
394-
}
395-
// Names didn't match so let's check if one of the generic types could.
396-
if (literalSearch) {
397-
if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
398+
} else if (literalSearch) {
399+
if ((!val.generics || val.generics.length === 0) &&
400+
obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
398401
return obj[GENERICS_DATA].some(
399402
function(name) {
400403
return name === val.name;
@@ -1167,7 +1170,48 @@ window.initSearch = function(rawSearchIndex) {
11671170
return ret;
11681171
}
11691172

1170-
var queries = query.raw.split(",");
1173+
// Split search query by ",", while respecting angle bracket nesting.
1174+
// Since "<" is an alias for the Ord family of traits, it also uses
1175+
// lookahead to distinguish "<"-as-less-than from "<"-as-angle-bracket.
1176+
//
1177+
// tokenizeQuery("A<B, C>, D") == ["A<B, C>", "D"]
1178+
// tokenizeQuery("A<B, C, D") == ["A<B", "C", "D"]
1179+
function tokenizeQuery(raw) {
1180+
var i, matched;
1181+
var l = raw.length;
1182+
var depth = 0;
1183+
var nextAngle = /(<|>)/g;
1184+
var ret = [];
1185+
var start = 0;
1186+
for (i = 0; i < l; ++i) {
1187+
switch (raw[i]) {
1188+
case "<":
1189+
nextAngle.lastIndex = i + 1;
1190+
matched = nextAngle.exec(raw);
1191+
if (matched && matched[1] === '>') {
1192+
depth += 1;
1193+
}
1194+
break;
1195+
case ">":
1196+
if (depth > 0) {
1197+
depth -= 1;
1198+
}
1199+
break;
1200+
case ",":
1201+
if (depth === 0) {
1202+
ret.push(raw.substring(start, i));
1203+
start = i + 1;
1204+
}
1205+
break;
1206+
}
1207+
}
1208+
if (start !== i) {
1209+
ret.push(raw.substring(start, i));
1210+
}
1211+
return ret;
1212+
}
1213+
1214+
var queries = tokenizeQuery(query.raw);
11711215
var results = {
11721216
"in_args": [],
11731217
"returned": [],

src/test/rustdoc-js-std/alias-4.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const QUERY = '<';
2+
3+
const EXPECTED = {
4+
'others': [
5+
{ 'name': 'Ord' },
6+
],
7+
};

src/test/rustdoc-js/generics-trait.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const QUERY = [
2+
'Result<SomeTrait>',
3+
'OtherThingxxxxxxxx',
4+
];
5+
6+
const EXPECTED = [
7+
{
8+
'in_args': [
9+
{ 'path': 'generics_trait', 'name': 'beta' },
10+
],
11+
'returned': [
12+
{ 'path': 'generics_trait', 'name': 'bet' },
13+
],
14+
},
15+
{
16+
'in_args': [
17+
{ 'path': 'generics_trait', 'name': 'alpha' },
18+
],
19+
'returned': [
20+
{ 'path': 'generics_trait', 'name': 'alef' },
21+
],
22+
},
23+
];

src/test/rustdoc-js/generics-trait.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pub trait SomeTrait {}
2+
pub trait OtherThingxxxxxxxx {}
3+
4+
pub fn alef<T: OtherThingxxxxxxxx>() -> Result<T, ()> { loop {} }
5+
pub fn bet<T: SomeTrait>() -> Result<T, ()> { loop {} }
6+
7+
pub fn alpha<T: OtherThingxxxxxxxx>(_param: Result<T, ()>) { loop {} }
8+
pub fn beta<T: SomeTrait>(_param: Result<T, ()>) { loop {} }

src/test/rustdoc-js/generics.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// exact-check
2+
3+
const QUERY = [
4+
'"R<P>"',
5+
'"P"',
6+
'P',
7+
'"ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>"',
8+
];
9+
10+
const EXPECTED = [
11+
{
12+
'returned': [
13+
{ 'path': 'generics', 'name': 'alef' },
14+
],
15+
'in_args': [
16+
{ 'path': 'generics', 'name': 'alpha' },
17+
],
18+
},
19+
{
20+
'others': [
21+
{ 'path': 'generics', 'name': 'P' },
22+
],
23+
'returned': [
24+
{ 'path': 'generics', 'name': 'alef' },
25+
],
26+
'in_args': [
27+
{ 'path': 'generics', 'name': 'alpha' },
28+
],
29+
},
30+
{
31+
'returned': [
32+
{ 'path': 'generics', 'name': 'alef' },
33+
],
34+
'in_args': [
35+
{ 'path': 'generics', 'name': 'alpha' },
36+
],
37+
},
38+
{
39+
'in_args': [
40+
{ 'path': 'generics', 'name': 'extracreditlabhomework' },
41+
],
42+
'returned': [],
43+
},
44+
];

src/test/rustdoc-js/generics.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
pub struct P;
2+
pub struct Q;
3+
pub struct R<T>(T);
4+
5+
// returns test
6+
pub fn alef() -> R<P> { loop {} }
7+
pub fn bet() -> R<Q> { loop {} }
8+
9+
// in_args test
10+
pub fn alpha(_x: R<P>) { loop {} }
11+
pub fn beta(_x: R<Q>) { loop {} }
12+
13+
// test case with multiple appearances of the same type
14+
pub struct ExtraCreditStructMulti<T, U> { t: T, u: U }
15+
pub struct ExtraCreditInnerMulti {}
16+
pub fn extracreditlabhomework(
17+
_param: ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>
18+
) { loop {} }
19+
pub fn redherringmatchforextracredit(
20+
_param: ExtraCreditStructMulti<ExtraCreditInnerMulti, ()>
21+
) { loop {} }

0 commit comments

Comments
 (0)