Skip to content

Commit efdc3fa

Browse files
committed
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'.
1 parent ee683ef commit efdc3fa

File tree

3 files changed

+59
-32
lines changed

3 files changed

+59
-32
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

+57-30
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
262262
false,
263263
) {
264264
if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
265-
return Ok(res.map_id(|_| panic!("unexpected id")));
265+
return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
266266
}
267267
}
268268
if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
269-
return Ok(res.map_id(|_| panic!("unexpected id")));
269+
return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
270270
}
271271
if let Some(module_id) = parent_id {
272272
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
@@ -276,14 +276,32 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
276276
// don't resolve builtins like `#[derive]`
277277
if let Res::Def(..) = res {
278278
let res = res.map_id(|_| panic!("unexpected node_id"));
279-
return Ok(res);
279+
return Some(Ok(res));
280280
}
281281
}
282282
} else {
283283
debug!("attempting to resolve item without parent module: {}", path_str);
284-
return Err(ResolutionFailure::NoParentItem);
284+
return Some(Err(ResolutionFailure::NoParentItem));
285285
}
286-
return Err(ResolutionFailure::NotInScope(path_str.into()));
286+
None
287+
})
288+
// This weird control flow is so we don't borrow the resolver more than once at a time
289+
.unwrap_or_else(|| {
290+
let mut split = path_str.rsplitn(2, "::");
291+
if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
292+
if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) {
293+
return Err(if matches!(res, Res::PrimTy(_)) {
294+
ResolutionFailure::NoPrimitiveAssocItem {
295+
res,
296+
prim_name: parent,
297+
assoc_item: Symbol::intern(base),
298+
}
299+
} else {
300+
ResolutionFailure::NoAssocItem(res, Symbol::intern(base))
301+
});
302+
}
303+
}
304+
Err(ResolutionFailure::NotInScope(path_str.into()))
287305
})
288306
}
289307
/// 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> {
981999
cx,
9821000
&item,
9831001
path_str,
1002+
disambiguator,
9841003
&dox,
9851004
link_range,
9861005
smallvec![kind],
@@ -1060,6 +1079,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
10601079
cx,
10611080
&item,
10621081
path_str,
1082+
disambiguator,
10631083
&dox,
10641084
link_range,
10651085
candidates.into_iter().filter_map(|res| res.err()).collect(),
@@ -1114,6 +1134,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
11141134
cx,
11151135
&item,
11161136
path_str,
1137+
disambiguator,
11171138
&dox,
11181139
link_range,
11191140
smallvec![kind],
@@ -1489,6 +1510,7 @@ fn resolution_failure(
14891510
cx: &DocContext<'_>,
14901511
item: &Item,
14911512
path_str: &str,
1513+
disambiguator: Option<Disambiguator>,
14921514
dox: &str,
14931515
link_range: Option<Range<usize>>,
14941516
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
@@ -1581,34 +1603,39 @@ fn resolution_failure(
15811603

15821604
let (kind, def_id) = match res {
15831605
Res::Def(kind, def_id) => (kind, def_id),
1584-
_ => unreachable!(
1585-
"primitives are covered above and other `Res` variants aren't possible at module scope"
1606+
x => unreachable!(
1607+
"primitives are covered above and other `Res` variants aren't possible at module scope: {:?}",
1608+
x,
15861609
),
15871610
};
15881611
let name = cx.tcx.item_name(def_id);
1589-
let path_description = match kind {
1590-
Mod | ForeignMod => "inner item",
1591-
Struct => "field or associated item",
1592-
Enum | Union => "variant or associated item",
1593-
Variant
1594-
| Field
1595-
| Closure
1596-
| Generator
1597-
| AssocTy
1598-
| AssocConst
1599-
| AssocFn
1600-
| Fn
1601-
| Macro(_)
1602-
| Const
1603-
| ConstParam
1604-
| ExternCrate
1605-
| Use
1606-
| LifetimeParam
1607-
| Ctor(_, _)
1608-
| AnonConst => return assoc_item_not_allowed(res, diag),
1609-
Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1610-
| Static => "associated item",
1611-
Impl | GlobalAsm => unreachable!("not a path"),
1612+
let path_description = if let Some(disambiguator) = disambiguator {
1613+
disambiguator.descr()
1614+
} else {
1615+
match kind {
1616+
Mod | ForeignMod => "inner item",
1617+
Struct => "field or associated item",
1618+
Enum | Union => "variant or associated item",
1619+
Variant
1620+
| Field
1621+
| Closure
1622+
| Generator
1623+
| AssocTy
1624+
| AssocConst
1625+
| AssocFn
1626+
| Fn
1627+
| Macro(_)
1628+
| Const
1629+
| ConstParam
1630+
| ExternCrate
1631+
| Use
1632+
| LifetimeParam
1633+
| Ctor(_, _)
1634+
| AnonConst => return assoc_item_not_allowed(res, diag),
1635+
Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1636+
| Static => "associated item",
1637+
Impl | GlobalAsm => unreachable!("not a path"),
1638+
}
16121639
};
16131640
let note = format!(
16141641
"the {} `{}` has no {} named `{}`",

src/test/rustdoc-ui/intra-link-errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl S {
6565

6666
/// [T::h!]
6767
//~^ ERROR unresolved link
68-
//~| NOTE no item named `T::h`
68+
//~| NOTE `T` has no macro named `h`
6969
pub trait T {
7070
fn g() {}
7171
}

src/test/rustdoc-ui/intra-link-errors.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ error: unresolved link to `T::h`
9797
LL | /// [T::h!]
9898
| ^^^^^
9999
|
100-
= note: no item named `T::h` is in scope
100+
= note: the trait `T` has no macro named `h`
101101

102102
error: unresolved link to `S::h`
103103
--> $DIR/intra-link-errors.rs:54:6

0 commit comments

Comments
 (0)