diff options
Diffstat (limited to 'src/librustdoc/html/render')
-rw-r--r-- | src/librustdoc/html/render/context.rs | 37 | ||||
-rw-r--r-- | src/librustdoc/html/render/mod.rs | 399 | ||||
-rw-r--r-- | src/librustdoc/html/render/print_item.rs | 95 | ||||
-rw-r--r-- | src/librustdoc/html/render/search_index.rs | 13 | ||||
-rw-r--r-- | src/librustdoc/html/render/span_map.rs | 34 | ||||
-rw-r--r-- | src/librustdoc/html/render/write_shared.rs | 79 |
6 files changed, 413 insertions, 244 deletions
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 2ed7a6f1b..62def4a94 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -31,6 +31,7 @@ use crate::formats::FormatRenderer; use crate::html::escape::Escape; use crate::html::format::{join_with_double_colon, Buffer}; use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap}; +use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources}; use crate::scrape_examples::AllCallLocations; use crate::try_err; @@ -71,7 +72,7 @@ pub(crate) struct Context<'tcx> { } // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))] rustc_data_structures::static_assert_size!(Context<'_>, 128); /// Shared mutable state used in [`Context`] and elsewhere. @@ -301,13 +302,10 @@ impl<'tcx> Context<'tcx> { /// may happen, for example, with externally inlined items where the source /// of their crate documentation isn't known. pub(super) fn src_href(&self, item: &clean::Item) -> Option<String> { - self.href_from_span(item.span(self.tcx()), true) + self.href_from_span(item.span(self.tcx())?, true) } pub(crate) fn href_from_span(&self, span: clean::Span, with_lines: bool) -> Option<String> { - if span.is_dummy() { - return None; - } let mut root = self.root_path(); let mut path = String::new(); let cnum = span.cnum(self.sess()); @@ -373,6 +371,35 @@ impl<'tcx> Context<'tcx> { anchor = anchor )) } + + pub(crate) fn href_from_span_relative( + &self, + span: clean::Span, + relative_to: &str, + ) -> Option<String> { + self.href_from_span(span, false).map(|s| { + let mut url = UrlPartsBuilder::new(); + let mut dest_href_parts = s.split('/'); + let mut cur_href_parts = relative_to.split('/'); + for (cur_href_part, dest_href_part) in (&mut cur_href_parts).zip(&mut dest_href_parts) { + if cur_href_part != dest_href_part { + url.push(dest_href_part); + break; + } + } + for dest_href_part in dest_href_parts { + url.push(dest_href_part); + } + let loline = span.lo(self.sess()).line; + let hiline = span.hi(self.sess()).line; + format!( + "{}{}#{}", + "../".repeat(cur_href_parts.count()), + url.finish(), + if loline == hiline { loline.to_string() } else { format!("{loline}-{hiline}") } + ) + }) + } } /// Generates the documentation for `crate` into the directory `dst` diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a262c8f7d..1e6f20d2b 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -191,12 +191,6 @@ impl StylePath { } } -fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { - if let Some(l) = cx.src_href(item) { - write!(buf, "<a class=\"srclink\" href=\"{}\">source</a>", l) - } -} - #[derive(Debug, Eq, PartialEq, Hash)] struct ItemEntry { url: String, @@ -522,7 +516,14 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option<Strin (cfg, _) => cfg.as_deref().cloned(), }; - debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.and_then(|p| p.cfg.as_ref()), cfg); + debug!( + "Portability {:?} {:?} (parent: {:?}) - {:?} = {:?}", + item.name, + item.cfg, + parent, + parent.and_then(|p| p.cfg.as_ref()), + cfg + ); Some(format!("<div class=\"stab portability\">{}</div>", cfg?.render_long_html())) } @@ -569,7 +570,10 @@ fn short_item_info( message.push_str(&format!(": {}", html.into_string())); } extra_info.push(format!( - "<div class=\"stab deprecated\"><span class=\"emoji\">๐</span> {}</div>", + "<div class=\"stab deprecated\">\ + <span class=\"emoji\">๐</span>\ + <span>{}</span>\ + </div>", message, )); } @@ -582,8 +586,9 @@ fn short_item_info( .filter(|stab| stab.feature != sym::rustc_private) .map(|stab| (stab.level, stab.feature)) { - let mut message = - "<span class=\"emoji\">๐ฌ</span> This is a nightly-only experimental API.".to_owned(); + let mut message = "<span class=\"emoji\">๐ฌ</span>\ + <span>This is a nightly-only experimental API." + .to_owned(); let mut feature = format!("<code>{}</code>", Escape(feature.as_str())); if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) { @@ -594,7 +599,7 @@ fn short_item_info( )); } - message.push_str(&format!(" ({})", feature)); + message.push_str(&format!(" ({})</span>", feature)); extra_info.push(format!("<div class=\"stab unstable\">{}</div>", message)); } @@ -608,10 +613,10 @@ fn short_item_info( // Render the list of items inside one of the sections "Trait Implementations", // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). -fn render_impls( +pub(crate) fn render_impls( cx: &mut Context<'_>, w: &mut Buffer, - impls: &[&&Impl], + impls: &[&Impl], containing_item: &clean::Item, toggle_open_by_default: bool, ) { @@ -816,7 +821,7 @@ fn assoc_method( href = href, name = name, generics = g.print(cx), - decl = d.full_print(header_len, indent, header.asyncness, cx), + decl = d.full_print(header_len, indent, cx), notable_traits = notable_traits_decl(d, cx), where_clause = print_where_clause(g, cx, indent, end_newline), ) @@ -836,12 +841,13 @@ fn assoc_method( /// Note that it is possible for an unstable function to be const-stable. In that case, the span /// will include the const-stable version, but no stable version will be emitted, as a natural /// consequence of the above rules. -fn render_stability_since_raw( +fn render_stability_since_raw_with_extra( w: &mut Buffer, ver: Option<Symbol>, const_stability: Option<ConstStability>, containing_ver: Option<Symbol>, containing_const_ver: Option<Symbol>, + extra_class: &str, ) -> bool { let stable_version = ver.filter(|inner| !inner.is_empty() && Some(*inner) != containing_ver); @@ -889,12 +895,30 @@ fn render_stability_since_raw( } if !stability.is_empty() { - write!(w, r#"<span class="since" title="{}">{}</span>"#, title, stability); + write!(w, r#"<span class="since{extra_class}" title="{title}">{stability}</span>"#); } !stability.is_empty() } +#[inline] +fn render_stability_since_raw( + w: &mut Buffer, + ver: Option<Symbol>, + const_stability: Option<ConstStability>, + containing_ver: Option<Symbol>, + containing_const_ver: Option<Symbol>, +) -> bool { + render_stability_since_raw_with_extra( + w, + ver, + const_stability, + containing_ver, + containing_const_ver, + "", + ) +} + fn render_assoc_item( w: &mut Buffer, item: &clean::Item, @@ -1001,6 +1025,47 @@ impl<'a> AssocItemLink<'a> { } } +fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) { + write!( + w, + "<h2 id=\"{id}\" class=\"small-section-header\">\ + {title}\ + <a href=\"#{id}\" class=\"anchor\"></a>\ + </h2>" + ); +} + +pub(crate) fn render_all_impls( + w: &mut Buffer, + cx: &mut Context<'_>, + containing_item: &clean::Item, + concrete: &[&Impl], + synthetic: &[&Impl], + blanket_impl: &[&Impl], +) { + let mut impls = Buffer::empty_from(w); + render_impls(cx, &mut impls, concrete, containing_item, true); + let impls = impls.into_inner(); + if !impls.is_empty() { + write_impl_section_heading(w, "Trait Implementations", "trait-implementations"); + write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls); + } + + if !synthetic.is_empty() { + write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations"); + w.write_str("<div id=\"synthetic-implementations-list\">"); + render_impls(cx, w, synthetic, containing_item, false); + w.write_str("</div>"); + } + + if !blanket_impl.is_empty() { + write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations"); + w.write_str("<div id=\"blanket-implementations-list\">"); + render_impls(cx, w, blanket_impl, containing_item, false); + w.write_str("</div>"); + } +} + fn render_assoc_items( w: &mut Buffer, cx: &mut Context<'_>, @@ -1030,12 +1095,7 @@ fn render_assoc_items_inner( let mut tmp_buf = Buffer::empty_from(w); let (render_mode, id) = match what { AssocItemRender::All => { - tmp_buf.write_str( - "<h2 id=\"implementations\" class=\"small-section-header\">\ - Implementations\ - <a href=\"#implementations\" class=\"anchor\"></a>\ - </h2>", - ); + write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations"); (RenderMode::Normal, "implementations-list".to_owned()) } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { @@ -1044,15 +1104,14 @@ fn render_assoc_items_inner( if let Some(def_id) = type_.def_id(cx.cache()) { cx.deref_id_map.insert(def_id, id.clone()); } - write!( - tmp_buf, - "<h2 id=\"{id}\" class=\"small-section-header\">\ - <span>Methods from {trait_}<Target = {type_}></span>\ - <a href=\"#{id}\" class=\"anchor\"></a>\ - </h2>", - id = id, - trait_ = trait_.print(cx), - type_ = type_.print(cx), + write_impl_section_heading( + &mut tmp_buf, + &format!( + "<span>Methods from {trait_}<Target = {type_}></span>", + trait_ = trait_.print(cx), + type_ = type_.print(cx), + ), + &id, ); (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id)) } @@ -1099,49 +1158,12 @@ fn render_assoc_items_inner( return; } - let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = - traits.iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&&Impl>, _) = + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); - let mut impls = Buffer::empty_from(w); - render_impls(cx, &mut impls, &concrete, containing_item, true); - let impls = impls.into_inner(); - if !impls.is_empty() { - write!( - w, - "<h2 id=\"trait-implementations\" class=\"small-section-header\">\ - Trait Implementations\ - <a href=\"#trait-implementations\" class=\"anchor\"></a>\ - </h2>\ - <div id=\"trait-implementations-list\">{}</div>", - impls - ); - } - - if !synthetic.is_empty() { - w.write_str( - "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\ - Auto Trait Implementations\ - <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\ - </h2>\ - <div id=\"synthetic-implementations-list\">", - ); - render_impls(cx, w, &synthetic, containing_item, false); - w.write_str("</div>"); - } - - if !blanket_impl.is_empty() { - w.write_str( - "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\ - Blanket Implementations\ - <a href=\"#blanket-implementations\" class=\"anchor\"></a>\ - </h2>\ - <div id=\"blanket-implementations-list\">", - ); - render_impls(cx, w, &blanket_impl, containing_item, false); - w.write_str("</div>"); - } + render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); } } @@ -1550,6 +1572,15 @@ fn render_impl( rendering_params: ImplRenderingParameters, ) { for trait_item in &t.items { + // Skip over any default trait items that are impossible to call + // (e.g. if it has a `Self: Sized` bound on an unsized type). + if let Some(impl_def_id) = parent.item_id.as_def_id() + && let Some(trait_item_def_id) = trait_item.item_id.as_def_id() + && cx.tcx().is_impossible_method((impl_def_id, trait_item_def_id)) + { + continue; + } + let n = trait_item.name; if i.items.iter().any(|m| m.name == n) { continue; @@ -1668,23 +1699,29 @@ fn render_rightside( RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)), RenderMode::ForDeref { .. } => (None, None), }; + let src_href = cx.src_href(item); + let has_src_ref = src_href.is_some(); let mut rightside = Buffer::new(); - let has_stability = render_stability_since_raw( + let has_stability = render_stability_since_raw_with_extra( &mut rightside, item.stable_since(tcx), const_stability, containing_item.stable_since(tcx), const_stable_since, + if has_src_ref { "" } else { " rightside" }, ); - let mut srclink = Buffer::empty_from(w); - write_srclink(cx, item, &mut srclink); - if has_stability && !srclink.is_empty() { - rightside.write_str(" ยท "); + if let Some(l) = src_href { + if has_stability { + write!(rightside, " ยท <a class=\"srclink\" href=\"{}\">source</a>", l) + } else { + write!(rightside, "<a class=\"srclink rightside\" href=\"{}\">source</a>", l) + } } - rightside.push_buffer(srclink); - if !rightside.is_empty() { + if has_stability && has_src_ref { write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner()); + } else { + w.push_buffer(rightside); } } @@ -1700,8 +1737,8 @@ pub(crate) fn render_impl_summary( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], ) { - let id = - cx.derive_id(get_id_for_impl(&i.inner_impl().for_, i.inner_impl().trait_.as_ref(), cx)); + let inner_impl = i.inner_impl(); + let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx)); let aliases = if aliases.is_empty() { String::new() } else { @@ -1713,9 +1750,9 @@ pub(crate) fn render_impl_summary( write!(w, "<h3 class=\"code-header in-band\">"); if let Some(use_absolute) = use_absolute { - write!(w, "{}", i.inner_impl().print(use_absolute, cx)); + write!(w, "{}", inner_impl.print(use_absolute, cx)); if show_def_docs { - for it in &i.inner_impl().items { + for it in &inner_impl.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { w.write_str("<span class=\"where fmt-newline\"> "); assoc_type( @@ -1733,11 +1770,11 @@ pub(crate) fn render_impl_summary( } } } else { - write!(w, "{}", i.inner_impl().print(false, cx)); + write!(w, "{}", inner_impl.print(false, cx)); } write!(w, "</h3>"); - let is_trait = i.inner_impl().trait_.is_some(); + let is_trait = inner_impl.trait_.is_some(); if is_trait { if let Some(portability) = portability(&i.impl_item, Some(parent)) { write!(w, "<span class=\"item-info\">{}</span>", portability); @@ -1931,6 +1968,70 @@ fn small_url_encode(s: String) -> String { } } +pub(crate) fn sidebar_render_assoc_items( + cx: &Context<'_>, + out: &mut Buffer, + id_map: &mut IdMap, + concrete: Vec<&Impl>, + synthetic: Vec<&Impl>, + blanket_impl: Vec<&Impl>, +) { + let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { + let mut links = FxHashSet::default(); + + let mut ret = impls + .iter() + .filter_map(|it| { + let trait_ = it.inner_impl().trait_.as_ref()?; + let encoded = + id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); + + let i_display = format!("{:#}", trait_.print(cx)); + let out = Escape(&i_display); + let prefix = match it.inner_impl().polarity { + ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", + ty::ImplPolarity::Negative => "!", + }; + let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out); + if links.insert(generated.clone()) { Some(generated) } else { None } + }) + .collect::<Vec<String>>(); + ret.sort(); + ret + }; + + let concrete_format = format_impls(concrete, id_map); + let synthetic_format = format_impls(synthetic, id_map); + let blanket_format = format_impls(blanket_impl, id_map); + + if !concrete_format.is_empty() { + print_sidebar_block( + out, + "trait-implementations", + "Trait Implementations", + concrete_format.iter(), + ); + } + + if !synthetic_format.is_empty() { + print_sidebar_block( + out, + "synthetic-implementations", + "Auto Trait Implementations", + synthetic_format.iter(), + ); + } + + if !blanket_format.is_empty() { + print_sidebar_block( + out, + "blanket-implementations", + "Blanket Implementations", + blanket_format.iter(), + ); + } +} + fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { let did = it.item_id.expect_def_id(); let cache = cx.cache(); @@ -1976,68 +2077,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { { let mut derefs = FxHashSet::default(); derefs.insert(did); - sidebar_deref_methods(cx, out, impl_, v, &mut derefs); + sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links); } - let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| { - let mut links = FxHashSet::default(); - - let mut ret = impls - .iter() - .filter_map(|it| { - let trait_ = it.inner_impl().trait_.as_ref()?; - let encoded = - id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx)); - - let i_display = format!("{:#}", trait_.print(cx)); - let out = Escape(&i_display); - let prefix = match it.inner_impl().polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", - ty::ImplPolarity::Negative => "!", - }; - let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out); - if links.insert(generated.clone()) { Some(generated) } else { None } - }) - .collect::<Vec<String>>(); - ret.sort(); - ret - }; - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto()); let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket()); - let concrete_format = format_impls(concrete, &mut id_map); - let synthetic_format = format_impls(synthetic, &mut id_map); - let blanket_format = format_impls(blanket_impl, &mut id_map); - - if !concrete_format.is_empty() { - print_sidebar_block( - out, - "trait-implementations", - "Trait Implementations", - concrete_format.iter(), - ); - } - - if !synthetic_format.is_empty() { - print_sidebar_block( - out, - "synthetic-implementations", - "Auto Trait Implementations", - synthetic_format.iter(), - ); - } - - if !blanket_format.is_empty() { - print_sidebar_block( - out, - "blanket-implementations", - "Blanket Implementations", - blanket_format.iter(), - ); - } + sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl); } } } @@ -2048,6 +2096,7 @@ fn sidebar_deref_methods( impl_: &Impl, v: &[Impl], derefs: &mut FxHashSet<DefId>, + used_links: &mut FxHashSet<String>, ) { let c = cx.cache(); @@ -2080,13 +2129,10 @@ fn sidebar_deref_methods( .and_then(|did| c.impls.get(&did)); if let Some(impls) = inner_impl { debug!("found inner_impl: {:?}", impls); - let mut used_links = FxHashSet::default(); let mut ret = impls .iter() .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| { - get_methods(i.inner_impl(), true, &mut used_links, deref_mut, cx.tcx()) - }) + .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) .collect::<Vec<_>>(); if !ret.is_empty() { let id = if let Some(target_def_id) = real_target.def_id(c) { @@ -2115,7 +2161,14 @@ fn sidebar_deref_methods( .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) .unwrap_or(false) }) { - sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs); + sidebar_deref_methods( + cx, + out, + target_deref_impl, + target_impls, + derefs, + used_links, + ); } } } @@ -2302,9 +2355,54 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean buf.push_str("</section>") } +/// Returns the list of implementations for the primitive reference type, filtering out any +/// implementations that are on concrete or partially generic types, only keeping implementations +/// of the form `impl<T> Trait for &T`. +pub(crate) fn get_filtered_impls_for_reference<'a>( + shared: &'a Rc<SharedContext<'_>>, + it: &clean::Item, +) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) { + let def_id = it.item_id.expect_def_id(); + // If the reference primitive is somehow not defined, exit early. + let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) }; + // Since there is no "direct implementation" on the reference primitive type, we filter out + // every implementation which isn't a trait implementation. + let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some()); + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = + traits.partition(|t| t.inner_impl().kind.is_auto()); + + let (blanket_impl, concrete): (Vec<&Impl>, _) = + concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); + // Now we keep only references over full generic types. + let concrete: Vec<_> = concrete + .into_iter() + .filter(|t| match t.inner_impl().for_ { + clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(), + _ => false, + }) + .collect(); + + (concrete, synthetic, blanket_impl) +} + fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { let mut sidebar = Buffer::new(); - sidebar_assoc_items(cx, &mut sidebar, it); + + if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { + sidebar_assoc_items(cx, &mut sidebar, it); + } else { + let shared = Rc::clone(&cx.shared); + let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); + + sidebar_render_assoc_items( + cx, + &mut sidebar, + &mut IdMap::new(), + concrete, + synthetic, + blanket_impl, + ); + } if !sidebar.is_empty() { write!(buf, "<section>{}</section>", sidebar.into_inner()); @@ -2614,8 +2712,8 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> { clean::Type::BorrowedRef { type_, .. } => { work.push_back(*type_); } - clean::Type::QPath { self_type, trait_, .. } => { - work.push_back(*self_type); + clean::Type::QPath(box clean::QPathData { self_type, trait_, .. }) => { + work.push_back(self_type); process_path(trait_.def_id()); } _ => {} @@ -2668,7 +2766,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite let contents = match fs::read_to_string(&path) { Ok(contents) => contents, Err(err) => { - let span = item.span(tcx).inner(); + let span = item.span(tcx).map_or(rustc_span::DUMMY_SP, |span| span.inner()); tcx.sess .span_err(span, &format!("failed to read file {}: {}", path.display(), err)); return false; @@ -2764,11 +2862,10 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite sources::print_src( w, contents_subset, - call_data.edition, file_span, cx, &root_path, - Some(highlight::DecorationInfo(decoration_info)), + highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded { offset: line_min }, ); write!(w, "</div></div>"); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 99cf42919..cfa450942 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -16,10 +16,10 @@ use std::fmt; use std::rc::Rc; use super::{ - collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section, - notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code, - render_attributes_in_pre, render_impl, render_stability_since_raw, write_srclink, - AssocItemLink, Context, ImplRenderingParameters, + collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, + item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item, + render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl, + render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters, }; use crate::clean; use crate::config::ModuleSorting; @@ -371,16 +371,21 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: } clean::ImportKind::Glob => String::new(), }; + let stab_tags = stab_tags.unwrap_or_default(); + let (stab_tags_before, stab_tags_after) = if stab_tags.is_empty() { + ("", "") + } else { + ("<div class=\"item-right docblock-short\">", "</div>") + }; write!( w, "<div class=\"item-left {stab}{add}import-item\"{id}>\ <code>{vis}{imp}</code>\ </div>\ - <div class=\"item-right docblock-short\">{stab_tags}</div>", + {stab_tags_before}{stab_tags}{stab_tags_after}", stab = stab.unwrap_or_default(), vis = myitem.visibility.print_with_space(myitem.item_id, cx), imp = import.print(cx), - stab_tags = stab_tags.unwrap_or_default(), ); w.write_str(ITEM_TABLE_ROW_CLOSE); } @@ -412,6 +417,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: let doc_value = myitem.doc_value().unwrap_or_default(); w.write_str(ITEM_TABLE_ROW_OPEN); + let docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(); + let (docs_before, docs_after) = if docs.is_empty() { + ("", "") + } else { + ("<div class=\"item-right docblock-short\">", "</div>") + }; write!( w, "<div class=\"item-left {stab}{add}module-item\">\ @@ -420,11 +431,10 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: {unsafety_flag}\ {stab_tags}\ </div>\ - <div class=\"item-right docblock-short\">{docs}</div>", + {docs_before}{docs}{docs_after}", name = myitem.name.unwrap(), visibility_emoji = visibility_emoji, stab_tags = extra_info_tags(myitem, item, cx.tcx()), - docs = MarkdownSummaryLine(&doc_value, &myitem.links(cx)).into_string(), class = myitem.type_(), add = add, stab = stab.unwrap_or_default(), @@ -477,7 +487,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> (cfg, _) => cfg.as_deref().cloned(), }; - debug!("Portability {:?} - {:?} = {:?}", item.cfg, parent.cfg, cfg); + debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg); if let Some(ref cfg) = cfg { tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); } @@ -520,7 +530,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle name = name, generics = f.generics.print(cx), where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline), - decl = f.decl.full_print(header_len, 0, header.asyncness, cx), + decl = f.decl.full_print(header_len, 0, cx), notable_traits = notable_traits_decl(&f.decl, cx), ); }); @@ -709,14 +719,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: write!(w, "<details class=\"rustdoc-toggle\" open><summary>"); } write!(w, "<div id=\"{}\" class=\"method has-srclink\">", id); - write!(w, "<div class=\"rightside\">"); - - let has_stability = render_stability_since(w, m, t, cx.tcx()); - if has_stability { - w.write_str(" ยท "); - } - write_srclink(cx, m, w); - write!(w, "</div>"); + render_rightside(w, cx, m, t, RenderMode::Normal); write!(w, "<h4 class=\"code-header\">"); render_assoc_item( w, @@ -994,7 +997,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // So C's HTML will have something like this: // // ```html - // <script type="text/javascript" src="/implementors/A/trait.Foo.js" + // <script src="/implementors/A/trait.Foo.js" // data-ignore-extern-crates="A,B" async></script> // ``` // @@ -1020,9 +1023,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: .map(|cnum| cx.shared.tcx.crate_name(cnum).to_string()) .collect::<Vec<_>>() .join(","); + let (extern_before, extern_after) = + if extern_crates.is_empty() { ("", "") } else { (" data-ignore-extern-crates=\"", "\"") }; write!( w, - "<script type=\"text/javascript\" src=\"{src}\" data-ignore-extern-crates=\"{extern_crates}\" async></script>", + "<script src=\"{src}\"{extern_before}{extern_crates}{extern_after} async></script>", src = js_src_path.finish(), ); } @@ -1198,7 +1203,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let name = v.name.unwrap(); match *v.kind { clean::VariantItem(ref var) => match var { - clean::Variant::CLike => write!(w, "{}", name), + // FIXME(#101337): Show discriminant + clean::Variant::CLike(..) => write!(w, "{}", name), clean::Variant::Tuple(ref s) => { write!(w, "{}(", name); print_tuple_struct_fields(w, cx, s); @@ -1260,7 +1266,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w.write_str(")"); } w.write_str("</code>"); - render_stability_since(w, variant, it, cx.tcx()); + render_stability_since_raw( + w, + variant.stable_since(cx.tcx()), + variant.const_stability(cx.tcx()), + it.stable_since(cx.tcx()), + it.const_stable_since(cx.tcx()), + ); w.write_str("</h3>"); use crate::clean::Variant; @@ -1322,17 +1334,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { wrap_into_docblock(w, |w| { - highlight::render_with_highlighting( - &t.source, - w, - Some("macro"), - None, - None, - it.span(cx.tcx()).inner().edition(), - None, - None, - None, - ); + highlight::render_macro_with_highlighting(&t.source, w); }); document(w, cx, it, None, HeadingOffset::H2) } @@ -1370,8 +1372,18 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c } fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { + let def_id = it.item_id.expect_def_id(); document(w, cx, it, None, HeadingOffset::H2); - render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) + if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { + render_assoc_items(w, cx, it, def_id, AssocItemRender::All); + } else { + // We handle the "reference" primitive type on its own because we only want to list + // implementations on generic types. + let shared = Rc::clone(&cx.shared); + let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); + + render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl); + } } fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { @@ -1601,21 +1613,6 @@ where w.write_str("</code></pre>"); } -fn render_stability_since( - w: &mut Buffer, - item: &clean::Item, - containing_item: &clean::Item, - tcx: TyCtxt<'_>, -) -> bool { - render_stability_since_raw( - w, - item.stable_since(tcx), - item.const_stability(tcx), - containing_item.stable_since(tcx), - containing_item.const_stable_since(tcx), - ) -} - fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> Ordering { let lhss = format!("{}", lhs.inner_impl().print(false, cx)); let rhss = format!("{}", rhs.inner_impl().print(false, cx)); diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index d672f0bb5..8eb9c07f8 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -544,10 +544,15 @@ fn get_fn_inputs_and_outputs<'tcx>( (true, _) => (Some(impl_self), &func.generics), (_, true) => (Some(impl_self), impl_generics), (false, false) => { - let mut params = func.generics.params.clone(); - params.extend(impl_generics.params.clone()); - let mut where_predicates = func.generics.where_predicates.clone(); - where_predicates.extend(impl_generics.where_predicates.clone()); + let params = + func.generics.params.iter().chain(&impl_generics.params).cloned().collect(); + let where_predicates = func + .generics + .where_predicates + .iter() + .chain(&impl_generics.where_predicates) + .cloned() + .collect(); combined_generics = clean::Generics { params, where_predicates }; (Some(impl_self), &combined_generics) } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 34d590fb2..151ec2b28 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -166,25 +166,23 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) { if let ExprKind::MethodCall(segment, ..) = expr.kind { - if let Some(hir_id) = segment.hir_id { - let hir = self.tcx.hir(); - let body_id = hir.enclosing_body_owner(hir_id); - // FIXME: this is showing error messages for parts of the code that are not - // compiled (because of cfg)! - // - // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352 - let typeck_results = self.tcx.typeck_body( - hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"), + let hir = self.tcx.hir(); + let body_id = hir.enclosing_body_owner(segment.hir_id); + // FIXME: this is showing error messages for parts of the code that are not + // compiled (because of cfg)! + // + // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352 + let typeck_results = self + .tcx + .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body")); + if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) { + self.matches.insert( + segment.ident.span, + match hir.span_if_local(def_id) { + Some(span) => LinkFromSrc::Local(clean::Span::new(span)), + None => LinkFromSrc::External(def_id), + }, ); - if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) { - self.matches.insert( - segment.ident.span, - match hir.span_if_local(def_id) { - Some(span) => LinkFromSrc::Local(clean::Span::new(span)), - None => LinkFromSrc::External(def_id), - }, - ); - } } } else if self.handle_macro(expr.span) { // We don't want to go deeper into the macro. diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 6fb41ff32..fc4d46fe6 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -1,5 +1,4 @@ use std::ffi::OsStr; -use std::fmt::Write; use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; @@ -10,7 +9,8 @@ use std::sync::LazyLock as Lazy; use itertools::Itertools; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use serde::Serialize; +use serde::ser::SerializeSeq; +use serde::{Serialize, Serializer}; use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS}; use crate::clean::Crate; @@ -284,25 +284,43 @@ pub(super) fn write_shared( cx.write_shared(SharedResource::Unversioned { name }, contents, &options.emit)?; } - fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec<String>, Vec<String>)> { + /// Read a file and return all lines that match the `"{crate}":{data},` format, + /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`. + /// + /// This forms the payload of files that look like this: + /// + /// ```javascript + /// var data = { + /// "{crate1}":{data}, + /// "{crate2}":{data} + /// }; + /// use_data(data); + /// ``` + /// + /// The file needs to be formatted so that *only crate data lines start with `"`*. + fn collect(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> { let mut ret = Vec::new(); let mut krates = Vec::new(); if path.exists() { - let prefix = format!(r#"{}["{}"]"#, key, krate); + let prefix = format!("\"{}\"", krate); for line in BufReader::new(File::open(path)?).lines() { let line = line?; - if !line.starts_with(key) { + if !line.starts_with('"') { continue; } if line.starts_with(&prefix) { continue; } - ret.push(line.to_string()); + if line.ends_with(',') { + ret.push(line[..line.len() - 1].to_string()); + } else { + // No comma (it's the case for the last added crate line) + ret.push(line.to_string()); + } krates.push( - line[key.len() + 2..] - .split('"') - .next() + line.split('"') + .find(|s| !s.is_empty()) .map(|s| s.to_owned()) .unwrap_or_else(String::new), ); @@ -311,6 +329,20 @@ pub(super) fn write_shared( Ok((ret, krates)) } + /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format, + /// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`. + /// + /// This forms the payload of files that look like this: + /// + /// ```javascript + /// var data = JSON.parse('{\ + /// "{crate1}":{data},\ + /// "{crate2}":{data}\ + /// }'); + /// use_data(data); + /// ``` + /// + /// The file needs to be formatted so that *only crate data lines start with `"`*. fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> { let mut ret = Vec::new(); let mut krates = Vec::new(); @@ -526,13 +558,27 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; }, }; - #[derive(Serialize)] struct Implementor { text: String, synthetic: bool, types: Vec<String>, } + impl Serialize for Implementor { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.text)?; + if self.synthetic { + seq.serialize_element(&1)?; + seq.serialize_element(&self.types)?; + } + seq.end() + } + } + let implementors = imps .iter() .filter_map(|imp| { @@ -563,9 +609,9 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; } let implementors = format!( - r#"implementors["{}"] = {};"#, + r#""{}":{}"#, krate.name(cx.tcx()), - serde_json::to_string(&implementors).unwrap() + serde_json::to_string(&implementors).expect("failed serde conversion"), ); let mut mydst = dst.clone(); @@ -576,16 +622,15 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); let (mut all_implementors, _) = - try_err!(collect(&mydst, krate.name(cx.tcx()).as_str(), "implementors"), &mydst); + try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst); all_implementors.push(implementors); // Sort the implementors by crate so the file will be generated // identically even with rustdoc running in parallel. all_implementors.sort(); - let mut v = String::from("(function() {var implementors = {};\n"); - for implementor in &all_implementors { - writeln!(v, "{}", *implementor).unwrap(); - } + let mut v = String::from("(function() {var implementors = {\n"); + v.push_str(&all_implementors.join(",\n")); + v.push_str("\n};"); v.push_str( "if (window.register_implementors) {\ window.register_implementors(implementors);\ |