Skip to content

Commit 6da1fe2

Browse files
committed
rustdoc: improve support for trait method results
1 parent ac77084 commit 6da1fe2

File tree

5 files changed

+196
-19
lines changed

5 files changed

+196
-19
lines changed

src/librustdoc/formats/cache.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
355355
&item,
356356
self.tcx,
357357
clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
358+
parent,
358359
self.cache,
359360
),
360361
aliases: item.attrs.get_doc_aliases(),

src/librustdoc/html/render/search_index.rs

Lines changed: 150 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use std::collections::BTreeMap;
33

44
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
55
use rustc_middle::ty::TyCtxt;
6+
use rustc_span::def_id::DefId;
67
use rustc_span::symbol::Symbol;
78
use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
9+
use thin_vec::ThinVec;
810

911
use crate::clean;
1012
use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -37,7 +39,13 @@ pub(crate) fn build_index<'tcx>(
3739
desc,
3840
parent: Some(parent),
3941
parent_idx: None,
40-
search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
42+
search_type: get_function_type_for_search(
43+
item,
44+
tcx,
45+
impl_generics.as_ref(),
46+
Some(parent),
47+
cache,
48+
),
4149
aliases: item.attrs.get_doc_aliases(),
4250
deprecation: item.deprecation(tcx),
4351
});
@@ -473,12 +481,42 @@ pub(crate) fn get_function_type_for_search<'tcx>(
473481
item: &clean::Item,
474482
tcx: TyCtxt<'tcx>,
475483
impl_generics: Option<&(clean::Type, clean::Generics)>,
484+
parent: Option<DefId>,
476485
cache: &Cache,
477486
) -> Option<IndexItemFunctionType> {
487+
let mut trait_info = None;
488+
let impl_or_trait_generics = impl_generics.or_else(|| {
489+
if let Some(def_id) = parent &&
490+
let Some(trait_) = cache.traits.get(&def_id) &&
491+
let Some((path, _)) = cache.paths.get(&def_id)
492+
.or_else(|| cache.external_paths.get(&def_id) )
493+
{
494+
let path = clean::Path {
495+
res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, def_id),
496+
segments: path.iter().map(|name| clean::PathSegment {
497+
name: *name,
498+
args: clean::GenericArgs::AngleBracketed {
499+
args: Vec::new().into_boxed_slice(),
500+
bindings: ThinVec::new(),
501+
},
502+
}).collect(),
503+
};
504+
trait_info = Some((clean::Type::Path { path }, trait_.generics.clone()));
505+
Some(trait_info.as_ref().unwrap())
506+
} else {
507+
None
508+
}
509+
});
478510
let (mut inputs, mut output, where_clause) = match *item.kind {
479-
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
480-
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
481-
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
511+
clean::FunctionItem(ref f) => {
512+
get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache)
513+
}
514+
clean::MethodItem(ref m, _) => {
515+
get_fn_inputs_and_outputs(m, tcx, impl_or_trait_generics, cache)
516+
}
517+
clean::TyMethodItem(ref m) => {
518+
get_fn_inputs_and_outputs(m, tcx, impl_or_trait_generics, cache)
519+
}
482520
_ => return None,
483521
};
484522

@@ -488,33 +526,54 @@ pub(crate) fn get_function_type_for_search<'tcx>(
488526
Some(IndexItemFunctionType { inputs, output, where_clause })
489527
}
490528

491-
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
529+
fn get_index_type(
530+
clean_type: &clean::Type,
531+
generics: Vec<RenderType>,
532+
rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
533+
) -> RenderType {
492534
RenderType {
493-
id: get_index_type_id(clean_type),
535+
id: get_index_type_id(clean_type, rgen),
494536
generics: if generics.is_empty() { None } else { Some(generics) },
495537
bindings: None,
496538
}
497539
}
498540

