rustdoc: include impl generics / self in search index

This commit is contained in:
Michael Howell 2022-05-02 14:51:45 -07:00
parent 9fed13030c
commit b9ed8784b3
5 changed files with 204 additions and 17 deletions

View File

@ -1410,6 +1410,12 @@ pub(crate) struct Generics {
pub(crate) where_predicates: Vec<WherePredicate>, pub(crate) where_predicates: Vec<WherePredicate>,
} }
impl Generics {
pub(crate) fn is_empty(&self) -> bool {
self.params.is_empty() && self.where_predicates.is_empty()
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct Function { pub(crate) struct Function {
pub(crate) decl: FnDecl, pub(crate) decl: FnDecl,

View File

@ -95,7 +95,9 @@ pub(crate) struct Cache {
// Private fields only used when initially crawling a crate to build a cache // Private fields only used when initially crawling a crate to build a cache
stack: Vec<Symbol>, stack: Vec<Symbol>,
parent_stack: Vec<DefId>, parent_stack: Vec<DefId>,
impl_generics_stack: Vec<(clean::Type, clean::Generics)>,
parent_is_trait_impl: bool, parent_is_trait_impl: bool,
parent_is_blanket_or_auto_impl: bool,
stripped_mod: bool, stripped_mod: bool,
pub(crate) search_index: Vec<IndexItem>, pub(crate) search_index: Vec<IndexItem>,
@ -105,7 +107,8 @@ pub(crate) struct Cache {
// then the fully qualified name of the structure isn't presented in `paths` // then the fully qualified name of the structure isn't presented in `paths`
// yet when its implementation methods are being indexed. Caches such methods // yet when its implementation methods are being indexed. Caches such methods
// and their parent id here and indexes them at the end of crate parsing. // and their parent id here and indexes them at the end of crate parsing.
pub(crate) orphan_impl_items: Vec<(DefId, clean::Item)>, pub(crate) orphan_impl_items:
Vec<(DefId, clean::Item, Option<(clean::Type, clean::Generics)>, bool)>,
// Similarly to `orphan_impl_items`, sometimes trait impls are picked up // Similarly to `orphan_impl_items`, sometimes trait impls are picked up
// even though the trait itself is not exported. This can happen if a trait // even though the trait itself is not exported. This can happen if a trait
@ -315,7 +318,13 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
desc, desc,
parent, parent,
parent_idx: None, parent_idx: None,
search_type: get_function_type_for_search(&item, self.tcx, self.cache), search_type: get_function_type_for_search(
&item,
self.tcx,
self.cache.impl_generics_stack.last(),
self.cache.parent_is_blanket_or_auto_impl,
self.cache,
),
aliases: item.attrs.get_doc_aliases(), aliases: item.attrs.get_doc_aliases(),
}); });
} }
@ -323,7 +332,12 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
(Some(parent), None) if is_inherent_impl_item => { (Some(parent), None) if is_inherent_impl_item => {
// We have a parent, but we don't know where they're // We have a parent, but we don't know where they're
// defined yet. Wait for later to index this item. // defined yet. Wait for later to index this item.
self.cache.orphan_impl_items.push((parent, item.clone())); self.cache.orphan_impl_items.push((
parent,
item.clone(),
self.cache.impl_generics_stack.last().cloned(),
self.cache.parent_is_blanket_or_auto_impl,
));
} }
_ => {} _ => {}
} }
@ -440,9 +454,34 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
_ => false, _ => false,
}; };
// When recursing into an impl item, make the generics context visible
// to the child items.
let item = {
let mut item = item;
let mut old_parent_is_blanket_or_auto_impl = false;
if let clean::Item { kind: box clean::ImplItem(ref mut i), .. } = item {
old_parent_is_blanket_or_auto_impl = mem::replace(
&mut self.cache.parent_is_blanket_or_auto_impl,
!matches!(i.kind, clean::ImplKind::Normal),
);
self.cache.impl_generics_stack.push((
mem::replace(&mut i.for_, clean::Type::Infer),
mem::replace(
&mut i.generics,
clean::Generics { params: Vec::new(), where_predicates: Vec::new() },
),
));
}
let mut item = self.fold_item_recur(item);
if let clean::Item { kind: box clean::ImplItem(ref mut i), .. } = item {
self.cache.parent_is_blanket_or_auto_impl = old_parent_is_blanket_or_auto_impl;
(i.for_, i.generics) = self.cache.impl_generics_stack.pop().expect("pushed above");
}
item
};
// Once we've recursively found all the generics, hoard off all the // Once we've recursively found all the generics, hoard off all the
// implementations elsewhere. // implementations elsewhere.
let item = self.fold_item_recur(item);
let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item { let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item {
// Figure out the id of this impl. This may map to a // Figure out the id of this impl. This may map to a
// primitive rather than always to a struct/enum. // primitive rather than always to a struct/enum.

View File

@ -25,7 +25,7 @@ pub(crate) fn build_index<'tcx>(
// Attach all orphan items to the type's definition if the type // Attach all orphan items to the type's definition if the type
// has since been learned. // has since been learned.
for &(did, ref item) in &cache.orphan_impl_items { for &(did, ref item, ref impl_generics, from_blanket_or_auto_impl) in &cache.orphan_impl_items {
if let Some(&(ref fqp, _)) = cache.paths.get(&did) { if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
let desc = item let desc = item
.doc_value() .doc_value()
@ -37,7 +37,13 @@ pub(crate) fn build_index<'tcx>(
desc, desc,
parent: Some(did), parent: Some(did),
parent_idx: None, parent_idx: None,
search_type: get_function_type_for_search(item, tcx, cache), search_type: get_function_type_for_search(
item,
tcx,
impl_generics.as_ref(),
from_blanket_or_auto_impl,
cache,
),
aliases: item.attrs.get_doc_aliases(), aliases: item.attrs.get_doc_aliases(),
}); });
} }
@ -192,12 +198,18 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
pub(crate) fn get_function_type_for_search<'tcx>( pub(crate) fn get_function_type_for_search<'tcx>(
item: &clean::Item, item: &clean::Item,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
from_blanket_or_auto_impl: bool,
cache: &Cache, cache: &Cache,
) -> Option<IndexItemFunctionType> { ) -> Option<IndexItemFunctionType> {
if from_blanket_or_auto_impl {
return None;
}
let (mut inputs, mut output) = match *item.kind { let (mut inputs, mut output) = match *item.kind {
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, cache), clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, cache), clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, cache), clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
_ => return None, _ => return None,
}; };
@ -247,9 +259,10 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option<Symbol> {
/// Important note: It goes through generics recursively. So if you have /// Important note: It goes through generics recursively. So if you have
/// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`. /// `T: Option<Result<(), ()>>`, it'll go into `Option` and then into `Result`.
#[instrument(level = "trace", skip(tcx, res, cache))] #[instrument(level = "trace", skip(tcx, res, cache))]
fn add_generics_and_bounds_as_types<'tcx>( fn add_generics_and_bounds_as_types<'tcx, 'a>(
self_: Option<&'a Type>,
generics: &Generics, generics: &Generics,
arg: &Type, arg: &'a Type,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
recurse: usize, recurse: usize,
res: &mut Vec<TypeWithKind>, res: &mut Vec<TypeWithKind>,
@ -334,6 +347,17 @@ fn insert_ty(
return; return;
} }
// First, check if it's "Self".
let arg = if let Some(self_) = self_ {
match &*arg {
Type::BorrowedRef { type_, .. } if type_.is_self_type() => self_,
type_ if type_.is_self_type() => self_,
arg => arg,
}
} else {
arg
};
// If this argument is a type parameter and not a trait bound or a type, we need to look // If this argument is a type parameter and not a trait bound or a type, we need to look
// for its bounds. // for its bounds.
if let Type::Generic(arg_s) = *arg { if let Type::Generic(arg_s) = *arg {
@ -350,6 +374,7 @@ fn insert_ty(
match &param_def.kind { match &param_def.kind {
clean::GenericParamDefKind::Type { default: Some(ty), .. } => { clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
add_generics_and_bounds_as_types( add_generics_and_bounds_as_types(
self_,
generics, generics,
ty, ty,
tcx, tcx,
@ -372,6 +397,7 @@ fn insert_ty(
if let Some(path) = bound.get_trait_path() { if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path }; let ty = Type::Path { path };
add_generics_and_bounds_as_types( add_generics_and_bounds_as_types(
self_,
generics, generics,
&ty, &ty,
tcx, tcx,
@ -393,6 +419,7 @@ fn insert_ty(
if let Some(arg_generics) = arg.generics() { if let Some(arg_generics) = arg.generics() {
for gen in arg_generics.iter() { for gen in arg_generics.iter() {
add_generics_and_bounds_as_types( add_generics_and_bounds_as_types(
self_,
generics, generics,
gen, gen,
tcx, tcx,
@ -413,18 +440,33 @@ fn insert_ty(
fn get_fn_inputs_and_outputs<'tcx>( fn get_fn_inputs_and_outputs<'tcx>(
func: &Function, func: &Function,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
impl_generics: Option<&(clean::Type, clean::Generics)>,
cache: &Cache, cache: &Cache,
) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) { ) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
let decl = &func.decl; let decl = &func.decl;
let generics = &func.generics;
let combined_generics;
let (self_, generics) = if let Some(&(ref impl_self, ref impl_generics)) = impl_generics {
match (impl_generics.is_empty(), func.generics.is_empty()) {
(true, _) => (Some(impl_self), &func.generics),
(_, true) => (Some(impl_self), impl_generics),
(false, false) => {
let mut params = func.generics.params.clone();
params.extend(impl_generics.params.clone());
let mut where_predicates = func.generics.where_predicates.clone();
where_predicates.extend(impl_generics.where_predicates.clone());
combined_generics = clean::Generics { params, where_predicates };
(Some(impl_self), &combined_generics)
}
}
} else {
(None, &func.generics)
};
let mut all_types = Vec::new(); let mut all_types = Vec::new();
for arg in decl.inputs.values.iter() { for arg in decl.inputs.values.iter() {
if arg.type_.is_self_type() {
continue;
}
let mut args = Vec::new(); let mut args = Vec::new();
add_generics_and_bounds_as_types(generics, &arg.type_, tcx, 0, &mut args, cache); add_generics_and_bounds_as_types(self_, generics, &arg.type_, tcx, 0, &mut args, cache);
if !args.is_empty() { if !args.is_empty() {
all_types.extend(args); all_types.extend(args);
} else { } else {
@ -437,7 +479,15 @@ fn get_fn_inputs_and_outputs<'tcx>(
let mut ret_types = Vec::new(); let mut ret_types = Vec::new();
match decl.output { match decl.output {
FnRetTy::Return(ref return_type) => { FnRetTy::Return(ref return_type) => {
add_generics_and_bounds_as_types(generics, return_type, tcx, 0, &mut ret_types, cache); add_generics_and_bounds_as_types(
self_,
generics,
return_type,
tcx,
0,
&mut ret_types,
cache,
);
if ret_types.is_empty() { if ret_types.is_empty() {
if let Some(kind) = return_type.def_id(cache).map(|did| tcx.def_kind(did).into()) { if let Some(kind) = return_type.def_id(cache).map(|did| tcx.def_kind(did).into()) {
ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind))); ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));

View File

@ -0,0 +1,57 @@
// exact-check
const QUERY = [
'Aaaaaaa -> u32',
'Aaaaaaa -> bool',
'Aaaaaaa -> usize',
'Read -> u64',
'bool -> u64',
'Ddddddd -> u64',
'-> Ddddddd'
];
const EXPECTED = [
{
// Aaaaaaa -> u32
'others': [
{ 'path': 'generics_impl::Aaaaaaa', 'name': 'bbbbbbb' },
],
},
{
// Aaaaaaa -> bool
'others': [
{ 'path': 'generics_impl::Aaaaaaa', 'name': 'ccccccc' },
],
},
{
// Aaaaaaa -> usize
'others': [
{ 'path': 'generics_impl::Aaaaaaa', 'name': 'read' },
],
},
{
// Read -> u64
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' },
{ 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
],
},
{
// bool -> u64
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'fffffff' },
],
},
{
// Ddddddd -> u64
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' },
],
},
{
// -> "Ddddddd"
'returned': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'hhhhhhh' },
],
},
];

View File

@ -0,0 +1,35 @@
use std::io::{Result as IoResult, Read};
pub struct Aaaaaaa;
impl Aaaaaaa {
pub fn bbbbbbb(self) -> u32 {
1
}
pub fn ccccccc(&self) -> bool {
true
}
}
impl Read for Aaaaaaa {
fn read(&mut self, out: &mut [u8]) -> IoResult<usize> {
Ok(out.len())
}
}
pub struct Ddddddd<T>(T);
impl<T: Read> Ddddddd<T> {
pub fn eeeeeee(_: T) -> u64 {
1
}
pub fn fffffff(_: bool) -> u64 {
1
}
pub fn ggggggg(self) -> u64 {
1
}
pub fn hhhhhhh() -> Self where T: Default {
Ddddddd(T::default())
}
}