about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2023-02-15 14:44:31 -0700
committerMichael Howell <michael@notriddle.com>2023-02-22 21:00:10 -0700
commit49d995a4cf7c4355aa846fa039b7af656ffa287d (patch)
tree5d1e3a819485cd244be1333d18cd6c3a3b5e7810
parent0978711950b77582e4f8f334f6e9848d48ab7790 (diff)
downloadrust-49d995a4cf7c4355aa846fa039b7af656ffa287d.tar.gz
rust-49d995a4cf7c4355aa846fa039b7af656ffa287d.zip
rustdoc: reduce allocations when generating tooltips
An attempt to reduce the perf regression in
https://github.com/rust-lang/rust/pull/108052#issuecomment-1430631861
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs16
-rw-r--r--src/librustdoc/clean/types.rs8
-rw-r--r--src/librustdoc/html/format.rs19
-rw-r--r--src/librustdoc/html/markdown.rs8
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs14
-rw-r--r--src/librustdoc/passes/lint/html_tags.rs2
7 files changed, 40 insertions, 29 deletions
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index 5e4b66018e4..b8853c1744c 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -265,9 +265,9 @@ fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, Malfor
     }
 }
 
-pub fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGenerics> {
+pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGenerics> {
     if !path_str.contains(['<', '>']) {
-        return Ok(path_str.to_string());
+        return Ok(path_str.into());
     }
     let mut stripped_segments = vec![];
     let mut path = path_str.chars().peekable();
@@ -322,7 +322,11 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGener
 
     let stripped_path = stripped_segments.join("::");
 
-    if !stripped_path.is_empty() { Ok(stripped_path) } else { Err(MalformedGenerics::MissingType) }
+    if !stripped_path.is_empty() {
+        Ok(stripped_path.into())
+    } else {
+        Err(MalformedGenerics::MissingType)
+    }
 }
 
 /// Returns whether the first doc-comment is an inner attribute.
@@ -336,7 +340,7 @@ pub fn inner_docs(attrs: &[ast::Attribute]) -> bool {
 /// Simplified version of the corresponding function in rustdoc.
 /// If the rustdoc version returns a successful result, this function must return the same result.
 /// Otherwise this function may return anything.
-fn preprocess_link(link: &str) -> String {
+fn preprocess_link(link: &str) -> Box<str> {
     let link = link.replace('`', "");
     let link = link.split('#').next().unwrap();
     let link = link.trim();
@@ -345,7 +349,7 @@ fn preprocess_link(link: &str) -> String {
     let link = link.strip_suffix("{}").unwrap_or(link);
     let link = link.strip_suffix("[]").unwrap_or(link);
     let link = if link != "!" { link.strip_suffix('!').unwrap_or(link) } else { link };
-    strip_generics_from_path(link).unwrap_or_else(|_| link.to_string())
+    strip_generics_from_path(link).unwrap_or_else(|_| link.into())
 }
 
 /// Keep inline and reference links `[]`,
@@ -365,7 +369,7 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool {
 
 /// Simplified version of `preprocessed_markdown_links` from rustdoc.
 /// Must return at least the same links as it, but may add some more links on top of that.
-pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<String> {
+pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<str>> {
     let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
     let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap();
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index fc1396e86f6..27d18aad7a3 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1017,12 +1017,12 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String {
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) struct ItemLink {
     /// The original link written in the markdown
-    pub(crate) link: String,
+    pub(crate) link: Box<str>,
     /// The link text displayed in the HTML.
     ///
     /// This may not be the same as `link` if there was a disambiguator
     /// in an intra-doc link (e.g. \[`fn@f`\])
-    pub(crate) link_text: String,
+    pub(crate) link_text: Box<str>,
     /// The `DefId` of the Item whose **HTML Page** contains the item being
     /// linked to. This will be different to `item_id` on item's that don't
     /// have their own page, such as struct fields and enum variants.
@@ -1035,9 +1035,9 @@ pub struct RenderedLink {
     /// The text the link was original written as.
     ///
     /// This could potentially include disambiguators and backticks.
-    pub(crate) original_text: String,
+    pub(crate) original_text: Box<str>,
     /// The text to display in the HTML
-    pub(crate) new_text: String,
+    pub(crate) new_text: Box<str>,
     /// The URL to put in the `href`
     pub(crate) href: String,
     /// The tooltip.
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 6bdd9db9bfa..0e4c5ed6836 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -772,14 +772,21 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Cont
     let Some((fqp, shortty)) = cache.paths.get(&did)
         .or_else(|| cache.external_paths.get(&did))
         else { return String::new() };
-    let fqp = fqp.iter().map(|sym| sym.as_str()).join("::");
+    let mut buf = Buffer::new();
     if let &Some(UrlFragment::Item(id)) = fragment {
-        let name = cx.tcx().item_name(id);
-        let descr = cx.tcx().def_descr(id);
-        format!("{descr} {fqp}::{name}")
-    } else {
-        format!("{shortty} {fqp}")
+        write!(buf, "{} ", cx.tcx().def_descr(id));
+        for component in fqp {
+            write!(buf, "{component}::");
+        }
+        write!(buf, "{}", cx.tcx().item_name(id));
+    } else if !fqp.is_empty() {
+        let mut fqp_it = fqp.into_iter();
+        write!(buf, "{shortty} {}", fqp_it.next().unwrap());
+        for component in fqp_it {
+            write!(buf, "::{component}");
+        }
     }
+    buf.into_inner()
 }
 
 /// Used to render a [`clean::Path`].
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 9ef0b501c08..b5a34814382 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -981,7 +981,7 @@ impl Markdown<'_> {
         let mut replacer = |broken_link: BrokenLink<'_>| {
             links
                 .iter()
-                .find(|link| link.original_text.as_str() == &*broken_link.reference)
+                .find(|link| &*link.original_text == &*broken_link.reference)
                 .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
         };
 
@@ -1064,7 +1064,7 @@ impl MarkdownSummaryLine<'_> {
         let mut replacer = |broken_link: BrokenLink<'_>| {
             links
                 .iter()
-                .find(|link| link.original_text.as_str() == &*broken_link.reference)
+                .find(|link| &*link.original_text == &*broken_link.reference)
                 .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
         };
 
@@ -1111,7 +1111,7 @@ fn markdown_summary_with_limit(
     let mut replacer = |broken_link: BrokenLink<'_>| {
         link_names
             .iter()
-            .find(|link| link.original_text.as_str() == &*broken_link.reference)
+            .find(|link| &*link.original_text == &*broken_link.reference)
             .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
     };
 
@@ -1192,7 +1192,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
     let mut replacer = |broken_link: BrokenLink<'_>| {
         link_names
             .iter()
-            .find(|link| link.original_text.as_str() == &*broken_link.reference)
+            .find(|link| &*link.original_text == &*broken_link.reference)
             .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
     };
 
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index d5e9010eb4e..18c45fd6991 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -38,7 +38,7 @@ impl JsonRenderer<'_> {
                     Some(UrlFragment::UserWritten(_)) | None => *page_id,
                 };
 
-                (link.clone(), id_from_item_default(id.into(), self.tcx))
+                (String::from(&**link), id_from_item_default(id.into(), self.tcx))
             })
             .collect();
         let docs = item.attrs.collapsed_doc_value();
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 920a3b22f25..cbfc581389c 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -228,7 +228,7 @@ struct ResolutionInfo {
     item_id: ItemId,
     module_id: DefId,
     dis: Option<Disambiguator>,
-    path_str: String,
+    path_str: Box<str>,
     extra_fragment: Option<String>,
 }
 
@@ -849,10 +849,10 @@ impl PreprocessingError {
 
 #[derive(Clone)]
 struct PreprocessingInfo {
-    path_str: String,
+    path_str: Box<str>,
     disambiguator: Option<Disambiguator>,
     extra_fragment: Option<String>,
-    link_text: String,
+    link_text: Box<str>,
 }
 
 // Not a typedef to avoid leaking several private structures from this module.
@@ -937,7 +937,7 @@ fn preprocess_link(
         path_str,
         disambiguator,
         extra_fragment: extra_fragment.map(|frag| frag.to_owned()),
-        link_text: link_text.to_owned(),
+        link_text: Box::<str>::from(link_text),
     }))
 }
 
