1
Fork 0

rustdoc-search: simplify rules for generics and type params

This commit is a response to feedback on the displayed type
signatures results, by making generics act stricter.

Generics are tightened by making order significant. This means
`Vec<Allocator>` now matches only with a true vector of allocators,
instead of matching the second type param. It also makes unboxing
within generics stricter, so `Result<A, B>` only matches if `B`
is in the error type and `A` is in the success type. The top level
of the function search is unaffected.

Find the discussion on:

* <https://rust-lang.zulipchat.com/#narrow/stream/393423-t-rustdoc.2Fmeetings/topic/meeting.202024-07-08/near/449965149>
* <https://github.com/rust-lang/rust/pull/124544#issuecomment-2204272265>
* <https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/deciding.20on.20semantics.20of.20generics.20in.20rustdoc.20search/near/476841363>
This commit is contained in:
Michael Howell 2024-09-24 18:18:01 -07:00
parent 20a4b4fea1
commit 12dc24f460
40 changed files with 630 additions and 217 deletions

View file

@ -6,7 +6,6 @@ const EXPECTED = [
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' },
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
],
},
{
@ -14,6 +13,19 @@ const EXPECTED = [
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' },
],
},
{
'query': 'cloned<mytrait>, mytrait2 -> T',
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
],
},
{
'query': 'cloned<mytrait<U>>, mytrait2 -> T',
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
],
},
@ -22,7 +34,6 @@ const EXPECTED = [
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::MyTrait', 'name': 'fold' },
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'fold' },
],
},
{
@ -50,14 +61,14 @@ const EXPECTED = [
],
},
{
'query': 'mytrait<U> -> Option<T>',
'query': 'cloned<mytrait<U>> -> Option<T>',
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' },
],
},
{
'query': 'mytrait<Item=U> -> Option<T>',
'query': 'cloned<mytrait<Item=U>> -> Option<T>',
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::Cloned', 'name': 'next' },
@ -89,19 +100,21 @@ const EXPECTED = [
],
},
{
'query': 'myintofuture<myfuture<t>> -> myfuture<t>',
'query': 'myintofuture<t, myfuture<t>> -> myfuture<t>',
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
],
},
// Invalid unboxing of the one-argument case.
// If you unbox one of the myfutures, you need to unbox both of them.
// Unboxings of the one-argument case.
{
'query': 'myintofuture<fut=t> -> myfuture<t>',
'correction': null,
'others': [],
'others': [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future' },
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
],
},
// Unboxings of the two-argument case.
{
@ -119,7 +132,7 @@ const EXPECTED = [
],
},
{
'query': 'myintofuture<myfuture>, myintofuture<myfuture> -> myfuture',
'query': 'myintofuture<t, myfuture>, myintofuture<t, myfuture> -> myfuture',
'correction': null,
'others': [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
@ -132,24 +145,29 @@ const EXPECTED = [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
],
},
// Invalid unboxings of the two-argument case.
// If you unbox one of the myfutures, you need to unbox all of them.
// If you unbox one of the myfutures, you don't need to unbox all of them.
{
'query': 'myintofuture<fut=t>, myintofuture<fut=myfuture<t>> -> myfuture<t>',
'correction': null,
'others': [],
'others': [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
],
},
{
'query': 'myintofuture<fut=myfuture<t>>, myintofuture<fut=t> -> myfuture<t>',
'correction': null,
'others': [],
'others': [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
],
},
{
'query': 'myintofuture<fut=myfuture<t>>, myintofuture<fut=myfuture<t>> -> t',
'correction': null,
'others': [],
'others': [
{ 'path': 'assoc_type_backtrack::MyIntoFuture', 'name': 'into_future_2' },
],
},
// different generics don't match up either
// different generics must match up
{
'query': 'myintofuture<fut=myfuture<u>>, myintofuture<fut=myfuture<t>> -> myfuture<t>',
'correction': null,

View file

@ -1,3 +1,5 @@
#![feature(rustdoc_internals)]
pub trait MyTrait2<X> {
type Output;
}
@ -31,10 +33,12 @@ where
}
}
#[doc(search_unbox)]
pub trait MyFuture {
type Output;
}
#[doc(search_unbox)]
pub trait MyIntoFuture {
type Output;
type Fut: MyFuture<Output = Self::Output>;

View file

@ -13,7 +13,7 @@ const EXPECTED = [
'path': 'assoc_type_unbound::MyIter',
'name': 'next',
'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
'displayMappedNames': 'MyIter::Item = T',
'displayMappedNames': 'T = MyIter::Item',
'displayWhereClause': '',
},
],
@ -26,7 +26,7 @@ const EXPECTED = [
'path': 'assoc_type_unbound::MyIter',
'name': 'next',
'displayType': '&mut `MyIter` -> `Option`<`MyIter::Item`>',
'displayMappedNames': 'MyIter::Item = T',
'displayMappedNames': 'T = MyIter::Item',
'displayWhereClause': '',
},
],

