1
Fork 0

Use more appropriate return type for resolve_associated_item

Previously, the types looked like this:

- None means this is not an associated item (but may be a variant field)
- Some(Err) means this is known to be an error. I think the only way that can happen is if it resolved and but you had your own anchor.
- Some(Ok(_, None)) was impossible.

Now, this returns a nested Option and does the error handling and
fiddling with the side channel in the caller. As a side-effect, it also
removes duplicate error handling.

This has one small change in behavior, which is that
`resolve_primitive_associated_item` now goes through `variant_field` if
it fails to resolve something.  This is not ideal, but since it will be
quickly rejected anyway, I think the performance hit is worth the
cleanup.

This also fixes a bug where struct fields would forget to set the side
channel, adds a test for the bug, and ignores `private_intra_doc_links`
in rustc_resolve (since it's always documented with
--document-private-items).
This commit is contained in:
Joshua Nelson 2021-04-04 16:23:08 -04:00
parent ac04dbd056
commit 3b7e654fad
6 changed files with 103 additions and 98 deletions

View file

@ -18,6 +18,7 @@
#![feature(nll)] #![feature(nll)]
#![cfg_attr(bootstrap, feature(or_patterns))] #![cfg_attr(bootstrap, feature(or_patterns))]
#![recursion_limit = "256"] #![recursion_limit = "256"]
#![allow(rustdoc::private_intra_doc_links)]
pub use rustc_hir::def::{Namespace, PerNS}; pub use rustc_hir::def::{Namespace, PerNS};

View file

