Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: Return a String from collapsed_doc_value, not an Option #92078

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ impl Item {

/// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
/// with newlines.
crate fn collapsed_doc_value(&self) -> Option<String> {
crate fn collapsed_doc_value(&self) -> String {
self.attrs.collapsed_doc_value()
}

Expand Down Expand Up @@ -956,16 +956,6 @@ fn add_doc_fragment(out: &mut String, frag: &DocFragment) {
}
}

/// Collapse a collection of [`DocFragment`]s into one string,
/// handling indentation and newlines as needed.
crate fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String {
let mut acc = String::new();
for frag in doc_strings {
add_doc_fragment(&mut acc, frag);
}
acc
}

/// A link that has not yet been rendered.
///
/// This link will be turned into a rendered link by [`Item::links`].
Expand Down Expand Up @@ -1104,12 +1094,15 @@ impl Attributes {

/// Finds all `doc` attributes as NameValues and returns their corresponding values, joined
/// with newlines.
crate fn collapsed_doc_value(&self) -> Option<String> {
if self.doc_strings.is_empty() {
None
} else {
Some(collapse_doc_fragments(&self.doc_strings))
}
///
/// Unlike `doc_value`, this does not stop processing lines when switching between `#[doc]` and
/// sugared syntax.
crate fn collapsed_doc_value(&self) -> String {
let mut acc = String::new();
for frag in &self.doc_strings {
add_doc_fragment(&mut acc, frag);
}
acc
}

crate fn get_doc_aliases(&self) -> Box<[Symbol]> {
Expand Down
8 changes: 4 additions & 4 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,17 +1123,17 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
}

attrs.unindent_doc_comments();
// The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
// anything else, this will combine them for us.
if let Some(doc) = attrs.collapsed_doc_value() {
// `doc_value()` doesn't combine sugared/raw doc attributes, this will combine them for us.
let docs = attrs.collapsed_doc_value();
if !docs.is_empty() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure this slight behavior change here and elsewhere doesn't matter? Before, this checked for if there were no DocFragments; now, it checks if the collapsed docs is an empty string.

I think it's probably "more correct" now, but I just wanted to double-check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look at https://github.com/rust-lang/rust/blob/c7476adc38bc9348398afcf24117d7d48e7ba9f1/src/librustdoc/html/markdown.rs#L732, it doesn't do anything for empty strings any way; having a check here at all is a microoptimization we could probably remove. So the change shouldn't have an impact.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this code is looking for doctests, not rendering Markdown.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there should be a behavior change, but there is a very slight possibility.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if calls find_testable_code immediately below. The parser there is parsing the code for doctests.

// Use the outermost invocation, so that doctest names come from where the docs were written.
let span = ast_attrs
.span()
.map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
.unwrap_or(DUMMY_SP);
self.collector.set_position(span);
markdown::find_testable_code(
&doc,
&docs,
self.collector,
self.codes,
self.collector.enable_per_target_ignores,
Expand Down
13 changes: 8 additions & 5 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,19 +567,21 @@ fn document_full_inner(
is_collapsible: bool,
heading_offset: HeadingOffset,
) {
if let Some(s) = item.collapsed_doc_value() {
debug!("Doc block: =====\n{}\n=====", s);
// NOTE: "collapsed" here is unrelated to `is_collapsible`, it just combines `#[doc]` and sugared attributes
let docs = item.collapsed_doc_value();
if !docs.is_empty() {
debug!("Doc block: =====\n{}\n=====", docs);
if is_collapsible {
w.write_str(
"<details class=\"rustdoc-toggle top-doc\" open>\
<summary class=\"hideme\">\
<span>Expand description</span>\
</summary>",
);
render_markdown(w, cx, &s, item.links(cx), heading_offset);
render_markdown(w, cx, &docs, item.links(cx), heading_offset);
w.write_str("</details>");
} else {
render_markdown(w, cx, &s, item.links(cx), heading_offset);
render_markdown(w, cx, &docs, item.links(cx), heading_offset);
}
}

Expand Down Expand Up @@ -1612,7 +1614,8 @@ fn render_impl(
write!(w, "</summary>")
}

if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
let dox = i.impl_item.collapsed_doc_value();
if !dox.is_empty() {
let mut ids = cx.id_map.borrow_mut();
write!(
w,
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/passes/bare_urls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> {
return;
}
};
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
let dox = item.attrs.collapsed_doc_value();
if !dox.is_empty() {
let report_diag = |cx: &DocContext<'_>, msg: &str, url: &str, range: Range<usize>| {
let sp = super::source_span_for_markdown_range(cx.tcx, &dox, &range, &item.attrs)
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/passes/calculate_doc_coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
let mut tests = Tests { found_tests: 0 };

find_testable_code(
&i.attrs.collapsed_doc_value().unwrap_or_default(),
&i.attrs.collapsed_doc_value(),
&mut tests,
ErrorCodes::No,
false,
Expand Down
3 changes: 2 additions & 1 deletion src/librustdoc/passes/check_code_block_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {

impl<'a, 'tcx> DocVisitor for SyntaxChecker<'a, 'tcx> {
fn visit_item(&mut self, item: &clean::Item) {
if let Some(dox) = &item.attrs.collapsed_doc_value() {
let dox = &item.attrs.collapsed_doc_value();
if !dox.is_empty() {
let sp = item.attr_span(self.cx.tcx);
let extra = crate::html::markdown::ExtraInfo::new_did(
self.cx.tcx,
Expand Down
4 changes: 1 addition & 3 deletions src/librustdoc/passes/check_doc_test_visibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ crate fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Cra

impl<'a, 'tcx> DocVisitor for DocTestVisibilityLinter<'a, 'tcx> {
fn visit_item(&mut self, item: &Item) {
let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);

look_for_tests(self.cx, &dox, &item);
look_for_tests(self.cx, &item.attrs.collapsed_doc_value(), &item);

self.visit_item_recur(item)
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/passes/html_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> {
return;
}
};
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
let dox = item.attrs.collapsed_doc_value();
if !dox.is_empty() {
let report_diag = |msg: &str, range: &Range<usize>| {
let sp = match super::source_span_for_markdown_range(tcx, &dox, range, &item.attrs)
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/passes/petrochenkov-suggestions.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- use rustc's map instead of separate traits in scope
- have a map from def id + link index to resolved DefId
- need to have all 3 namespaces in case the link doesn't resolve
11 changes: 7 additions & 4 deletions src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,12 @@ pub struct Item {
/// By default all documented items are public, but you can tell rustdoc to output private items
/// so this field is needed to differentiate.
pub visibility: Visibility,
/// The full markdown docstring of this item. Absent if there is no documentation at all,
/// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`).
pub docs: Option<String>,
/// The full markdown docstring of this item.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only change in behavior is that the JSON backend no longer distinguishes
"undocumented" and "documented with the empty string". This doesn't seem like a particularly useful distinction,
but I can add it back for that code only if you think it's important.

This change seems good to me, but I think this sould be added to the docs field documentation.

Also their should probably be a test for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing, done :)

///
/// This field has already been pre-parsed, including combining lines and de-indenting
/// whitespace. Each line is delimited by a newline, but the docs will not end with a trailing
/// newline (unless added explicitly by the user with `#[doc = "\n"]`).
pub docs: String,
/// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
pub links: HashMap<String, Id>,
/// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
Expand Down Expand Up @@ -511,7 +514,7 @@ pub struct Static {
}

/// rustdoc format-version.
pub const FORMAT_VERSION: u32 = 9;
pub const FORMAT_VERSION: u32 = 10;

#[cfg(test)]
mod tests;
12 changes: 12 additions & 0 deletions src/test/rustdoc-json/empty-lines.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @has empty_lines.json "$.index[*][?(@.name == 'foo')]"
// @has - "$.index[*][?(@.name == 'foo')].docs" \"\\n\\n\"
///
///
///
// ^ note that the above line does *not* include a trailing new line in the docs
pub fn foo() {}

// @has empty_lines.json "$.index[*][?(@.name == 'bar')].docs" "\"first line\\nsecond line \""
#[doc = "\n first line"]
#[doc = "\n second line \n"]
pub fn bar() {}
6 changes: 3 additions & 3 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2401,8 +2401,6 @@ impl<'test> TestCx<'test> {
}

let root = self.config.find_rust_src_root().unwrap();
let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
json_out.set_extension("json");
let res = self.cmd2procres(
Command::new(self.config.jsondocck_path.as_ref().unwrap())
.arg("--doc-dir")
Expand All @@ -2415,7 +2413,9 @@ impl<'test> TestCx<'test> {
self.fatal_proc_rec("jsondocck failed!", &res)
}

let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
let crate_name =
self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_");
let mut json_out = out_dir.join(crate_name);
json_out.set_extension("json");
let res = self.cmd2procres(
Command::new(&self.config.docck_python)
Expand Down
9 changes: 7 additions & 2 deletions src/tools/jsondocck/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ impl CommandKind {
};

if !count {
print_err(&format!("Incorrect number of arguments to `@{}`", self), lineno);
print_err(
&format!("Incorrect number of arguments to `@{}` (got {})", self, args.len()),
lineno,
);
return false;
}

Expand Down Expand Up @@ -317,6 +320,8 @@ fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables)
}))
} else {
Cow::Owned(serde_json::from_str(s).unwrap())
Cow::Owned(serde_json::from_str(s).unwrap_or_else(|err| {
panic!("failed to parse {:?} as a string: {}", s, err);
}))
}
}