@@ -993,7 +993,7 @@ impl LinkCollector<'_, '_> {
                 item_id: item.item_id,
                 module_id,
                 dis: disambiguator,
-                path_str: path_str.to_owned(),
+                path_str: path_str.clone(),
                 extra_fragment: extra_fragment.clone(),
             },
             diag_info.clone(), // this struct should really be Copy, but Range is not :(
@@ -1067,7 +1067,7 @@ impl LinkCollector<'_, '_> {
                 }
 
                 res.def_id(self.cx.tcx).map(|page_id| ItemLink {
-                    link: ori_link.link.clone(),
+                    link: Box::<str>::from(&*ori_link.link),
                     link_text: link_text.clone(),
                     page_id,
                     fragment,
@@ -1091,7 +1091,7 @@ impl LinkCollector<'_, '_> {
 
                 let page_id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id));
                 Some(ItemLink {
-                    link: ori_link.link.clone(),
+                    link: Box::<str>::from(&*ori_link.link),
                     link_text: link_text.clone(),
                     page_id,
                     fragment,
diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs
index eac362b37b2..4f72df5a5cd 100644
--- a/src/librustdoc/passes/lint/html_tags.rs
+++ b/src/librustdoc/passes/lint/html_tags.rs
@@ -113,7 +113,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) {
             if let Some(link) =
                 link_names.iter().find(|link| *link.original_text == *broken_link.reference)
             {
-                Some((link.href.as_str().into(), link.new_text.as_str().into()))
+                Some((link.href.as_str().into(), link.new_text.to_string().into()))
             } else if matches!(
                 &broken_link.link_type,
                 LinkType::Reference | LinkType::ReferenceUnknown