Rollup merge of #97066 - petrochenkov:nofragkind, r=camelid
rustdoc: Remove `ItemFragment(Kind)` And stop using `write!` when rendering URL fragments to avoid impossible errors.
This commit is contained in:
commit
8eb4fc6057
@ -525,7 +525,7 @@ impl Item {
|
|||||||
if let Ok((mut href, ..)) = href(*did, cx) {
|
if let Ok((mut href, ..)) = href(*did, cx) {
|
||||||
debug!(?href);
|
debug!(?href);
|
||||||
if let Some(ref fragment) = *fragment {
|
if let Some(ref fragment) = *fragment {
|
||||||
fragment.render(&mut href, cx.tcx()).unwrap()
|
fragment.render(&mut href, cx.tcx())
|
||||||
}
|
}
|
||||||
Some(RenderedLink {
|
Some(RenderedLink {
|
||||||
original_text: s.clone(),
|
original_text: s.clone(),
|
||||||
|
@ -20,7 +20,6 @@ use rustc_span::BytePos;
|
|||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::Write;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
@ -220,80 +219,43 @@ enum MalformedGenerics {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub(crate) enum UrlFragment {
|
pub(crate) enum UrlFragment {
|
||||||
Item(ItemFragment),
|
Item(DefId),
|
||||||
UserWritten(String),
|
UserWritten(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UrlFragment {
|
impl UrlFragment {
|
||||||
/// Render the fragment, including the leading `#`.
|
/// Render the fragment, including the leading `#`.
|
||||||
pub(crate) fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result {
|
pub(crate) fn render(&self, s: &mut String, tcx: TyCtxt<'_>) {
|
||||||
|
s.push('#');
|
||||||
match self {
|
match self {
|
||||||
UrlFragment::Item(frag) => frag.render(s, tcx),
|
&UrlFragment::Item(def_id) => {
|
||||||
UrlFragment::UserWritten(raw) => write!(s, "#{}", raw),
|
let kind = match tcx.def_kind(def_id) {
|
||||||
}
|
DefKind::AssocFn => {
|
||||||
}
|
if tcx.associated_item(def_id).defaultness.has_value() {
|
||||||
}
|
"method."
|
||||||
|
} else {
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
"tymethod."
|
||||||
pub(crate) struct ItemFragment(FragmentKind, DefId);
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
|
||||||
pub(crate) enum FragmentKind {
|
|
||||||
Method,
|
|
||||||
TyMethod,
|
|
||||||
AssociatedConstant,
|
|
||||||
AssociatedType,
|
|
||||||
|
|
||||||
StructField,
|
|
||||||
Variant,
|
|
||||||
VariantField,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FragmentKind {
|
|
||||||
fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> FragmentKind {
|
|
||||||
match tcx.def_kind(def_id) {
|
|
||||||
DefKind::AssocFn => {
|
|
||||||
if tcx.associated_item(def_id).defaultness.has_value() {
|
|
||||||
FragmentKind::Method
|
|
||||||
} else {
|
|
||||||
FragmentKind::TyMethod
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DefKind::AssocConst => FragmentKind::AssociatedConstant,
|
|
||||||
DefKind::AssocTy => FragmentKind::AssociatedType,
|
|
||||||
DefKind::Variant => FragmentKind::Variant,
|
|
||||||
DefKind::Field => {
|
|
||||||
if tcx.def_kind(tcx.parent(def_id)) == DefKind::Variant {
|
|
||||||
FragmentKind::VariantField
|
|
||||||
} else {
|
|
||||||
FragmentKind::StructField
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kind => bug!("unexpected associated item kind: {:?}", kind),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ItemFragment {
|
|
||||||
/// Render the fragment, including the leading `#`.
|
|
||||||
pub(crate) fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result {
|
|
||||||
write!(s, "#")?;
|
|
||||||
match *self {
|
|
||||||
ItemFragment(kind, def_id) => {
|
|
||||||
let name = tcx.item_name(def_id);
|
|
||||||
match kind {
|
|
||||||
FragmentKind::Method => write!(s, "method.{}", name),
|
|
||||||
FragmentKind::TyMethod => write!(s, "tymethod.{}", name),
|
|
||||||
FragmentKind::AssociatedConstant => write!(s, "associatedconstant.{}", name),
|
|
||||||
FragmentKind::AssociatedType => write!(s, "associatedtype.{}", name),
|
|
||||||
FragmentKind::StructField => write!(s, "structfield.{}", name),
|
|
||||||
FragmentKind::Variant => write!(s, "variant.{}", name),
|
|
||||||
FragmentKind::VariantField => {
|
|
||||||
let variant = tcx.item_name(tcx.parent(def_id));
|
|
||||||
write!(s, "variant.{}.field.{}", variant, name)
|
|
||||||
}
|
}
|
||||||
}
|
DefKind::AssocConst => "associatedconstant.",
|
||||||
|
DefKind::AssocTy => "associatedtype.",
|
||||||
|
DefKind::Variant => "variant.",
|
||||||
|
DefKind::Field => {
|
||||||
|
let parent_id = tcx.parent(def_id);
|
||||||
|
if tcx.def_kind(parent_id) == DefKind::Variant {
|
||||||
|
s.push_str("variant.");
|
||||||
|
s.push_str(tcx.item_name(parent_id).as_str());
|
||||||
|
".field."
|
||||||
|
} else {
|
||||||
|
"structfield."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kind => bug!("unexpected associated item kind: {:?}", kind),
|
||||||
|
};
|
||||||
|
s.push_str(kind);
|
||||||
|
s.push_str(tcx.item_name(def_id).as_str());
|
||||||
}
|
}
|
||||||
|
UrlFragment::UserWritten(raw) => s.push_str(&raw),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,11 +277,6 @@ struct DiagnosticInfo<'a> {
|
|||||||
link_range: Range<usize>,
|
link_range: Range<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash)]
|
|
||||||
struct CachedLink {
|
|
||||||
res: (Res, Option<UrlFragment>),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LinkCollector<'a, 'tcx> {
|
struct LinkCollector<'a, 'tcx> {
|
||||||
cx: &'a mut DocContext<'tcx>,
|
cx: &'a mut DocContext<'tcx>,
|
||||||
/// A stack of modules used to decide what scope to resolve in.
|
/// A stack of modules used to decide what scope to resolve in.
|
||||||
@ -329,7 +286,7 @@ struct LinkCollector<'a, 'tcx> {
|
|||||||
mod_ids: Vec<DefId>,
|
mod_ids: Vec<DefId>,
|
||||||
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
|
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
|
||||||
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
|
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
|
||||||
visited_links: FxHashMap<ResolutionInfo, Option<CachedLink>>,
|
visited_links: FxHashMap<ResolutionInfo, Option<(Res, Option<UrlFragment>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
||||||
@ -1097,6 +1054,9 @@ impl LinkCollector<'_, '_> {
|
|||||||
extra_fragment: extra_fragment.clone(),
|
extra_fragment: extra_fragment.clone(),
|
||||||
},
|
},
|
||||||
diag_info.clone(), // this struct should really be Copy, but Range is not :(
|
diag_info.clone(), // this struct should really be Copy, but Range is not :(
|
||||||
|
// For reference-style links we want to report only one error so unsuccessful
|
||||||
|
// resolutions are cached, for other links we want to report an error every
|
||||||
|
// time so they are not cached.
|
||||||
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
|
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -1123,7 +1083,7 @@ impl LinkCollector<'_, '_> {
|
|||||||
|
|
||||||
match res {
|
match res {
|
||||||
Res::Primitive(prim) => {
|
Res::Primitive(prim) => {
|
||||||
if let Some(UrlFragment::Item(ItemFragment(_, id))) = fragment {
|
if let Some(UrlFragment::Item(id)) = fragment {
|
||||||
// We're actually resolving an associated item of a primitive, so we need to
|
// We're actually resolving an associated item of a primitive, so we need to
|
||||||
// verify the disambiguator (if any) matches the type of the associated item.
|
// verify the disambiguator (if any) matches the type of the associated item.
|
||||||
// This case should really follow the same flow as the `Res::Def` branch below,
|
// This case should really follow the same flow as the `Res::Def` branch below,
|
||||||
@ -1171,12 +1131,11 @@ impl LinkCollector<'_, '_> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Res::Def(kind, id) => {
|
Res::Def(kind, id) => {
|
||||||
let (kind_for_dis, id_for_dis) =
|
let (kind_for_dis, id_for_dis) = if let Some(UrlFragment::Item(id)) = fragment {
|
||||||
if let Some(UrlFragment::Item(ItemFragment(_, id))) = fragment {
|
(self.cx.tcx.def_kind(id), id)
|
||||||
(self.cx.tcx.def_kind(id), id)
|
} else {
|
||||||
} else {
|
(kind, id)
|
||||||
(kind, id)
|
};
|
||||||
};
|
|
||||||
self.verify_disambiguator(
|
self.verify_disambiguator(
|
||||||
path_str,
|
path_str,
|
||||||
ori_link,
|
ori_link,
|
||||||
@ -1294,53 +1253,33 @@ impl LinkCollector<'_, '_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
key: ResolutionInfo,
|
key: ResolutionInfo,
|
||||||
diag: DiagnosticInfo<'_>,
|
diag: DiagnosticInfo<'_>,
|
||||||
cache_resolution_failure: bool,
|
// If errors are cached then they are only reported on first ocurrence
|
||||||
|
// which we want in some cases but not in others.
|
||||||
|
cache_errors: bool,
|
||||||
) -> Option<(Res, Option<UrlFragment>)> {
|
) -> Option<(Res, Option<UrlFragment>)> {
|
||||||
if let Some(ref cached) = self.visited_links.get(&key) {
|
if let Some(res) = self.visited_links.get(&key) {
|
||||||
match cached {
|
if res.is_some() || cache_errors {
|
||||||
Some(cached) => {
|
return res.clone();
|
||||||
return Some(cached.res.clone());
|
|
||||||
}
|
|
||||||
None if cache_resolution_failure => return None,
|
|
||||||
None => {
|
|
||||||
// Although we hit the cache and found a resolution error, this link isn't
|
|
||||||
// supposed to cache those. Run link resolution again to emit the expected
|
|
||||||
// resolution error.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = self.resolve_with_disambiguator(&key, diag.clone()).and_then(|(res, def_id)| {
|
let res = self.resolve_with_disambiguator(&key, diag.clone()).and_then(|(res, def_id)| {
|
||||||
let fragment = match (&key.extra_fragment, def_id) {
|
let fragment = match (&key.extra_fragment, def_id) {
|
||||||
(Some(_), Some(def_id)) => {
|
(Some(_), Some(def_id)) => {
|
||||||
report_anchor_conflict(self.cx, diag, Res::from_def_id(self.cx.tcx, def_id));
|
report_anchor_conflict(self.cx, diag, def_id);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
(Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())),
|
(Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())),
|
||||||
(None, Some(def_id)) => Some(UrlFragment::Item(ItemFragment(
|
(None, Some(def_id)) => Some(UrlFragment::Item(def_id)),
|
||||||
FragmentKind::from_def_id(self.cx.tcx, def_id),
|
|
||||||
def_id,
|
|
||||||
))),
|
|
||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
};
|
};
|
||||||
Some((res, fragment))
|
Some((res, fragment))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cache only if resolved successfully - don't silence duplicate errors
|
if res.is_some() || cache_errors {
|
||||||
if let Some(res) = res {
|
self.visited_links.insert(key, res.clone());
|
||||||
// Store result for the actual namespace
|
|
||||||
self.visited_links.insert(key, Some(CachedLink { res: res.clone() }));
|
|
||||||
|
|
||||||
Some(res)
|
|
||||||
} else {
|
|
||||||
if cache_resolution_failure {
|
|
||||||
// For reference-style links we only want to report one resolution error
|
|
||||||
// so let's cache them as well.
|
|
||||||
self.visited_links.insert(key, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// After parsing the disambiguator, resolve the main part of the link.
|
/// After parsing the disambiguator, resolve the main part of the link.
|
||||||
@ -1916,8 +1855,8 @@ fn report_multiple_anchors(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
|
|||||||
anchor_failure(cx, diag_info, &msg, 1)
|
anchor_failure(cx, diag_info, &msg, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_anchor_conflict(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, res: Res) {
|
fn report_anchor_conflict(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, def_id: DefId) {
|
||||||
let (link, kind) = (diag_info.ori_link, res.descr());
|
let (link, kind) = (diag_info.ori_link, Res::from_def_id(cx.tcx, def_id).descr());
|
||||||
let msg = format!("`{link}` contains an anchor, but links to {kind}s are already anchored");
|
let msg = format!("`{link}` contains an anchor, but links to {kind}s are already anchored");
|
||||||
anchor_failure(cx, diag_info, &msg, 0)
|
anchor_failure(cx, diag_info, &msg, 0)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user