@ -368,54 +368,28 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
} }
/// Given a primitive type, try to resolve an associated item. /// Given a primitive type, try to resolve an associated item.
///
/// HACK(jynelson): `item_str` is passed in instead of derived from `item_name` so the
/// lifetimes on `&'path` will work.
fn resolve_primitive_associated_item( fn resolve_primitive_associated_item(
&self, &self,
prim_ty: PrimitiveType, prim_ty: PrimitiveType,
ns: Namespace, ns: Namespace,
module_id: DefId,
item_name: Symbol, item_name: Symbol,
) -> Result<(Res, Option<String>), ErrorKind<'path>> { ) -> Option<(Res, String, Option<(DefKind, DefId)>)> {
let tcx = self.cx.tcx; let tcx = self.cx.tcx;
prim_ty prim_ty.impls(tcx).into_iter().find_map(|&impl_| {
.impls(tcx) tcx.associated_items(impl_)
.into_iter() .find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_)
.find_map(|&impl_| { .map(|item| {
tcx.associated_items(impl_) let kind = item.kind;
.find_by_name_and_namespace(tcx, Ident::with_dummy_span(item_name), ns, impl_) let out = match kind {
.map(|item| { ty::AssocKind::Fn => "method",
let kind = item.kind; ty::AssocKind::Const => "associatedconstant",
self.kind_side_channel.set(Some((kind.as_def_kind(), item.def_id))); ty::AssocKind::Type => "associatedtype",
match kind { };
ty::AssocKind::Fn => "method", let fragment = format!("{}#{}.{}", prim_ty.as_str(), out, item_name);
ty::AssocKind::Const => "associatedconstant", (Res::Primitive(prim_ty), fragment, Some((kind.as_def_kind(), item.def_id)))
ty::AssocKind::Type => "associatedtype", })
} })
})
.map(|out| {
(
Res::Primitive(prim_ty),
Some(format!("{}#{}.{}", prim_ty.as_str(), out, item_name)),
)
})
})
.ok_or_else(|| {
debug!(
"returning primitive error for {}::{} in {} namespace",
prim_ty.as_str(),
item_name,
ns.descr()
);
ResolutionFailure::NotResolved {
module_id,
partial_res: Some(Res::Primitive(prim_ty)),
unresolved: item_name.to_string().into(),
}
.into()
})
} }
/// Resolves a string as a macro. /// Resolves a string as a macro.
@ -538,7 +512,21 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
resolve_primitive(&path_root, TypeNS) resolve_primitive(&path_root, TypeNS)
.or_else(|| self.resolve_path(&path_root, TypeNS, module_id)) .or_else(|| self.resolve_path(&path_root, TypeNS, module_id))
.and_then(|ty_res| { .and_then(|ty_res| {
self.resolve_associated_item(ty_res, item_name, ns, module_id, extra_fragment) let (res, fragment, side_channel) =
self.resolve_associated_item(ty_res, item_name, ns, module_id)?;
let result = if extra_fragment.is_some() {
let diag_res = side_channel.map_or(res, |(k, r)| Res::Def(k, r));
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(diag_res)))
} else {
// HACK(jynelson): `clean` expects the type, not the associated item
// but the disambiguator logic expects the associated item.
// Store the kind in a side channel so that only the disambiguator logic looks at it.
if let Some((kind, id)) = side_channel {
self.kind_side_channel.set(Some((kind, id)));
}
Ok((res, Some(fragment)))
};
Some(result)
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
if ns == Namespace::ValueNS { if ns == Namespace::ValueNS {
@ -554,21 +542,21 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
}) })
} }
/// Returns:
/// - None if no associated item was found
/// - Some((_, _, Some(_))) if an item was found and should go through a side channel
/// - Some((_, _, None)) otherwise
fn resolve_associated_item( fn resolve_associated_item(
&mut self, &mut self,
root_res: Res, root_res: Res,
item_name: Symbol, item_name: Symbol,
ns: Namespace, ns: Namespace,
module_id: DefId, module_id: DefId,
extra_fragment: &Option<String>, ) -> Option<(Res, String, Option<(DefKind, DefId)>)> {
// lol this is so bad
) -> Option<Result<(Res, Option<String>), ErrorKind<'static>>> {
let tcx = self.cx.tcx; let tcx = self.cx.tcx;
match root_res { match root_res {
Res::Primitive(prim) => { Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name),
Some(self.resolve_primitive_associated_item(prim, ns, module_id, item_name))
}
Res::Def( Res::Def(
DefKind::Struct DefKind::Struct
| DefKind::Union | DefKind::Union
@ -611,17 +599,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
ty::AssocKind::Const => "associatedconstant", ty::AssocKind::Const => "associatedconstant",
ty::AssocKind::Type => "associatedtype", ty::AssocKind::Type => "associatedtype",
}; };
return Some(if extra_fragment.is_some() { // HACK(jynelson): `clean` expects the type, not the associated item
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( // but the disambiguator logic expects the associated item.
root_res, // Store the kind in a side channel so that only the disambiguator logic looks at it.
))) return Some((
} else { root_res,
// HACK(jynelson): `clean` expects the type, not the associated item format!("{}.{}", out, item_name),
// but the disambiguator logic expects the associated item. Some((kind.as_def_kind(), id)),
// Store the kind in a side channel so that only the disambiguator logic looks at it. ));
self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
Ok((root_res, Some(format!("{}.{}", out, item_name))))
});
} }
if ns != Namespace::ValueNS { if ns != Namespace::ValueNS {
@ -640,22 +625,16 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
} else { } else {
def.non_enum_variant().fields.iter().find(|item| item.ident.name == item_name) def.non_enum_variant().fields.iter().find(|item| item.ident.name == item_name)
}?; }?;
Some(if extra_fragment.is_some() { let kind = if def.is_enum() { DefKind::Variant } else { DefKind::Field };
let res = Res::Def( Some((
if def.is_enum() { DefKind::Variant } else { DefKind::Field }, root_res,
field.did, format!(
); "{}.{}",
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) if def.is_enum() { "variant" } else { "structfield" },
} else { field.ident
Ok(( ),
root_res, Some((kind, field.did)),
Some(format!( ))
"{}.{}",
if def.is_enum() { "variant" } else { "structfield" },
field.ident
)),
))
})
} }
Res::Def(DefKind::Trait, did) => tcx Res::Def(DefKind::Trait, did) => tcx
.associated_items(did) .associated_items(did)
@ -673,14 +652,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
} }
}; };
if extra_fragment.is_some() { let res = Res::Def(item.kind.as_def_kind(), item.def_id);
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( (res, format!("{}.{}", kind, item_name), None)
root_res,
)))
} else {
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
Ok((res, Some(format!("{}.{}", kind, item_name))))
}
}), }),
_ => None, _ => None,
} }

View file

