Don't merge doctests with `#[global_allocator]`
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 92ab8eb..c67dc45 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -49,20 +49,26 @@ pub(crate) fn new(
has_features,
has_no_std,
} = partition_source(source, edition);
- let mut supports_color = false;
// Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
// crate already is included.
- let Ok((has_main_fn, already_has_extern_crate, failed_ast)) =
- check_for_main_and_extern_crate(
- crate_name,
- source,
- &everything_else,
- &crates,
- edition,
- &mut supports_color,
- can_merge_doctests,
- )
+ let Ok((
+ ParseSourceInfo {
+ has_main_fn,
+ found_extern_crate,
+ supports_color,
+ has_global_allocator,
+ ..
+ },
+ failed_ast,
+ )) = check_for_main_and_extern_crate(
+ crate_name,
+ source,
+ &everything_else,
+ &crates,
+ edition,
+ can_merge_doctests,
+ )
else {
// If the parser panicked due to a fatal error, pass the test code through unchanged.
// The error will be reported during compilation.
@@ -86,12 +92,12 @@ pub(crate) fn new(
maybe_crate_attrs,
crates,
everything_else,
- already_has_extern_crate,
+ already_has_extern_crate: found_extern_crate,
test_id,
failed_ast: false,
// If the AST returned an error, we don't want this doctest to be merged with the
// others. Same if it contains `#[feature]` or `#[no_std]`.
- can_be_merged: !failed_ast && !has_no_std && !has_features,
+ can_be_merged: !failed_ast && !has_no_std && !has_features && !has_global_allocator,
}
}
@@ -231,11 +237,8 @@ fn cancel_error_count(psess: &ParseSess) {
fn parse_source(
source: String,
- has_main_fn: &mut bool,
- found_extern_crate: &mut bool,
- found_macro: &mut bool,
+ info: &mut ParseSourceInfo,
crate_name: &Option<&str>,
- supports_color: &mut bool,
) -> ParsingResult {
use rustc_errors::emitter::{Emitter, HumanEmitter};
use rustc_errors::DiagCtxt;
@@ -251,7 +254,7 @@ fn parse_source(
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false,
);
- *supports_color =
+ info.supports_color =
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
.supports_color();
@@ -274,43 +277,38 @@ fn parse_source(
// Recurse through functions body. It is necessary because the doctest source code is
// wrapped in a function to limit the number of AST errors. If we don't recurse into
// functions, we would thing all top-level items (so basically nothing).
- fn check_item(
- item: &ast::Item,
- has_main_fn: &mut bool,
- found_extern_crate: &mut bool,
- found_macro: &mut bool,
- crate_name: &Option<&str>,
- ) {
+ fn check_item(item: &ast::Item, info: &mut ParseSourceInfo, crate_name: &Option<&str>) {
+ if !info.has_global_allocator
+ && item.attrs.iter().any(|attr| attr.name_or_empty() == sym::global_allocator)
+ {
+ info.has_global_allocator = true;
+ }
match item.kind {
- ast::ItemKind::Fn(ref fn_item) if !*has_main_fn => {
+ ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => {
if item.ident.name == sym::main {
- *has_main_fn = true;
+ info.has_main_fn = true;
}
if let Some(ref body) = fn_item.body {
for stmt in &body.stmts {
match stmt.kind {
- ast::StmtKind::Item(ref item) => check_item(
- item,
- has_main_fn,
- found_extern_crate,
- found_macro,
- crate_name,
- ),
- ast::StmtKind::MacCall(..) => *found_macro = true,
+ ast::StmtKind::Item(ref item) => check_item(item, info, crate_name),
+ ast::StmtKind::MacCall(..) => info.found_macro = true,
_ => {}
}
}
}
}
ast::ItemKind::ExternCrate(original) => {
- if !*found_extern_crate && let Some(ref crate_name) = crate_name {
- *found_extern_crate = match original {
+ if !info.found_extern_crate
+ && let Some(ref crate_name) = crate_name
+ {
+ info.found_extern_crate = match original {
Some(name) => name.as_str() == *crate_name,
None => item.ident.as_str() == *crate_name,
};
}
}
- ast::ItemKind::MacCall(..) => *found_macro = true,
+ ast::ItemKind::MacCall(..) => info.found_macro = true,
_ => {}
}
}
@@ -318,9 +316,9 @@ fn check_item(
loop {
match parser.parse_item(ForceCollect::No) {
Ok(Some(item)) => {
- check_item(&item, has_main_fn, found_extern_crate, found_macro, crate_name);
+ check_item(&item, info, crate_name);
- if *has_main_fn && *found_extern_crate {
+ if info.has_main_fn && info.found_extern_crate {
break;
}
}
@@ -341,30 +339,30 @@ fn check_item(
parsing_result
}
-/// Returns `(has_main_fn, already_has_extern_crate, failed_ast)`.
+#[derive(Default)]
+struct ParseSourceInfo {
+ has_main_fn: bool,
+ found_extern_crate: bool,
+ found_macro: bool,
+ supports_color: bool,
+ has_global_allocator: bool,
+}
+
fn check_for_main_and_extern_crate(
crate_name: Option<&str>,
original_source_code: &str,
everything_else: &str,
crates: &str,
edition: Edition,
- supports_color: &mut bool,
can_merge_doctests: bool,
-) -> Result<(bool, bool, bool), FatalError> {
+) -> Result<(ParseSourceInfo, bool), FatalError> {
let result = rustc_driver::catch_fatal_errors(|| {
rustc_span::create_session_if_not_set_then(edition, |_| {
- let mut has_main_fn = false;
- let mut found_extern_crate = crate_name.is_none();
- let mut found_macro = false;
+ let mut info =
+ ParseSourceInfo { found_extern_crate: crate_name.is_none(), ..Default::default() };
- let mut parsing_result = parse_source(
- format!("{crates}{everything_else}"),
- &mut has_main_fn,
- &mut found_extern_crate,
- &mut found_macro,
- &crate_name,
- supports_color,
- );
+ let mut parsing_result =
+ parse_source(format!("{crates}{everything_else}"), &mut info, &crate_name);
// No need to double-check this if the "merged doctests" feature isn't enabled (so
// before the 2024 edition).
if can_merge_doctests && parsing_result != ParsingResult::Ok {
@@ -380,30 +378,25 @@ fn check_for_main_and_extern_crate(
// faster doctests run time.
parsing_result = parse_source(
format!("{crates}\nfn __doctest_wrap(){{{everything_else}\n}}"),
- &mut has_main_fn,
- &mut found_extern_crate,
- &mut found_macro,
+ &mut info,
&crate_name,
- supports_color,
);
}
- (has_main_fn, found_extern_crate, found_macro, parsing_result)
+ (info, parsing_result)
})
});
- let (mut has_main_fn, already_has_extern_crate, found_macro, parsing_result) = match result {
- Err(..) | Ok((_, _, _, ParsingResult::Failed)) => return Err(FatalError),
- Ok((has_main_fn, already_has_extern_crate, found_macro, parsing_result)) => {
- (has_main_fn, already_has_extern_crate, found_macro, parsing_result)
- }
+ let (mut info, parsing_result) = match result {
+ Err(..) | Ok((_, ParsingResult::Failed)) => return Err(FatalError),
+ Ok((info, parsing_result)) => (info, parsing_result),
};
// 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
- if found_macro
- && !has_main_fn
+ if info.found_macro
+ && !info.has_main_fn
&& original_source_code
.lines()
.map(|line| {
@@ -412,10 +405,10 @@ fn check_for_main_and_extern_crate(
})
.any(|code| code.contains("fn main"))
{
- has_main_fn = true;
+ info.has_main_fn = true;
}
- Ok((has_main_fn, already_has_extern_crate, parsing_result != ParsingResult::Ok))
+ Ok((info, parsing_result != ParsingResult::Ok))
}
enum AttrKind {
diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs
index adff942..1a4a375 100644
--- a/src/librustdoc/doctest/runner.rs
+++ b/src/librustdoc/doctest/runner.rs
@@ -45,11 +45,11 @@ pub(crate) fn add_test(
self.crate_attrs.insert(line.to_string());
}
}
- if !self.ids.is_empty() {
- self.ids.push(',');
- }
+ // if !self.ids.is_empty() {
+ // self.ids.push(',');
+ // }
self.ids.push_str(&format!(
- "{}::TEST",
+ "tests.push({}::TEST);\n",
generate_mergeable_doctest(
doctest,
scraped_test,
@@ -107,9 +107,11 @@ pub(crate) fn run_merged_tests(
#[rustc_main]
#[coverage(off)]
fn main() {{
-test::test_main_static_with_args(
+let mut tests = Vec::new();
+{ids}
+test::test_main(
&[{test_args}],
- &mut [{ids}],
+ tests,
None,
);
}}",