Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: include impl generics / self in search index #96652

Merged
merged 4 commits into from
May 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,12 @@ pub(crate) struct Generics {
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)]
pub(crate) struct Function {
pub(crate) decl: FnDecl,
Expand Down
144 changes: 98 additions & 46 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ pub(crate) struct Cache {

// Private fields only used when initially crawling a crate to build a cache
stack: Vec<Symbol>,
parent_stack: Vec<DefId>,
parent_is_trait_impl: bool,
parent_stack: Vec<ParentStackItem>,
stripped_mod: bool,

pub(crate) search_index: Vec<IndexItem>,
Expand All @@ -105,7 +104,7 @@ 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<OrphanImplItem>,

// 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
Expand Down Expand Up @@ -261,7 +260,11 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
let (parent, is_inherent_impl_item) = match *item.kind {
clean::StrippedItem(..) => ((None, None), false),
clean::AssocConstItem(..) | clean::AssocTypeItem(..)
if self.cache.parent_is_trait_impl =>
if self
.cache
.parent_stack
.last()
.map_or(false, |parent| parent.is_trait_impl()) =>
{
// skip associated items in trait impls
((None, None), false)
Expand All @@ -272,7 +275,14 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
| clean::StructFieldItem(..)
| clean::VariantItem(..) => (
(
Some(*self.cache.parent_stack.last().expect("parent_stack is empty")),
Some(
self.cache
.parent_stack
.last()
.expect("parent_stack is empty")
.item_id()
.expect_def_id(),
),
Some(&self.cache.stack[..self.cache.stack.len() - 1]),
),
false,
Expand All @@ -282,16 +292,19 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
((None, None), false)
} else {
let last = self.cache.parent_stack.last().expect("parent_stack is empty 2");
let did = *last;
let path = match self.cache.paths.get(&did) {
let did = match &*last {
ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache),
ParentStackItem::Type(item_id) => item_id.as_def_id(),
};
let path = match did.and_then(|did| self.cache.paths.get(&did)) {
// The current stack not necessarily has correlation
// for where the type was defined. On the other
// hand, `paths` always has the right
// information if present.
Some(&(ref fqp, _)) => Some(&fqp[..fqp.len() - 1]),
None => None,
};
((Some(*last), path), true)
((did, path), true)
}
}
_ => ((None, Some(&*self.cache.stack)), false),
Expand All @@ -315,15 +328,25 @@ 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,
clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
self.cache,
),
aliases: item.attrs.get_doc_aliases(),
});
}
}
(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()));
let impl_generics = clean_impl_generics(self.cache.parent_stack.last());
self.cache.orphan_impl_items.push(OrphanImplItem {
parent,
item: item.clone(),
impl_generics,
});
}
_ => {}
}
Expand Down Expand Up @@ -398,51 +421,23 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
}
}

// Maintain the parent stack
let orig_parent_is_trait_impl = self.cache.parent_is_trait_impl;
let parent_pushed = match *item.kind {
// Maintain the parent stack.
let (item, parent_pushed) = match *item.kind {
clean::TraitItem(..)
| clean::EnumItem(..)
| clean::ForeignTypeItem
| clean::StructItem(..)
| clean::UnionItem(..)
| clean::VariantItem(..) => {
self.cache.parent_stack.push(item.item_id.expect_def_id());
self.cache.parent_is_trait_impl = false;
true
}
clean::ImplItem(ref i) => {
self.cache.parent_is_trait_impl = i.trait_.is_some();
match i.for_ {
clean::Type::Path { ref path } => {
self.cache.parent_stack.push(path.def_id());
true
}
clean::DynTrait(ref bounds, _)
| clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => {
self.cache.parent_stack.push(bounds[0].trait_.def_id());
true
}
ref t => {
let prim_did = t
.primitive_type()
.and_then(|t| self.cache.primitive_locations.get(&t).cloned());
match prim_did {
Some(did) => {
self.cache.parent_stack.push(did);
true
}
None => false,
}
}
}
| clean::VariantItem(..)
| clean::ImplItem(..) => {
self.cache.parent_stack.push(ParentStackItem::new(&item));
(self.fold_item_recur(item), true)
}
_ => false,
_ => (self.fold_item_recur(item), false),
};

// 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.
Expand Down Expand Up @@ -511,7 +506,64 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
self.cache.parent_stack.pop().expect("parent stack already empty");
}
self.cache.stripped_mod = orig_stripped_mod;
self.cache.parent_is_trait_impl = orig_parent_is_trait_impl;
ret
}
}