499-
fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
541+
fn get_index_type_id(
542+
clean_type: &clean::Type,
543+
rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
544+
) -> Option<RenderTypeId> {
545+
use rustc_hir::def::{DefKind, Res};
500546
match *clean_type {
501547
clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())),
502548
clean::DynTrait(ref bounds, _) => {
503549
bounds.get(0).map(|b| RenderTypeId::DefId(b.trait_.def_id()))
504550
}
505551
clean::Primitive(p) => Some(RenderTypeId::Primitive(p)),
506552
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
507-
get_index_type_id(type_)
553+
get_index_type_id(type_, rgen)
508554
}
509555
// The type parameters are converted to generics in `simplify_fn_type`
510556
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
511557
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
512558
clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
559+
clean::QPath(ref data) => {
560+
if data.self_type.is_self_type()
561+
&& let Some(clean::Path { res: Res::Def(DefKind::Trait, trait_), .. }) = data.trait_
562+
{
563+
let idx = -isize::try_from(rgen.len() + 1).unwrap();
564+
let (idx, _) = rgen.entry(SimplifiedParam::AssociatedType(trait_, data.assoc.name))
565+
.or_insert_with(|| {
566+
(idx, Vec::new())
567+
});
568+
Some(RenderTypeId::Index(*idx))
569+
} else {
570+
None
571+
}
572+
}
513573
// Not supported yet
514574
clean::BareFunction(_)
515575
| clean::Generic(_)
516576
| clean::ImplTrait(_)
517-
| clean::QPath { .. }
518577
| clean::Infer => None,
519578
}
520579
}
@@ -525,6 +584,9 @@ enum SimplifiedParam {
525584
Symbol(Symbol),
526585
// every argument-position impl trait is its own type parameter
527586
Anonymous(isize),
587+
// in a trait definition, the associated types are all bound to
588+
// their own type parameter
589+
AssociatedType(DefId, Symbol),
528590
}
529591

530592
/// The point of this function is to lower generics and types into the simplified form that the
@@ -555,10 +617,17 @@ fn simplify_fn_type<'tcx, 'a>(
555617
}
556618

