Auto merge of #57019 - QuietMisdreavus:semi-revert-doctest-parsing, r=GuillaumeGomez

[beta] rustdoc: semi-revert libsyntax doctest parsing if a macro is wrapping main

Fixes https://github.com/rust-lang/rust/issues/56898

This is a patch to doctest parsing on beta (that i plan to "forward-port" to master) that reverts to the old text-based `fn main` scan if it found a macro invocation in the doctest but did not find a main function. This should solve situations like `allocator_api` which wrap their main functions in a macro invocation, but doesn't solve something like the initial version of `quicli` which used a macro to *generate* the main function. (Properly doing the latter involves running macro expansion before checking, which is a bit too involved for a beta fix.)
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index d9bab91..2aff128 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -395,7 +395,7 @@
 
     // Uses libsyntax to parse the doctest and find if there's a main fn and the extern
     // crate already is included.
-    let (already_has_main, already_has_extern_crate) = crate::syntax::with_globals(|| {
+    let (already_has_main, already_has_extern_crate, found_macro) = crate::syntax::with_globals(|| {
         use crate::syntax::{ast, parse::{self, ParseSess}, source_map::FilePathMapping};
         use crate::syntax_pos::FileName;
         use errors::emitter::EmitterWriter;
@@ -415,6 +415,7 @@
 
         let mut found_main = false;
         let mut found_extern_crate = cratename.is_none();
+        let mut found_macro = false;
 
         let mut parser = match parse::maybe_new_parser_from_source_str(&sess, filename, source) {
             Ok(p) => p,
@@ -423,7 +424,7 @@
                     err.cancel();
                 }
 
-                return (found_main, found_extern_crate);
+                return (found_main, found_extern_crate, found_macro);
             }
         };
 
@@ -451,6 +452,12 @@
                         }
                     }
 
+                    if !found_macro {
+                        if let ast::ItemKind::Mac(..) = item.node {
+                            found_macro = true;
+                        }
+                    }
+
                     if found_main && found_extern_crate {
                         break;
                     }
@@ -463,9 +470,28 @@
             }
         }
 
-        (found_main, found_extern_crate)
+        (found_main, found_extern_crate, found_macro)
     });
 
+    // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
+    // see it. In that case, run the old text-based scan to see if they at least have a main
+    // function written inside a macro invocation. See
+    // https://github.com/rust-lang/rust/issues/56898
+    let already_has_main = if found_macro && !already_has_main {
+        s.lines()
+            .map(|line| {
+                let comment = line.find("//");
+                if let Some(comment_begins) = comment {
+                    &line[0..comment_begins]
+                } else {
+                    line
+                }
+            })
+            .any(|code| code.contains("fn main"))
+    } else {
+        already_has_main
+    };
+
     // Don't inject `extern crate std` because it's already injected by the
     // compiler.
     if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") {
@@ -1106,4 +1132,23 @@
         let output = make_test(input, Some("asdf"), false, &opts);
         assert_eq!(output, (expected, 3));
     }
+
+    #[test]
+    fn make_test_main_in_macro() {
+        let opts = TestOptions::default();
+        let input =
+"#[macro_use] extern crate my_crate;
+test_wrapper! {
+    fn main() {}
+}";
+        let expected =
+"#![allow(unused)]
+#[macro_use] extern crate my_crate;
+test_wrapper! {
+    fn main() {}
+}".to_string();
+
+        let output = make_test(input, Some("my_crate"), false, &opts);
+        assert_eq!(output, (expected, 1));
+    }
 }