Rollup merge of #68911 - jonas-schievink:inherent-overlap, r=petrochenkov

Speed up the inherent impl overlap check

This gives a ~7% improvement in compile times for the stm32f0(x2) crate.

Also addresses @eddyb's comment in https://github.com/rust-lang/rust/pull/68837#discussion_r375701767.
This commit is contained in:
Jonas Schievink 2020-02-09 18:23:33 +01:00 committed by GitHub
commit f6b8281d34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 129 additions and 99 deletions

View File

@ -323,7 +323,7 @@ rustc_queries! {
query associated_item(_: DefId) -> ty::AssocItem {}
/// Collects the associated items defined on a trait or impl.
query associated_items(key: DefId) -> ty::AssocItemsIterator<'tcx> {
query associated_items(key: DefId) -> &'tcx [ty::AssocItem] {
desc { |tcx| "collecting associated items of {}", tcx.def_path_str(key) }
}

View File

@ -541,6 +541,7 @@ fn vtable_methods<'tcx>(
tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| {
let trait_methods = tcx
.associated_items(trait_ref.def_id())
.iter()
.filter(|item| item.kind == ty::AssocKind::Method);
// Now list each method's DefId and InternalSubsts (for within its trait).

View File

@ -212,6 +212,7 @@ fn object_safety_violations_for_trait(
// Check methods for violations.
let mut violations: Vec<_> = tcx
.associated_items(trait_def_id)
.iter()
.filter(|item| item.kind == ty::AssocKind::Method)
.filter_map(|item| {
object_safety_violation_for_method(tcx, trait_def_id, &item)
@ -277,6 +278,7 @@ fn object_safety_violations_for_trait(
violations.extend(
tcx.associated_items(trait_def_id)
.iter()
.filter(|item| item.kind == ty::AssocKind::Const)
.map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)),
);
@ -632,7 +634,9 @@ fn object_ty_for_trait<'tcx>(
let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref))
.flat_map(|super_trait_ref| {
tcx.associated_items(super_trait_ref.def_id()).map(move |item| (super_trait_ref, item))
tcx.associated_items(super_trait_ref.def_id())
.iter()
.map(move |item| (super_trait_ref, item))
})
.filter(|(_, item)| item.kind == ty::AssocKind::Type)
.collect::<Vec<_>>();

View File

@ -1473,7 +1473,7 @@ fn assoc_ty_def(
{
return specialization_graph::NodeItem {
node: specialization_graph::Node::Impl(impl_def_id),
item,
item: *item,
};
}
}

View File

@ -81,7 +81,7 @@ impl<'tcx> Node {
}
/// Iterate over the items defined directly by the given (impl or trait) node.
pub fn items(&self, tcx: TyCtxt<'tcx>) -> ty::AssocItemsIterator<'tcx> {
pub fn items(&self, tcx: TyCtxt<'tcx>) -> &'tcx [ty::AssocItem] {
tcx.associated_items(self.def_id())
}
@ -98,8 +98,10 @@ impl<'tcx> Node {
) -> Option<ty::AssocItem> {
use crate::ty::AssocKind::*;
tcx.associated_items(self.def_id()).find(move |impl_item| {
match (trait_item_kind, impl_item.kind) {
tcx.associated_items(self.def_id())
.iter()
.find(move |impl_item| {
match (trait_item_kind, impl_item.kind) {
| (Const, Const)
| (Method, Method)
| (Type, Type)
@ -112,7 +114,8 @@ impl<'tcx> Node {
| (OpaqueTy, _)
=> false,
}
})
})
.copied()
}
pub fn def_id(&self) -> DefId {

View File

@ -166,7 +166,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let extend_cause_with_original_assoc_item_obligation =
|cause: &mut traits::ObligationCause<'_>,
pred: &ty::Predicate<'_>,
trait_assoc_items: ty::AssocItemsIterator<'_>| {
trait_assoc_items: &[ty::AssocItem]| {
let trait_item = tcx
.hir()
.as_local_hir_id(trait_ref.def_id)
@ -283,6 +283,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
) = (&proj.skip_binder().self_ty().kind, item.map(|i| &i.kind))
{
if let Some((impl_item, trait_assoc_item)) = trait_assoc_items
.iter()
.filter(|i| i.def_id == *item_def_id)
.next()
.and_then(|trait_assoc_item| {
@ -325,7 +326,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
extend_cause_with_original_assoc_item_obligation(
&mut cause,
&pred,
trait_assoc_items.clone(),
trait_assoc_items,
);
traits::Obligation::new(cause, param_env, pred)
});

View File

@ -122,6 +122,7 @@ impl<'tcx> OverloadedDeref<'tcx> {
};
let method_def_id = tcx
.associated_items(trait_def_id.unwrap())
.iter()
.find(|m| m.kind == ty::AssocKind::Method)
.unwrap()
.def_id;

View File

@ -376,6 +376,7 @@ impl<'tcx> Instance<'tcx> {
let fn_once = tcx.lang_items().fn_once_trait().unwrap();
let call_once = tcx
.associated_items(fn_once)
.iter()
.find(|it| it.kind == ty::AssocKind::Method)
.unwrap()
.def_id;

View File

@ -2705,14 +2705,14 @@ impl<'tcx> TyCtxt<'tcx> {
.for_each(|&body_id| f(self.hir().body_owner_def_id(body_id)));
}
pub fn provided_trait_methods(self, id: DefId) -> Vec<AssocItem> {
pub fn provided_trait_methods(self, id: DefId) -> impl Iterator<Item = &'tcx AssocItem> {
self.associated_items(id)
.iter()
.filter(|item| item.kind == AssocKind::Method && item.defaultness.has_value())
.collect()
}
pub fn trait_relevant_for_never(self, did: DefId) -> bool {
self.associated_items(did).any(|item| item.relevant_for_never())
self.associated_items(did).iter().any(|item| item.relevant_for_never())
}
pub fn opt_item_name(self, def_id: DefId) -> Option<Ident> {
@ -2974,25 +2974,6 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
#[derive(Copy, Clone, HashStable)]
pub struct AssocItemsIterator<'tcx> {
pub items: &'tcx [AssocItem],
}
impl<'tcx> Iterator for AssocItemsIterator<'tcx> {
type Item = AssocItem;
#[inline]
fn next(&mut self) -> Option<AssocItem> {
if let Some((first, rest)) = self.items.split_first() {
self.items = rest;
Some(*first)
} else {
None
}
}
}
#[derive(Clone, HashStable)]
pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]);

View File

@ -1066,6 +1066,7 @@ impl<'tcx> ProjectionTy<'tcx> {
) -> ProjectionTy<'tcx> {
let item_def_id = tcx
.associated_items(trait_ref.def_id)
.iter()
.find(|item| {
item.kind == ty::AssocKind::Type
&& tcx.hygienic_eq(item_name, item.ident, trait_ref.def_id)

View File

@ -355,7 +355,7 @@ impl<'tcx> TyCtxt<'tcx> {
let mut dtor_did = None;
let ty = self.type_of(adt_did);
self.for_each_relevant_impl(drop_trait, ty, |impl_did| {
if let Some(item) = self.associated_items(impl_did).next() {
if let Some(item) = self.associated_items(impl_did).first() {
if validate(self, impl_did).is_ok() {
dtor_did = Some(item.def_id);
}

View File

@ -68,6 +68,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
let fn_mut = tcx.lang_items().fn_mut_trait().unwrap();
let call_mut = tcx
.associated_items(fn_mut)
.iter()
.find(|it| it.kind == ty::AssocKind::Method)
.unwrap()
.def_id;

View File

@ -539,7 +539,7 @@ where
debug!("destructor_call_block({:?}, {:?})", self, succ);
let tcx = self.tcx();
let drop_trait = tcx.lang_items().drop_trait().unwrap();
let drop_fn = tcx.associated_items(drop_trait).next().unwrap();
let drop_fn = tcx.associated_items(drop_trait)[0];
let ty = self.place_ty(self.place);
let substs = tcx.mk_substs_trait(ty, &[]);

View File

@ -362,12 +362,12 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for CollectPrivateImplItemsVisitor<'a, 'tcx
return;
}
let provided_trait_methods = self.tcx.provided_trait_methods(trait_def_id);
self.worklist.reserve(provided_trait_methods.len());
for default_method in provided_trait_methods {
let hir_id = self.tcx.hir().as_local_hir_id(default_method.def_id).unwrap();
self.worklist.push(hir_id);
}
// FIXME(#53488) remove `let`
let tcx = self.tcx;
self.worklist.extend(
tcx.provided_trait_methods(trait_def_id)
.map(|assoc| tcx.hir().as_local_hir_id(assoc.def_id).unwrap()),
);
}
}
}

View File

@ -468,6 +468,7 @@ impl Visitor<'tcx> for Checker<'tcx> {
let trait_item_def_id = self
.tcx
.associated_items(trait_did)
.iter()
.find(|item| item.ident.name == impl_item.ident.name)
.map(|item| item.def_id);
if let Some(def_id) = trait_item_def_id {

View File

@ -423,6 +423,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
qualname.push_str(&self.tcx.def_path_str(def_id));
self.tcx
.associated_items(def_id)
.iter()
.find(|item| item.ident.name == ident.name)
.map(|item| decl_id = Some(item.def_id));
}
@ -717,6 +718,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
let ti = self.tcx.associated_item(decl_id);
self.tcx
.associated_items(ti.container.id())
.iter()
.find(|item| {
item.ident.name == ti.ident.name && item.defaultness.has_value()
})

View File

@ -206,12 +206,10 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
}
}
fn associated_items<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AssocItemsIterator<'tcx> {
ty::AssocItemsIterator {
items: tcx.arena.alloc_from_iter(
tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)),
),
}
fn associated_items<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx [ty::AssocItem] {
tcx.arena.alloc_from_iter(
tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did)),
)
}
fn def_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {

View File

@ -1109,7 +1109,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_def_id: DefId,
assoc_name: ast::Ident,
) -> bool {
self.tcx().associated_items(trait_def_id).any(|item| {
self.tcx().associated_items(trait_def_id).iter().any(|item| {
item.kind == ty::AssocKind::Type
&& self.tcx().hygienic_eq(assoc_name, item.ident, trait_def_id)
})
@ -1347,6 +1347,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
let assoc_ty = tcx
.associated_items(candidate.def_id())
.iter()
.find(|i| i.kind == ty::AssocKind::Type && i.ident.modern() == assoc_ident)
.expect("missing associated type");
@ -1512,6 +1513,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
ty::Predicate::Trait(pred, _) => {
associated_types.entry(span).or_default().extend(
tcx.associated_items(pred.def_id())
.iter()
.filter(|item| item.kind == ty::AssocKind::Type)
.map(|item| item.def_id),
);
@ -1969,6 +1971,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let bound_span = self
.tcx()
.associated_items(bound.def_id())
.iter()
.find(|item| {
item.kind == ty::AssocKind::Type
&& self.tcx().hygienic_eq(assoc_name, item.ident, bound.def_id())
@ -2198,6 +2201,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id);
let item = tcx
.associated_items(trait_did)
.iter()
.find(|i| Namespace::from(i.kind) == Namespace::Type && i.ident.modern() == assoc_ident)
.expect("missing associated type");

View File

@ -248,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if is_gen {
// Check that we deduce the signature from the `<_ as std::ops::Generator>::Return`
// associated item and not yield.
let return_assoc_item = self.tcx.associated_items(gen_trait).nth(1).unwrap().def_id;
let return_assoc_item = self.tcx.associated_items(gen_trait)[1].def_id;
if return_assoc_item != projection.projection_def_id() {
debug!("deduce_sig_from_projection: not return assoc item of generator");
return None;
@ -673,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// The `Future` trait has only one associted item, `Output`,
// so check that this is what we see.
let output_assoc_item = self.tcx.associated_items(future_trait).nth(0).unwrap().def_id;
let output_assoc_item = self.tcx.associated_items(future_trait)[0].def_id;
if output_assoc_item != predicate.projection_ty.item_def_id {
span_bug!(
cause_span,

View File

@ -536,6 +536,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let item_def_id = self
.tcx
.associated_items(deref_trait)
.iter()
.find(|item| item.kind == ty::AssocKind::Type)
.unwrap()
.def_id;

View File

@ -474,8 +474,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name: ast::Ident,
ns: Namespace,
) -> Option<ty::AssocItem> {
self.tcx.associated_items(def_id).find(|item| {
Namespace::from(item.kind) == ns && self.tcx.hygienic_eq(item_name, item.ident, def_id)
})
self.tcx
.associated_items(def_id)
.iter()
.find(|item| {
Namespace::from(item.kind) == ns
&& self.tcx.hygienic_eq(item_name, item.ident, def_id)
})
.copied()
}
}

View File

@ -1696,10 +1696,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let max_dist = max(name.as_str().len(), 3) / 3;
self.tcx
.associated_items(def_id)
.iter()
.filter(|x| {
let dist = lev_distance(&*name.as_str(), &x.ident.as_str());
Namespace::from(x.kind) == Namespace::Value && dist > 0 && dist <= max_dist
})
.copied()
.collect()
} else {
self.fcx
@ -1707,7 +1709,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
.map_or(Vec::new(), |x| vec![x])
}
} else {
self.tcx.associated_items(def_id).collect()
self.tcx.associated_items(def_id).to_vec()
}
}
}

View File

@ -1976,6 +1976,7 @@ fn check_impl_items_against_trait<'tcx>(
let ty_impl_item = tcx.associated_item(tcx.hir().local_def_id(impl_item.hir_id));
let ty_trait_item = tcx
.associated_items(impl_trait_ref.def_id)
.iter()
.find(|ac| {
Namespace::from(&impl_item.kind) == Namespace::from(ac.kind)
&& tcx.hygienic_eq(ty_impl_item.ident, ac.ident, impl_trait_ref.def_id)
@ -1983,6 +1984,7 @@ fn check_impl_items_against_trait<'tcx>(
.or_else(|| {
// Not compatible, but needed for the error message
tcx.associated_items(impl_trait_ref.def_id)
.iter()
.find(|ac| tcx.hygienic_eq(ty_impl_item.ident, ac.ident, impl_trait_ref.def_id))
});
@ -2096,7 +2098,7 @@ fn check_impl_items_against_trait<'tcx>(
if !is_implemented && !traits::impl_is_default(tcx, impl_id) {
if !trait_item.defaultness.has_value() {
missing_items.push(trait_item);
missing_items.push(*trait_item);
} else if associated_type_overridden {
invalidated_items.push(trait_item.ident);
}
@ -5175,7 +5177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Check for `Future` implementations by constructing a predicate to
// prove: `<T as Future>::Output == U`
let future_trait = self.tcx.lang_items().future_trait().unwrap();
let item_def_id = self.tcx.associated_items(future_trait).next().unwrap().def_id;
let item_def_id = self.tcx.associated_items(future_trait)[0].def_id;
let predicate =
ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
// `<T as Future>::Output`

View File

@ -1,6 +1,6 @@
use crate::namespace::Namespace;
use rustc::traits::{self, IntercrateMode, SkipLeakCheck};
use rustc::ty::TyCtxt;
use rustc::ty::{AssocItem, TyCtxt};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
@ -17,38 +17,60 @@ struct InherentOverlapChecker<'tcx> {
}
impl InherentOverlapChecker<'tcx> {
/// Checks whether any associated items in impls 1 and 2 share the same identifier and
/// namespace.
fn impls_have_common_items(&self, impl1: DefId, impl2: DefId) -> bool {
let impl_items1 = self.tcx.associated_items(impl1);
let impl_items2 = self.tcx.associated_items(impl2);
for item1 in &impl_items1[..] {
for item2 in &impl_items2[..] {
// Avoid costly `.modern()` calls as much as possible by doing them as late as we
// can. Compare raw symbols first.
if item1.ident.name == item2.ident.name
&& Namespace::from(item1.kind) == Namespace::from(item2.kind)
{
// Symbols and namespace match, compare hygienically.
if item1.ident.modern() == item2.ident.modern() {
return true;
}
}
}
}
false
}
fn check_for_common_items_in_impls(
&self,
impl1: DefId,
impl2: DefId,
overlap: traits::OverlapResult<'_>,
) {
let name_and_namespace = |def_id| {
let item = self.tcx.associated_item(def_id);
(item.ident.modern(), Namespace::from(item.kind))
};
let name_and_namespace =
|assoc: &AssocItem| (assoc.ident.modern(), Namespace::from(assoc.kind));
let impl_items1 = self.tcx.associated_item_def_ids(impl1);
let impl_items2 = self.tcx.associated_item_def_ids(impl2);
let impl_items1 = self.tcx.associated_items(impl1);
let impl_items2 = self.tcx.associated_items(impl2);
for &item1 in &impl_items1[..] {
for item1 in &impl_items1[..] {
let (name, namespace) = name_and_namespace(item1);
for &item2 in &impl_items2[..] {
for item2 in &impl_items2[..] {
if (name, namespace) == name_and_namespace(item2) {
let mut err = struct_span_err!(
self.tcx.sess,
self.tcx.span_of_impl(item1).unwrap(),
self.tcx.span_of_impl(item1.def_id).unwrap(),
E0592,
"duplicate definitions with name `{}`",
name
);
err.span_label(
self.tcx.span_of_impl(item1).unwrap(),
self.tcx.span_of_impl(item1.def_id).unwrap(),
format!("duplicate definitions for `{}`", name),
);
err.span_label(
self.tcx.span_of_impl(item2).unwrap(),
self.tcx.span_of_impl(item2.def_id).unwrap(),
format!("other definition for `{}`", name),
);
@ -66,27 +88,21 @@ impl InherentOverlapChecker<'tcx> {
}
}
fn check_for_overlapping_inherent_impls(&self, ty_def_id: DefId) {
let impls = self.tcx.inherent_impls(ty_def_id);
for (i, &impl1_def_id) in impls.iter().enumerate() {
for &impl2_def_id in &impls[(i + 1)..] {
traits::overlapping_impls(
self.tcx,
impl1_def_id,
impl2_def_id,
IntercrateMode::Issue43355,
// We go ahead and just skip the leak check for
// inherent impls without warning.
SkipLeakCheck::Yes,
|overlap| {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
false
},
|| true,
);
}
}
fn check_for_overlapping_inherent_impls(&self, impl1_def_id: DefId, impl2_def_id: DefId) {
traits::overlapping_impls(
self.tcx,
impl1_def_id,
impl2_def_id,
IntercrateMode::Issue43355,
// We go ahead and just skip the leak check for
// inherent impls without warning.
SkipLeakCheck::Yes,
|overlap| {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
false
},
|| true,
);
}
}
@ -97,8 +113,16 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> {
| hir::ItemKind::Struct(..)
| hir::ItemKind::Trait(..)
| hir::ItemKind::Union(..) => {
let type_def_id = self.tcx.hir().local_def_id(item.hir_id);
self.check_for_overlapping_inherent_impls(type_def_id);
let ty_def_id = self.tcx.hir().local_def_id(item.hir_id);
let impls = self.tcx.inherent_impls(ty_def_id);
for (i, &impl1_def_id) in impls.iter().enumerate() {
for &impl2_def_id in &impls[(i + 1)..] {
if self.impls_have_common_items(impl1_def_id, impl2_def_id) {
self.check_for_overlapping_inherent_impls(impl1_def_id, impl2_def_id);
}
}
}
}
_ => {}
}

View File

@ -87,7 +87,6 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
.cx
.tcx
.provided_trait_methods(trait_def_id)
.into_iter()
.map(|meth| meth.ident.to_string())
.collect();
@ -115,6 +114,8 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
.cx
.tcx
.associated_items(impl_def_id)
.iter()
.copied()
.collect::<Vec<_>>()
.clean(self.cx),
polarity: None,

View File

@ -191,7 +191,7 @@ pub fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind)
pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait {
let auto_trait = cx.tcx.trait_def(did).has_auto_impl;
let trait_items = cx.tcx.associated_items(did).map(|item| item.clean(cx)).collect();
let trait_items = cx.tcx.associated_items(did).iter().map(|item| item.clean(cx)).collect();
let predicates = cx.tcx.predicates_of(did);
let generics = (cx.tcx.generics_of(did), predicates).clean(cx);
let generics = filter_non_trait_generics(did, generics);
@ -376,6 +376,7 @@ pub fn build_impl(
} else {
(
tcx.associated_items(did)
.iter()
.filter_map(|item| {
if associated_trait.is_some() || item.vis == ty::Visibility::Public {
Some(item.clean(cx))
@ -401,9 +402,7 @@ pub fn build_impl(
let provided = trait_
.def_id()
.map(|did| {
tcx.provided_trait_methods(did).into_iter().map(|meth| meth.ident.to_string()).collect()
})
.map(|did| tcx.provided_trait_methods(did).map(|meth| meth.ident.to_string()).collect())
.unwrap_or_default();
debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());

View File

@ -2108,11 +2108,7 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
let provided: FxHashSet<String> = trait_
.def_id()
.map(|did| {
cx.tcx
.provided_trait_methods(did)
.into_iter()
.map(|meth| meth.ident.to_string())
.collect()
cx.tcx.provided_trait_methods(did).map(|meth| meth.ident.to_string()).collect()
})
.unwrap_or_default();

View File

@ -206,6 +206,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
return cx
.tcx
.associated_items(did)
.iter()
.find(|item| item.ident.name == item_name)
.and_then(|item| match item.kind {
ty::AssocKind::Method => Some("method"),