Skip to content

Commit 7a6e6f8

Browse files
authored
Unrolled build for rust-lang#119676
Rollup merge of rust-lang#119676 - notriddle:notriddle/rustdoc-search-hof, r=GuillaumeGomez rustdoc-search: search types by higher-order functions This feature extends rustdoc with syntax and search index information for searching function pointers and closures (Higher-Order Functions, or HOF). Part of rust-lang#60485 This PR adds two syntaxes: a high-level one for finding any kind of HOF, and a direct implementation of the parenthesized path syntax that Rust itself uses. ## Preview pages | Query | Results | |-------|---------| | [`option<T>, (fnonce (T) -> bool) -> option<T>`][optionfilter] | `Option::filter` | | [`option<T>, (T -> bool) -> option<T>`][optionfilter2] | `Option::filter` | Updated chapter of the book: https://notriddle.com/rustdoc-html-demo-9/search-hof/rustdoc/read-documentation/search.html [optionfilter]: https://notriddle.com/rustdoc-html-demo-9/search-hof/std/vec/struct.Vec.html?search=option<T>%2C+(fnonce+(T)+->+bool)+->+option<T>&filter-crate=std [optionfilter2]: https://notriddle.com/rustdoc-html-demo-9/search-hof/std/vec/struct.Vec.html?search=option<T>%2C+(T+->+bool)+->+option<T>&filter-crate=std ## Motivation When type-based search was first landed, it was directly [described as incomplete][a comment]. [a comment]: rust-lang#23289 (comment) Filling out the missing functionality is going to mean adding support for more of Rust's [type expression] syntax, such as references, raw pointers, function pointers, and closures. This PR adds function pointers and closures. [type expression]: https://doc.rust-lang.org/reference/types.html#type-expressions There's been demand for something "like Hoogle, but for Rust" expressed a few times [1](https://www.reddit.com/r/rust/comments/y8sbid/is_there_a_website_like_haskells_hoogle_for_rust/) [2](https://users.rust-lang.org/t/rust-equivalent-of-haskells-hoogle/102280) [3](https://internals.rust-lang.org/t/std-library-inclusion-policy/6852/2) [4](https://discord.com/channels/442252698964721669/448238009733742612/1109502307495858216). Some of them just don't realize what functionality already exists ([`Duration -> u64`](https://doc.rust-lang.org/nightly/std/?search=duration%20-%3E%20u64) already works), but a lot of them specifically want to search for higher-order functions like option combinators. ## Guide-level explanation (from the Rustdoc book) To search for a function that accepts a function as a parameter, like `Iterator::all`, wrap the nested signature in parenthesis, as in [`Iterator<T>, (T -> bool) -> bool`][iterator-all]. You can also search for a specific closure trait, such as `Iterator<T>, (FnMut(T) -> bool) -> bool`, but you need to know which one you want. [iterator-all]: https://notriddle.com/rustdoc-html-demo-9/search-hof/std/vec/struct.Vec.html?search=Iterator<T>%2C+(T+->+bool)+->+bool&filter-crate=std ## Reference-level description (also from the Rustdoc book) ### Primitives with Special Syntax <table> <thead> <tr> <th>Shorthand</th> <th>Explicit names</th> </tr> </thead> <tbody> <tr><td colspan="2">Before this PR</td></tr> <tr> <td><code>[]</code></td> <td><code>primitive:slice</code> and/or <code>primitive:array</code></td> </tr> <tr> <td><code>[T]</code></td> <td><code>primitive:slice&lt;T&gt;</code> and/or <code>primitive:array&lt;T&gt;</code></td> </tr> <tr> <td><code>!</code></td> <td><code>primitive:never</code></td> </tr> <tr> <td><code>()</code></td> <td><code>primitive:unit</code> and/or <code>primitive:tuple</code></td> </tr> <tr> <td><code>(T)</code></td> <td><code>T</code></td> </tr> <tr> <td><code>(T,)</code></td> <td><code>primitive:tuple&lt;T&gt;</code></td> </tr> <tr><td colspan="2">After this PR</td></tr> <tr> <td><code>(T, U -> V, W)</code></td> <td><code>fn(T, U) -> (V, W)</code>, Fn, FnMut, and FnOnce</td> </tr> </tbody> </table> The `->` operator has lower precedence than comma. If it's not wrapped in brackets, it delimits the return value for the function being searched for. To search for functions that take functions as parameters, use parenthesis. ### Search query grammar ```ebnf ident = *(ALPHA / DIGIT / "_") path = ident *(DOUBLE-COLON ident) [BANG] slice-like = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET tuple-like = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like) type-sep = COMMA/WS *(COMMA/WS) nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) [ return-args ] generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep) normal-generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep) CLOSE-ANGLE-BRACKET fn-like-generics = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN [ RETURN-ARROW arg ] generics = normal-generics / fn-like-generics return-args = RETURN-ARROW *(type-sep) nonempty-arg-list exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ] type-search = [ nonempty-arg-list ] query = *WS (exact-search / type-search) *WS ; unchanged parts of the grammar, like the full list of type filters, are omitted ``` ## Future direction ### The remaining type expression grammar As described in rust-lang#118194, this is another step in the type expression grammar: BareFunction, and the function-like mode of TypePath, are now supported. * RawPointerType and ReferenceType actually are a priority. * ImplTraitType and TraitObjectType (and ImplTraitTypeOneBound and TraitObjectTypeOneBound) aren't as much of a priority, since they desugar pretty easily. ### Search subtyping and traits This is the other major factor that makes it less useful than it should be. * `iterator<result<t>> -> result<t>` doesn't find `Result::from_iter`. You have to search [`intoiterator<result<t>> -> result<t>`](https://notriddle.com/rustdoc-html-demo-9/search-hof/std/vec/struct.Vec.html?search=intoiterator%3Cresult%3Ct%3E%3E%20-%3E%20result%3Ct%3E&filter-crate=std). Nobody's going to search for IntoIterator unless they basically already know about it and don't need the search engine anyway. * Iterator combinators are usually structs that happen to implement Iterator, like `std::iter::Map`. To solve these cases, it needs to look at trait implementations, knowing that Iterator is a "subtype of" IntoIterator, and Map is a "subtype of" Iterator, so `iterator -> result` is a subtype of `intoiterator -> result` and `iterator<t>, (t -> u) -> iterator<u>` is a subtype of [`iterator<t>, (t -> u) -> map<t -> u>`](https://notriddle.com/rustdoc-html-demo-9/search-hof/std/vec/struct.Vec.html?search=iterator%3Ct%3E%2C%20(t%20-%3E%20u)%20-%3E%20map%3Ct%20-%3E%20u%3E&filter-crate=std).
2 parents e69f14b + 7b92655 commit 7a6e6f8

