5887: Improve fenced code block support for doctests r=fmease a=fmease

Fixes  #5783

Co-authored-by: León Orell Valerian Liehr <liehr.exchange@gmx.net>
This commit is contained in:
bors[bot] 2020-08-26 14:03:47 +00:00 committed by GitHub
commit 84acd56e97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 170 additions and 19 deletions

View file

@ -211,12 +211,29 @@ fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
.any(|attribute_text| attribute_text.contains("test"))
}
const RUSTDOC_FENCE: &str = "```";
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
&["", "rust", "should_panic", "edition2015", "edition2018"];
fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool {
fn_def.doc_comment_text().map_or(false, |comments_text| {
comments_text.contains("```")
&& !comments_text.contains("```ignore")
&& !comments_text.contains("```no_run")
&& !comments_text.contains("```compile_fail")
let mut in_code_block = false;
for line in comments_text.lines() {
if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
in_code_block = !in_code_block;
if in_code_block
&& header
.split(',')
.all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))
{
return true;
}
}
}
false
})
}
@ -421,7 +438,21 @@ fn main() {}
/// ```
/// let x = 5;
/// ```
fn foo() {}
fn should_have_runnable() {}
/// ```edition2018
/// let x = 5;
/// ```
fn should_have_runnable_1() {}
/// ```
/// let z = 55;
/// ```
///
/// ```ignore
/// let z = 56;
/// ```
fn should_have_runnable_2() {}
/// ```no_run
/// let z = 55;
@ -437,8 +468,27 @@ fn should_have_no_runnable_2() {}
/// let z = 55;
/// ```
fn should_have_no_runnable_3() {}
/// ```text
/// arbitrary plain text
/// ```
fn should_have_no_runnable_4() {}
/// ```text
/// arbitrary plain text
/// ```
///
/// ```sh
/// $ shell code
/// ```
fn should_have_no_runnable_5() {}
/// ```rust,no_run
/// let z = 55;
/// ```
fn should_have_no_runnable_6() {}
"#,
&[&BIN, &DOCTEST],
&[&BIN, &DOCTEST, &DOCTEST, &DOCTEST],
expect![[r#"
[
Runnable {
@ -464,9 +514,9 @@ fn should_have_no_runnable_3() {}
file_id: FileId(
1,
),
full_range: 15..57,
full_range: 15..74,
focus_range: None,
name: "foo",
name: "should_have_runnable",
kind: FN,
container_name: None,
description: None,
@ -474,7 +524,47 @@ fn should_have_no_runnable_3() {}
},
kind: DocTest {
test_id: Path(
"foo",
"should_have_runnable",
),
},
cfg_exprs: [],
},
Runnable {
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 76..148,
focus_range: None,
name: "should_have_runnable_1",
kind: FN,
container_name: None,
description: None,
docs: None,
},
kind: DocTest {
test_id: Path(
"should_have_runnable_1",
),
},
cfg_exprs: [],
},
Runnable {
nav: NavigationTarget {
file_id: FileId(
1,
),
full_range: 150..254,
focus_range: None,
name: "should_have_runnable_2",
kind: FN,
container_name: None,
description: None,
docs: None,
},
kind: DocTest {
test_id: Path(
"should_have_runnable_2",
),
},
cfg_exprs: [],

View file

@ -1,22 +1,32 @@
//! Transforms markdown
const RUSTDOC_FENCE: &str = "```";
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] =
&["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
pub(crate) fn format_docs(src: &str) -> String {
let mut processed_lines = Vec::new();
let mut in_code_block = false;
for line in src.lines() {
if in_code_block && code_line_ignored_by_rustdoc(line) {
let mut is_rust = false;
for mut line in src.lines() {
if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
continue;
}
if line.starts_with("```") {
in_code_block ^= true
}
if let Some(header) = line.strip_prefix(RUSTDOC_FENCE) {
in_code_block ^= true;
let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
"```rust"
} else {
line
};
if in_code_block {
is_rust = header
.split(',')
.all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim()));
if is_rust {
line = "```rust";
}
}
}
processed_lines.push(line);
}
@ -38,6 +48,30 @@ mod tests {
assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
}
#[test]
fn test_format_docs_handles_plain_text() {
let comment = "```text\nthis is plain text\n```";
assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
}
#[test]
fn test_format_docs_handles_non_rust() {
let comment = "```sh\nsupposedly shell code\n```";
assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
}
#[test]
fn test_format_docs_handles_rust_alias() {
let comment = "```ignore\nlet z = 55;\n```";
assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
}
#[test]
fn test_format_docs_handles_complex_code_block_attrs() {
let comment = "```rust,no_run\nlet z = 55;\n```";
assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
}
#[test]
fn test_format_docs_skips_comments_in_rust_block() {
let comment =
@ -45,6 +79,16 @@ mod tests {
assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
}
#[test]
fn test_format_docs_does_not_skip_lines_if_plain_text() {
let comment =
"```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
assert_eq!(
format_docs(comment),
"```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
);
}
#[test]
fn test_format_docs_keeps_comments_outside_of_rust_block() {
let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
@ -72,4 +116,21 @@ let a = 1;
"```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
);
}
#[test]
fn test_code_blocks_in_comments_marked_as_text() {
let comment = r#"```text
filler
text
```
Some comment.
```
let a = 1;
```"#;
assert_eq!(
format_docs(comment),
"```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
);
}
}