Skip to content

Commit e03cbef

Browse files
committed
Add support for associated items even outside the impl itself
Previously, associated items would only be available for linking directly on the `impl Trait for Type`. Now they can be used anywhere.
1 parent 7d31ffc commit e03cbef

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed

src/librustdoc/clean/types.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_hir::lang_items;
1919
use rustc_hir::Mutability;
2020
use rustc_index::vec::IndexVec;
2121
use rustc_middle::middle::stability;
22+
use rustc_middle::ty::AssocKind;
2223
use rustc_span::hygiene::MacroKind;
2324
use rustc_span::source_map::DUMMY_SP;
2425
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -291,6 +292,15 @@ impl ItemEnum {
291292
_ => false,
292293
}
293294
}
295+
296+
pub fn as_assoc_kind(&self) -> Option<AssocKind> {
297+
match *self {
298+
ItemEnum::AssocConstItem(..) => Some(AssocKind::Const),
299+
ItemEnum::AssocTypeItem(..) => Some(AssocKind::Type),
300+
ItemEnum::TyMethodItem(..) | ItemEnum::MethodItem(..) => Some(AssocKind::Fn),
301+
_ => None,
302+
}
303+
}
294304
}
295305

296306
#[derive(Clone, Debug)]

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,53 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
287287
DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias,
288288
did,
289289
) => {
290+
debug!("looking for associated item named {} for item {:?}", item_name, did);
291+
let ty = cx.tcx.type_of(did);
290292
// Checks if item_name belongs to `impl SomeItem`
291-
let impl_item = cx
292-
.tcx
293-
.inherent_impls(did)
294-
.iter()
295-
.flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
296-
.find(|item| item.ident.name == item_name);
297-
let trait_item = item_opt
293+
let impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
294+
let impl_kind = impls
295+
.flat_map(|impl_outer| {
296+
match impl_outer.inner {
297+
ImplItem(impl_) => {
298+
debug!("considering trait {:?}", impl_.trait_);
299+
// Give precedence to methods that were overridden
300+
if !impl_.provided_trait_methods.contains(&*item_name.as_str()) {
301+
impl_.items.into_iter()
302+
.filter(|assoc| assoc.name.as_deref() == Some(&*item_name.as_str()))
303+
.map(|assoc| {
304+
trace!("considering associated item {:?}", assoc.inner);
305+
// We have a slight issue: normal methods come from `clean` types,
306+
// but provided methods come directly from `tcx`.
307+
// Fortunately, we don't need the whole method, we just need to know
308+
// what kind of associated item it is.
309+
assoc.inner.as_assoc_kind()
310+
.expect("inner items for a trait should be associated items")
311+
})
312+
// TODO: this collect seems a shame
313+
.collect()
314+
} else {
315+
// These are provided methods or default types:
316+
// ```
317+
// trait T {
318+
// type A = usize;
319+
// fn has_default() -> A { 0 }
320+
// }
321+
// ```
322+
// TODO: this is wrong, it should look at the trait, not the impl
323+
cx.tcx.associated_items(impl_outer.def_id)
324+
.filter_by_name(cx.tcx, Ident::with_dummy_span(item_name), impl_outer.def_id)
325+
.map(|assoc| assoc.kind)
326+
// TODO: this collect seems a shame
327+
.collect::<Vec<_>>()
328+
}
329+
}
330+
_ => panic!("get_impls returned something that wasn't an impl"),
331+
}
332+
})
333+
// TODO: give a warning if this is ambiguous
334+
.next();
335+
// TODO: is this necessary? It doesn't look right, and also only works for local items
336+
let trait_kind = item_opt
298337
.and_then(|item| self.cx.as_local_hir_id(item.def_id))
299338
.and_then(|item_hir| {
300339
// Checks if item_name belongs to `impl SomeTrait for SomeItem`
@@ -312,32 +351,34 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
312351
let associated_item = cx.tcx.associated_item(*child);
313352
associated_item
314353
})
315-
.find(|child| child.ident.name == item_name),
354+
.find(|child| child.ident.name == item_name)
355+
.map(|child| child.kind),
316356
_ => None,
317357
}
318358
});
319-
let item = match (impl_item, trait_item) {
320-
(Some(from_impl), Some(_)) => {
359+
debug!("considering items {:?} and {:?}", impl_kind, trait_kind);
360+
let kind = match (impl_kind, trait_kind) {
361+
(Some(from_kind), Some(_)) => {
321362
// Although it's ambiguous, return impl version for compat. sake.
322363
// To handle that properly resolve() would have to support
323364
// something like
324365
// [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
325-
Some(from_impl)
366+
Some(from_kind)
326367
}
327-
(None, Some(from_trait)) => Some(from_trait),
328-
(Some(from_impl), None) => Some(from_impl),
368+
(None, Some(from_kind)) => Some(from_kind),
369+
(Some(from_kind), None) => Some(from_kind),
329370
_ => None,
330371
};
331372

332-
if let Some(item) = item {
333-
let out = match item.kind {
373+
if let Some(kind) = kind {
374+
let out = match kind {
334375
ty::AssocKind::Fn if ns == ValueNS => "method",
335376
ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
336377
ty::AssocKind::Type if ns == ValueNS => "associatedtype",
337378
_ => return self.variant_field(path_str, current_item, module_id),
338379
};
339380
if extra_fragment.is_some() {
340-
Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Fn {
381+
Err(ErrorKind::AnchorFailure(if kind == ty::AssocKind::Fn {
341382
"methods cannot be followed by anchors"
342383
} else {
343384
"associated constants cannot be followed by anchors"

0 commit comments

Comments
 (0)