From 8701c186b5376b5a440e0b78b5cb95e777d7fb6b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2014 17:12:48 -0700 Subject: [PATCH 1/4] rustdoc: Refactor structure of html::run Instead of one giant function, this breaks it up into several smaller functions which have explicit dependencies among one another. There are no code changes as a result of this commit. --- src/librustdoc/html/render.rs | 345 ++++++++++++++++++---------------- 1 file changed, 179 insertions(+), 166 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 22619bdbf8517..7f1e42ce60284 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -229,6 +229,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { }; try!(mkdir(&cx.dst)); + // Crawl the crate attributes looking for attributes which control how we're + // going to emit HTML match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(&[])) { Some(attrs) => { for attr in attrs.iter() { @@ -297,19 +299,37 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { cache.stack.push(krate.name.clone()); krate = cache.fold_crate(krate); + + for &(n, ref e) in krate.externs.iter() { + cache.extern_locations.insert(n, extern_location(e, &cx.dst)); + let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID }; + cache.paths.insert(did, (vec![e.name.to_string()], item_type::Module)); + } + + let index = try!(build_index(&krate, &mut cache)); + try!(write_shared(&cx, &krate, &cache, index)); + let krate = try!(render_sources(&mut cx, krate)); + + // And finally render the whole crate's documentation + cx.krate(krate, cache) +} + +fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::IoResult { + // Build the search index from the collected metadata let mut nodeid_to_pathid = HashMap::new(); let mut pathid_to_nodeid = Vec::new(); { - let Cache { search_index: ref mut index, - orphan_methods: ref meths, paths: ref mut paths, ..} = cache; + let Cache { ref mut search_index, + ref orphan_methods, + ref mut paths, .. } = *cache; // Attach all orphan methods to the type's definition if the type // has since been learned. - for &(pid, ref item) in meths.iter() { + for &(pid, ref item) in orphan_methods.iter() { let did = ast_util::local_def(pid); match paths.find(&did) { Some(&(ref fqp, _)) => { - index.push(IndexItem { + search_index.push(IndexItem { ty: shortty(item), name: item.name.clone().unwrap(), path: fqp.slice_to(fqp.len() - 1).connect("::") @@ -324,7 +344,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { // Reduce `NodeId` in paths into smaller sequential numbers, // and prune the paths that do not appear in the index. - for item in index.iter() { + for item in search_index.iter() { match item.parent { Some(nodeid) => { if !nodeid_to_pathid.contains_key(&nodeid) { @@ -339,189 +359,182 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len()); } - // Publish the search index - let index = { - let mut w = MemWriter::new(); - try!(write!(&mut w, r#"searchIndex['{}'] = \{"items":["#, krate.name)); - - let mut lastpath = "".to_string(); - for (i, item) in cache.search_index.iter().enumerate() { - // Omit the path if it is same to that of the prior item. - let path; - if lastpath.as_slice() == item.path.as_slice() { - path = ""; - } else { - lastpath = item.path.to_string(); - path = item.path.as_slice(); - }; + // Collect the index into a string + let mut w = MemWriter::new(); + try!(write!(&mut w, r#"searchIndex['{}'] = \{"items":["#, krate.name)); - if i > 0 { - try!(write!(&mut w, ",")); - } - try!(write!(&mut w, r#"[{:u},"{}","{}",{}"#, - item.ty, item.name, path, - item.desc.to_json().to_str())); - match item.parent { - Some(nodeid) => { - let pathid = *nodeid_to_pathid.find(&nodeid).unwrap(); - try!(write!(&mut w, ",{}", pathid)); - } - None => {} + let mut lastpath = "".to_string(); + for (i, item) in cache.search_index.iter().enumerate() { + // Omit the path if it is same to that of the prior item. + let path; + if lastpath.as_slice() == item.path.as_slice() { + path = ""; + } else { + lastpath = item.path.to_string(); + path = item.path.as_slice(); + }; + + if i > 0 { + try!(write!(&mut w, ",")); + } + try!(write!(&mut w, r#"[{:u},"{}","{}",{}"#, + item.ty, item.name, path, + item.desc.to_json().to_str())); + match item.parent { + Some(nodeid) => { + let pathid = *nodeid_to_pathid.find(&nodeid).unwrap(); + try!(write!(&mut w, ",{}", pathid)); } - try!(write!(&mut w, "]")); + None => {} } + try!(write!(&mut w, "]")); + } - try!(write!(&mut w, r#"],"paths":["#)); + try!(write!(&mut w, r#"],"paths":["#)); - for (i, &did) in pathid_to_nodeid.iter().enumerate() { - let &(ref fqp, short) = cache.paths.find(&did).unwrap(); - if i > 0 { - try!(write!(&mut w, ",")); - } - try!(write!(&mut w, r#"[{:u},"{}"]"#, - short, *fqp.last().unwrap())); + for (i, &did) in pathid_to_nodeid.iter().enumerate() { + let &(ref fqp, short) = cache.paths.find(&did).unwrap(); + if i > 0 { + try!(write!(&mut w, ",")); } + try!(write!(&mut w, r#"[{:u},"{}"]"#, + short, *fqp.last().unwrap())); + } - try!(write!(&mut w, r"]\};")); + try!(write!(&mut w, r"]\};")); - str::from_utf8(w.unwrap().as_slice()).unwrap().to_string() - }; + Ok(str::from_utf8(w.unwrap().as_slice()).unwrap().to_string()) +} +fn write_shared(cx: &Context, + krate: &clean::Crate, + cache: &Cache, + search_index: String) -> io::IoResult<()> { // Write out the shared files. Note that these are shared among all rustdoc // docs placed in the output directory, so this needs to be a synchronized // operation with respect to all other rustdocs running around. - { - try!(mkdir(&cx.dst)); - let _lock = ::flock::Lock::new(&cx.dst.join(".lock")); - - // Add all the static files. These may already exist, but we just - // overwrite them anyway to make sure that they're fresh and up-to-date. - try!(write(cx.dst.join("jquery.js"), - include_bin!("static/jquery-2.1.0.min.js"))); - try!(write(cx.dst.join("main.js"), include_bin!("static/main.js"))); - try!(write(cx.dst.join("main.css"), include_bin!("static/main.css"))); - try!(write(cx.dst.join("normalize.css"), - include_bin!("static/normalize.css"))); - try!(write(cx.dst.join("FiraSans-Regular.woff"), - include_bin!("static/FiraSans-Regular.woff"))); - try!(write(cx.dst.join("FiraSans-Medium.woff"), - include_bin!("static/FiraSans-Medium.woff"))); - try!(write(cx.dst.join("Heuristica-Regular.woff"), - include_bin!("static/Heuristica-Regular.woff"))); - try!(write(cx.dst.join("Heuristica-Italic.woff"), - include_bin!("static/Heuristica-Italic.woff"))); - try!(write(cx.dst.join("Heuristica-Bold.woff"), - include_bin!("static/Heuristica-Bold.woff"))); - - fn collect(path: &Path, krate: &str, - key: &str) -> io::IoResult> { - let mut ret = Vec::new(); - if path.exists() { - for line in BufferedReader::new(File::open(path)).lines() { - let line = try!(line); - if !line.as_slice().starts_with(key) { - continue - } - if line.as_slice().starts_with( - format!("{}['{}']", key, krate).as_slice()) { - continue - } - ret.push(line.to_string()); + try!(mkdir(&cx.dst)); + let _lock = ::flock::Lock::new(&cx.dst.join(".lock")); + + // Add all the static files. These may already exist, but we just + // overwrite them anyway to make sure that they're fresh and up-to-date. + try!(write(cx.dst.join("jquery.js"), + include_bin!("static/jquery-2.1.0.min.js"))); + try!(write(cx.dst.join("main.js"), include_bin!("static/main.js"))); + try!(write(cx.dst.join("main.css"), include_bin!("static/main.css"))); + try!(write(cx.dst.join("normalize.css"), + include_bin!("static/normalize.css"))); + try!(write(cx.dst.join("FiraSans-Regular.woff"), + include_bin!("static/FiraSans-Regular.woff"))); + try!(write(cx.dst.join("FiraSans-Medium.woff"), + include_bin!("static/FiraSans-Medium.woff"))); + try!(write(cx.dst.join("Heuristica-Regular.woff"), + include_bin!("static/Heuristica-Regular.woff"))); + try!(write(cx.dst.join("Heuristica-Italic.woff"), + include_bin!("static/Heuristica-Italic.woff"))); + try!(write(cx.dst.join("Heuristica-Bold.woff"), + include_bin!("static/Heuristica-Bold.woff"))); + + fn collect(path: &Path, krate: &str, + key: &str) -> io::IoResult> { + let mut ret = Vec::new(); + if path.exists() { + for line in BufferedReader::new(File::open(path)).lines() { + let line = try!(line); + if !line.as_slice().starts_with(key) { + continue + } + if line.as_slice().starts_with( + format!("{}['{}']", key, krate).as_slice()) { + continue } + ret.push(line.to_string()); } - return Ok(ret); } + return Ok(ret); + } - // Update the search index - let dst = cx.dst.join("search-index.js"); - let all_indexes = try!(collect(&dst, krate.name.as_slice(), - "searchIndex")); - let mut w = try!(File::create(&dst)); - try!(writeln!(&mut w, r"var searchIndex = \{\};")); - try!(writeln!(&mut w, "{}", index)); - for index in all_indexes.iter() { - try!(writeln!(&mut w, "{}", *index)); + // Update the search index + let dst = cx.dst.join("search-index.js"); + let all_indexes = try!(collect(&dst, krate.name.as_slice(), + "searchIndex")); + let mut w = try!(File::create(&dst)); + try!(writeln!(&mut w, r"var searchIndex = \{\};")); + try!(writeln!(&mut w, "{}", search_index)); + for index in all_indexes.iter() { + try!(writeln!(&mut w, "{}", *index)); + } + try!(writeln!(&mut w, "initSearch(searchIndex);")); + + // Update the list of all implementors for traits + let dst = cx.dst.join("implementors"); + try!(mkdir(&dst)); + for (&did, imps) in cache.implementors.iter() { + if ast_util::is_local(did) { continue } + let &(ref remote_path, remote_item_type) = cache.paths.get(&did); + + let mut mydst = dst.clone(); + for part in remote_path.slice_to(remote_path.len() - 1).iter() { + mydst.push(part.as_slice()); + try!(mkdir(&mydst)); + } + mydst.push(format!("{}.{}.js", + remote_item_type.to_static_str(), + *remote_path.get(remote_path.len() - 1))); + let all_implementors = try!(collect(&mydst, krate.name.as_slice(), + "implementors")); + + try!(mkdir(&mydst.dir_path())); + let mut f = BufferedWriter::new(try!(File::create(&mydst))); + try!(writeln!(&mut f, r"(function() \{var implementors = \{\};")); + + for implementor in all_implementors.iter() { + try!(writeln!(&mut f, "{}", *implementor)); } - try!(writeln!(&mut w, "initSearch(searchIndex);")); - - // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); - try!(mkdir(&dst)); - for (&did, imps) in cache.implementors.iter() { - if ast_util::is_local(did) { continue } - let &(ref remote_path, remote_item_type) = cache.paths.get(&did); - - let mut mydst = dst.clone(); - for part in remote_path.slice_to(remote_path.len() - 1).iter() { - mydst.push(part.as_slice()); - try!(mkdir(&mydst)); - } - mydst.push(format!("{}.{}.js", - remote_item_type.to_static_str(), - *remote_path.get(remote_path.len() - 1))); - let all_implementors = try!(collect(&mydst, krate.name.as_slice(), - "implementors")); - - try!(mkdir(&mydst.dir_path())); - let mut f = BufferedWriter::new(try!(File::create(&mydst))); - try!(writeln!(&mut f, r"(function() \{var implementors = \{\};")); - - for implementor in all_implementors.iter() { - try!(writeln!(&mut f, "{}", *implementor)); - } - try!(write!(&mut f, r"implementors['{}'] = \{", krate.name)); - for imp in imps.iter() { - let &(ref path, item_type) = match *imp { - PathType(clean::ResolvedPath { did, .. }) => { - cache.paths.get(&did) - } - PathType(..) | OtherType(..) => continue, - }; - try!(write!(&mut f, r#"{}:"#, *path.get(path.len() - 1))); - try!(write!(&mut f, r#""{}"#, - path.slice_to(path.len() - 1).connect("/"))); - try!(write!(&mut f, r#"/{}.{}.html","#, - item_type.to_static_str(), - *path.get(path.len() - 1))); - } - try!(writeln!(&mut f, r"\};")); - try!(writeln!(&mut f, "{}", r" - if (window.register_implementors) { - window.register_implementors(implementors); - } else { - window.pending_implementors = implementors; + try!(write!(&mut f, r"implementors['{}'] = \{", krate.name)); + for imp in imps.iter() { + let &(ref path, item_type) = match *imp { + PathType(clean::ResolvedPath { did, .. }) => { + cache.paths.get(&did) } - ")); - try!(writeln!(&mut f, r"\})()")); + PathType(..) | OtherType(..) => continue, + }; + try!(write!(&mut f, r#"{}:"#, *path.get(path.len() - 1))); + try!(write!(&mut f, r#""{}"#, + path.slice_to(path.len() - 1).connect("/"))); + try!(write!(&mut f, r#"/{}.{}.html","#, + item_type.to_static_str(), + *path.get(path.len() - 1))); } + try!(writeln!(&mut f, r"\};")); + try!(writeln!(&mut f, "{}", r" + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } + ")); + try!(writeln!(&mut f, r"\})()")); } + Ok(()) +} - // Render all source files (this may turn into a giant no-op) - { - info!("emitting source files"); - let dst = cx.dst.join("src"); - try!(mkdir(&dst)); - let dst = dst.join(krate.name.as_slice()); - try!(mkdir(&dst)); - let mut folder = SourceCollector { - dst: dst, - seen: HashSet::new(), - cx: &mut cx, - }; - // skip all invalid spans - folder.seen.insert("".to_string()); - krate = folder.fold_crate(krate); - } - - for &(n, ref e) in krate.externs.iter() { - cache.extern_locations.insert(n, extern_location(e, &cx.dst)); - let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID }; - cache.paths.insert(did, (vec![e.name.to_string()], item_type::Module)); - } - - // And finally render the whole crate's documentation - cx.krate(krate, cache) +fn render_sources(cx: &mut Context, + krate: clean::Crate) -> io::IoResult { + info!("emitting source files"); + let dst = cx.dst.join("src"); + try!(mkdir(&dst)); + let dst = dst.join(krate.name.as_slice()); + try!(mkdir(&dst)); + let mut folder = SourceCollector { + dst: dst, + seen: HashSet::new(), + cx: cx, + }; + // skip all invalid spans + folder.seen.insert("".to_string()); + Ok(folder.fold_crate(krate)) } /// Writes the entire contents of a string to a destination, not attempting to From 9e0db2d8aedaa653bdf600a79731e6a0b0907400 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2014 17:15:10 -0700 Subject: [PATCH 2/4] rustdoc: Freeze the cache ASAP The cache is going to be used earlier in the HTML generation process, which means that it needs to get into TLS as soon as possible. --- src/librustdoc/html/render.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 7f1e42ce60284..ab985828a4655 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -307,11 +307,17 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { } let index = try!(build_index(&krate, &mut cache)); - try!(write_shared(&cx, &krate, &cache, index)); + + // Freeze the cache now that the index has been built. Put an Arc into TLS + // for future parallelization opportunities + let cache = Arc::new(cache); + cache_key.replace(Some(cache.clone())); + + try!(write_shared(&cx, &krate, &*cache, index)); let krate = try!(render_sources(&mut cx, krate)); // And finally render the whole crate's documentation - cx.krate(krate, cache) + cx.krate(krate) } fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::IoResult { @@ -954,16 +960,13 @@ impl Context { /// /// This currently isn't parallelized, but it'd be pretty easy to add /// parallelization to this function. - fn krate(self, mut krate: clean::Crate, cache: Cache) -> io::IoResult<()> { + fn krate(self, mut krate: clean::Crate) -> io::IoResult<()> { let mut item = match krate.module.take() { Some(i) => i, None => return Ok(()) }; item.name = Some(krate.name); - // using a rwarc makes this parallelizable in the future - cache_key.replace(Some(Arc::new(cache))); - let mut work = vec!((self, item)); loop { match work.pop() { From 34ef4d2a1b5b5530f9f3be19a89bb5fe04a05caf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2014 17:52:40 -0700 Subject: [PATCH 3/4] rustdoc: Show all implementors of traits When inlining documentation across crates, primitive implementors of traits were not shown. This commit tweaks the infrastructure to treat primitive and Path-like impls the same way, displaying all implementors everywhere. cc #14462 --- src/librustdoc/html/render.rs | 92 ++++++++++++------------------ src/librustdoc/html/static/main.js | 14 ++--- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index ab985828a4655..c5e6c73e38fc4 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -102,13 +102,11 @@ pub enum ExternalLocation { Unknown, } -/// Different ways an implementor of a trait can be rendered. -pub enum Implementor { - /// Paths are displayed specially by omitting the `impl XX for` cruft - PathType(clean::Type), - /// This is the generic representation of a trait implementor, used for - /// primitive types and otherwise non-path types. - OtherType(clean::Generics, /* trait */ clean::Type, /* for */ clean::Type), +/// Metadata about an implementor of a trait. +pub struct Implementor { + generics: clean::Generics, + trait_: clean::Type, + for_: clean::Type, } /// This cache is used to store information about the `clean::Crate` being @@ -312,6 +310,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { // for future parallelization opportunities let cache = Arc::new(cache); cache_key.replace(Some(cache.clone())); + current_location_key.replace(Some(Vec::new())); try!(write_shared(&cx, &krate, &*cache, index)); let krate = try!(render_sources(&mut cx, krate)); @@ -476,7 +475,6 @@ fn write_shared(cx: &Context, let dst = cx.dst.join("implementors"); try!(mkdir(&dst)); for (&did, imps) in cache.implementors.iter() { - if ast_util::is_local(did) { continue } let &(ref remote_path, remote_item_type) = cache.paths.get(&did); let mut mydst = dst.clone(); @@ -495,25 +493,15 @@ fn write_shared(cx: &Context, try!(writeln!(&mut f, r"(function() \{var implementors = \{\};")); for implementor in all_implementors.iter() { - try!(writeln!(&mut f, "{}", *implementor)); + try!(write!(&mut f, "{}", *implementor)); } - try!(write!(&mut f, r"implementors['{}'] = \{", krate.name)); + try!(write!(&mut f, r"implementors['{}'] = [", krate.name)); for imp in imps.iter() { - let &(ref path, item_type) = match *imp { - PathType(clean::ResolvedPath { did, .. }) => { - cache.paths.get(&did) - } - PathType(..) | OtherType(..) => continue, - }; - try!(write!(&mut f, r#"{}:"#, *path.get(path.len() - 1))); - try!(write!(&mut f, r#""{}"#, - path.slice_to(path.len() - 1).connect("/"))); - try!(write!(&mut f, r#"/{}.{}.html","#, - item_type.to_static_str(), - *path.get(path.len() - 1))); + try!(write!(&mut f, r#""impl{} {} for {}","#, + imp.generics, imp.trait_, imp.for_)); } - try!(writeln!(&mut f, r"\};")); + try!(writeln!(&mut f, r"];")); try!(writeln!(&mut f, "{}", r" if (window.register_implementors) { window.register_implementors(implementors); @@ -737,16 +725,11 @@ impl DocFolder for Cache { let v = self.implementors.find_or_insert_with(did, |_| { Vec::new() }); - match i.for_ { - clean::ResolvedPath{..} => { - v.unshift(PathType(i.for_.clone())); - } - _ => { - v.push(OtherType(i.generics.clone(), - i.trait_.get_ref().clone(), - i.for_.clone())); - } - } + v.push(Implementor { + generics: i.generics.clone(), + trait_: i.trait_.get_ref().clone(), + for_: i.for_.clone(), + }); } Some(..) | None => {} } @@ -1489,34 +1472,33 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, try!(write!(w, "")); } - match cache_key.get().unwrap().implementors.find(&it.def_id) { + let cache = cache_key.get().unwrap(); + try!(write!(w, " +

