Correctly handle doctests with invalid AST
This commit is contained in:
parent
59a9e0986d
commit
7ec3cabe17
@ -635,7 +635,7 @@ fn drop(&mut self) {
|
|||||||
cmd.current_dir(run_directory);
|
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 {
|
cmd.status().map(|status| process::Output {
|
||||||
status,
|
status,
|
||||||
stdout: Vec::new(),
|
stdout: Vec::new(),
|
||||||
@ -801,10 +801,15 @@ fn add_test(&mut self, scraped_test: ScrapedDoctest) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let edition = scraped_test.edition(&self.rustdoc_options);
|
let edition = scraped_test.edition(&self.rustdoc_options);
|
||||||
let doctest =
|
let doctest = DocTest::new(
|
||||||
DocTest::new(&scraped_test.text, Some(&self.opts.crate_name), edition, Some(test_id));
|
&scraped_test.text,
|
||||||
|
Some(&self.opts.crate_name),
|
||||||
|
edition,
|
||||||
|
self.can_merge_doctests,
|
||||||
|
Some(test_id),
|
||||||
|
);
|
||||||
let is_standalone = !self.can_merge_doctests
|
let is_standalone = !self.can_merge_doctests
|
||||||
|| doctest.failed_ast
|
|| !doctest.can_be_merged
|
||||||
|| scraped_test.langstr.compile_fail
|
|| scraped_test.langstr.compile_fail
|
||||||
|| scraped_test.langstr.test_harness
|
|| scraped_test.langstr.test_harness
|
||||||
|| scraped_test.langstr.standalone
|
|| scraped_test.langstr.standalone
|
||||||
|
@ -26,6 +26,7 @@ pub(crate) struct DocTest {
|
|||||||
pub(crate) everything_else: String,
|
pub(crate) everything_else: String,
|
||||||
pub(crate) test_id: Option<String>,
|
pub(crate) test_id: Option<String>,
|
||||||
pub(crate) failed_ast: bool,
|
pub(crate) failed_ast: bool,
|
||||||
|
pub(crate) can_be_merged: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocTest {
|
impl DocTest {
|
||||||
@ -33,6 +34,7 @@ pub(crate) fn new(
|
|||||||
source: &str,
|
source: &str,
|
||||||
crate_name: Option<&str>,
|
crate_name: Option<&str>,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
|
can_merge_doctests: bool,
|
||||||
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
|
// If `test_id` is `None`, it means we're generating code for a code example "run" link.
|
||||||
test_id: Option<String>,
|
test_id: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -49,6 +51,7 @@ pub(crate) fn new(
|
|||||||
&crates,
|
&crates,
|
||||||
edition,
|
edition,
|
||||||
&mut supports_color,
|
&mut supports_color,
|
||||||
|
can_merge_doctests,
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
// If the parser panicked due to a fatal error, pass the test code through unchanged.
|
// 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,
|
already_has_extern_crate: false,
|
||||||
test_id,
|
test_id,
|
||||||
failed_ast: true,
|
failed_ast: true,
|
||||||
|
can_be_merged: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
@ -72,7 +76,10 @@ pub(crate) fn new(
|
|||||||
everything_else,
|
everything_else,
|
||||||
already_has_extern_crate,
|
already_has_extern_crate,
|
||||||
test_id,
|
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,
|
opts: &GlobalTestOptions,
|
||||||
crate_name: Option<&str>,
|
crate_name: Option<&str>,
|
||||||
) -> (String, usize) {
|
) -> (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 line_offset = 0;
|
||||||
let mut prog = String::new();
|
let mut prog = String::new();
|
||||||
let everything_else = self.everything_else.trim();
|
let everything_else = self.everything_else.trim();
|
||||||
@ -323,6 +335,7 @@ fn check_for_main_and_extern_crate(
|
|||||||
crates: &str,
|
crates: &str,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
supports_color: &mut bool,
|
supports_color: &mut bool,
|
||||||
|
can_merge_doctests: bool,
|
||||||
) -> Result<(Option<Span>, bool, bool), FatalError> {
|
) -> Result<(Option<Span>, bool, bool), FatalError> {
|
||||||
let result = rustc_driver::catch_fatal_errors(|| {
|
let result = rustc_driver::catch_fatal_errors(|| {
|
||||||
rustc_span::create_session_if_not_set_then(edition, |_| {
|
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
|
// No need to double-check this if the "merged doctests" feature isn't enabled (so
|
||||||
// before the 2024 edition).
|
// 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
|
// If we found an AST error, we want to ensure it's because of an expression being
|
||||||
// used outside of a function.
|
// used outside of a function.
|
||||||
//
|
//
|
||||||
@ -525,5 +538,5 @@ enum PartitionState {
|
|||||||
debug!("crates:\n{crates}");
|
debug!("crates:\n{crates}");
|
||||||
debug!("after:\n{after}");
|
debug!("after:\n{after}");
|
||||||
|
|
||||||
(before, after, crates)
|
(before, after.trim().to_owned(), crates)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ fn make_test(
|
|||||||
test_id: Option<&str>,
|
test_id: Option<&str>,
|
||||||
) -> (String, usize) {
|
) -> (String, usize) {
|
||||||
let doctest =
|
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) =
|
let (code, line_offset) =
|
||||||
doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name);
|
doctest.generate_unique_doctest(test_code, dont_insert_main, opts, crate_name);
|
||||||
(code, line_offset)
|
(code, line_offset)
|
||||||
|
@ -297,7 +297,7 @@ fn next(&mut self) -> Option<Self::Item> {
|
|||||||
attrs: vec![],
|
attrs: vec![],
|
||||||
args_file: PathBuf::new(),
|
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 (test, _) = doctest.generate_unique_doctest(&test, false, &opts, krate);
|
||||||
let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" };
|
let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" };
|
||||||
|
|
||||||
|
20
tests/rustdoc-ui/doctest/wrong-ast-2024.rs
Normal file
20
tests/rustdoc-ui/doctest/wrong-ast-2024.rs
Normal 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() {}
|
35
tests/rustdoc-ui/doctest/wrong-ast-2024.stdout
Normal file
35
tests/rustdoc-ui/doctest/wrong-ast-2024.stdout
Normal 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
|
||||||
|
|
20
tests/rustdoc-ui/doctest/wrong-ast.rs
Normal file
20
tests/rustdoc-ui/doctest/wrong-ast.rs
Normal 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() {}
|
36
tests/rustdoc-ui/doctest/wrong-ast.stdout
Normal file
36
tests/rustdoc-ui/doctest/wrong-ast.stdout
Normal 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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user