about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2022-05-02 14:51:45 -0700
committerMichael Howell <michael@notriddle.com>2022-05-25 11:01:43 -0700
commitb9ed8784b3f6ffd16d562198cfef5f66a0253a18 (patch)
tree9afecf12809eed2f0297b2d49da3ed4a12f55b11
parent9fed13030c2a2ebd79bfb1fd8be4f768cbe8c9d9 (diff)
downloadrust-b9ed8784b3f6ffd16d562198cfef5f66a0253a18.tar.gz
rust-b9ed8784b3f6ffd16d562198cfef5f66a0253a18.zip
rustdoc: include impl generics / self in search index
-rw-r--r--src/librustdoc/clean/types.rs6
-rw-r--r--src/librustdoc/formats/cache.rs47
-rw-r--r--src/librustdoc/html/render/search_index.rs76
-rw-r--r--src/test/rustdoc-js/generics-impl.js57
-rw-r--r--src/test/rustdoc-js/generics-impl.rs35
5 files changed, 204 insertions, 17 deletions
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<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,
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<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,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<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)));
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<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())
+    }
+}