Skip to content

Commit 214ad2f

Browse files
committed
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.
1 parent b6f580a commit 214ad2f

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

src/librustdoc/doctest.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_ast as ast;
1+
use rustc_ast::{self as ast, token};
22
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
33
use rustc_data_structures::sync::Lrc;
44
use rustc_errors::{ColorConfig, ErrorReported, FatalError};
@@ -537,7 +537,6 @@ crate fn make_test(
537537
use rustc_errors::emitter::{Emitter, EmitterWriter};
538538
use rustc_errors::Handler;
539539
use rustc_parse::maybe_new_parser_from_source_str;
540-
use rustc_parse::parser::ForceCollect;
541540
use rustc_session::parse::ParseSess;
542541
use rustc_span::source_map::FilePathMapping;
543542

@@ -573,9 +572,9 @@ crate fn make_test(
573572
}
574573
};
575574

576-
loop {
577-
match parser.parse_item(ForceCollect::No) {
578-
Ok(Some(item)) => {
575+
match parser.parse_mod(&token::Eof) {
576+
Ok((_attrs, items, _span)) => {
577+
for item in items {
579578
if !found_main {
580579
if let ast::ItemKind::Fn(..) = item.kind {
581580
if item.ident.name == sym::main {
@@ -607,11 +606,9 @@ crate fn make_test(
607606
break;
608607
}
609608
}
610-
Ok(None) => break,
611-
Err(mut e) => {
612-
e.cancel();
613-
break;
614-
}
609+
}
610+
Err(mut e) => {
611+
e.cancel();
615612
}
616613
}
617614

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// FIXME: if/when the output of the test harness can be tested on its own, this test should be
2+
// adapted to use that, and that normalize line can go away
3+
4+
// compile-flags:--test
5+
// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
6+
// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
7+
// failure-status: 101
8+
9+
/// <https://github.com/rust-lang/rust/issues/91014>
10+
///
11+
/// ```rust
12+
/// struct S {}; // unexpected semicolon after struct def
13+
///
14+
/// fn main() {
15+
/// assert_eq!(0, 1);
16+
/// }
17+
/// ```
18+
mod m {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
running 1 test
3+
test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... FAILED
4+
5+
failures:
6+
7+
---- $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) stdout ----
8+
error: expected item, found `;`
9+
--> $DIR/failed-doctest-extra-semicolon-on-item.rs:12:12
10+
|
11+
LL | struct S {}; // unexpected semicolon after struct def
12+
| ^ help: remove this semicolon
13+
|
14+
= help: braced struct declarations are not followed by a semicolon
15+
16+
error: aborting due to previous error
17+
18+
Couldn't compile the test.
19+
20+
failures:
21+
$DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11)
22+
23+
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
24+

0 commit comments

Comments
 (0)