Merge #5887
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:
commit
84acd56e97
2 changed files with 170 additions and 19 deletions
|
@ -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: [],
|
||||
|
|
|
@ -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```"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue