Auto merge of #12918 - lowr:fix/doctest-names, r=Veykril

fix: remove whitespaces from doctest names

When rustdoc runs doctests, it removes whitespaces from the tests' path ([code](25bb1c13bd/src/librustdoc/doctest.rs (L951))). See https://github.com/rust-lang/rust/pull/89422 for details.

Interestingly enough, "Run doctest" has been working without much problem even though rust-analyzer hasn't followed the change. This is because cargo passes the test name to rustdoc via `--test-args` option, and then rustdoc [splits it by whitespace](25bb1c13bd/src/librustdoc/config.rs (L513-L514)); the last element of the split test name **always** matches the test name that rustdoc generates.

However, it may run other tests unexpectedly (to be precise, this has long since been a thing because of the split). Consider the following example:

```rust
struct A<T, U>(T, U);
struct B<T, U>(T, U);
/// ```
/// doctest here
/// ```
impl<T, U> A<T, U> {}
/// ```
/// doctest here
/// ```
impl<T, U> B<T, U> {}
```

When you "Run doctest" either of the two, rustdoc considers "U>" one of the test specs and both doctests are run. This patch fixes it by following rustdoc and removing the whitespace from the doctests' name.
This commit is contained in:
bors 2022-08-01 10:12:59 +00:00
commit 5edbdd127a

View file

@ -373,11 +373,13 @@ pub(crate) fn runnable_impl(
let adt_name = ty.as_adt()?.name(sema.db); let adt_name = ty.as_adt()?.name(sema.db);
let mut ty_args = ty.type_arguments().peekable(); let mut ty_args = ty.type_arguments().peekable();
let params = if ty_args.peek().is_some() { let params = if ty_args.peek().is_some() {
format!("<{}>", ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))) format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db))))
} else { } else {
String::new() String::new()
}; };
let test_id = TestId::Path(format!("{}{}", adt_name, params)); let mut test_id = format!("{}{}", adt_name, params);
test_id.retain(|c| c != ' ');
let test_id = TestId::Path(test_id);
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg }) Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
} }
@ -441,10 +443,11 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
format_to!( format_to!(
path, path,
"<{}>", "<{}>",
ty_args.format_with(", ", |ty, cb| cb(&ty.display(db))) ty_args.format_with(",", |ty, cb| cb(&ty.display(db)))
); );
} }
format_to!(path, "::{}", def_name); format_to!(path, "::{}", def_name);
path.retain(|c| c != ' ');
return Some(path); return Some(path);
} }
} }
@ -2067,13 +2070,23 @@ mod tests {
$0 $0
struct Foo<T, U>; struct Foo<T, U>;
/// ```
/// ```
impl<T, U> Foo<T, U> { impl<T, U> Foo<T, U> {
/// ```rust /// ```rust
/// ```` /// ````
fn t() {} fn t() {}
} }
/// ```
/// ```
impl Foo<Foo<(), ()>, ()> {
/// ```
/// ```
fn t() {}
}
"#, "#,
&[DocTest], &[DocTest, DocTest, DocTest, DocTest],
expect![[r#" expect![[r#"
[ [
Runnable { Runnable {
@ -2082,12 +2095,64 @@ impl<T, U> Foo<T, U> {
file_id: FileId( file_id: FileId(
0, 0,
), ),
full_range: 47..85, full_range: 20..103,
focus_range: 47..56,
name: "impl",
kind: Impl,
},
kind: DocTest {
test_id: Path(
"Foo<T,U>",
),
},
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 63..101,
name: "t", name: "t",
}, },
kind: DocTest { kind: DocTest {
test_id: Path( test_id: Path(
"Foo<T, U>::t", "Foo<T,U>::t",
),
},
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 105..188,
focus_range: 126..146,
name: "impl",
kind: Impl,
},
kind: DocTest {
test_id: Path(
"Foo<Foo<(),()>,()>",
),
},
cfg: None,
},
Runnable {
use_name_in_title: false,
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 153..186,
name: "t",
},
kind: DocTest {
test_id: Path(
"Foo<Foo<(),()>,()>::t",
), ),
}, },
cfg: None, cfg: None,