pub(crate) struct OrphanImplItem {
pub(crate) parent: DefId,
pub(crate) item: clean::Item,
pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>,
}

/// Information about trait and type parents is tracked while traversing the item tree to build
/// the cache.
///
/// We don't just store `Item` in there, because `Item` contains the list of children being
/// traversed and it would be wasteful to clone all that. We also need the item id, so just
/// storing `ItemKind` won't work, either.
enum ParentStackItem {
Impl {
for_: clean::Type,
trait_: Option<clean::Path>,
generics: clean::Generics,
kind: clean::ImplKind,
item_id: ItemId,
},
Type(ItemId),
}

impl ParentStackItem {
fn new(item: &clean::Item) -> Self {
match &*item.kind {
clean::ItemKind::ImplItem(clean::Impl { for_, trait_, generics, kind, .. }) => {
ParentStackItem::Impl {
for_: for_.clone(),
trait_: trait_.clone(),
generics: generics.clone(),
kind: kind.clone(),
item_id: item.item_id,
}
}
_ => ParentStackItem::Type(item.item_id),
}
}
fn is_trait_impl(&self) -> bool {
matches!(self, ParentStackItem::Impl { trait_: Some(..), .. })
}
fn item_id(&self) -> ItemId {
match self {
ParentStackItem::Impl { item_id, .. } => *item_id,
ParentStackItem::Type(item_id) => *item_id,
}
}
}

fn clean_impl_generics(item: Option<&ParentStackItem>) -> Option<(clean::Type, clean::Generics)> {
if let Some(ParentStackItem::Impl { for_, generics, kind: clean::ImplKind::Normal, .. }) = item
{
Some((for_.clone(), generics.clone()))
} else {
None
}
}
71 changes: 55 additions & 16 deletions src/librustdoc/html/render/search_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer};

use crate::clean;
use crate::clean::types::{FnRetTy, Function, GenericBound, Generics, Type, WherePredicate};
use crate::formats::cache::Cache;
use crate::formats::cache::{Cache, OrphanImplItem};
use crate::formats::item_type::ItemType;
use crate::html::format::join_with_double_colon;
use crate::html::markdown::short_markdown_summary;
Expand All @@ -25,8 +25,8 @@ 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 {
if let Some(&(ref fqp, _)) = cache.paths.get(&did) {
for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
if let Some(&(ref fqp, _)) = cache.paths.get(&parent) {
let desc = item
.doc_value()
.map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
Expand All @@ -35,9 +35,9 @@ pub(crate) fn build_index<'tcx>(
name: item.name.unwrap().to_string(),
path: join_with_double_colon(&fqp[..fqp.len() - 1]),
desc,
parent: Some(did),
parent: Some(parent),
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(), cache),
aliases: item.attrs.get_doc_aliases(),
});
}
Expand Down Expand Up @@ -192,12 +192,13 @@ 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)>,
cache: &Cache,
) -> Option<IndexItemFunctionType> {
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,
};

Expand Down Expand Up @@ -247,9 +248,10 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option<Symbol> {
/// Important note: It goes through generics recursively. So if you have
/// `T: Option<Result<(), ()>>`, 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<TypeWithKind>,
Expand Down Expand Up @@ -334,6 +336,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 {
Expand All @@ -350,6 +363,7 @@ fn add_generics_and_bounds_as_types<'tcx>(
match &param_def.kind {
clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
add_generics_and_bounds_as_types(
self_,
generics,
ty,
tcx,
Expand All @@ -372,6 +386,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,
Expand All @@ -393,6 +408,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,
Expand All @@ -413,18 +429,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<TypeWithKind>, Vec<TypeWithKind>) {
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 {
Expand All @@ -437,7 +468,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)));
Expand Down
Loading