Skip to content
Open
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
82 changes: 68 additions & 14 deletions crates/ide/src/runnables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,29 +572,56 @@ impl TestAttr {
fn has_runnable_doc_test(db: &RootDatabase, attrs: &hir::AttrsWithOwner) -> bool {
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021", "edition2024"];

attrs.hir_docs(db).is_some_and(|doc| {
let mut in_code_block = false;
let doc = match attrs.hir_docs(db) {
Some(doc) => doc,
None => return false,
};
let mut in_code_block = false;
let mut runnable_found = false;
let mut has_compile_fail = false;

for line in doc.docs().lines() {
let trimmed_line = line.trim_start();
if let Some(header) =
RUSTDOC_FENCES.into_iter().find_map(|fence| trimmed_line.strip_prefix(fence))
{
if in_code_block {
in_code_block = false;
continue;
}

in_code_block = true;
let mut block_has_compile_fail = false;
let mut block_runnable = true;

for line in doc.docs().lines() {
if let Some(header) =
RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
for attr in header
.split(',')
.flat_map(|segment| segment.split_ascii_whitespace())
.map(str::trim)
.filter(|attr| !attr.is_empty())
{
in_code_block = !in_code_block;
if attr.eq_ignore_ascii_case("compile_fail") {
block_has_compile_fail = true;
block_runnable = false;
break;
}

if in_code_block
&& header
.split(',')
.all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE.contains(&sub.trim()))
if !RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE
.iter()
.any(|allowed| allowed.eq_ignore_ascii_case(attr))
{
return true;
block_runnable = false;
}
}

has_compile_fail |= block_has_compile_fail;
runnable_found |= block_runnable;
}
}

false
})
runnable_found && !has_compile_fail
}

// We could create runnables for modules with number_of_test_submodules > 0,
Expand Down Expand Up @@ -940,6 +967,33 @@ impl Test for StructWithRunnable {}
);
}

#[test]
fn doc_test_with_compile_fail_blocks_is_skipped() {
check(
r#"
//- /lib.rs
$0
fn main() {}

/// ```compile_fail
/// let x = 5;
/// x += 1;
/// ```
///
/// ```
/// let x = 5;
/// x + 1;
/// ```
fn add() {}
"#,
expect![[r#"
[
"(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })",
]
"#]],
);
}

#[test]
fn test_runnables_doc_test_in_impl() {
check(
Expand Down