Skip to content

Commit 2bde39b

Browse files
committed
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 #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.)
2 parents e64fee6 + 339148d commit 2bde39b

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

src/librustdoc/test.rs

+48-3
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ pub fn make_test(s: &str,
395395

396396
// Uses libsyntax to parse the doctest and find if there's a main fn and the extern
397397
// crate already is included.
398-
let (already_has_main, already_has_extern_crate) = crate::syntax::with_globals(|| {
398+
let (already_has_main, already_has_extern_crate, found_macro) = crate::syntax::with_globals(|| {
399399
use crate::syntax::{ast, parse::{self, ParseSess}, source_map::FilePathMapping};
400400
use crate::syntax_pos::FileName;
401401
use errors::emitter::EmitterWriter;
@@ -415,6 +415,7 @@ pub fn make_test(s: &str,
415415

416416
let mut found_main = false;
417417
let mut found_extern_crate = cratename.is_none();
418+
let mut found_macro = false;
418419

419420
let mut parser = match parse::maybe_new_parser_from_source_str(&sess, filename, source) {
420421
Ok(p) => p,
@@ -423,7 +424,7 @@ pub fn make_test(s: &str,
423424
err.cancel();
424425
}
425426

426-
return (found_main, found_extern_crate);
427+
return (found_main, found_extern_crate, found_macro);
427428
}
428429
};
429430

@@ -451,6 +452,12 @@ pub fn make_test(s: &str,
451452
}
452453
}
453454

455+
if !found_macro {
456+
if let ast::ItemKind::Mac(..) = item.node {
457+
found_macro = true;
458+
}
459+
}
460+
454461
if found_main && found_extern_crate {
455462
break;
456463
}
@@ -463,9 +470,28 @@ pub fn make_test(s: &str,
463470
}
464471
}
465472

466-
(found_main, found_extern_crate)
473+
(found_main, found_extern_crate, found_macro)
467474
});
468475

476+
// If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
477+
// see it. In that case, run the old text-based scan to see if they at least have a main
478+
// function written inside a macro invocation. See
479+
// https://github.com/rust-lang/rust/issues/56898
480+
let already_has_main = if found_macro && !already_has_main {
481+
s.lines()
482+
.map(|line| {
483+
let comment = line.find("//");
484+
if let Some(comment_begins) = comment {
485+
&line[0..comment_begins]
486+
} else {
487+
line
488+
}
489+
})
490+
.any(|code| code.contains("fn main"))
491+
} else {
492+
already_has_main
493+
};
494+
469495
// Don't inject `extern crate std` because it's already injected by the
470496
// compiler.
471497
if !already_has_extern_crate && !opts.no_crate_inject && cratename != Some("std") {
@@ -1106,4 +1132,23 @@ assert_eq!(asdf::foo, 4);
11061132
let output = make_test(input, Some("asdf"), false, &opts);
11071133
assert_eq!(output, (expected, 3));
11081134
}
1135+
1136+
#[test]
1137+
fn make_test_main_in_macro() {
1138+
let opts = TestOptions::default();
1139+
let input =
1140+
"#[macro_use] extern crate my_crate;
1141+
test_wrapper! {
1142+
fn main() {}
1143+
}";
1144+
let expected =
1145+
"#![allow(unused)]
1146+
#[macro_use] extern crate my_crate;
1147+
test_wrapper! {
1148+
fn main() {}
1149+
}".to_string();
1150+
1151+
let output = make_test(input, Some("my_crate"), false, &opts);
1152+
assert_eq!(output, (expected, 1));
1153+
}
11091154
}

0 commit comments

Comments
 (0)