From 214ad2f5b5ff529548ce13dabcda8d5841df0bd7 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 18 Nov 2021 16:15:12 -0700 Subject: [PATCH] rustdoc doctest: detect `fn main` after an unexpected semicolon The basic problem with this is that rustdoc, when hunting for `fn main`, will stop parsing after it reaches a fatal error. This unexpected semicolon was a fatal error, so in `src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs`, it would wrap the doctest in an implied main function, turning it into this: fn main() { struct S {}; fn main() { assert_eq!(0, 1); } } This, as it turns out, is totally valid, and it executes no assertions, so *it passes,* even though the user wanted it to execute the assertion. The Rust parser already has the ability to recover from these unexpected semicolons, but to do so, it needs to use the `parse_mod` function, so this commit changes it to do that. --- src/librustdoc/doctest.rs | 17 ++++++------- .../failed-doctest-extra-semicolon-on-item.rs | 18 ++++++++++++++ ...led-doctest-extra-semicolon-on-item.stdout | 24 +++++++++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs create mode 100644 src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c10eebf49fc8d..37db20aaefa8d 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,4 +1,4 @@ -use rustc_ast as ast; +use rustc_ast::{self as ast, token}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_errors::{ColorConfig, ErrorReported, FatalError}; @@ -537,7 +537,6 @@ crate fn make_test( use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::Handler; use rustc_parse::maybe_new_parser_from_source_str; - use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; use rustc_span::source_map::FilePathMapping; @@ -573,9 +572,9 @@ crate fn make_test( } }; - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { + match parser.parse_mod(&token::Eof) { + Ok((_attrs, items, _span)) => { + for item in items { if !found_main { if let ast::ItemKind::Fn(..) = item.kind { if item.ident.name == sym::main { @@ -607,11 +606,9 @@ crate fn make_test( break; } } - Ok(None) => break, - Err(mut e) => { - e.cancel(); - break; - } + } + Err(mut e) => { + e.cancel(); } } diff --git a/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs new file mode 100644 index 0000000000000..16d737106ea89 --- /dev/null +++ b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs @@ -0,0 +1,18 @@ +// FIXME: if/when the output of the test harness can be tested on its own, this test should be +// adapted to use that, and that normalize line can go away + +// compile-flags:--test +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" +// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +// failure-status: 101 + +/// +/// +/// ```rust +/// struct S {}; // unexpected semicolon after struct def +/// +/// fn main() { +/// assert_eq!(0, 1); +/// } +/// ``` +mod m {} diff --git a/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout new file mode 100644 index 0000000000000..61468b6c7457b --- /dev/null +++ b/src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout @@ -0,0 +1,24 @@ + +running 1 test +test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... FAILED + +failures: + +---- $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) stdout ---- +error: expected item, found `;` + --> $DIR/failed-doctest-extra-semicolon-on-item.rs:12:12 + | +LL | struct S {}; // unexpected semicolon after struct def + | ^ help: remove this semicolon + | + = help: braced struct declarations are not followed by a semicolon + +error: aborting due to previous error + +Couldn't compile the test. + +failures: + $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +