about summary refs log tree commit diff
path: root/src/librustdoc/html/format.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/format.rs')
-rw-r--r--src/librustdoc/html/format.rs121
1 files changed, 82 insertions, 39 deletions
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 981eb9589e9..a4cdef9358b 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -19,6 +19,7 @@ use rustc_middle::ty;
 use rustc_middle::ty::DefIdTree;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::CRATE_DEF_INDEX;
+use rustc_span::{sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
 use crate::clean::{
@@ -502,11 +503,45 @@ crate enum HrefError {
     NotInExternalCache,
 }
 
+// This mostly works with sequences of symbols, but sometimes the first item
+// comes from a string, and in that case we want to trim any trailing `/`.
+// `syms` can be empty.
+crate fn join_with_slash(first: Option<&str>, syms: &[Symbol]) -> String {
+    // 64 bytes covers 99.9%+ of cases.
+    let mut s = String::with_capacity(64);
+    if let Some(first) = first {
+        s.push_str(first.trim_end_matches('/'));
+        if !syms.is_empty() {
+            s.push('/');
+        }
+    }
+    if !syms.is_empty() {
+        s.push_str(&syms[0].as_str());
+        for sym in &syms[1..] {
+            s.push('/');
+            s.push_str(&sym.as_str());
+        }
+    }
+    s
+}
+
+// Panics if `syms` is empty.
+crate fn join_with_double_colon(syms: &[Symbol]) -> String {
+    // 64 bytes covers 99.9%+ of cases.
+    let mut s = String::with_capacity(64);
+    s.push_str(&syms[0].as_str());
+    for sym in &syms[1..] {
+        s.push_str("::");
+        s.push_str(&sym.as_str());
+    }
+    s
+}
+
 crate fn href_with_root_path(
     did: DefId,
     cx: &Context<'_>,
     root_path: Option<&str>,
-) -> Result<(String, ItemType, Vec<String>), HrefError> {
+) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
     let tcx = cx.tcx();
     let def_kind = tcx.def_kind(did);
     let did = match def_kind {
@@ -518,7 +553,7 @@ crate fn href_with_root_path(
     };
     let cache = cx.cache();
     let relative_to = &cx.current;
-    fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
+    fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
         if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
     }
 
@@ -533,9 +568,9 @@ crate fn href_with_root_path(
     let mut is_remote = false;
     let (fqp, shortty, mut url_parts) = match cache.paths.get(&did) {
         Some(&(ref fqp, shortty)) => (fqp, shortty, {
-            let module_fqp = to_module_fqp(shortty, fqp);
+            let module_fqp = to_module_fqp(shortty, fqp.as_slice());
             debug!(?fqp, ?shortty, ?module_fqp);
-            href_relative_parts(module_fqp, relative_to)
+            href_relative_parts(module_fqp, relative_to).collect()
         }),
         None => {
             if let Some(&(ref fqp, shortty)) = cache.external_paths.get(&did) {
@@ -548,10 +583,12 @@ crate fn href_with_root_path(
                             is_remote = true;
                             let s = s.trim_end_matches('/');
                             let mut builder = UrlPartsBuilder::singleton(s);
-                            builder.extend(module_fqp.iter().map(String::as_str));
+                            builder.extend(module_fqp.iter().copied());
                             builder
                         }
-                        ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
+                        ExternalLocation::Local => {
+                            href_relative_parts(module_fqp, relative_to).collect()
+                        }
                         ExternalLocation::Unknown => return Err(HrefError::DocumentationNotBuilt),
                     },
                 )
@@ -567,45 +604,50 @@ crate fn href_with_root_path(
         }
     }
     debug!(?url_parts);
-    let last = &fqp.last().unwrap()[..];
     match shortty {
         ItemType::Module => {
             url_parts.push("index.html");
         }
         _ => {
-            let filename = format!("{}.{}.html", shortty.as_str(), last);
-            url_parts.push(&filename);
+            let prefix = shortty.as_str();
+            let last = fqp.last().unwrap();
+            url_parts.push_fmt(format_args!("{}.{}.html", prefix, last));
         }
     }
     Ok((url_parts.finish(), shortty, fqp.to_vec()))
 }
 
-crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
+crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
     href_with_root_path(did, cx, None)
 }
 
 /// Both paths should only be modules.
 /// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
 /// both need `../iter/trait.Iterator.html` to get at the iterator trait.
-crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder {
+crate fn href_relative_parts<'fqp>(
+    fqp: &'fqp [Symbol],
+    relative_to_fqp: &[Symbol],
+) -> Box<dyn Iterator<Item = Symbol> + 'fqp> {
     for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
         // e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
         if f != r {
             let dissimilar_part_count = relative_to_fqp.len() - i;
-            let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
-            return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
+            let fqp_module = &fqp[i..fqp.len()];
+            return box iter::repeat(sym::dotdot)
+                .take(dissimilar_part_count)
+                .chain(fqp_module.iter().copied());
         }
     }
     // e.g. linking to std::sync::atomic from std::sync
     if relative_to_fqp.len() < fqp.len() {
-        fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
+        box fqp[relative_to_fqp.len()..fqp.len()].iter().copied()
     // e.g. linking to std::sync from std::sync::atomic
     } else if fqp.len() < relative_to_fqp.len() {
         let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
-        iter::repeat("..").take(dissimilar_part_count).collect()
+        box iter::repeat(sym::dotdot).take(dissimilar_part_count)
     // linking to the same module
     } else {
-        UrlPartsBuilder::new()
+        box iter::empty()
     }
 }
 
