Skip to content

Commit e61d06f

Browse files
committed
rustdoc: improve support for trait method results
1 parent c6b34ce commit e61d06f

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
@@ -366,6 +366,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
366366
&item,
367367
self.tcx,
368368
clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
369+
parent,
369370
self.cache,
370371
),
371372
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};
@@ -39,7 +41,13 @@ pub(crate) fn build_index<'tcx>(
3941
parent: Some(parent),
4042
parent_idx: None,
4143
impl_id,
42-
search_type: get_function_type_for_search(item, tcx, impl_generics.as_ref(), cache),
44+
search_type: get_function_type_for_search(
45+
item,
46+
tcx,
47+
impl_generics.as_ref(),
48+
Some(parent),
49+
cache,
50+
),
4351
aliases: item.attrs.get_doc_aliases(),
4452
deprecation: item.deprecation(tcx),
4553
});
@@ -502,12 +510,42 @@ pub(crate) fn get_function_type_for_search<'tcx>(
502510
item: &clean::Item,
503511
tcx: TyCtxt<'tcx>,
504512
impl_generics: Option<&(clean::Type, clean::Generics)>,
513+
parent: Option<DefId>,
505514
cache: &Cache,
506515
) -> Option<IndexItemFunctionType> {
516+
let mut trait_info = None;
517+
let impl_or_trait_generics = impl_generics.or_else(|| {
518+
if let Some(def_id) = parent &&
519+
let Some(trait_) = cache.traits.get(&def_id) &&
520+
let Some((path, _)) = cache.paths.get(&def_id)
521+
.or_else(|| cache.external_paths.get(&def_id) )
522+
{
523+
let path = clean::Path {
524+
res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, def_id),
525+
segments: path.iter().map(|name| clean::PathSegment {
526+
name: *name,
527+
args: clean::GenericArgs::AngleBracketed {
528+
args: Vec::new().into_boxed_slice(),
529+
bindings: ThinVec::new(),
530+
},
531+
}).collect(),
532+
};
533+
trait_info = Some((clean::Type::Path { path }, trait_.generics.clone()));
534+
Some(trait_info.as_ref().unwrap())
535+
} else {
536+
None
537+
}
538+
});
507539
let (mut inputs, mut output, where_clause) = match *item.kind {
508-
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
509-
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
510-
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
540+
clean::FunctionItem(ref f) => {
541+
get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache)
542+
}
543+
clean::MethodItem(ref m, _) => {
544+
get_fn_inputs_and_outputs(m, tcx, impl_or_trait_generics, cache)
545+
}
546+
clean::TyMethodItem(ref m) => {
547+
get_fn_inputs_and_outputs(m, tcx, impl_or_trait_generics, cache)
548+
}
511549
_ => return None,
512550
};
513551

@@ -517,33 +555,54 @@ pub(crate) fn get_function_type_for_search<'tcx>(
517555
Some(IndexItemFunctionType { inputs, output, where_clause })
518556
}
519557

520-
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
558+
fn get_index_type(
559+
clean_type: &clean::Type,
560+
generics: Vec<RenderType>,
561+
rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
562+
) -> RenderType {
521563
RenderType {
522-
id: get_index_type_id(clean_type),
564+
id: get_index_type_id(clean_type, rgen),
523565
generics: if generics.is_empty() { None } else { Some(generics) },
524566
bindings: None,
525567
}
526568
}
527569

528-
fn get_index_type_id(clean_type: &clean::Type) -> Option<RenderTypeId> {
570+
fn get_index_type_id(
571+
clean_type: &clean::Type,
572+
rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
573+
) -> Option<RenderTypeId> {
574+
use rustc_hir::def::{DefKind, Res};
529575
match *clean_type {
530576
clean::Type::Path { ref path, .. } => Some(RenderTypeId::DefId(path.def_id())),
531577
clean::DynTrait(ref bounds, _) => {
532578
bounds.get(0).map(|b| RenderTypeId::DefId(b.trait_.def_id()))
533579
}
534580
clean::Primitive(p) => Some(RenderTypeId::Primitive(p)),
535581
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
536-
get_index_type_id(type_)
582+
get_index_type_id(type_, rgen)
537583
}
538584
// The type parameters are converted to generics in `simplify_fn_type`
539585
clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)),
540586
clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)),
541587
clean::Tuple(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Tuple)),
588+
clean::QPath(ref data) => {
589+
if data.self_type.is_self_type()
590+
&& let Some(clean::Path { res: Res::Def(DefKind::Trait, trait_), .. }) = data.trait_
591+
{
592+
let idx = -isize::try_from(rgen.len() + 1).unwrap();
593+
let (idx, _) = rgen.entry(SimplifiedParam::AssociatedType(trait_, data.assoc.name))
594+
.or_insert_with(|| {
595+
(idx, Vec::new())
596+
});
597+
Some(RenderTypeId::Index(*idx))
598+
} else {
599+
None
600+
}
601+
}
542602
// Not supported yet
543603
clean::BareFunction(_)
544604
| clean::Generic(_)
545605
| clean::ImplTrait(_)
546-
| clean::QPath { .. }
547606
| clean::Infer => None,
548607
}
549608
}
@@ -554,6 +613,9 @@ enum SimplifiedParam {
554613
Symbol(Symbol),
555614
// every argument-position impl trait is its own type parameter
556615
Anonymous(isize),
616+
// in a trait definition, the associated types are all bound to
617+
// their own type parameter
618+
AssociatedType(DefId, Symbol),
557619
}
558620

559621
/// The point of this function is to lower generics and types into the simplified form that the
@@ -584,10 +646,17 @@ fn simplify_fn_type<'tcx, 'a>(
584646
}
585647