View file

@ -1,12 +1,22 @@
pub fn my_fn<X: Iterator<Item = Something>>(_x: X) -> u32 {
#![feature(rustdoc_internals)]
pub fn my_fn<X: other::Iterator<Item = Something>>(_x: X) -> u32 {
3
}
pub struct Something;
pub mod my {
#[doc(search_unbox)]
pub trait Iterator<T> {}
pub fn other_fn<X: Iterator<crate::Something>>(_: X) -> u32 {
3
}
}
pub mod other {
#[doc(search_unbox)]
pub trait Iterator {
type Item;
}
}

View file

@ -14,7 +14,7 @@ const EXPECTED = [
],
},
{
'query': 'Aaaaaaa -> usize',
'query': 'Aaaaaaa -> Result<usize>',
'others': [
{ 'path': 'generics_impl::Aaaaaaa', 'name': 'read' },
],
@ -23,6 +23,11 @@ const EXPECTED = [
'query': 'Read -> u64',
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
],
},
{
'query': 'Ddddddd<Read> -> u64',
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
],
},
@ -30,7 +35,6 @@ const EXPECTED = [
'query': 'trait:Read -> u64',
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
{ 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
],
},
{

View file

@ -1,4 +1,4 @@
use std::io::{Read, Result as IoResult};
use std::io::{self, Read};
pub struct Aaaaaaa;
@ -12,7 +12,7 @@ impl Aaaaaaa {
}
impl Read for Aaaaaaa {
fn read(&mut self, out: &mut [u8]) -> IoResult<usize> {
fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
Ok(out.len())
}
}

View file

@ -0,0 +1,68 @@
// ignore-order
// exact-check
// Make sure that results are order-agnostic, even when there's search items that only differ
// by generics.
const EXPECTED = [
{
'query': 'Wrap',
'in_args': [
{ 'path': 'generics_match_ambiguity', 'name': 'bar' },
{ 'path': 'generics_match_ambiguity', 'name': 'foo' },
],
},
{
'query': 'Wrap<i32>',
'in_args': [
{ 'path': 'generics_match_ambiguity', 'name': 'bar' },
{ 'path': 'generics_match_ambiguity', 'name': 'foo' },
],
},
{
'query': 'Wrap<i32>, Wrap<i32, u32>',
'others': [
{ 'path': 'generics_match_ambiguity', 'name': 'bar' },
{ 'path': 'generics_match_ambiguity', 'name': 'foo' },
],
},
{
'query': 'Wrap<i32, u32>, Wrap<i32>',
'others': [
{ 'path': 'generics_match_ambiguity', 'name': 'bar' },
{ 'path': 'generics_match_ambiguity', 'name': 'foo' },
],
},
{
'query': 'W3<i32>, W3<i32, u32>',
'others': [
{ 'path': 'generics_match_ambiguity', 'name': 'baaa' },
{ 'path': 'generics_match_ambiguity', 'name': 'baab' },
],
},
{
'query': 'W3<i32, u32>, W3<i32>',
'others': [
{ 'path': 'generics_match_ambiguity', 'name': 'baaa' },
{ 'path': 'generics_match_ambiguity', 'name': 'baab' },
],
},
{
// strict generics matching; W2<i32, u32> doesn't match W2<W3<i32, u32>>,
// even though W2<i32> works just fine (ignoring the W3)
'query': 'W2<i32>, W2<i32, u32>',
'others': [],
},
{
'query': 'W2<i32, u32>, W2<i32>',
'others': [],
},
{
'query': 'W2<i32>, W3<i32, u32>',
'others': [],
},
{
'query': 'W2<i32>, W2<i32>',
'others': [],
},
];

View file

@ -0,0 +1,18 @@
#![crate_name = "generics_match_ambiguity"]
pub struct Wrap<T, U = ()>(pub T, pub U);
pub fn foo(a: Wrap<i32>, b: Wrap<i32, u32>) {}
pub fn bar(a: Wrap<i32, u32>, b: Wrap<i32>) {}
pub struct W2<T>(pub T);
pub struct W3<T, U = ()>(pub T, pub U);
pub fn baaa(a: W3<i32>, b: W3<i32, u32>) {}
pub fn baab(a: W3<i32, u32>, b: W3<i32>) {}
pub fn baac(a: W2<W3<i32>>, b: W3<i32, u32>) {}
pub fn baad(a: W2<W3<i32, u32>>, b: W3<i32>) {}
pub fn baae(a: W3<i32>, b: W2<W3<i32, u32>>) {}
pub fn baaf(a: W3<i32, u32>, b: W2<W3<i32>>) {}
pub fn baag(a: W2<W3<i32>>, b: W2<W3<i32, u32>>) {}
pub fn baah(a: W2<W3<i32, u32>>, b: W2<W3<i32>>) {}

View file

@ -60,18 +60,14 @@ const EXPECTED = [
],
},
{
// strict generics matching; W2<i32, u32> doesn't match W2<W3<i32, u32>>,
// even though W2<i32> works just fine (ignoring the W3)
'query': 'W2<i32>, W2<i32, u32>',
'others': [
{ 'path': 'generics_match_ambiguity', 'name': 'baag' },
{ 'path': 'generics_match_ambiguity', 'name': 'baah' },
],
'others': [],
},
{
'query': 'W2<i32, u32>, W2<i32>',
'others': [
{ 'path': 'generics_match_ambiguity', 'name': 'baag' },
{ 'path': 'generics_match_ambiguity', 'name': 'baah' },
],
'others': [],
},
{
'query': 'W2<i32>, W3<i32, u32>',

View file

@ -1,9 +1,14 @@
#![feature(rustdoc_internals)]
#[doc(search_unbox)]
pub struct Wrap<T, U = ()>(pub T, pub U);
pub fn foo(a: Wrap<i32>, b: Wrap<i32, u32>) {}
pub fn bar(a: Wrap<i32, u32>, b: Wrap<i32>) {}
#[doc(search_unbox)]
pub struct W2<T>(pub T);
#[doc(search_unbox)]
pub struct W3<T, U = ()>(pub T, pub U);
pub fn baaa(a: W3<i32>, b: W3<i32, u32>) {}
@ -14,4 +19,3 @@ pub fn baae(a: W3<i32>, b: W2<W3<i32, u32>>) {}
pub fn baaf(a: W3<i32, u32>, b: W2<W3<i32>>) {}
pub fn baag(a: W2<W3<i32>>, b: W2<W3<i32, u32>>) {}
pub fn baah(a: W2<W3<i32, u32>>, b: W2<W3<i32>>) {}
//

View file

@ -18,9 +18,8 @@ const EXPECTED = [
],
},
{
// can't put generics out of order
'query': '-> Out<Second, First>',
'others': [
{ 'path': 'generics_nested', 'name': 'bet' },
],
'others': [],
},
];