Implementors

+
    + ")); + match cache.implementors.find(&it.def_id) { Some(implementors) => { - try!(write!(w, " -

    Implementors

    -
      - ")); for i in implementors.iter() { - match *i { - PathType(ref ty) => { - try!(write!(w, "
    • {}
    • ", *ty)); - } - OtherType(ref generics, ref trait_, ref for_) => { - try!(write!(w, "
    • impl{} {} for {}
    • ", - *generics, *trait_, *for_)); - } - } + try!(writeln!(w, "
    • impl{} {} for {}
    • ", + i.generics, i.trait_, i.for_)); } - try!(write!(w, "
    ")); - try!(write!(w, r#""#, - cx.current.iter().map(|_| "..") - .collect::>().connect("/"), - cx.current.connect("/"), - shortty(it).to_static_str(), - *it.name.get_ref())); } None => {} } + try!(write!(w, "
")); + try!(write!(w, r#""#, + root_path = Vec::from_elem(cx.current.len(), "..").connect("/"), + path = if ast_util::is_local(it.def_id) { + cx.current.connect("/") + } else { + let path = cache.external_paths.get(&it.def_id); + path.slice_to(path.len() - 1).connect("/") + }, + ty = shortty(it).to_static_str(), + name = *it.name.get_ref())); Ok(()) } diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 2fb824653d3db..d153728ccce54 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -658,15 +658,13 @@ var list = $('#implementors-list'); var libs = Object.getOwnPropertyNames(imp); for (var i = 0; i < libs.length; i++) { - var structs = Object.getOwnPropertyNames(imp[libs[i]]); + if (libs[i] == currentCrate) continue; + var structs = imp[libs[i]]; for (var j = 0; j < structs.length; j++) { - console.log(i, structs[j]); - var path = rootPath + imp[libs[i]][structs[j]]; - var klass = path.contains("type.") ? "type" : "struct"; - var link = $('').text(structs[j]) - .attr('href', path) - .attr('class', klass); - var code = $('').append(link); + var code = $('').append(structs[j]); + $.each(code.find('a'), function(idx, a) { + $(a).attr('href', rootPath + $(a).attr('href')); + }); var li = $('
  • ').append(code); list.append(li); } From 55325e68ecc62a0c627e24905e9299050cbe25e7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 May 2014 18:07:08 -0700 Subject: [PATCH 4/4] rustdoc: Stringify more named lifetimes cc #14462 --- src/librustdoc/clean/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2e41b2c2f49d3..0a2de70147d85 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -519,9 +519,9 @@ impl Clean> for ty::Region { ty::ReStatic => Some(Lifetime("static".to_string())), ty::ReLateBound(_, ty::BrNamed(_, name)) => Some(Lifetime(token::get_name(name).get().to_string())), + ty::ReEarlyBound(_, _, name) => Some(Lifetime(name.clean())), ty::ReLateBound(..) | - ty::ReEarlyBound(..) | ty::ReFree(..) | ty::ReScope(..) | ty::ReInfer(..) |