Rollup merge of #91026 - notriddle:notriddle/rustdoc-doctest-semicolon, r=jyn514

rustdoc doctest: detect `fn main` after an unexpected semicolon

Fixes #91014

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 PR changes it to do that.
This commit is contained in:
Matthias Krüger 2021-11-20 01:09:42 +01:00 committed by GitHub
commit a9858ce78f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 10 deletions

View File

@ -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 @@ fn drop(&mut self) {
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 @@ fn drop(&mut self) {
}
};
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 @@ fn drop(&mut self) {
break;
}
}
Ok(None) => break,
Err(mut e) => {
e.cancel();
break;
}
}
Err(mut e) => {
e.cancel();
}
}

View File

@ -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
/// <https://github.com/rust-lang/rust/issues/91014>
///
/// ```rust
/// struct S {}; // unexpected semicolon after struct def
///
/// fn main() {
/// assert_eq!(0, 1);
/// }
/// ```
mod m {}

View File

@ -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