Skip to content

Commit e8f4b82

Browse files
committed
[wip] handle ambiguous associated items
mentioning rust-lang#83849 (comment)
1 parent 35de890 commit e8f4b82

File tree

2 files changed

+76
-68
lines changed

2 files changed

+76
-68
lines changed

src/librustdoc/clean/mod.rs

+38-36
Original file line numberDiff line numberDiff line change
@@ -82,49 +82,51 @@ impl<T: Clean<U>, U> Clean<Option<U>> for Option<T> {
8282
}
8383
}
8484

85+
// Collect all inner modules which are tagged as implementations of
86+
// primitives.
87+
//
88+
// Note that this loop only searches the top-level items of the crate,
89+
// and this is intentional. If we were to search the entire crate for an
90+
// item tagged with `#[doc(primitive)]` then we would also have to
91+
// search the entirety of external modules for items tagged
92+
// `#[doc(primitive)]`, which is a pretty inefficient process (decoding
93+
// all that metadata unconditionally).
94+
//
95+
// In order to keep the metadata load under control, the
96+
// `#[doc(primitive)]` feature is explicitly designed to only allow the
97+
// primitive tags to show up as the top level items in a crate.
98+
//
99+
// Also note that this does not attempt to deal with modules tagged
100+
// duplicately for the same primitive. This is handled later on when
101+
// rendering by delegating everything to a hash map.
102+
crate fn parse_primitive(res: Res, cx: &mut DocContext<'_>) -> Option<(DefId, PrimitiveType)> {
103+
if let Res::Def(DefKind::Mod, def_id) = res {
104+
let attrs = cx.tcx.get_attrs(def_id).clean(cx);
105+
let mut prim = None;
106+
for attr in attrs.lists(sym::doc) {
107+
if let Some(v) = attr.value_str() {
108+
if attr.has_name(sym::primitive) {
109+
prim = PrimitiveType::from_symbol(v);
110+
if prim.is_some() {
111+
break;
112+
}
113+
// FIXME: should warn on unknown primitives?
114+
}
115+
}
116+
}
117+
return prim.map(|p| (def_id, p));
118+
}
119+
None
120+
}
121+
85122
impl Clean<ExternalCrate> for CrateNum {
86123
fn clean(&self, cx: &mut DocContext<'_>) -> ExternalCrate {
87124
let tcx = cx.tcx;
88125
let root = DefId { krate: *self, index: CRATE_DEF_INDEX };
89126
let krate_span = tcx.def_span(root);
90127
let krate_src = cx.sess().source_map().span_to_filename(krate_span);
128+
let mut as_primitive = |p| parse_primitive(p, cx);
91129

92-
// Collect all inner modules which are tagged as implementations of
93-
// primitives.
94-
//
95-
// Note that this loop only searches the top-level items of the crate,
96-
// and this is intentional. If we were to search the entire crate for an
97-
// item tagged with `#[doc(primitive)]` then we would also have to
98-
// search the entirety of external modules for items tagged
99-
// `#[doc(primitive)]`, which is a pretty inefficient process (decoding
100-
// all that metadata unconditionally).
101-
//
102-
// In order to keep the metadata load under control, the
103-
// `#[doc(primitive)]` feature is explicitly designed to only allow the
104-
// primitive tags to show up as the top level items in a crate.
105-
//
106-
// Also note that this does not attempt to deal with modules tagged
107-
// duplicately for the same primitive. This is handled later on when
108-
// rendering by delegating everything to a hash map.
109-
let mut as_primitive = |res: Res| {
110-
if let Res::Def(DefKind::Mod, def_id) = res {
111-
let attrs = cx.tcx.get_attrs(def_id).clean(cx);
112-
let mut prim = None;
113-
for attr in attrs.lists(sym::doc) {
114-
if let Some(v) = attr.value_str() {
115-
if attr.has_name(sym::primitive) {
116-
prim = PrimitiveType::from_symbol(v);
117-
if prim.is_some() {
118-
break;
119-
}
120-
// FIXME: should warn on unknown primitives?
121-
}
122-
}
123-
}
124-
return prim.map(|p| (def_id, p));
125-
}
126-
None
127-
};
128130
let primitives = if root.is_local() {
129131
tcx.hir()
130132
.krate()

src/librustdoc/passes/collect_intra_doc_links.rs

+38-32
Original file line numberDiff line numberDiff line change
@@ -442,18 +442,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
442442
/// NOTE: `resolve_str_path_error` knows only about paths, not about types.
443443
/// Associated items will never be resolved by this function.
444444
fn resolve_path(&self, path_str: &str, ns: Namespace, module_id: DefId) -> Option<Res> {
445-
let result = self.cx.enter_resolver(|resolver| {
446-
resolver
447-
.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
448-
.and_then(|(_, res)| res.try_into())
445+
// resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
446+
// manually as bool. Give them precedence because the `doc(primitive)` check depends on it.
447+
let result = resolve_primitive(path_str, ns).or_else(|| {
448+
self.cx.enter_resolver(|resolver| {
449+
resolver
450+
.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
451+
})
452+
.and_then(|(_, res)| res.try_into())
453+
.ok()
449454
});
450455
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
451-
match result {
452-
// resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
453-
// manually as bool
454-
Err(()) => resolve_primitive(path_str, ns),
455-
Ok(res) => Some(res),
456-
}
456+
result
457457
}
458458

459459
/// Resolves a string as a path within a particular namespace. Returns an
@@ -1075,29 +1075,35 @@ impl LinkCollector<'_, '_> {
10751075
if matches!(
10761076
disambiguator,
10771077
None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
1078-
) && !matches!(res, Res::Primitive(_))
1079-
{
1080-
if let Some(prim) = resolve_primitive(path_str, TypeNS) {
1081-
// `prim@char`
1082-
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
1083-
if fragment.is_some() {
1084-
anchor_failure(
1085-
self.cx,
1086-
&item,
1087-
path_str,
1088-
dox,
1089-
ori_link.range,
1090-
AnchorFailure::RustdocAnchorConflict(prim),
1091-
);
1092-
return None;
1078+
) {
1079+
if let Res::Def(kind, id) = res {
1080+
if let Some(prim) = resolve_primitive(path_str, TypeNS) {
1081+
// `prim@char`
1082+
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
1083+
if fragment.is_some() {
1084+
anchor_failure(
1085+
self.cx,
1086+
&item,
1087+
path_str,
1088+
dox,
1089+
ori_link.range,
1090+
AnchorFailure::RustdocAnchorConflict(prim),
1091+
);
1092+
return None;
1093+
}
1094+
res = prim;
1095+
fragment = Some(prim.name(self.cx.tcx));
1096+
} else {
1097+
// `[char]` when a `char` module is in scope
1098+
assert_eq!(kind, DefKind::Mod);
1099+
// Very special case: when the mod has `doc(primitive)`, don't give an error.
1100+
let mod_ = rustc_hir::def::Res::Def(DefKind::Mod, id);
1101+
if crate::clean::parse_primitive(mod_, self.cx).is_none() {
1102+
let candidates = vec![res, prim];
1103+
ambiguity_error(self.cx, &item, path_str, dox, ori_link.range, candidates);
1104+
return None;
1105+
}
10931106
}
1094-
res = prim;
1095-
fragment = Some(prim.name(self.cx.tcx));
1096-
} else {
1097-
// `[char]` when a `char` module is in scope
1098-
let candidates = vec![res, prim];
1099-
ambiguity_error(self.cx, &item, path_str, dox, ori_link.range, candidates);
1100-
return None;
11011107
}
11021108
}
11031109
}

0 commit comments

Comments
 (0)