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:
parent
ac04dbd056
commit
3b7e654fad
6 changed files with 103 additions and 98 deletions
|
@ -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};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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() {}
|
||||||
|
|
|
@ -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() {}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue