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));
+ }
}