View file

@ -11,20 +11,17 @@ const EXPECTED = [
'query': 'Inside<T> -> Out3<T>',
'others': [
{ 'path': 'generics_unbox', 'name': 'beta' },
{ 'path': 'generics_unbox', 'name': 'gamma' },
],
},
{
'query': 'Inside<T> -> Out4<T>',
'others': [
{ 'path': 'generics_unbox', 'name': 'beta' },
{ 'path': 'generics_unbox', 'name': 'gamma' },
],
},
{
'query': 'Inside<T> -> Out3<U, T>',
'others': [
{ 'path': 'generics_unbox', 'name': 'beta' },
{ 'path': 'generics_unbox', 'name': 'gamma' },
],
},
@ -32,7 +29,6 @@ const EXPECTED = [
'query': 'Inside<T> -> Out4<U, T>',
'others': [
{ 'path': 'generics_unbox', 'name': 'beta' },
{ 'path': 'generics_unbox', 'name': 'gamma' },
],
},
];

View file

@ -1,26 +1,34 @@
#![feature(rustdoc_internals)]
#[doc(search_unbox)]
pub struct Out<A, B = ()> {
a: A,
b: B,
}
#[doc(search_unbox)]
pub struct Out1<A, const N: usize> {
a: [A; N],
}
#[doc(search_unbox)]
pub struct Out2<A, const N: usize> {
a: [A; N],
}
#[doc(search_unbox)]
pub struct Out3<A, B> {
a: A,
b: B,
}
#[doc(search_unbox)]
pub struct Out4<A, B> {
a: A,
b: B,
}
#[doc(search_unbox)]
pub struct Inside<T>(T);
pub fn alpha<const N: usize, T>(_: Inside<T>) -> Out<Out1<T, N>, Out2<T, N>> {

View file

@ -30,21 +30,13 @@ const EXPECTED = [
'others': [
{ 'path': 'generics', 'name': 'P' },
],
'returned': [
{ 'path': 'generics', 'name': 'alef' },
],
'in_args': [
{ 'path': 'generics', 'name': 'alpha' },
],
'returned': [],
'in_args': [],
},
{
'query': 'P',
'returned': [
{ 'path': 'generics', 'name': 'alef' },
],
'in_args': [
{ 'path': 'generics', 'name': 'alpha' },
],
'returned': [],
'in_args': [],
},
{
'query': '"ExtraCreditStructMulti"<ExtraCreditInnerMulti, ExtraCreditInnerMulti>',

View file

@ -9,19 +9,19 @@ const EXPECTED = [
// ML-style higher-order function notation
{
'query': 'bool, (u32 -> !) -> ()',
'query': 'bool, (first<u32> -> !) -> ()',
'others': [
{"path": "hof", "name": "fn_ptr"},
],
},
{
'query': 'u8, (u32 -> !) -> ()',
'query': 'u8, (second<u32> -> !) -> ()',
'others': [
{"path": "hof", "name": "fn_once"},
],
},
{
'query': 'i8, (u32 -> !) -> ()',
'query': 'i8, (third<u32> -> !) -> ()',
'others': [
{"path": "hof", "name": "fn_mut"},
],
@ -54,9 +54,6 @@ const EXPECTED = [
'query': '(u32 -> !) -> ()',
'others': [
{"path": "hof", "name": "fn_"},
{"path": "hof", "name": "fn_ptr"},
{"path": "hof", "name": "fn_mut"},
{"path": "hof", "name": "fn_once"},
],
},
{
@ -95,30 +92,30 @@ const EXPECTED = [
// Rust-style higher-order function notation
{
'query': 'bool, fn(u32) -> ! -> ()',
'query': 'bool, fn(first<u32>) -> ! -> ()',
'others': [
{"path": "hof", "name": "fn_ptr"},
],
},
{
'query': 'u8, fnonce(u32) -> ! -> ()',
'query': 'u8, fnonce(second<u32>) -> ! -> ()',
'others': [
{"path": "hof", "name": "fn_once"},
],
},
{
'query': 'u8, fn(u32) -> ! -> ()',
'query': 'u8, fn(second<u32>) -> ! -> ()',
// fnonce != fn
'others': [],
},
{
'query': 'i8, fnmut(u32) -> ! -> ()',
'query': 'i8, fnmut(third<u32>) -> ! -> ()',
'others': [
{"path": "hof", "name": "fn_mut"},
],
},
{
'query': 'i8, fn(u32) -> ! -> ()',
'query': 'i8, fn(third<u32>) -> ! -> ()',
// fnmut != fn
'others': [],
},
@ -152,7 +149,7 @@ const EXPECTED = [
],
},
{
'query': 'fn(u32) -> ! -> ()',
'query': 'fn() -> ! -> ()',
'others': [
// fn matches primitive:fn and trait:Fn
{"path": "hof", "name": "fn_"},
@ -160,14 +157,14 @@ const EXPECTED = [
],
},
{
'query': 'trait:fn(u32) -> ! -> ()',
'query': 'trait:fn() -> ! -> ()',
'others': [
// fn matches primitive:fn and trait:Fn
{"path": "hof", "name": "fn_"},
],
},
{
'query': 'primitive:fn(u32) -> ! -> ()',
'query': 'primitive:fn() -> ! -> ()',
'others': [
// fn matches primitive:fn and trait:Fn
{"path": "hof", "name": "fn_ptr"},

View file

@ -1,9 +1,15 @@
// https://github.com/rust-lang/rust/pull/122247
// exact-check
const EXPECTED = {
'query': 'canonicalvarinfo, intoiterator -> intoiterator',
'others': [
{ 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' },
],
};
const EXPECTED = [
{
'query': 'canonicalvarinfo, intoiterator -> intoiterator',
'others': [],
},
{
'query': '[canonicalvarinfo], interner<tys=intoiterator> -> intoiterator',
'others': [
{ 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' },
],
},
];

View file

@ -33,9 +33,8 @@ const EXPECTED = [
},
{
'query': '-> Result<i32, u32, bool>',
'others': [
{ 'path': 'nested_unboxed', 'name': 'something' },
],
// can't put nested generics out of order
'others': [],
},
{
'query': '-> Result<Object<i32>, bool>',
@ -45,9 +44,7 @@ const EXPECTED = [
},
{
'query': '-> Result<Object<u32>, bool>',
'others': [
{ 'path': 'nested_unboxed', 'name': 'something' },
],
'others': [],
},
{
'query': '-> Result<Object<i32>, u32, bool>',

View file

@ -1,3 +1,6 @@
#![feature(rustdoc_internals)]
#[doc(search_unbox)]
pub struct Object<T, U>(T, U);
pub fn something() -> Result<Object<i32, u32>, bool> {

View file

@ -79,9 +79,8 @@ const EXPECTED = [
},
{
'query': 'reference<ring>, reference<ring> -> ()',
'others': [
{ 'path': 'reference::Ring', 'name': 'wear' },
],
// can't leave out the `mut`, because can't reorder like that
'others': [],
},
{
'query': 'reference<mut, ring>, reference<ring> -> ()',
@ -102,9 +101,8 @@ const EXPECTED = [
},
{
'query': 'reference<middle>, reference<middle> -> ()',
'others': [
{ 'path': 'reference', 'name': 'show' },
],
// can't leave out the mut
'others': [],
},
{
'query': 'reference<mut, middle>, reference<mut, middle> -> ()',
@ -203,9 +201,8 @@ const EXPECTED = [
// middle with shorthand
{
'query': '&middle, &middle -> ()',
'others': [
{ 'path': 'reference', 'name': 'show' },
],
// can't leave out the mut
'others': [],
},
{
'query': '&mut middle, &mut middle -> ()',

View file

@ -57,7 +57,7 @@ const EXPECTED = [
'in_args': [],
},
{
'query': '(Q, ())',
'query': '(Q, R<()>)',
'returned': [
{ 'path': 'tuple_unit', 'name': 'nest' },
],
@ -71,7 +71,7 @@ const EXPECTED = [
'in_args': [],
},
{
'query': '(u32)',
'query': 'R<(u32)>',
'returned': [
{ 'path': 'tuple_unit', 'name': 'nest' },
],