Skip to content

Commit 3312a30

Browse files
committed
Auto merge of #109802 - notriddle:notriddle/rustdoc-search-generics-nested, r=GuillaumeGomez
rustdoc-search: add support for nested generics This change allows `search.js` to parse nested generics (which look `Like<This<Example>>`) and match them. It maintains the existing "bag semantics", so that the order of type parameters is ignored but the number is required to be greater than or equal to what's in the query. For example, a function with the signature `fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>` will match these queries: * `Read -> Result<Vec<u8>, Error>` * `Read -> Result<Error, Vec>` * `Read -> Result<Vec<u8>>` But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
2 parents fef27e0 + 6ce5327 commit 3312a30

File tree

5 files changed

+195
-13
lines changed

5 files changed

+195
-13
lines changed

src/doc/rustdoc/src/how-to-read-rustdoc.md

+11
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ can be matched with the following queries:
9494
* `trait:Iterator<primitive:u32> -> primitive:usize`
9595
* `Iterator -> usize`
9696

97+
Generics and function parameters are order-agnostic, but sensitive to nesting
98+
and number of matches. For example, a function with the signature
99+
`fn read_all(&mut self: impl Read) -> Result<Vec<u8>, Error>`
100+
will match these queries:
101+
102+
* `Read -> Result<Vec<u8>, Error>`
103+
* `Read -> Result<Error, Vec>`
104+
* `Read -> Result<Vec<u8>>`
105+
106+
But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
107+
97108
### Changing displayed theme
98109