@@ -632,14 +674,14 @@ fn resolved_path<'cx>(
             if let Ok((_, _, fqp)) = href(did, cx) {
                 format!(
                     "{}::{}",
-                    fqp[..fqp.len() - 1].join("::"),
-                    anchor(did, fqp.last().unwrap(), cx)
+                    join_with_double_colon(&fqp[..fqp.len() - 1]),
+                    anchor(did, *fqp.last().unwrap(), cx)
                 )
             } else {
                 last.name.to_string()
             }
         } else {
-            anchor(did, last.name.as_str(), cx).to_string()
+            anchor(did, last.name, cx).to_string()
         };
         write!(w, "{}{}", path, last.args.print(cx))?;
     }
@@ -668,30 +710,31 @@ fn primitive_link(
                 needs_termination = true;
             }
             Some(&def_id) => {
-                let cname_sym;
                 let loc = match m.extern_locations[&def_id.krate] {
                     ExternalLocation::Remote(ref s) => {
-                        cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
-                        Some(vec![s.trim_end_matches('/'), cname_sym.as_str()])
+                        let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
+                        let builder: UrlPartsBuilder =
+                            [s.as_str().trim_end_matches('/'), cname_sym.as_str()]
+                                .into_iter()
+                                .collect();
+                        Some(builder)
                     }
                     ExternalLocation::Local => {
-                        cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
-                        Some(if cx.current.first().map(|x| &x[..]) == Some(cname_sym.as_str()) {
-                            iter::repeat("..").take(cx.current.len() - 1).collect()
+                        let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx());
+                        Some(if cx.current.first() == Some(&cname_sym) {
+                            iter::repeat(sym::dotdot).take(cx.current.len() - 1).collect()
                         } else {
-                            let cname = iter::once(cname_sym.as_str());
-                            iter::repeat("..").take(cx.current.len()).chain(cname).collect()
+                            iter::repeat(sym::dotdot)
+                                .take(cx.current.len())
+                                .chain(iter::once(cname_sym))
+                                .collect()
                         })
                     }
                     ExternalLocation::Unknown => None,
                 };
-                if let Some(loc) = loc {
-                    write!(
-                        f,
-                        "<a class=\"primitive\" href=\"{}/primitive.{}.html\">",
-                        loc.join("/"),
-                        prim.as_sym()
-                    )?;
+                if let Some(mut loc) = loc {
+                    loc.push_fmt(format_args!("primitive.{}.html", prim.as_sym()));
+                    write!(f, "<a class=\"primitive\" href=\"{}\">", loc.finish())?;
                     needs_termination = true;
                 }
             }
@@ -730,7 +773,7 @@ fn tybounds<'a, 'tcx: 'a>(
 
 crate fn anchor<'a, 'cx: 'a>(
     did: DefId,
-    text: &'a str,
+    text: Symbol,
     cx: &'cx Context<'_>,
 ) -> impl fmt::Display + 'a {
     let parts = href(did, cx);
@@ -742,8 +785,8 @@ crate fn anchor<'a, 'cx: 'a>(
                 short_ty,
                 url,
                 short_ty,
-                fqp.join("::"),
-                text
+                join_with_double_colon(&fqp),
+                &*text.as_str()
             )
         } else {
             write!(f, "{}", text)
@@ -960,7 +1003,7 @@ fn fmt_type<'cx>(
                         url = url,
                         shortty = ItemType::AssocType,
                         name = name,
-                        path = path.join("::")
+                        path = join_with_double_colon(path),
                     )?;
                 }
                 _ => write!(f, "{}", name)?,
@@ -1270,7 +1313,7 @@ impl clean::Visibility {
                     debug!("path={:?}", path);
                     // modified from `resolved_path()` to work with `DefPathData`
                     let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
-                    let anchor = anchor(vis_did, last_name.as_str(), cx).to_string();
+                    let anchor = anchor(vis_did, last_name, cx).to_string();
 
                     let mut s = "pub(in ".to_owned();
                     for seg in &path.data[..path.data.len() - 1] {