Skip to content

Commit

Permalink
[wip] handle ambiguous associated items
Browse files Browse the repository at this point in the history
  • Loading branch information
jyn514 committed Apr 4, 2021
1 parent 35de890 commit e8f4b82
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 68 deletions.
74 changes: 38 additions & 36 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,49 +82,51 @@ impl<T: Clean<U>, U> Clean<Option<U>> for Option<T> {
}
}

// Collect all inner modules which are tagged as implementations of
// primitives.
//
// Note that this loop only searches the top-level items of the crate,
// and this is intentional. If we were to search the entire crate for an
// item tagged with `#[doc(primitive)]` then we would also have to
// search the entirety of external modules for items tagged
// `#[doc(primitive)]`, which is a pretty inefficient process (decoding
// all that metadata unconditionally).
//
// In order to keep the metadata load under control, the
// `#[doc(primitive)]` feature is explicitly designed to only allow the
// primitive tags to show up as the top level items in a crate.
//
// Also note that this does not attempt to deal with modules tagged
// duplicately for the same primitive. This is handled later on when
// rendering by delegating everything to a hash map.
crate fn parse_primitive(res: Res, cx: &mut DocContext<'_>) -> Option<(DefId, PrimitiveType)> {
if let Res::Def(DefKind::Mod, def_id) = res {
let attrs = cx.tcx.get_attrs(def_id).clean(cx);
let mut prim = None;
for attr in attrs.lists(sym::doc) {
if let Some(v) = attr.value_str() {
if attr.has_name(sym::primitive) {
prim = PrimitiveType::from_symbol(v);
if prim.is_some() {
break;
}
// FIXME: should warn on unknown primitives?
}
}
}
return prim.map(|p| (def_id, p));
}
None
}

impl Clean<ExternalCrate> for CrateNum {
fn clean(&self, cx: &mut DocContext<'_>) -> ExternalCrate {
let tcx = cx.tcx;
let root = DefId { krate: *self, index: CRATE_DEF_INDEX };
let krate_span = tcx.def_span(root);
let krate_src = cx.sess().source_map().span_to_filename(krate_span);
let mut as_primitive = |p| parse_primitive(p, cx);

// Collect all inner modules which are tagged as implementations of
// primitives.
//
// Note that this loop only searches the top-level items of the crate,
// and this is intentional. If we were to search the entire crate for an
// item tagged with `#[doc(primitive)]` then we would also have to
// search the entirety of external modules for items tagged
// `#[doc(primitive)]`, which is a pretty inefficient process (decoding
// all that metadata unconditionally).
//
// In order to keep the metadata load under control, the
// `#[doc(primitive)]` feature is explicitly designed to only allow the
// primitive tags to show up as the top level items in a crate.
//
// Also note that this does not attempt to deal with modules tagged
// duplicately for the same primitive. This is handled later on when
// rendering by delegating everything to a hash map.
let mut as_primitive = |res: Res| {
if let Res::Def(DefKind::Mod, def_id) = res {
let attrs = cx.tcx.get_attrs(def_id).clean(cx);
let mut prim = None;
for attr in attrs.lists(sym::doc) {
if let Some(v) = attr.value_str() {
if attr.has_name(sym::primitive) {
prim = PrimitiveType::from_symbol(v);
if prim.is_some() {
break;
}
// FIXME: should warn on unknown primitives?
}
}
}
return prim.map(|p| (def_id, p));
}
None
};
let primitives = if root.is_local() {
tcx.hir()
.krate()
Expand Down
70 changes: 38 additions & 32 deletions src/librustdoc/passes/collect_intra_doc_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,18 +442,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
/// NOTE: `resolve_str_path_error` knows only about paths, not about types.
/// Associated items will never be resolved by this function.
fn resolve_path(&self, path_str: &str, ns: Namespace, module_id: DefId) -> Option<Res> {
let result = self.cx.enter_resolver(|resolver| {
resolver
.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
.and_then(|(_, res)| res.try_into())
// resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
// manually as bool. Give them precedence because the `doc(primitive)` check depends on it.
let result = resolve_primitive(path_str, ns).or_else(|| {
self.cx.enter_resolver(|resolver| {
resolver
.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
})
.and_then(|(_, res)| res.try_into())
.ok()
});
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
match result {
// resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
// manually as bool
Err(()) => resolve_primitive(path_str, ns),
Ok(res) => Some(res),
}
result
}

/// Resolves a string as a path within a particular namespace. Returns an
Expand Down Expand Up @@ -1075,29 +1075,35 @@ impl LinkCollector<'_, '_> {
if matches!(
disambiguator,
None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
) && !matches!(res, Res::Primitive(_))
{
if let Some(prim) = resolve_primitive(path_str, TypeNS) {
// `prim@char`
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
if fragment.is_some() {
anchor_failure(
self.cx,
&item,
path_str,
dox,
ori_link.range,
AnchorFailure::RustdocAnchorConflict(prim),
);
return None;
) {
if let Res::Def(kind, id) = res {
if let Some(prim) = resolve_primitive(path_str, TypeNS) {
// `prim@char`
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
if fragment.is_some() {
anchor_failure(
self.cx,
&item,
path_str,
dox,
ori_link.range,
AnchorFailure::RustdocAnchorConflict(prim),
);
return None;
}
res = prim;
fragment = Some(prim.name(self.cx.tcx));
} else {
// `[char]` when a `char` module is in scope
assert_eq!(kind, DefKind::Mod);
// Very special case: when the mod has `doc(primitive)`, don't give an error.
let mod_ = rustc_hir::def::Res::Def(DefKind::Mod, id);
if crate::clean::parse_primitive(mod_, self.cx).is_none() {
let candidates = vec![res, prim];
ambiguity_error(self.cx, &item, path_str, dox, ori_link.range, candidates);
return None;
}
}
res = prim;
fragment = Some(prim.name(self.cx.tcx));
} else {
// `[char]` when a `char` module is in scope
let candidates = vec![res, prim];
ambiguity_error(self.cx, &item, path_str, dox, ori_link.range, candidates);
return None;
}
}
}
Expand Down

0 comments on commit e8f4b82

Please sign in to comment.