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
Changes from 2 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
@@ -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,
47 changes: 43 additions & 4 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
@@ -95,7 +95,9 @@ pub(crate) struct Cache {
// Private fields only used when initially crawling a crate to build a cache
stack: Vec<Symbol>,
parent_stack: Vec<DefId>,
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<IndexItem>,
@@ -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,15 +318,26 @@ 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(),
});
}
}
(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.
76 changes: 63 additions & 13 deletions src/librustdoc/html/render/search_index.rs
Original file line number Diff line number Diff line change
@@ -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<IndexItemFunctionType> {
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<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>,
@@ -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 &param_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<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 {
@@ -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)));
57 changes: 57 additions & 0 deletions src/test/rustdoc-js/generics-impl.js
Original file line number Diff line number Diff line change
@@ -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
'others': [
{ 'path': 'generics_impl::Ddddddd', 'name': 'hhhhhhh' },
],
},
];
35 changes: 35 additions & 0 deletions src/test/rustdoc-js/generics-impl.rs
Original file line number Diff line number Diff line change
@@ -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())
}
}