@@ -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,40 @@ 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. peek ( )
424+ && let TokenKind :: Ident ( sym:: main, _) = ident. kind
425+ && let Some ( TokenTree :: Delimited ( .., Delimiter :: Parenthesis , _) ) = {
426+ iter. next ( ) ;
427+ iter. peek ( )
428+ }
429+ {
430+ info. has_main_fn = true ;
443431 }
444- {
445- info. has_main_fn = true ;
446- break ;
447432 }
448433 }
449434 }
450- _ => {
435+ StmtKind :: Expr ( ref expr) => {
436+ if matches ! ( expr. kind, ast:: ExprKind :: Err ( _) ) {
437+ reset_error_count ( & psess) ;
438+ return Err ( ( ) ) ;
439+ }
451440 has_non_items = true ;
452441 }
442+ StmtKind :: Let ( _) | StmtKind :: Semi ( _) | StmtKind :: Empty => has_non_items = true ,
453443 }
454444
455445 // Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to
0 commit comments