File tree

8 files changed

+1178
-148
lines changed

8 files changed

+1178
-148
lines changed

src/doc/rustdoc/src/read-documentation/search.md

+35-19
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ Before describing the syntax in more detail, here's a few sample searches of
6363
the standard library and functions that are included in the results list:
6464

6565
| Query | Results |
66-
|-------|--------|
66+
|-------|---------|
6767
| [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` |
6868
| [`vec, vec -> bool`][] | `Vec::eq` |
6969
| [`option<T>, fnonce -> option<U>`][] | `Option::map` and `Option::and_then` |
70-
| [`option<T>, fnonce -> option<T>`][] | `Option::filter` and `Option::inspect` |
70+
| [`option<T>, (fnonce (T) -> bool) -> option<T>`][optionfilter] | `Option::filter` |
71+
| [`option<T>, (T -> bool) -> option<T>`][optionfilter2] | `Option::filter` |
7172
| [`option -> default`][] | `Option::unwrap_or_default` |
7273
| [`stdout, [u8]`][stdoutu8] | `Stdout::write` |
7374
| [`any -> !`][] | `panic::panic_any` |
@@ -77,7 +78,8 @@ the standard library and functions that are included in the results list:
7778
[`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std
7879
[`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std
7980
[`option<T>, fnonce -> option<U>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<U>&filter-crate=std
80-
[`option<T>, fnonce -> option<T>`]: ../../std/vec/struct.Vec.html?search=option<T>%2C%20fnonce%20->%20option<T>&filter-crate=std
81+
[optionfilter]: ../../std/vec/struct.Vec.html?search=option<T>%2C+(fnonce+(T)+->+bool)+->+option<T>&filter-crate=std
82+
[optionfilter2]: ../../std/vec/struct.Vec.html?search=option<T>%2C+(T+->+bool)+->+option<T>&filter-crate=std
8183
[`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std
8284
[`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std
8385
[stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std
@@ -151,16 +153,26 @@ will match these queries:
151153

152154
But it *does not* match `Result<Vec, u8>` or `Result<u8<Vec>>`.
153155

156+
To search for a function that accepts a function as a parameter,
157+
like `Iterator::all`, wrap the nested signature in parenthesis,
158+
as in [`Iterator<T>, (T -> bool) -> bool`][iterator-all].
159+
You can also search for a specific closure trait,
160+
such as `Iterator<T>, (FnMut(T) -> bool) -> bool`,
161+
but you need to know which one you want.
162+
163+
[iterator-all]: ../../std/vec/struct.Vec.html?search=Iterator<T>%2C+(T+->+bool)+->+bool&filter-crate=std
164+
154165
### Primitives with Special Syntax
155166

156-
| Shorthand | Explicit names |
157-
| --------- | ------------------------------------------------ |
158-
| `[]` | `primitive:slice` and/or `primitive:array` |
159-
| `[T]` | `primitive:slice<T>` and/or `primitive:array<T>` |
160-
| `()` | `primitive:unit` and/or `primitive:tuple` |
161-
| `(T)` | `T` |
162-
| `(T,)` | `primitive:tuple<T>` |
163-
| `!` | `primitive:never` |
167+
| Shorthand | Explicit names |
168+
| ---------------- | ------------------------------------------------- |
169+
| `[]` | `primitive:slice` and/or `primitive:array` |
170+
| `[T]` | `primitive:slice<T>` and/or `primitive:array<T>` |
171+
| `()` | `primitive:unit` and/or `primitive:tuple` |
172+
| `(T)` | `T` |
173+
| `(T,)` | `primitive:tuple<T>` |
174+
| `!` | `primitive:never` |
175+
| `(T, U -> V, W)` | `fn(T, U) -> (V, W)`, `Fn`, `FnMut`, and `FnOnce` |
164176

165177
When searching for `[]`, Rustdoc will return search results with either slices
166178
or arrays. If you know which one you want, you can force it to return results
@@ -180,6 +192,10 @@ results for types that match tuples, even though it also matches the type on
180192
its own. That is, `(u32)` matches `(u32,)` for the exact same reason that it
181193
also matches `Result<u32, Error>`.
182194

195+
The `->` operator has lower precedence than comma. If it's not wrapped
196+
in brackets, it delimits the return value for the function being searched for.
197+
To search for functions that take functions as parameters, use parenthesis.
198+
183199
### Limitations and quirks of type-based search
184200

185201
Type-based search is still a buggy, experimental, work-in-progress feature.
@@ -218,9 +234,6 @@ Most of these limitations should be addressed in future version of Rustdoc.
218234

219235
* Searching for lifetimes is not supported.
220236

221-
* It's impossible to search for closures based on their parameters or
222-
return values.
223-
224237
* It's impossible to search based on the length of an array.
225238

226239
## Item filtering
@@ -237,19 +250,21 @@ Item filters can be used in both name-based and type signature-based searches.
237250

238251
```text
239252
ident = *(ALPHA / DIGIT / "_")
240-
path = ident *(DOUBLE-COLON ident) [!]
253+
path = ident *(DOUBLE-COLON ident) [BANG]
241254
slice-like = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET
242255
tuple-like = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN
243-
arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like / [!])
256+
arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like)
244257
type-sep = COMMA/WS *(COMMA/WS)
245-
nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep)
258+
nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) [ return-args ]
246259
generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep)
247-
generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep)
260+
normal-generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep)
248261
CLOSE-ANGLE-BRACKET
262+
fn-like-generics = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN [ RETURN-ARROW arg ]
263+
generics = normal-generics / fn-like-generics
249264
return-args = RETURN-ARROW *(type-sep) nonempty-arg-list
250265
251266
exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ]
252-
type-search = [ nonempty-arg-list ] [ return-args ]
267+
type-search = [ nonempty-arg-list ]
253268
254269
query = *WS (exact-search / type-search) *WS
255270
@@ -294,6 +309,7 @@ QUOTE = %x22
294309
COMMA = ","
295310
RETURN-ARROW = "->"
296311
EQUAL = "="
312+
BANG = "!"
297313
298314
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
299315
DIGIT = %x30-39

src/librustdoc/html/render/search_index.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::collections::{BTreeMap, VecDeque};
44
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
55
use rustc_middle::ty::TyCtxt;
66
use rustc_span::def_id::DefId;
7+
use rustc_span::sym;
78
use rustc_span::symbol::Symbol;
89
use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
910
use thin_vec::ThinVec;
@@ -566,6 +567,7 @@ fn get_index_type_id(
566567
// The type parameters are converted to generics in `simplify_fn_type`
567568
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
568569
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
570+
clean::BareFunction(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Fn)),
569571
clean::Tuple(ref n) if n.is_empty() => {
570572
Some(RenderTypeId::Primitive(clean::PrimitiveType::Unit))
571573
}
@@ -584,7 +586,7 @@ fn get_index_type_id(
584586
}
585587
}
586588
// Not supported yet
587-
clean::BareFunction(_) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None,
589+
clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None,
588590
}
589591
}
590592

@@ -785,6 +787,42 @@ fn simplify_fn_type<'tcx, 'a>(
785787
);
786788
}
787789
res.push(get_index_type(arg, ty_generics, rgen));
790+
} else if let Type::BareFunction(ref bf) = *arg {
791+
let mut ty_generics = Vec::new();
792+
for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) {
793+
simplify_fn_type(
794+
self_,
795+
generics,
796+
ty,
797+
tcx,
798+
recurse + 1,
799+
&mut ty_generics,
800+
rgen,
801+
is_return,
802+
cache,
803+
);
804+
}
805+
// The search index, for simplicity's sake, represents fn pointers and closures
806+
// the same way: as a tuple for the parameters, and an associated type for the
807+
// return type.
808+
let mut ty_output = Vec::new();
809+
simplify_fn_type(
810+
self_,
811+
generics,
812+
&bf.decl.output,
813+
tcx,
814+
recurse + 1,
815+
&mut ty_output,
816+
rgen,
817+
is_return,
818+
cache,
819+
);
820+
let ty_bindings = vec![(RenderTypeId::AssociatedType(sym::Output), ty_output)];
821+
res.push(RenderType {
822+
id: get_index_type_id(&arg, rgen),
823+
bindings: Some(ty_bindings),
824+
generics: Some(ty_generics),
825+
});
788826
} else {
789827
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
790828
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.

0 commit comments

Comments
 (0)