Skip to content

Commit 944ee37

Browse files
committed
rustdoc: Fix doctest heuristic for main fn wrapping
1 parent 25cdf1f commit 944ee37

File tree

3 files changed

+25
-59
lines changed

3 files changed

+25
-59
lines changed

src/librustdoc/doctest/make.rs

+25-37
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
301301

302302
let filename = FileName::anon_source_code(&wrapped_source);
303303

304-
// Any errors in parsing should also appear when the doctest is compiled for real, so just
305-
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
306304
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
307305
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
308306
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
@@ -311,7 +309,8 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
311309
info.supports_color =
312310
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
313311
.supports_color();
314-
312+
// Any errors in parsing should also appear when the doctest is compiled for real, so just
313+
// send all the errors that the parser emits directly into a `Sink` instead of stderr.
315314
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
316315

317316
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
@@ -339,9 +338,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
339338
*prev_span_hi = hi;
340339
}
341340

342-
// Recurse through functions body. It is necessary because the doctest source code is
343-
// wrapped in a function to limit the number of AST errors. If we don't recurse into
344-
// functions, we would thing all top-level items (so basically nothing).
345341
fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) -> bool {
346342
let mut is_extern_crate = false;
347343
if !info.has_global_allocator
@@ -351,8 +347,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
351347
}
352348
match item.kind {
353349
ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => {
354-
// We only push if it's the top item because otherwise, we would duplicate
355-
// its content since the top-level item was already added.
356350
if fn_item.ident.name == sym::main {
357351
info.has_main_fn = true;
358352
}
@@ -412,44 +406,38 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
412406
let mut is_extern_crate = false;
413407
match stmt.kind {
414408
StmtKind::Item(ref item) => {
415-
is_extern_crate = check_item(&item, &mut info, crate_name);
416-
}
417-
StmtKind::Expr(ref expr) => {
418-
if matches!(expr.kind, ast::ExprKind::Err(_)) {
419-
reset_error_count(&psess);
420-
return Err(());
421-
}
422-
has_non_items = true;
409+
is_extern_crate = check_item(item, &mut info, crate_name);
423410
}
424411
// We assume that the macro calls will expand to item(s) even though they could
425-
// expand to statements and expressions. And the simple fact that we're trying
426-
// to retrieve a `main` function inside it is a terrible idea.
412+
// expand to statements and expressions.
427413
StmtKind::MacCall(ref mac_call) => {
428-
if info.has_main_fn {
429-
continue;
430-
}
431-
let mut iter = mac_call.mac.args.tokens.iter();
432-
433-
while let Some(token) = iter.next() {
434-
if let TokenTree::Token(token, _) = token
435-
&& let TokenKind::Ident(name, _) = token.kind
436-
&& name == kw::Fn
437-
&& let Some(TokenTree::Token(fn_token, _)) = iter.peek()
438-
&& let TokenKind::Ident(fn_name, _) = fn_token.kind
439-
&& fn_name == sym::main
440-
&& let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) = {
441-
iter.next();
442-
iter.peek()
414+
if !info.has_main_fn {
415+
// For backward compatibility, we look for the token sequence `fn main(…)`
416+
// in the macro input (!) to crudely detect main functions "masked by a
417+
// wrapper macro". For the record, this is a horrible heuristic!
418+
// See <https://github.com/rust-lang/rust/issues/56898>.
419+
let mut iter = mac_call.mac.args.tokens.iter();
420+
while let Some(token) = iter.next() {
421+
if let TokenTree::Token(token, _) = token
422+
&& let TokenKind::Ident(kw::Fn, _) = token.kind
423+
&& let Some(TokenTree::Token(ident, _)) = iter.next()
424+
&& let TokenKind::Ident(sym::main, _) = ident.kind
425+
&& let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, _)) =
426+
iter.next()
427+
{
428+
info.has_main_fn = true;
443429
}
444-
{
445-
info.has_main_fn = true;
446-
break;
447430
}
448431
}
449432
}
450-
_ => {
433+
StmtKind::Expr(ref expr) => {
434+
if matches!(expr.kind, ast::ExprKind::Err(_)) {
435+
reset_error_count(&psess);
436+
return Err(());
437+
}
451438
has_non_items = true;
452439
}
440+
StmtKind::Let(_) | StmtKind::Semi(_) | StmtKind::Empty => has_non_items = true,
453441
}
454442

455443
// Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to

tests/rustdoc-ui/doctest/macro-after-main.rs

-16
This file was deleted.

tests/rustdoc-ui/doctest/macro-after-main.stdout

-6
This file was deleted.

0 commit comments

Comments
 (0)