586648
// First, check if it's "Self".
649+
let mut is_self = false;
587650
let mut arg = if let Some(self_) = self_ {
588651
match &*arg {
589-
Type::BorrowedRef { type_, .. } if type_.is_self_type() => self_,
590-
type_ if type_.is_self_type() => self_,
652+
Type::BorrowedRef { type_, .. } if type_.is_self_type() => {
653+
is_self = true;
654+
self_
655+
}
656+
type_ if type_.is_self_type() => {
657+
is_self = true;
658+
self_
659+
}
591660
arg => arg,
592661
}
593662
} else {
@@ -704,7 +773,7 @@ fn simplify_fn_type<'tcx, 'a>(
704773
is_return,
705774
cache,
706775
);
707-
res.push(get_index_type(arg, ty_generics));
776+
res.push(get_index_type(arg, ty_generics, rgen));
708777
} else if let Type::Array(ref ty, _) = *arg {
709778
let mut ty_generics = Vec::new();
710779
simplify_fn_type(
@@ -718,7 +787,7 @@ fn simplify_fn_type<'tcx, 'a>(
718787
is_return,
719788
cache,
720789
);
721-
res.push(get_index_type(arg, ty_generics));
790+
res.push(get_index_type(arg, ty_generics, rgen));
722791
} else if let Type::Tuple(ref tys) = *arg {
723792
let mut ty_generics = Vec::new();
724793
for ty in tys {
@@ -734,7 +803,7 @@ fn simplify_fn_type<'tcx, 'a>(
734803
cache,
735804
);
736805
}
737-
res.push(get_index_type(arg, ty_generics));
806+
res.push(get_index_type(arg, ty_generics, rgen));
738807
} else {
739808
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
740809
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
@@ -771,7 +840,69 @@ fn simplify_fn_type<'tcx, 'a>(
771840
);
772841
}
773842
}
774-
let id = get_index_type_id(&arg);
843+
// Every trait associated type on self gets assigned to a type parameter index
844+
// this same one is used later for any appearances of these types
845+
//
846+
// for example, Iterator::next is:
847+
//
848+
// trait Iterator {
849+
// fn next(&mut self) -> Option<Self::Item>
850+
// }
851+
//
852+
// Self is technically just Iterator, but we want to pretend it's more like this:
853+
//
854+
// fn next<T>(self: Iterator<Item=T>) -> Option<T>
855+
if is_self &&
856+
let Type::Path { path } = arg &&
857+
let def_id = path.def_id() &&
858+
let Some(trait_) = cache.traits.get(&def_id) &&
859+
trait_.items.iter().any(|at| at.is_ty_associated_type())
860+
{
861+
for assoc_ty in &trait_.items {
862+
if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &*assoc_ty.kind &&
863+
let Some(name) = assoc_ty.name
864+
{
865+
let idx = -isize::try_from(rgen.len() + 1).unwrap();
866+
let (idx, stored_bounds) = rgen.entry(SimplifiedParam::AssociatedType(def_id, name))
867+
.or_insert_with(|| {
868+
(idx, Vec::new())
869+
});
870+
let idx = *idx;
871+
if stored_bounds.is_empty() {
872+
// Can't just pass stored_bounds to simplify_fn_type,
873+
// because it also accepts rgen as a parameter.
874+
// Instead, have it fill in this local, then copy it into the map afterward.
875+
let mut type_bounds = Vec::new();
876+
for bound in bounds {
877+
if let Some(path) = bound.get_trait_path() {
878+
let ty = Type::Path { path };
879+
simplify_fn_type(
880+
self_,
881+
generics,
882+
&ty,
883+
tcx,
884+
recurse + 1,
885+
&mut type_bounds,
886+
rgen,
887+
is_return,
888+
cache,
889+
);
890+
}
891+
}
892+
let stored_bounds = &mut rgen.get_mut(&SimplifiedParam::AssociatedType(def_id, name)).unwrap().1;
893+
if stored_bounds.is_empty() {
894+
*stored_bounds = type_bounds;
895+
}
896+
}
897+
ty_bindings.push((RenderTypeId::AssociatedType(name), vec![RenderType {
898+
id: Some(RenderTypeId::Index(idx)),
899+
generics: None,
900+
bindings: None,
901+
}]))
902+
}
903+
}
904+
}
905+
let id = get_index_type_id(&arg, rgen);
775906
if id.is_some() || !ty_generics.is_empty() {
776907
res.push(RenderType {
777908
id,
@@ -871,13 +1002,15 @@ fn simplify_fn_binding<'tcx, 'a>(
8711002
fn get_fn_inputs_and_outputs<'tcx>(
8721003
func: &Function,
8731004
tcx: TyCtxt<'tcx>,
874-
impl_generics: Option<&(clean::Type, clean::Generics)>,
1005+
impl_or_trait_generics: Option<&(clean::Type, clean::Generics)>,
8751006
cache: &Cache,
8761007
) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
8771008
let decl = &func.decl;
8781009

1010+
let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
1011+
8791012
let combined_generics;
880-
let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics {
1013+
let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics {
8811014
match (impl_generics.is_empty(), func.generics.is_empty()) {
8821015
(true, _) => (Some(impl_self), &func.generics),
8831016
(_, true) => (Some(impl_self), impl_generics),
@@ -899,8 +1032,6 @@ fn get_fn_inputs_and_outputs<'tcx>(
8991032
(None, &func.generics)
9001033
};
9011034

902-
let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
903-
9041035
let mut arg_types = Vec::new();
9051036
for arg in decl.inputs.values.iter() {
9061037
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)