Skip to content

Commit

Permalink
rustdoc doctest: detect fn main after an unexpected semicolon
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
notriddle committed Nov 18, 2021
1 parent b6f580a commit 214ad2f
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 10 deletions.
17 changes: 7 additions & 10 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -607,11 +606,9 @@ crate fn make_test(
break;
}
}
Ok(None) => break,
Err(mut e) => {
e.cancel();
break;
}
}
Err(mut e) => {
e.cancel();
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.rs
Original file line number Diff line number Diff line change
@@ -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 {}
24 changes: 24 additions & 0 deletions src/test/rustdoc-ui/failed-doctest-extra-semicolon-on-item.stdout
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 214ad2f

Please sign in to comment.