Skip to content

Commit

Permalink
Auto merge of #96652 - notriddle:notriddle/self, r=GuillaumeGomez
Browse files Browse the repository at this point in the history
rustdoc: include impl generics / self in search index

Fixes #92205
  • Loading branch information
bors committed May 29, 2022
2 parents 84288ed + 718269a commit 0acc4a3
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 62 deletions.
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

0 comments on commit 0acc4a3

Please sign in to comment.