Correctly handle doctests with invalid AST

This commit is contained in:
Guillaume Gomez 2024-06-10 18:29:33 +02:00
parent 59a9e0986d
commit 7ec3cabe17
8 changed files with 138 additions and 9 deletions

View File

@ -635,7 +635,7 @@ fn drop(&mut self) {
cmd.current_dir(run_directory);
}
let result = if rustdoc_options.nocapture {
let result = if is_multiple_tests || rustdoc_options.nocapture {
cmd.status().map(|status| process::Output {
status,
stdout: Vec::new(),
@ -801,10 +801,15 @@ fn add_test(&mut self, scraped_test: ScrapedDoctest) {
);
let edition = scraped_test.edition(&self.rustdoc_options);
let doctest =
DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, Some(test_id));
let doctest = DocTest::new(
&scraped_test.text,
Some(&self.opts.crate_name),
edition,
self.can_merge_doctests,
Some(test_id),
);
let is_standalone = !self.can_merge_doctests
|| doctest.failed_ast
|| !doctest.can_be_merged
|| scraped_test.langstr.compile_fail
|| scraped_test.langstr.test_harness
|| scraped_test.langstr.standalone

View File

@ -26,6 +26,7 @@ pub(crate) struct DocTest {
pub(crate) everything_else: String,
pub(crate) test_id: Option<String>,
pub(crate) failed_ast: bool,
pub(crate) can_be_merged: bool,
}
impl DocTest {
@ -33,6 +34,7 @@ pub(crate) fn new(
source: &str,
crate_name: Option<&str>,
edition: Edition,
can_merge_doctests: bool,
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
test_id: Option<String>,
) -> Self {
@ -49,6 +51,7 @@ pub(crate) fn new(
&crates,
edition,
&mut supports_color,
can_merge_doctests,
)
else {
// If the parser panicked due to a fatal error, pass the test code through unchanged.
@ -62,6 +65,7 @@ pub(crate) fn new(
already_has_extern_crate: false,
test_id,
failed_ast: true,
can_be_merged: false,
};
};
Self {
@ -72,7 +76,10 @@ pub(crate) fn new(
everything_else,
already_has_extern_crate,
test_id,
failed_ast,
failed_ast: false,
// If the AST returned an error, we don't want this doctest to be merged with the
// others.
can_be_merged: !failed_ast,
}
}
@ -85,6 +92,11 @@ pub(crate) fn generate_unique_doctest(
opts: &GlobalTestOptions,
crate_name: Option<&str>,
) -> (String, usize) {
if self.failed_ast {
// If the AST failed to compile, no need to go generate a complete doctest, the error
// will be better this way.
return (test_code.to_string(), 0);
}
let mut line_offset = 0;
let mut prog = String::new();
let everything_else = self.everything_else.trim();
@ -323,6 +335,7 @@ fn check_for_main_and_extern_crate(
crates: &str,
edition: Edition,
supports_color: &mut bool,
can_merge_doctests: bool,
) -> Result<(Option<Span>, bool, bool), FatalError> {
let result = rustc_driver::catch_fatal_errors(|| {
rustc_span::create_session_if_not_set_then(edition, |_| {
@ -340,7 +353,7 @@ fn check_for_main_and_extern_crate(
);
// No need to double-check this if the "merged doctests" feature isn't enabled (so
// before the 2024 edition).
if edition >= Edition::Edition2024 && parsing_result != ParsingResult::Ok {
if can_merge_doctests && parsing_result != ParsingResult::Ok {
// If we found an AST error, we want to ensure it's because of an expression being
// used outside of a function.
//
@ -525,5 +538,5 @@ enum PartitionState {
debug!("crates:\n{crates}");
debug!("after:\n{after}");
(before, after, crates)
(before, after.trim().to_owned(), crates)
}

View File

@ -11,7 +11,7 @@ fn make_test(
test_id: Option<&str>,
) -> (String, usize) {
let doctest =
DocTest::new(test_code, crate_name, DEFAULT_EDITION, test_id.map(|s| s.to_string()));
DocTest::new(test_code, crate_name, DEFAULT_EDITION, false, test_id.map(|s| s.to_string()));
let (code, line_offset) =
doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name);
(code, line_offset)

View File

@ -297,7 +297,7 @@ fn next(&mut self) -> Option<Self::Item> {
attrs: vec![],
args_file: PathBuf::new(),
};
let doctest = doctest::DocTest::new(&test, krate, edition, None);
let doctest = doctest::DocTest::new(&test, krate, edition, false, None);
let (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate);
let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };

View File

@ -0,0 +1,20 @@
//@ compile-flags:--test --test-args=--test-threads=1 -Zunstable-options --edition 2024
//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL"
//@ failure-status: 101
/// ```
/// /* plop
/// ```
pub fn one() {}
/// ```
/// } mod __doctest_1 { fn main() {
/// ```
pub fn two() {}
/// ```should_panic
/// panic!()
/// ```
pub fn three() {}

View File

@ -0,0 +1,35 @@
running 2 tests
test $DIR/wrong-ast-2024.rs - one (line 7) ... FAILED
test $DIR/wrong-ast-2024.rs - two (line 12) ... FAILED
failures:
---- $DIR/wrong-ast-2024.rs - one (line 7) stdout ----
error[E0758]: unterminated block comment
--> $DIR/wrong-ast-2024.rs:8:1
|
LL | /* plop
| ^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0758`.
Couldn't compile the test.
---- $DIR/wrong-ast-2024.rs - two (line 12) stdout ----
error: unexpected closing delimiter: `}`
--> $DIR/wrong-ast-2024.rs:13:1
|
LL | } mod __doctest_1 { fn main() {
| ^ unexpected closing delimiter
error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/wrong-ast-2024.rs - one (line 7)
$DIR/wrong-ast-2024.rs - two (line 12)
test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME

View File

@ -0,0 +1,20 @@
//@ compile-flags:--test --test-args=--test-threads=1
//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ normalize-stdout-test "wrong-ast.rs:\d+:\d+" -> "wrong-ast.rs:$$LINE:$$COL"
//@ failure-status: 101
/// ```
/// /* plop
/// ```
pub fn one() {}
/// ```
/// } mod __doctest_1 { fn main() {
/// ```
pub fn two() {}
/// ```should_panic
/// panic!()
/// ```
pub fn three() {}

View File

@ -0,0 +1,36 @@
running 3 tests
test $DIR/wrong-ast.rs - one (line 7) ... FAILED
test $DIR/wrong-ast.rs - three (line 17) ... ok
test $DIR/wrong-ast.rs - two (line 12) ... FAILED
failures:
---- $DIR/wrong-ast.rs - one (line 7) stdout ----
error[E0758]: unterminated block comment
--> $DIR/wrong-ast.rs:$LINE:$COL
|
LL | /* plop
| ^^^^^^^^
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0758`.
Couldn't compile the test.
---- $DIR/wrong-ast.rs - two (line 12) stdout ----
error: unexpected closing delimiter: `}`
--> $DIR/wrong-ast.rs:$LINE:$COL
|
LL | } mod __doctest_1 { fn main() {
| ^ unexpected closing delimiter
error: aborting due to 1 previous error
Couldn't compile the test.
failures:
$DIR/wrong-ast.rs - one (line 7)
$DIR/wrong-ast.rs - two (line 12)
test result: FAILED. 1 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME