Skip to content

Commit 1ee0d94

Browse files
committed
rustdoc: improve support for trait method results
1 parent 1ee0694 commit 1ee0d94

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
@@ -369,6 +369,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
369369
&item,
370370
self.tcx,
371371
clean_impl_generics(self.cache.parent_stack.last()).as_ref(),
372+
parent,
372373
self.cache,
373374
),
374375
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
});
@@ -503,12 +511,42 @@ pub(crate) fn get_function_type_for_search<'tcx>(
503511
item: &clean::Item,
504512
tcx: TyCtxt<'tcx>,
505513
impl_generics: Option<&(clean::Type, clean::Generics)>,
514+
parent: Option<DefId>,
506515
cache: &Cache,
507516
) -> Option<IndexItemFunctionType> {
517+
let mut trait_info = None;
518+
let impl_or_trait_generics = impl_generics.or_else(|| {
519+
if let Some(def_id) = parent &&
520+
let Some(trait_) = cache.traits.get(&def_id) &&
521+
let Some((path, _)) = cache.paths.get(&def_id)
522+
.or_else(|| cache.external_paths.get(&def_id) )
523+
{
524+
let path = clean::Path {
525+
res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, def_id),
526+
segments: path.iter().map(|name| clean::PathSegment {
527+
name: *name,
528+
args: clean::GenericArgs::AngleBracketed {
529+
args: Vec::new().into_boxed_slice(),
530+
bindings: ThinVec::new(),
531+
},
532+
}).collect(),
533+
};
534+
trait_info = Some((clean::Type::Path { path }, trait_.generics.clone()));
535+
Some(trait_info.as_ref().unwrap())
536+
} else {
537+
None
538+
}
539+
});
508540
let (mut inputs, mut output, where_clause) = match *item.kind {
509-
clean::FunctionItem(ref f) => get_fn_inputs_and_outputs(f, tcx, impl_generics, cache),
510-
clean::MethodItem(ref m, _) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
511-
clean::TyMethodItem(ref m) => get_fn_inputs_and_outputs(m, tcx, impl_generics, cache),
541+
clean::FunctionItem(ref f) => {
542+
get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache)
543+
}
544+
clean::MethodItem(ref m, _) => {
545+
get_fn_inputs_and_outputs(m, tcx, impl_or_trait_generics, cache)
546+
}
547+
clean::TyMethodItem(ref m) => {
548+
get_fn_inputs_and_outputs(m, tcx, impl_or_trait_generics, cache)
549+
}
512550
_ => return None,
513551
};
514552

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

521-
fn get_index_type(clean_type: &clean::Type, generics: Vec<RenderType>) -> RenderType {
559+
fn get_index_type(
560+
clean_type: &clean::Type,
561+
generics: Vec<RenderType>,
562+
rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
563+
) -> RenderType {
522564
RenderType {
523-
id: get_index_type_id(clean_type),
565+
id: get_index_type_id(clean_type, rgen),
524566
generics: if generics.is_empty() { None } else { Some(generics) },
525567
bindings: None,
526568
}
527569
}
528570

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

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

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

1011+
let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
1012+
8801013
let combined_generics;
881-
let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics {
1014+
let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics {
8821015
match (impl_generics.is_empty(), func.generics.is_empty()) {
8831016
(true, _) => (Some(impl_self), &func.generics),
8841017
(_, true) => (Some(impl_self), impl_generics),
@@ -900,8 +1033,6 @@ fn get_fn_inputs_and_outputs<'tcx>(
9001033
(None, &func.generics)
9011034
};
9021035

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