@ -1,19 +1,27 @@
warning: public documentation for `DocMe` links to private item `DontDocMe` warning: public documentation for `DocMe` links to private item `DontDocMe`
--> $DIR/private.rs:5:11 --> $DIR/private.rs:7:11
| |
LL | /// docs [DontDocMe] [DontDocMe::f] LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
| ^^^^^^^^^ this item is private | ^^^^^^^^^ this item is private
| |
= note: `#[warn(rustdoc::private_intra_doc_links)]` on by default = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
= note: this link resolves only because you passed `--document-private-items`, but will break without = note: this link resolves only because you passed `--document-private-items`, but will break without
warning: public documentation for `DocMe` links to private item `DontDocMe::f` warning: public documentation for `DocMe` links to private item `DontDocMe::f`
--> $DIR/private.rs:5:23 --> $DIR/private.rs:7:23
| |
LL | /// docs [DontDocMe] [DontDocMe::f] LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
| ^^^^^^^^^^^^ this item is private | ^^^^^^^^^^^^ this item is private
| |
= note: this link resolves only because you passed `--document-private-items`, but will break without = note: this link resolves only because you passed `--document-private-items`, but will break without
warning: 2 warnings emitted warning: public documentation for `DocMe` links to private item `DontDocMe::x`
--> $DIR/private.rs:7:38
|
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
| ^^^^^^^^^^^^ this item is private
|
= note: this link resolves only because you passed `--document-private-items`, but will break without
warning: 3 warnings emitted

View file

@ -1,19 +1,27 @@
warning: public documentation for `DocMe` links to private item `DontDocMe` warning: public documentation for `DocMe` links to private item `DontDocMe`
--> $DIR/private.rs:5:11 --> $DIR/private.rs:7:11
| |
LL | /// docs [DontDocMe] [DontDocMe::f] LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
| ^^^^^^^^^ this item is private | ^^^^^^^^^ this item is private
| |
= note: `#[warn(rustdoc::private_intra_doc_links)]` on by default = note: `#[warn(rustdoc::private_intra_doc_links)]` on by default
= note: this link will resolve properly if you pass `--document-private-items` = note: this link will resolve properly if you pass `--document-private-items`
warning: public documentation for `DocMe` links to private item `DontDocMe::f` warning: public documentation for `DocMe` links to private item `DontDocMe::f`
--> $DIR/private.rs:5:23 --> $DIR/private.rs:7:23
| |
LL | /// docs [DontDocMe] [DontDocMe::f] LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
| ^^^^^^^^^^^^ this item is private | ^^^^^^^^^^^^ this item is private
| |
= note: this link will resolve properly if you pass `--document-private-items` = note: this link will resolve properly if you pass `--document-private-items`
warning: 2 warnings emitted warning: public documentation for `DocMe` links to private item `DontDocMe::x`
--> $DIR/private.rs:7:38
|
LL | /// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
| ^^^^^^^^^^^^ this item is private
|
= note: this link will resolve properly if you pass `--document-private-items`
warning: 3 warnings emitted

View file

@ -2,12 +2,16 @@
// revisions: public private // revisions: public private
// [private]compile-flags: --document-private-items // [private]compile-flags: --document-private-items
/// docs [DontDocMe] [DontDocMe::f] // make sure to update `rustdoc/intra-doc/private.rs` if you update this file
/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
//~^ WARNING public documentation for `DocMe` links to private item `DontDocMe` //~^ WARNING public documentation for `DocMe` links to private item `DontDocMe`
//~| WARNING public documentation for `DocMe` links to private item `DontDocMe::x`
//~| WARNING public documentation for `DocMe` links to private item `DontDocMe::f` //~| WARNING public documentation for `DocMe` links to private item `DontDocMe::f`
// FIXME: for [private] we should also make sure the link was actually generated
pub struct DocMe; pub struct DocMe;
struct DontDocMe; struct DontDocMe {
x: usize,
}
impl DontDocMe { impl DontDocMe {
fn f() {} fn f() {}

View file

@ -1,6 +1,17 @@
#![crate_name = "private"] #![crate_name = "private"]
// compile-flags: --document-private-items // compile-flags: --document-private-items
/// docs [DontDocMe]
// make sure to update `rustdoc-ui/intra-doc/private.rs` if you update this file
/// docs [DontDocMe] [DontDocMe::f] [DontDocMe::x]
// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe' // @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html"]' 'DontDocMe'
// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#method.f"]' 'DontDocMe::f'
// @has private/struct.DocMe.html '//*a[@href="../private/struct.DontDocMe.html#structfield.x"]' 'DontDocMe::x'
pub struct DocMe; pub struct DocMe;
struct DontDocMe; struct DontDocMe {
x: usize,
}
impl DontDocMe {
fn f() {}
}