Skip to content

Commit 8eb4fc6

Browse files
Rollup merge of #97066 - petrochenkov:nofragkind, r=camelid
rustdoc: Remove `ItemFragment(Kind)` And stop using `write!` when rendering URL fragments to avoid impossible errors.
2 parents e3813e4 + 3f21c31 commit 8eb4fc6

File tree

2 files changed

+53
-114
lines changed

2 files changed

+53
-114
lines changed

src/librustdoc/clean/types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ impl Item {
525525
if let Ok((mut href, ..)) = href(*did, cx) {
526526
debug!(?href);
527527
if let Some(ref fragment) = *fragment {
528-
fragment.render(&mut href, cx.tcx()).unwrap()
528+
fragment.render(&mut href, cx.tcx())
529529
}
530530
Some(RenderedLink {
531531
original_text: s.clone(),

src/librustdoc/passes/collect_intra_doc_links.rs

+52-113
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ use rustc_span::BytePos;
2020
use smallvec::{smallvec, SmallVec};
2121

2222
use std::borrow::Cow;
23-
use std::fmt::Write;
2423
use std::mem;
2524
use std::ops::Range;
2625

@@ -220,80 +219,43 @@ enum MalformedGenerics {
220219

221220
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
222221
pub(crate) enum UrlFragment {
223-
Item(ItemFragment),
222+
Item(DefId),
224223
UserWritten(String),
225224
}
226225

227226
impl UrlFragment {
228227
/// Render the fragment, including the leading `#`.
229-
pub(crate) fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result {
228+
pub(crate) fn render(&self, s: &mut String, tcx: TyCtxt<'_>) {
229+
s.push('#');
230230
match self {
231-
UrlFragment::Item(frag) => frag.render(s, tcx),
232-
UrlFragment::UserWritten(raw) => write!(s, "#{}", raw),
233-
}
234-
}
235-
}
236-
237-
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
238-
pub(crate) struct ItemFragment(FragmentKind, DefId);
239-
240-
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
241-
pub(crate) enum FragmentKind {
242-
Method,
243-
TyMethod,
244-
AssociatedConstant,
245-
AssociatedType,
246-
247-
StructField,
248-
Variant,
249-
VariantField,
250-
}
251-
252-
impl FragmentKind {
253-
fn from_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> FragmentKind {
254-
match tcx.def_kind(def_id) {
255-
DefKind::AssocFn => {
256-
if tcx.associated_item(def_id).defaultness.has_value() {
257-
FragmentKind::Method
258-
} else {
259-
FragmentKind::TyMethod
260-
}
261-
}
262-
DefKind::AssocConst => FragmentKind::AssociatedConstant,
263-
DefKind::AssocTy => FragmentKind::AssociatedType,
264-
DefKind::Variant => FragmentKind::Variant,
265-
DefKind::Field => {
266-
if tcx.def_kind(tcx.parent(def_id)) == DefKind::Variant {
267-
FragmentKind::VariantField
268-
} else {
269-
FragmentKind::StructField
270-
}
271-
}
272-
kind => bug!("unexpected associated item kind: {:?}", kind),
273-
}
274-
}
275-
}
276-
277-
impl ItemFragment {
278-
/// Render the fragment, including the leading `#`.
279-
pub(crate) fn render(&self, s: &mut String, tcx: TyCtxt<'_>) -> std::fmt::Result {
280-
write!(s, "#")?;
281-
match *self {
282-
ItemFragment(kind, def_id) => {
283-
let name = tcx.item_name(def_id);
284-
match kind {
285-
FragmentKind::Method => write!(s, "method.{}", name),
286-
FragmentKind::TyMethod => write!(s, "tymethod.{}", name),
287-
FragmentKind::AssociatedConstant => write!(s, "associatedconstant.{}", name),
288-
FragmentKind::AssociatedType => write!(s, "associatedtype.{}", name),
289-
FragmentKind::StructField => write!(s, "structfield.{}", name),
290-
FragmentKind::Variant => write!(s, "variant.{}", name),
291-
FragmentKind::VariantField => {
292-
let variant = tcx.item_name(tcx.parent(def_id));
293-
write!(s, "variant.{}.field.{}", variant, name)
231+
&UrlFragment::Item(def_id) => {
232+
let kind = match tcx.def_kind(def_id) {
233+
DefKind::AssocFn => {
234+
if tcx.associated_item(def_id).defaultness.has_value() {
235+
"method."
236+
} else {
237+
"tymethod."
238+
}
294239
}
295-
}
240+
DefKind::AssocConst => "associatedconstant.",
241+
DefKind::AssocTy => "associatedtype.",
242+
DefKind::Variant => "variant.",
243+
DefKind::Field => {
244+
let parent_id = tcx.parent(def_id);
245+
if tcx.def_kind(parent_id) == DefKind::Variant {
246+
s.push_str("variant.");
247+
s.push_str(tcx.item_name(parent_id).as_str());
248+
".field."
249+
} else {
250+
"structfield."
251+
}
252+
}
253+
kind => bug!("unexpected associated item kind: {:?}", kind),
254+
};
255+
s.push_str(kind);
256+
s.push_str(tcx.item_name(def_id).as_str());
296257
}
258+
UrlFragment::UserWritten(raw) => s.push_str(&raw),
297259
}
298260
}
299261
}
@@ -315,11 +277,6 @@ struct DiagnosticInfo<'a> {
315277
link_range: Range<usize>,
316278
}
317279

318-
#[derive(Clone, Debug, Hash)]
319-
struct CachedLink {
320-
res: (Res, Option<UrlFragment>),
321-
}
322-
323280
struct LinkCollector<'a, 'tcx> {
324281
cx: &'a mut DocContext<'tcx>,
325282
/// A stack of modules used to decide what scope to resolve in.
@@ -329,7 +286,7 @@ struct LinkCollector<'a, 'tcx> {
329286
mod_ids: Vec<DefId>,
330287
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
331288
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
332-
visited_links: FxHashMap<ResolutionInfo, Option<CachedLink>>,
289+
visited_links: FxHashMap<ResolutionInfo, Option<(Res, Option<UrlFragment>)>>,
333290
}
334291

335292
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
@@ -1097,6 +1054,9 @@ impl LinkCollector<'_, '_> {
10971054
extra_fragment: extra_fragment.clone(),
10981055
},
10991056
diag_info.clone(), // this struct should really be Copy, but Range is not :(
1057+
// For reference-style links we want to report only one error so unsuccessful
1058+
// resolutions are cached, for other links we want to report an error every
1059+
// time so they are not cached.
11001060
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
11011061
)?;
11021062

@@ -1123,7 +1083,7 @@ impl LinkCollector<'_, '_> {
11231083

11241084
match res {
11251085
Res::Primitive(prim) => {
1126-
if let Some(UrlFragment::Item(ItemFragment(_, id))) = fragment {
1086+
if let Some(UrlFragment::Item(id)) = fragment {
11271087
// We're actually resolving an associated item of a primitive, so we need to
11281088
// verify the disambiguator (if any) matches the type of the associated item.
11291089
// This case should really follow the same flow as the `Res::Def` branch below,
@@ -1171,12 +1131,11 @@ impl LinkCollector<'_, '_> {
11711131
})
11721132
}
11731133
Res::Def(kind, id) => {
1174-
let (kind_for_dis, id_for_dis) =
1175-
if let Some(UrlFragment::Item(ItemFragment(_, id))) = fragment {
1176-
(self.cx.tcx.def_kind(id), id)
1177-
} else {
1178-
(kind, id)
1179-
};
1134+
let (kind_for_dis, id_for_dis) = if let Some(UrlFragment::Item(id)) = fragment {
1135+
(self.cx.tcx.def_kind(id), id)
1136+
} else {
1137+
(kind, id)
1138+
};
11801139
self.verify_disambiguator(
11811140
path_str,
11821141
ori_link,
@@ -1294,53 +1253,33 @@ impl LinkCollector<'_, '_> {
12941253
&mut self,
12951254
key: ResolutionInfo,
12961255
diag: DiagnosticInfo<'_>,
1297-
cache_resolution_failure: bool,
1256+
// If errors are cached then they are only reported on first ocurrence
1257+
// which we want in some cases but not in others.
1258+
cache_errors: bool,
12981259
) -> Option<(Res, Option<UrlFragment>)> {
1299-
if let Some(ref cached) = self.visited_links.get(&key) {
1300-
match cached {
1301-
Some(cached) => {
1302-
return Some(cached.res.clone());
1303-
}
1304-
None if cache_resolution_failure => return None,
1305-
None => {
1306-
// Although we hit the cache and found a resolution error, this link isn't
1307-
// supposed to cache those. Run link resolution again to emit the expected
1308-
// resolution error.
1309-
}
1260+
if let Some(res) = self.visited_links.get(&key) {
1261+
if res.is_some() || cache_errors {
1262+
return res.clone();
13101263
}
13111264
}
13121265

13131266
let res = self.resolve_with_disambiguator(&key, diag.clone()).and_then(|(res, def_id)| {
13141267
let fragment = match (&key.extra_fragment, def_id) {
13151268
(Some(_), Some(def_id)) => {
1316-
report_anchor_conflict(self.cx, diag, Res::from_def_id(self.cx.tcx, def_id));
1269+
report_anchor_conflict(self.cx, diag, def_id);
13171270
return None;
13181271
}
13191272
(Some(u_frag), None) => Some(UrlFragment::UserWritten(u_frag.clone())),
1320-
(None, Some(def_id)) => Some(UrlFragment::Item(ItemFragment(
1321-
FragmentKind::from_def_id(self.cx.tcx, def_id),
1322-
def_id,
1323-
))),
1273+
(None, Some(def_id)) => Some(UrlFragment::Item(def_id)),
13241274
(None, None) => None,
13251275
};
13261276
Some((res, fragment))
13271277
});
13281278

1329-
// Cache only if resolved successfully - don't silence duplicate errors
1330-
if let Some(res) = res {
1331-
// Store result for the actual namespace
1332-
self.visited_links.insert(key, Some(CachedLink { res: res.clone() }));
1333-
1334-
Some(res)
1335-
} else {
1336-
if cache_resolution_failure {
1337-
// For reference-style links we only want to report one resolution error
1338-
// so let's cache them as well.
1339-
self.visited_links.insert(key, None);
1340-
}
1341-
1342-
None
1279+
if res.is_some() || cache_errors {
1280+
self.visited_links.insert(key, res.clone());
13431281
}
1282+
res
13441283
}
13451284

13461285
/// After parsing the disambiguator, resolve the main part of the link.
@@ -1916,8 +1855,8 @@ fn report_multiple_anchors(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>) {
19161855
anchor_failure(cx, diag_info, &msg, 1)
19171856
}
19181857

1919-
fn report_anchor_conflict(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, res: Res) {
1920-
let (link, kind) = (diag_info.ori_link, res.descr());
1858+
fn report_anchor_conflict(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, def_id: DefId) {
1859+
let (link, kind) = (diag_info.ori_link, Res::from_def_id(cx.tcx, def_id).descr());
19211860
let msg = format!("`{link}` contains an anchor, but links to {kind}s are already anchored");
19221861
anchor_failure(cx, diag_info, &msg, 0)
19231862
}

0 commit comments

Comments
 (0)