99110
You can change the displayed theme by opening the settings menu (the gear

src/librustdoc/html/static/js/search.js

+10-11
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,7 @@ function initSearch(rawSearchIndex) {
461461
if (parserState.pos < parserState.length &&
462462
parserState.userQuery[parserState.pos] === "<"
463463
) {
464-
if (isInGenerics) {
465-
throw ["Unexpected ", "<", " after ", "<"];
466-
} else if (start >= end) {
464+
if (start >= end) {
467465
throw ["Found generics without a path"];
468466
}
469467
parserState.pos += 1;
@@ -765,13 +763,10 @@ function initSearch(rawSearchIndex) {
765763
* ident = *(ALPHA / DIGIT / "_")
766764
* path = ident *(DOUBLE-COLON ident) [!]
767765
* arg = [type-filter *WS COLON *WS] path [generics]
768-
* arg-without-generic = [type-filter *WS COLON *WS] path
769766
* type-sep = COMMA/WS *(COMMA/WS)
770767
* nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
771-
* nonempty-arg-list-without-generics = *(type-sep) arg-without-generic
772-
* *(type-sep arg-without-generic) *(type-sep)
773-
* generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list-without-generics ] *(type-sep)
774-
* CLOSE-ANGLE-BRACKET/EOF
768+
* generics = OPEN-ANGLE-BRACKET [ nonempty-arg-list ] *(type-sep)
769+
* CLOSE-ANGLE-BRACKET
775770
* return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
776771
*
777772
* exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
@@ -1127,7 +1122,7 @@ function initSearch(rawSearchIndex) {
11271122
currentEntryElems = [];
11281123
elems.set(entry.name, currentEntryElems);
11291124
}
1130-
currentEntryElems.push(entry.ty);
1125+
currentEntryElems.push(entry);
11311126
}
11321127
// We need to find the type that matches the most to remove it in order
11331128
// to move forward.
@@ -1136,8 +1131,12 @@ function initSearch(rawSearchIndex) {
11361131
return false;
11371132
}
11381133
const matchElems = elems.get(generic.name);
1139-
const matchIdx = matchElems.findIndex(tmp_elem =>
1140-
typePassesFilter(generic.typeFilter, tmp_elem));
1134+
const matchIdx = matchElems.findIndex(tmp_elem => {
1135+
if (checkGenerics(tmp_elem, generic, 0, maxEditDistance) !== 0) {
1136+
return false;
1137+
}
1138+
return typePassesFilter(generic.typeFilter, tmp_elem.ty);
1139+
});
11411140
if (matchIdx === -1) {
11421141
return false;
11431142
}

tests/rustdoc-js-std/parser-generics.js

+122-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
const QUERY = ['A<B<C<D>, E>', 'p<> u8', '"p"<a>'];
1+
const QUERY = [
2+
'A<B<C<D>, E>',
3+
'p<> u8',
4+
'"p"<a>',
5+
'p<u<x>>',
6+
'p<u<x>, r>',
7+
'p<u<x, r>>',
8+
];
29

310
const PARSED = [
411
{
@@ -7,7 +14,7 @@ const PARSED = [
714
original: 'A<B<C<D>, E>',
815
returned: [],
916
userQuery: 'a<b<c<d>, e>',
10-
error: 'Unexpected `<` after `<`',
17+
error: 'Unclosed `<`',
1118
},
1219
{
1320
elems: [
@@ -59,4 +66,117 @@ const PARSED = [
5966
userQuery: '"p"<a>',
6067
error: null,
6168
},
69+
{
70+
elems: [
71+
{
72+
name: "p",
73+
fullPath: ["p"],
74+
pathWithoutLast: [],
75+
pathLast: "p",
76+
generics: [
77+
{
78+
name: "u",
79+
fullPath: ["u"],
80+
pathWithoutLast: [],
81+
pathLast: "u",
82+
generics: [
83+
{
84+
name: "x",
85+
fullPath: ["x"],
86+
pathWithoutLast: [],
87+
pathLast: "x",
88+
generics: [],
89+
},
90+
],
91+
},
92+
],
93+
typeFilter: -1,
94+
},
95+
],
96+
foundElems: 1,
97+
original: 'p<u<x>>',
98+
returned: [],
99+
userQuery: 'p<u<x>>',
100+
error: null,
101+
},
102+
{
103+
elems: [
104+
{
105+
name: "p",
106+
fullPath: ["p"],
107+
pathWithoutLast: [],
108+
pathLast: "p",
109+
generics: [
110+
{
111+
name: "u",
112+
fullPath: ["u"],
113+
pathWithoutLast: [],
114+
pathLast: "u",
115+
generics: [
116+
{
117+
name: "x",
118+
fullPath: ["x"],
119+
pathWithoutLast: [],
120+
pathLast: "x",
121+
generics: [],
122+
},
123+
],
124+
},
125+
{
126+
name: "r",
127+
fullPath: ["r"],
128+
pathWithoutLast: [],
129+
pathLast: "r",
130+
generics: [],
131+
},
132+
],
133+
typeFilter: -1,
134+
},
135+
],
136+
foundElems: 1,
137+
original: 'p<u<x>, r>',
138+
returned: [],
139+
userQuery: 'p<u<x>, r>',
140+
error: null,
141+
},
142+
{
143+
elems: [
144+
{
145+
name: "p",
146+
fullPath: ["p"],
147+
pathWithoutLast: [],
148+
pathLast: "p",
149+
generics: [
150+
{
151+
name: "u",
152+
fullPath: ["u"],
153+
pathWithoutLast: [],
154+
pathLast: "u",
155+
generics: [
156+
{
157+
name: "x",
158+
fullPath: ["x"],
159+
pathWithoutLast: [],
160+
pathLast: "x",
161+
generics: [],
162+
},
163+
{
164+
name: "r",
165+
fullPath: ["r"],
166+
pathWithoutLast: [],
167+
pathLast: "r",
168+
generics: [],
169+
},
170+
],
171+
},
172+
],
173+
typeFilter: -1,
174+
},
175+
],
176+
foundElems: 1,
177+
original: 'p<u<x, r>>',
178+
returned: [],
179+
userQuery: 'p<u<x, r>>',
180+
error: null,
181+
},
62182
];

tests/rustdoc-js/generics-nested.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// exact-check
2+
3+
const QUERY = [
4+
'-> Out<First<Second>>',
5+
'-> Out<Second<First>>',
6+
'-> Out<First, Second>',
7+
'-> Out<Second, First>',
8+
];
9+
10+
const EXPECTED = [
11+
{
12+
// -> Out<First<Second>>
13+
'others': [
14+
{ 'path': 'generics_nested', 'name': 'alef' },
15+
],
16+
},
17+
{
18+
// -> Out<Second<First>>
19+
'others': [],
20+
},
21+
{
22+
// -> Out<First, Second>
23+
'others': [
24+
{ 'path': 'generics_nested', 'name': 'bet' },
25+
],
26+
},
27+
{
28+
// -> Out<Second, First>
29+
'others': [
30+
{ 'path': 'generics_nested', 'name': 'bet' },
31+
],
32+
},
33+
];

tests/rustdoc-js/generics-nested.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pub struct Out<A, B = ()> {
2+
a: A,
3+
b: B,
4+
}
5+
6+
pub struct First<In = ()> {
7+
in_: In,
8+
}
9+
10+
pub struct Second;
11+
12+
// Out<First<Second>>
13+
pub fn alef() -> Out<First<Second>> {
14+
loop {}
15+
}
16+
17+
pub fn bet() -> Out<First, Second> {
18+
loop {}
19+
}

0 commit comments

Comments
 (0)