Skip to content

Fix detection of main function if there are expressions around it #140220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/librustdoc/doctest/make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,17 +407,27 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi);
}
}
let mut has_non_items = false;
for stmt in &body.stmts {
let mut is_extern_crate = false;
match stmt.kind {
StmtKind::Item(ref item) => {
is_extern_crate = check_item(&item, &mut info, crate_name);
}
StmtKind::Expr(ref expr) if matches!(expr.kind, ast::ExprKind::Err(_)) => {
reset_error_count(&psess);
return Err(());
StmtKind::Expr(ref expr) => {
if matches!(expr.kind, ast::ExprKind::Err(_)) {
reset_error_count(&psess);
return Err(());
}
has_non_items = true;
}
StmtKind::MacCall(ref mac_call) if !info.has_main_fn => {
// We assume that the macro calls will expand to item(s) even though they could
// expand to statements and expressions. And the simple fact that we're trying
// to retrieve a `main` function inside it is a terrible idea.
StmtKind::MacCall(ref mac_call) => {
if info.has_main_fn {
continue;
}
let mut iter = mac_call.mac.args.tokens.iter();

while let Some(token) = iter.next() {
Expand All @@ -437,7 +447,9 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
}
}
}
_ => {}
_ => {
has_non_items = true;
}
}

// Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to
Expand All @@ -462,6 +474,11 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
push_to_s(&mut info.crates, source, span, &mut prev_span_hi);
}
}
if has_non_items {
// FIXME: if `info.has_main_fn` is `true`, emit a warning here to mention that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Could you open an issue for that unless we already have one for that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it is: #140310

// this code will not be called.
info.has_main_fn = false;
}
Ok(info)
}
Err(e) => {
Expand Down
1 change: 1 addition & 0 deletions tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use std::string::String;
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ failure-status: 101
//@ check-pass

/// <https://github.com/rust-lang/rust/issues/91014>
///
/// ```rust
/// struct S {}; // unexpected semicolon after struct def
/// struct S {};
///
/// fn main() {
/// assert_eq!(0, 1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,6 @@

running 1 test
test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... FAILED
test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... ok

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: braced struct declarations are not followed by a semicolon
help: remove this semicolon
|
LL - struct S {}; // unexpected semicolon after struct def
LL + struct S {} // unexpected semicolon after struct def
|

error: aborting due to 1 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
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME

16 changes: 16 additions & 0 deletions tests/rustdoc-ui/doctest/macro-after-main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// This test checks a corner case where the macro calls used to be skipped,
// making them considered as statement, and therefore some cases where
// `include!` macro was then put into a function body, making the doctest
// compilation fail.

//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ check-pass

//! ```
//! include!("./auxiliary/macro-after-main.rs");
//!
//! fn main() {}
//! eprintln!();
//! ```
6 changes: 6 additions & 0 deletions tests/rustdoc-ui/doctest/macro-after-main.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

running 1 test
test $DIR/macro-after-main.rs - (line 11) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME

22 changes: 22 additions & 0 deletions tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This test ensures that if there is an expression alongside a `main`
// function, it will not consider the entire code to be part of the `main`
// function and will generate its own function to wrap everything.
//
// This is a regression test for:
// * <https://github.com/rust-lang/rust/issues/140162>
// * <https://github.com/rust-lang/rust/issues/139651>
//@ compile-flags:--test
//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
//@ check-pass

#![crate_name = "foo"]

//! ```
//! # if cfg!(miri) { return; }
//! use std::ops::Deref;
//!
//! fn main() {
//! println!("Hi!");
//! }
//! ```
6 changes: 6 additions & 0 deletions tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

running 1 test
test $DIR/test-main-alongside-exprs.rs - (line 15) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME

Loading