Skip to content

Commit 11fe2cd

Browse files
committed
Fix some bugs; handle ambiguity errors instead of assuming they're resolution failures
The version of rust-lang#74489 that warns about ambiguity errors
1 parent 59007a5 commit 11fe2cd

File tree

1 file changed

+87
-16
lines changed

1 file changed

+87
-16
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

+87-16
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
239239
// If resolution failed, it may still be a method
240240
// because methods are not handled by the resolver
241241
// If so, bail when we're not looking for a value.
242+
// TODO: is this correct? What about associated types?
242243
if ns != ValueNS {
243244
return Err(ErrorKind::ResolutionFailure);
244245
}
@@ -305,7 +306,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
305306
// To handle that properly resolve() would have to support
306307
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
307308
if kind.is_none() {
308-
kind = resolve_associated_trait_item(did, item_name, &self.cx)?;
309+
kind = resolve_associated_trait_item(did, item_name, ns, &self.cx)?;
310+
debug!("got associated item kind {:?}", kind);
309311
}
310312

311313
if let Some(kind) = kind {
@@ -405,23 +407,27 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
405407
}
406408
}
407409

408-
fn resolve_associated_trait_item(did: DefId, item_name: Symbol, cx: &DocContext<'_>) -> Result<Option<ty::AssocKind>, ErrorKind> {
410+
fn resolve_associated_trait_item(did: DefId, item_name: Symbol, ns: Namespace, cx: &DocContext<'_>) -> Result<Option<ty::AssocKind>, ErrorKind> {
411+
use rustc_hir::def_id::LOCAL_CRATE;
412+
409413
let ty = cx.tcx.type_of(did);
410-
// Checks if item_name belongs to `impl SomeItem`
411-
// `def_id` should be a trait
412-
let associated_items_for_def_id = |tcx: ty::TyCtxt<'_>, def_id: DefId| -> Vec<_> {
413-
tcx.associated_items(def_id)
414-
.filter_by_name(tcx, Ident::with_dummy_span(item_name), def_id)
414+
// Checks if item_name belongs to this `impl_`
415+
// `impl_` should be a trait or trait implementation.
416+
let associated_items_for_impl = |tcx: ty::TyCtxt<'_>, impl_: DefId| -> Vec<_> {
417+
tcx.associated_items(impl_)
418+
// TODO: should this be unhygienic?
419+
.filter_by_name(tcx, Ident::with_dummy_span(item_name), impl_)
415420
.map(|assoc| (assoc.def_id, assoc.kind))
416421
// TODO: this collect seems a shame
417422
.collect()
418423
};
419-
let impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
420-
let candidates: Vec<_> = impls
424+
// First consider automatic impls: `impl From<T> for T`
425+
let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
426+
let mut candidates: Vec<_> = implicit_impls
421427
.flat_map(|impl_outer| {
422428
match impl_outer.inner {
423429
ImplItem(impl_) => {
424-
debug!("considering trait {:?}", impl_.trait_);
430+
debug!("considering auto or blanket impl for trait {:?}", impl_.trait_);
425431
// Give precedence to methods that were overridden
426432
if !impl_.provided_trait_methods.contains(&*item_name.as_str()) {
427433
impl_.items.into_iter()
@@ -449,22 +455,60 @@ fn resolve_associated_trait_item(did: DefId, item_name: Symbol, cx: &DocContext<
449455
// }
450456
// ```
451457
// TODO: this is wrong, it should look at the trait, not the impl
452-
associated_items_for_def_id(cx.tcx, impl_outer.def_id)
458+
associated_items_for_impl(cx.tcx, impl_outer.def_id)
453459
}
454460
}
455461
_ => panic!("get_impls returned something that wasn't an impl"),
456462
}
457463
})
458-
.chain(cx.tcx.all_impls(did).flat_map(|impl_| associated_items_for_def_id(cx.tcx, impl_)))
459-
//.chain(cx.tcx.all_local_trait_impls(did).flat_map(|impl_| associated_items_for_def_id(cx.tcx, impl_)))
460464
.collect();
465+
// Next consider explicit impls: `impl MyTrait for MyType`
466+
// There isn't a cheap way to do this. Just look at every trait!
467+
for &trait_ in cx.tcx.all_traits(LOCAL_CRATE) {
468+
debug!("considering explicit impl for trait {:?}", trait_);
469+
// We can skip the trait if it doesn't have the associated item `item_name`
470+
let assoc_item = cx.tcx
471+
.associated_items(trait_)
472+
.find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
473+
.map(|assoc| (assoc.def_id, assoc.kind));
474+
if let Some(assoc_item) = assoc_item {
475+
debug!("considering item {:?}", assoc_item);
476+
// Look at each trait implementation to see if it's an impl for `did`
477+
cx.tcx.for_each_relevant_impl(trait_, ty, |impl_| {
478+
use ty::TyKind;
479+
480+
let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
481+
// Check if these are the same type.
482+
let impl_type = trait_ref.self_ty();
483+
debug!("comparing type {} with kind {:?} against def_id {:?}", impl_type, impl_type.kind, did);
484+
// Fast path: if this is a primitive simple `==` will work
485+
let same_type = impl_type == ty || match impl_type.kind {
486+
// Check if these are the same def_id
487+
TyKind::Adt(def, _) => {
488+
debug!("adt did: {:?}", def.did);
489+
def.did == did
490+
}
491+
TyKind::Foreign(def_id) => def_id == did,
492+
_ => false,
493+
};
494+
if same_type {
495+
// We found it!
496+
debug!("found a match!");
497+
candidates.push(assoc_item);
498+
}
499+
});
500+
}
501+
}
502+
461503
if candidates.len() > 1 {
504+
debug!("ambiguous");
462505
let candidates = candidates.into_iter()
463506
.map(|(def_id, kind)| Res::Def(kind.as_def_kind(), def_id))
464507
.collect();
465508
return Err(ErrorKind::Ambiguous { candidates });
466509
}
467-
Ok(candidates.into_iter().next().map(|(_, kind)| kind))
510+
// Cleanup and go home
511+
Ok(candidates.pop().map(|(_, kind)| kind))
468512
}
469513

470514
/// Check for resolve collisions between a trait and its derive
@@ -718,6 +762,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
718762
continue;
719763
}
720764
Err(ErrorKind::Ambiguous { candidates }) => {
765+
debug!("got ambiguity error: {:?}", candidates);
721766
ambiguity_error(
722767
cx,
723768
&item,
@@ -776,11 +821,23 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
776821
base_node,
777822
&extra_fragment,
778823
) {
824+
Ok(res) => Some(res),
779825
Err(ErrorKind::AnchorFailure(msg)) => {
780826
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
781827
continue;
782828
}
783-
x => x.ok(),
829+
Err(ErrorKind::ResolutionFailure) => None,
830+
Err(ErrorKind::Ambiguous { candidates }) => {
831+
ambiguity_error(
832+
cx,
833+
&item,
834+
path_str,
835+
&dox,
836+
link_range,
837+
candidates.into_iter().map(|res| (res, TypeNS)).collect(),
838+
);
839+
continue;
840+
}
784841
},
785842
value_ns: match self.resolve(
786843
path_str,
@@ -790,11 +847,23 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
790847
base_node,
791848
&extra_fragment,
792849
) {
850+
Ok(res) => Some(res),
793851
Err(ErrorKind::AnchorFailure(msg)) => {
794852
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
795853
continue;
796854
}
797-
x => x.ok(),
855+
Err(ErrorKind::ResolutionFailure) => None,
856+
Err(ErrorKind::Ambiguous { candidates }) => {
857+
ambiguity_error(
858+
cx,
859+
&item,
860+
path_str,
861+
&dox,
862+
link_range,
863+
candidates.into_iter().map(|res| (res, TypeNS)).collect(),
864+
);
865+
continue;
866+
}
798867
}
799868
.and_then(|(res, fragment)| {
800869
// Constructors are picked up in the type namespace.
@@ -1028,9 +1097,11 @@ fn ambiguity_error(
10281097
link_range: Option<Range<usize>>,
10291098
candidates: Vec<(Res, Namespace)>,
10301099
) {
1100+
assert!(candidates.len() >= 2);
10311101
let hir_id = match cx.as_local_hir_id(item.def_id) {
10321102
Some(hir_id) => hir_id,
10331103
None => {
1104+
debug!("ignoring ambiguity error for non-local item");
10341105
// If non-local, no need to check anything.
10351106
return;
10361107
}

0 commit comments

Comments
 (0)