Give a much better error message when an item has a macro disambiguator

Previously, this called `check_full_res` for values and types, but not
macros. This would result in not showing when there was a partial
resolution for a parent of the item.

This now calls `check_full_res`. Additionally, it checks if there was a
disambiguator, and if so, says that specific kind wasn't found instead
of saying generically 'associated item'.
This commit is contained in:
Joshua Nelson 2020-08-28 01:31:33 -04:00
parent ee683ef853
commit efdc3facdf
3 changed files with 59 additions and 32 deletions

View file

@ -262,11 +262,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
false,
) {
if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
return Ok(res.map_id(|_| panic!("unexpected id")));
return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
}
}
if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
return Ok(res.map_id(|_| panic!("unexpected id")));
return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
}
if let Some(module_id) = parent_id {
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
@ -276,14 +276,32 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// don't resolve builtins like `#[derive]`
if let Res::Def(..) = res {
let res = res.map_id(|_| panic!("unexpected node_id"));
return Ok(res);
return Some(Ok(res));
}
}
} else {
debug!("attempting to resolve item without parent module: {}", path_str);
return Err(ResolutionFailure::NoParentItem);
return Some(Err(ResolutionFailure::NoParentItem));
}
return Err(ResolutionFailure::NotInScope(path_str.into()));
None
})
// This weird control flow is so we don't borrow the resolver more than once at a time
.unwrap_or_else(|| {
let mut split = path_str.rsplitn(2, "::");
if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) {
return Err(if matches!(res, Res::PrimTy(_)) {
ResolutionFailure::NoPrimitiveAssocItem {
res,
prim_name: parent,
assoc_item: Symbol::intern(base),
}
} else {
ResolutionFailure::NoAssocItem(res, Symbol::intern(base))
});
}
}
Err(ResolutionFailure::NotInScope(path_str.into()))
})
}
/// Resolves a string as a path within a particular namespace. Also returns an optional
@ -981,6 +999,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
cx,
&item,
path_str,
disambiguator,
&dox,
link_range,
smallvec![kind],
@ -1060,6 +1079,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
cx,
&item,
path_str,
disambiguator,
&dox,
link_range,
candidates.into_iter().filter_map(|res| res.err()).collect(),
@ -1114,6 +1134,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
cx,
&item,
path_str,
disambiguator,
&dox,
link_range,
smallvec![kind],
@ -1489,6 +1510,7 @@ fn resolution_failure(
cx: &DocContext<'_>,
item: &Item,
path_str: &str,
disambiguator: Option<Disambiguator>,
dox: &str,
link_range: Option<Range<usize>>,
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
@ -1581,34 +1603,39 @@ fn resolution_failure(
let (kind, def_id) = match res {
Res::Def(kind, def_id) => (kind, def_id),
_ => unreachable!(
"primitives are covered above and other `Res` variants aren't possible at module scope"
x => unreachable!(
"primitives are covered above and other `Res` variants aren't possible at module scope: {:?}",
x,
),
};
let name = cx.tcx.item_name(def_id);
let path_description = match kind {
Mod | ForeignMod => "inner item",
Struct => "field or associated item",
Enum | Union => "variant or associated item",
Variant
| Field
| Closure
| Generator
| AssocTy
| AssocConst
| AssocFn
| Fn
| Macro(_)
| Const
| ConstParam
| ExternCrate
| Use
| LifetimeParam
| Ctor(_, _)
| AnonConst => return assoc_item_not_allowed(res, diag),
Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
| Static => "associated item",
Impl | GlobalAsm => unreachable!("not a path"),
let path_description = if let Some(disambiguator) = disambiguator {
disambiguator.descr()
} else {
match kind {
Mod | ForeignMod => "inner item",
Struct => "field or associated item",
Enum | Union => "variant or associated item",
Variant
| Field
| Closure
| Generator
| AssocTy
| AssocConst
| AssocFn
| Fn
| Macro(_)
| Const
| ConstParam
| ExternCrate
| Use
| LifetimeParam
| Ctor(_, _)
| AnonConst => return assoc_item_not_allowed(res, diag),
Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
| Static => "associated item",
Impl | GlobalAsm => unreachable!("not a path"),
}
};
let note = format!(
"the {} `{}` has no {} named `{}`",

View file

@ -65,7 +65,7 @@ impl S {
/// [T::h!]
//~^ ERROR unresolved link
//~| NOTE no item named `T::h`
//~| NOTE `T` has no macro named `h`
pub trait T {
fn g() {}
}

View file

@ -97,7 +97,7 @@ error: unresolved link to `T::h`
LL | /// [T::h!]
| ^^^^^
|
= note: no item named `T::h` is in scope
= note: the trait `T` has no macro named `h`
error: unresolved link to `S::h`
--> $DIR/intra-link-errors.rs:54:6