diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b141820fe42..aeaee524fd4 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -567,11 +567,12 @@ struct SummaryLine<'a, I: Iterator>> { inner: I, started: bool, depth: u32, + skipped_tags: u32, } impl<'a, I: Iterator>> SummaryLine<'a, I> { fn new(iter: I) -> Self { - SummaryLine { inner: iter, started: false, depth: 0 } + SummaryLine { inner: iter, started: false, depth: 0, skipped_tags: 0 } } } @@ -601,6 +602,7 @@ impl<'a, I: Iterator>> Iterator for SummaryLine<'a, I> { let is_allowed_tag = match event { Event::Start(ref c) => { if is_forbidden_tag(c) { + self.skipped_tags += 1; return None; } self.depth += 1; @@ -608,6 +610,7 @@ impl<'a, I: Iterator>> Iterator for SummaryLine<'a, I> { } Event::End(ref c) => { if is_forbidden_tag(c) { + self.skipped_tags += 1; return None; } self.depth -= 1; @@ -616,6 +619,9 @@ impl<'a, I: Iterator>> Iterator for SummaryLine<'a, I> { } _ => true, }; + if !is_allowed_tag { + self.skipped_tags += 1; + } return if !is_allowed_tag { if is_start { Some(Event::Start(Tag::Paragraph)) @@ -1096,11 +1102,11 @@ impl MarkdownItemInfo<'_> { } impl MarkdownSummaryLine<'_> { - pub(crate) fn into_string(self) -> String { + pub(crate) fn into_string_with_has_more_content(self) -> (String, bool) { let MarkdownSummaryLine(md, links) = self; // This is actually common enough to special-case if md.is_empty() { - return String::new(); + return (String::new(), false); } let mut replacer = |broken_link: BrokenLink<'_>| { @@ -1110,17 +1116,26 @@ impl MarkdownSummaryLine<'_> { .map(|link| (link.href.as_str().into(), link.new_text.as_str().into())) }; - let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)); + let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)) + .peekable(); + let mut summary = SummaryLine::new(p); let mut s = String::new(); - let without_paragraphs = LinkReplacer::new(SummaryLine::new(p), links).filter(|event| { + let without_paragraphs = LinkReplacer::new(&mut summary, links).filter(|event| { !matches!(event, Event::Start(Tag::Paragraph) | Event::End(Tag::Paragraph)) }); html::push_html(&mut s, without_paragraphs); - s + let has_more_content = + matches!(summary.inner.peek(), Some(Event::Start(_))) || summary.skipped_tags > 0; + + (s, has_more_content) + } + + pub(crate) fn into_string(self) -> String { + self.into_string_with_has_more_content().0 } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 80fbe9c1f06..146e5010e4e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -467,9 +467,10 @@ fn document_short( return; } if let Some(s) = item.doc_value() { - let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string(); + let (mut summary_html, has_more_content) = + MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content(); - if s.contains('\n') { + if has_more_content { let link = format!(r#" Read more"#, assoc_href_attr(item, link, cx)); if let Some(idx) = summary_html.rfind("

") { diff --git a/src/test/rustdoc/read-more-unneeded.rs b/src/test/rustdoc/read-more-unneeded.rs new file mode 100644 index 00000000000..0303e444261 --- /dev/null +++ b/src/test/rustdoc/read-more-unneeded.rs @@ -0,0 +1,34 @@ +// Regression test for https://github.com/rust-lang/rust/issues/105677. +// This test ensures that the "Read more" link is only generated when +// there is actually more documentation to read after the short summary. + +#![crate_name = "foo"] + +pub trait MyFrom { + /// # Hello + /// ## Yolo + /// more! + fn try_from1(); + /// a + /// b + /// c + fn try_from2(); + /// a + /// + /// b + /// + /// c + fn try_from3(); +} + +pub struct NonZero; + +// @has 'foo/struct.NonZero.html' +impl MyFrom for NonZero { + // @matches - '//*[@class="docblock"]' '^Hello Read more$' + fn try_from1() {} + // @matches - '//*[@class="docblock"]' '^a\sb\sc$' + fn try_from2() {} + // @matches - '//*[@class="docblock"]' '^a Read more$' + fn try_from3() {} +} diff --git a/src/test/rustdoc/trait-impl.rs b/src/test/rustdoc/trait-impl.rs index 195cdf009b9..9cf3226f738 100644 --- a/src/test/rustdoc/trait-impl.rs +++ b/src/test/rustdoc/trait-impl.rs @@ -30,8 +30,6 @@ impl Trait for Struct { // @has - '//*[@id="method.b"]/../../div[@class="docblock"]' 'These docs contain' // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a' 'reference link' // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a/@href' 'https://example.com' - // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a' 'Read more' - // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a/@href' 'trait.Trait.html#tymethod.b' fn b() {} // @!has - '//*[@id="method.c"]/../../div[@class="docblock"]' 'code block'