Skip to content

Commit

Permalink
Rollup merge of rust-lang#57417 - QuietMisdreavus:semi-revert-doctest…
Browse files Browse the repository at this point in the history
…-parsing, r=GuillaumeGomez

rustdoc: use text-based doctest parsing if a macro is wrapping main

This is a "forward-port" of rust-lang#57019, intended to get rust-lang#56898 on nightly, since it's now fixed on beta (and already worked on stable).

To recap:

* The libsyntax-based doctest parsing now checks to see whether there is a top-level macro invocation in the doctest while it's checking for `fn main` and an `extern crate` statement.
* If it finds a macro invocation and *didn't* find `fn main`, then it performs the older text-based scan to allow doctests like the ones in `allocator_api` to still compile.

A "proper" fix will involve changing how `make_test` works to call it later in the `run_test` function, after the initial steps of compilation have completed. I've filed [a separate issue](rust-lang#57415) for that, though.
  • Loading branch information
Centril authored Jan 12, 2019
2 parents 7be2ff3 + dac6eec commit a61ebac
Showing 1 changed file with 48 additions and 3 deletions.
51 changes: 48 additions & 3 deletions src/librustdoc/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ pub fn make_test(s: &str,

// 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;
Expand All @@ -412,6 +412,7 @@ pub fn make_test(s: &str,

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,
Expand All @@ -420,7 +421,7 @@ pub fn make_test(s: &str,
err.cancel();
}

return (found_main, found_extern_crate);
return (found_main, found_extern_crate, found_macro);
}
};

Expand Down Expand Up @@ -448,6 +449,12 @@ pub fn make_test(s: &str,
}
}

if !found_macro {
if let ast::ItemKind::Mac(..) = item.node {
found_macro = true;
}
}

if found_main && found_extern_crate {
break;
}
Expand All @@ -460,9 +467,28 @@ pub fn make_test(s: &str,
}
}

(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") {
Expand Down Expand Up @@ -1143,4 +1169,23 @@ assert_eq!(asdf::foo, 4);
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));
}
}

0 comments on commit a61ebac

Please sign in to comment.