From b9ed8784b3f6ffd16d562198cfef5f66a0253a18 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 2 May 2022 14:51:45 -0700 Subject: [PATCH] rustdoc: include impl generics / self in search index --- src/librustdoc/clean/types.rs | 6 ++ src/librustdoc/formats/cache.rs | 47 +++++++++++-- src/librustdoc/html/render/search_index.rs | 76 ++++++++++++++++++---- src/test/rustdoc-js/generics-impl.js | 57 ++++++++++++++++ src/test/rustdoc-js/generics-impl.rs | 35 ++++++++++ 5 files changed, 204 insertions(+), 17 deletions(-) create mode 100644 src/test/rustdoc-js/generics-impl.js create mode 100644 src/test/rustdoc-js/generics-impl.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index e10c61901d0..23bb020716d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1410,6 +1410,12 @@ pub(crate) struct Generics { pub(crate) where_predicates: Vec, } +impl Generics { + pub(crate) fn is_empty(&self) -> bool { + self.params.is_empty() && self.where_predicates.is_empty() + } +} + #[derive(Clone, Debug)] pub(crate) struct Function { pub(crate) decl: FnDecl, diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 09aa042b1d0..813e6fa24ec 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -95,7 +95,9 @@ pub(crate) struct Cache { // Private fields only used when initially crawling a crate to build a cache stack: Vec, parent_stack: Vec, + impl_generics_stack: Vec<(clean::Type, clean::Generics)>, parent_is_trait_impl: bool, + parent_is_blanket_or_auto_impl: bool, stripped_mod: bool, pub(crate) search_index: Vec, @@ -105,7 +107,8 @@ pub(crate) struct Cache { // then the fully qualified name of the structure isn't presented in `paths` // 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. - 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 // even though the trait itself is not exported. This can happen if a trait @@ -315,7 +318,13 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { desc, parent, 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(), }); } @@ -323,7 +332,12 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { (Some(parent), None) if is_inherent_impl_item => { // We have a parent, but we don't know where they're // 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 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { _ => 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 // implementations elsewhere. - let item = self.fold_item_recur(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 // primitive rather than always to a struct/enum. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 3daef3dbb79..323c0e89f29 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -25,7 +25,7 @@ pub(crate) fn build_index<'tcx>( // Attach all orphan items to the type's definition if the type // 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) { let desc = item .doc_value() @@ -37,7 +37,13 @@ pub(crate) fn build_index<'tcx>( desc, parent: Some(did), 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(), }); } @@ -192,12 +198,18 @@ pub(crate) fn build_index<'tcx>( pub(crate) fn get_function_type_for_search<'tcx>( item: &clean::Item, tcx: TyCtxt<'tcx>, + impl_generics: Option<&(clean::Type, clean::Generics)>, + from_blanket_or_auto_impl: bool, cache: &Cache, ) -> Option { + if from_blanket_or_auto_impl { + return None; + } + let (mut inputs, mut output) = match *item.kind { - clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, cache), - clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, cache), - clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, 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, impl_generics, cache), + clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache), _ => return None, }; @@ -247,9 +259,10 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option { /// Important note: It goes through generics recursively. So if you have /// `T: Option>`, it'll go into `Option` and then into `Result`. #[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, - arg: &Type, + arg: &'a Type, tcx: TyCtxt<'tcx>, recurse: usize, res: &mut Vec, @@ -334,6 +347,17 @@ fn add_generics_and_bounds_as_types<'tcx>( 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 // for its bounds. if let Type::Generic(arg_s) = *arg { @@ -350,6 +374,7 @@ fn add_generics_and_bounds_as_types<'tcx>( match ¶m_def.kind { clean::GenericParamDefKind::Type { default: Some(ty), .. } => { add_generics_and_bounds_as_types( + self_, generics, ty, tcx, @@ -372,6 +397,7 @@ fn add_generics_and_bounds_as_types<'tcx>( if let Some(path) = bound.get_trait_path() { let ty = Type::Path { path }; add_generics_and_bounds_as_types( + self_, generics, &ty, tcx, @@ -393,6 +419,7 @@ fn add_generics_and_bounds_as_types<'tcx>( if let Some(arg_generics) = arg.generics() { for gen in arg_generics.iter() { add_generics_and_bounds_as_types( + self_, generics, gen, tcx, @@ -413,18 +440,33 @@ fn add_generics_and_bounds_as_types<'tcx>( fn get_fn_inputs_and_outputs<'tcx>( func: &Function, tcx: TyCtxt<'tcx>, + impl_generics: Option<&(clean::Type, clean::Generics)>, cache: &Cache, ) -> (Vec, Vec) { 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(); for arg in decl.inputs.values.iter() { - if arg.type_.is_self_type() { - continue; - } 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() { all_types.extend(args); } else { @@ -437,7 +479,15 @@ fn get_fn_inputs_and_outputs<'tcx>( let mut ret_types = Vec::new(); match decl.output { 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 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))); diff --git a/src/test/rustdoc-js/generics-impl.js b/src/test/rustdoc-js/generics-impl.js new file mode 100644 index 00000000000..20bb7be6ba0 --- /dev/null +++ b/src/test/rustdoc-js/generics-impl.js @@ -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' }, + ], + }, +]; diff --git a/src/test/rustdoc-js/generics-impl.rs b/src/test/rustdoc-js/generics-impl.rs new file mode 100644 index 00000000000..696218021d5 --- /dev/null +++ b/src/test/rustdoc-js/generics-impl.rs @@ -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 { + Ok(out.len()) + } +} + +pub struct Ddddddd(T); + +impl Ddddddd { + 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()) + } +}