557619
// First, check if it's "Self".
620+
let mut is_self = false;
558621
let mut arg = if let Some(self_) = self_ {
559622
match &*arg {
560-
Type::BorrowedRef { type_, .. } if type_.is_self_type() => self_,
561-
type_ if type_.is_self_type() => self_,
623+
Type::BorrowedRef { type_, .. } if type_.is_self_type() => {
624+
is_self = true;
625+
self_
626+
}
627+
type_ if type_.is_self_type() => {
628+
is_self = true;
629+
self_
630+
}
562631
arg => arg,
563632
}
564633
} else {
@@ -675,7 +744,7 @@ fn simplify_fn_type<'tcx, 'a>(
675744
is_return,
676745
cache,
677746
);
678-
res.push(get_index_type(arg, ty_generics));
747+
res.push(get_index_type(arg, ty_generics, rgen));
679748
} else if let Type::Array(ref ty, _) = *arg {
680749
let mut ty_generics = Vec::new();
681750
simplify_fn_type(
@@ -689,7 +758,7 @@ fn simplify_fn_type<'tcx, 'a>(
689758
is_return,
690759
cache,
691760
);
692-
res.push(get_index_type(arg, ty_generics));
761+
res.push(get_index_type(arg, ty_generics, rgen));
693762
} else if let Type::Tuple(ref tys) = *arg {
694763
let mut ty_generics = Vec::new();
695764
for ty in tys {
@@ -705,7 +774,7 @@ fn simplify_fn_type<'tcx, 'a>(
705774
cache,
706775
);
707776
}
708-
res.push(get_index_type(arg, ty_generics));
777+
res.push(get_index_type(arg, ty_generics, rgen));
709778
} else {
710779
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
711780
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -742,7 +811,69 @@ fn simplify_fn_type<'tcx, 'a>(
742811
);
743812
}
744813
}
745-
let id = get_index_type_id(&arg);
814+
// Every trait associated type on self gets assigned to a type parameter index
815+
// this same one is used later for any appearances of these types
816+
//
817+
// for example, Iterator::next is:
818+
//
819+
// trait Iterator {
820+
// fn next(&mut self) -> Option<Self::Item>
821+
// }
822+
//
823+
// Self is technically just Iterator, but we want to pretend it's more like this:
824+
//
825+
// fn next<T>(self: Iterator<Item=T>) -> Option<T>
826+
if is_self &&
827+
let Type::Path { path } = arg &&
828+
let def_id = path.def_id() &&
829+
let Some(trait_) = cache.traits.get(&def_id) &&
830+
trait_.items.iter().any(|at| at.is_ty_associated_type())
831+
{
832+
for assoc_ty in &trait_.items {
833+
if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &*assoc_ty.kind &&
834+
let Some(name) = assoc_ty.name
835+
{
836+
let idx = -isize::try_from(rgen.len() + 1).unwrap();
837+
let (idx, stored_bounds) = rgen.entry(SimplifiedParam::AssociatedType(def_id, name))
838+
.or_insert_with(|| {
839+
(idx, Vec::new())
840+
});
841+
let idx = *idx;
842+
if stored_bounds.is_empty() {
843+
// Can't just pass stored_bounds to simplify_fn_type,
844+
// because it also accepts rgen as a parameter.
845+
// Instead, have it fill in this local, then copy it into the map afterward.
846+
let mut type_bounds = Vec::new();
847+
for bound in bounds {
848+
if let Some(path) = bound.get_trait_path() {
849+
let ty = Type::Path { path };
850+
simplify_fn_type(
851+
self_,
852+
generics,
853+
&ty,
854+
tcx,
855+
recurse + 1,
856+
&mut type_bounds,
857+
rgen,
858+
is_return,
859+
cache,
860+
);
861+
}
862+
}
863+
let stored_bounds = &mut rgen.get_mut(&SimplifiedParam::AssociatedType(def_id, name)).unwrap().1;
864+
if stored_bounds.is_empty() {
865+
*stored_bounds = type_bounds;
866+
}
867+
}
868+
ty_bindings.push((RenderTypeId::AssociatedType(name), vec![RenderType {
869+
id: Some(RenderTypeId::Index(idx)),
870+
generics: None,
871+
bindings: None,
872+
}]))
873+
}
874+
}
875+
}
876+
let id = get_index_type_id(&arg, rgen);
746877
if id.is_some() || !ty_generics.is_empty() {
747878
res.push(RenderType {
748879
id,
@@ -842,13 +973,15 @@ fn simplify_fn_binding<'tcx, 'a>(
842973
fn get_fn_inputs_and_outputs<'tcx>(
843974
func: &Function,
844975
tcx: TyCtxt<'tcx>,
845-
impl_generics: Option<&(clean::Type, clean::Generics)>,
976+
impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>,
846977
cache: &Cache,
847978
) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
848979
let decl = &func.decl;
849980

981+
let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
982+
850983
let combined_generics;
851-
let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics {
984+
let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics {
852985
match (impl_generics.is_empty(), func.generics.is_empty()) {
853986
(true, _) => (Some(impl_self), &func.generics),
854987
(_, true) => (Some(impl_self), impl_generics),
@@ -870,8 +1003,6 @@ fn get_fn_inputs_and_outputs<'tcx>(
8701003
(None, &func.generics)
8711004
};
8721005

873-
let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
874-
8751006
let mut arg_types = Vec::new();
8761007
for arg in decl.inputs.values.iter() {
8771008
simplify_fn_type(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// ignore-order
2+
3+
const FILTER_CRATE = "std";
4+
5+
const EXPECTED = [
6+
{
7+
'query': 'iterator<t> -> option<t>',
8+
'others': [
9+
{ 'path': 'std::iter::Iterator', 'name': 'max' },
10+
{ 'path': 'std::iter::Iterator', 'name': 'min' },
11+
{ 'path': 'std::iter::Iterator', 'name': 'last' },
12+
{ 'path': 'std::iter::Iterator', 'name': 'next' },
13+
],
14+
},
15+
{
16+
'query': 'iterator<t>, usize -> option<t>',
17+
'others': [
18+
{ 'path': 'std::iter::Iterator', 'name': 'nth' },
19+
],
20+
},
21+
{
22+
// Something should be done so that intoiterator is considered a match
23+
// for plain iterator.
24+
'query': 'iterator<t>, intoiterator<t> -> ordering',
25+
'others': [
26+
{ 'path': 'std::iter::Iterator', 'name': 'cmp' },
27+
],
28+
},
29+
];

tests/rustdoc-js/trait-methods.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// exact-check
2+
3+
const EXPECTED = [
4+
{
5+
'query': 'mytrait<t> -> option<t>',
6+
'correction': null,
7+
'in_args': [],
8+
'others': [
9+
{ 'path': 'trait_methods::MyTrait', 'name': 'next' },
10+
],
11+
},
12+
];

tests/rustdoc-js/trait-methods.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub trait MyTrait {
2+
type Item;
3+
fn next(&mut self) -> Option<Self::Item>;
4+
}

0 commit comments

Comments
 (0)