Skip to content

Commit fb33cd2

Browse files
committed
librustdoc: make assoc_href_attr formatting lazy
1 parent 9436b4a commit fb33cd2

File tree

1 file changed

+33
-11
lines changed
  • src/librustdoc/html/render

1 file changed

+33
-11
lines changed

src/librustdoc/html/render/mod.rs

+33-11
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub(crate) use self::context::*;
6363
pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
6464
pub(crate) use self::write_shared::*;
6565
use crate::clean::{self, ItemId, RenderedLink};
66+
use crate::display::MaybeDisplay as _;
6667
use crate::error::Error;
6768
use crate::formats::Impl;
6869
use crate::formats::cache::Cache;
@@ -569,7 +570,8 @@ fn document_short<'a, 'cx: 'a>(
569570
MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
570571

571572
if has_more_content {
572-
let link = format!(" <a{}>Read more</a>", assoc_href_attr(item, link, cx));
573+
let link =
574+
format!(" <a{}>Read more</a>", assoc_href_attr(item, link, cx).maybe_display());
573575

574576
if let Some(idx) = summary_html.rfind("</p>") {
575577
summary_html.insert_str(idx, &link);
@@ -788,13 +790,23 @@ pub(crate) fn render_impls(
788790
}
789791

790792
/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.
791-
fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) -> String {
793+
fn assoc_href_attr<'a, 'tcx>(
794+
it: &clean::Item,
795+
link: AssocItemLink<'a>,
796+
cx: &Context<'tcx>,
797+
) -> Option<impl fmt::Display + 'a + Captures<'tcx>> {
792798
let name = it.name.unwrap();
793799
let item_type = it.type_();
794800

801+
enum Href<'a> {
802+
AnchorId(&'a str),
803+
Anchor(ItemType),
804+
Url(String, ItemType),
805+
}
806+
795807
let href = match link {
796-
AssocItemLink::Anchor(Some(ref id)) => Some(format!("#{id}")),
797-
AssocItemLink::Anchor(None) => Some(format!("#{item_type}.{name}")),
808+
AssocItemLink::Anchor(Some(ref id)) => Href::AnchorId(id),
809+
AssocItemLink::Anchor(None) => Href::Anchor(item_type),
798810
AssocItemLink::GotoSource(did, provided_methods) => {
799811
// We're creating a link from the implementation of an associated item to its
800812
// declaration in the trait declaration.
@@ -814,7 +826,7 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
814826
};
815827

816828
match href(did.expect_def_id(), cx) {
817-
Ok((url, ..)) => Some(format!("{url}#{item_type}.{name}")),
829+
Ok((url, ..)) => Href::Url(url, item_type),
818830
// The link is broken since it points to an external crate that wasn't documented.
819831
// Do not create any link in such case. This is better than falling back to a
820832
// dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item
@@ -826,15 +838,25 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
826838
// those two items are distinct!
827839
// In this scenario, the actual `id` of this impl item would be
828840
// `#{item_type}.{name}-{n}` for some number `n` (a disambiguator).
829-
Err(HrefError::DocumentationNotBuilt) => None,
830-
Err(_) => Some(format!("#{item_type}.{name}")),
841+
Err(HrefError::DocumentationNotBuilt) => return None,
842+
Err(_) => Href::Anchor(item_type),
831843
}
832844
}
833845
};
834846

847+
let href = fmt::from_fn(move |f| match &href {
848+
Href::AnchorId(id) => write!(f, "#{id}"),
849+
Href::Url(url, item_type) => {
850+
write!(f, "{url}#{item_type}.{name}")
851+
}
852+
Href::Anchor(item_type) => {
853+
write!(f, "#{item_type}.{name}")
854+
}
855+
});
856+
835857
// If there is no `href` for the reason explained above, simply do not render it which is valid:
836858
// https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements
837-
href.map(|href| format!(" href=\"{href}\"")).unwrap_or_default()
859+
Some(fmt::from_fn(move |f| write!(f, " href=\"{href}\"")))
838860
}
839861

840862
#[derive(Debug)]
@@ -865,7 +887,7 @@ fn assoc_const(
865887
"{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
866888
indent = " ".repeat(indent),
867889
vis = visibility_print_with_space(it, cx),
868-
href = assoc_href_attr(it, link, cx),
890+
href = assoc_href_attr(it, link, cx).maybe_display(),
869891
name = it.name.as_ref().unwrap(),
870892
generics = generics.print(cx),
871893
ty = ty.print(cx),
@@ -905,7 +927,7 @@ fn assoc_type(
905927
"{indent}{vis}type <a{href} class=\"associatedtype\">{name}</a>{generics}",
906928
indent = " ".repeat(indent),
907929
vis = visibility_print_with_space(it, cx),
908-
href = assoc_href_attr(it, link, cx),
930+
href = assoc_href_attr(it, link, cx).maybe_display(),
909931
name = it.name.as_ref().unwrap(),
910932
generics = generics.print(cx),
911933
),
@@ -948,7 +970,7 @@ fn assoc_method(
948970
let asyncness = header.asyncness.print_with_space();
949971
let safety = header.safety.print_with_space();
950972
let abi = print_abi_with_space(header.abi).to_string();
951-
let href = assoc_href_attr(meth, link, cx);
973+
let href = assoc_href_attr(meth, link, cx).maybe_display();
952974

953975
// NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
954976
let generics_len = format!("{:#}", g.print(cx)).len();

0 commit comments

Comments
 (0)