rustdoc: Avoid panic when parsing codeblocks for playground links
`make_test` is also called when parsing codeblocks for the playground links so it should handle unwinds from the parser internally.
This commit is contained in:
parent
cd8377d37e
commit
efb876f557
|
@ -202,17 +202,7 @@ fn run_test(
|
||||||
opts: &TestOptions,
|
opts: &TestOptions,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
) -> Result<(), TestFailure> {
|
) -> Result<(), TestFailure> {
|
||||||
let (test, line_offset) = match panic::catch_unwind(|| {
|
let (test, line_offset) = make_test(test, Some(cratename), as_test_harness, opts, edition);
|
||||||
make_test(test, Some(cratename), as_test_harness, opts, edition)
|
|
||||||
}) {
|
|
||||||
Ok((test, line_offset)) => (test, line_offset),
|
|
||||||
Err(cause) if cause.is::<errors::FatalErrorMarker>() => {
|
|
||||||
// If the parser used by `make_test` panicked due to a fatal error, pass the test code
|
|
||||||
// through unchanged. The error will be reported during compilation.
|
|
||||||
(test.to_owned(), 0)
|
|
||||||
}
|
|
||||||
Err(cause) => panic::resume_unwind(cause),
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME(#44940): if doctests ever support path remapping, then this filename
|
// FIXME(#44940): if doctests ever support path remapping, then this filename
|
||||||
// needs to be the result of `SourceMap::span_to_unmapped_path`.
|
// needs to be the result of `SourceMap::span_to_unmapped_path`.
|
||||||
|
@ -362,11 +352,6 @@ fn run_test(
|
||||||
|
|
||||||
/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
|
/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
|
||||||
/// lines before the test code begins.
|
/// lines before the test code begins.
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function uses the compiler's parser internally. The parser will panic if it encounters a
|
|
||||||
/// fatal error while parsing the test.
|
|
||||||
pub fn make_test(
|
pub fn make_test(
|
||||||
s: &str,
|
s: &str,
|
||||||
cratename: Option<&str>,
|
cratename: Option<&str>,
|
||||||
|
@ -401,83 +386,94 @@ pub fn make_test(
|
||||||
|
|
||||||
// Uses libsyntax to parse the doctest and find if there's a main fn and the extern
|
// Uses libsyntax to parse the doctest and find if there's a main fn and the extern
|
||||||
// crate already is included.
|
// crate already is included.
|
||||||
let (already_has_main, already_has_extern_crate, found_macro) = with_globals(edition, || {
|
let result = rustc_driver::catch_fatal_errors(|| {
|
||||||
use errors::emitter::EmitterWriter;
|
with_globals(edition, || {
|
||||||
use errors::Handler;
|
use errors::emitter::EmitterWriter;
|
||||||
use rustc_parse::maybe_new_parser_from_source_str;
|
use errors::Handler;
|
||||||
use rustc_span::source_map::FilePathMapping;
|
use rustc_parse::maybe_new_parser_from_source_str;
|
||||||
use syntax::sess::ParseSess;
|
use rustc_span::source_map::FilePathMapping;
|
||||||
|
use syntax::sess::ParseSess;
|
||||||
|
|
||||||
let filename = FileName::anon_source_code(s);
|
let filename = FileName::anon_source_code(s);
|
||||||
let source = crates + &everything_else;
|
let source = crates + &everything_else;
|
||||||
|
|
||||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||||
// send all the errors that libsyntax emits directly into a `Sink` instead of stderr.
|
// send all the errors that libsyntax emits directly into a `Sink` instead of stderr.
|
||||||
let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||||
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
|
let emitter =
|
||||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
|
||||||
let handler = Handler::with_emitter(false, None, box emitter);
|
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||||
let sess = ParseSess::with_span_handler(handler, cm);
|
let handler = Handler::with_emitter(false, None, box emitter);
|
||||||
|
let sess = ParseSess::with_span_handler(handler, cm);
|
||||||
|
|
||||||
let mut found_main = false;
|
let mut found_main = false;
|
||||||
let mut found_extern_crate = cratename.is_none();
|
let mut found_extern_crate = cratename.is_none();
|
||||||
let mut found_macro = false;
|
let mut found_macro = false;
|
||||||
|
|
||||||
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) {
|
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(errs) => {
|
Err(errs) => {
|
||||||
for mut err in errs {
|
for mut err in errs {
|
||||||
err.cancel();
|
err.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (found_main, found_extern_crate, found_macro);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (found_main, found_extern_crate, found_macro);
|
loop {
|
||||||
}
|
match parser.parse_item() {
|
||||||
};
|
Ok(Some(item)) => {
|
||||||
|
if !found_main {
|
||||||
loop {
|
if let ast::ItemKind::Fn(..) = item.kind {
|
||||||
match parser.parse_item() {
|
if item.ident.name == sym::main {
|
||||||
Ok(Some(item)) => {
|
found_main = true;
|
||||||
if !found_main {
|
}
|
||||||
if let ast::ItemKind::Fn(..) = item.kind {
|
|
||||||
if item.ident.name == sym::main {
|
|
||||||
found_main = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !found_extern_crate {
|
if !found_extern_crate {
|
||||||
if let ast::ItemKind::ExternCrate(original) = item.kind {
|
if let ast::ItemKind::ExternCrate(original) = item.kind {
|
||||||
// This code will never be reached if `cratename` is none because
|
// This code will never be reached if `cratename` is none because
|
||||||
// `found_extern_crate` is initialized to `true` if it is none.
|
// `found_extern_crate` is initialized to `true` if it is none.
|
||||||
let cratename = cratename.unwrap();
|
let cratename = cratename.unwrap();
|
||||||
|
|
||||||
match original {
|
match original {
|
||||||
Some(name) => found_extern_crate = name.as_str() == cratename,
|
Some(name) => found_extern_crate = name.as_str() == cratename,
|
||||||
None => found_extern_crate = item.ident.as_str() == cratename,
|
None => found_extern_crate = item.ident.as_str() == cratename,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !found_macro {
|
if !found_macro {
|
||||||
if let ast::ItemKind::Mac(..) = item.kind {
|
if let ast::ItemKind::Mac(..) = item.kind {
|
||||||
found_macro = true;
|
found_macro = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found_main && found_extern_crate {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(None) => break,
|
||||||
if found_main && found_extern_crate {
|
Err(mut e) => {
|
||||||
|
e.cancel();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => break,
|
|
||||||
Err(mut e) => {
|
|
||||||
e.cancel();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
(found_main, found_extern_crate, found_macro)
|
(found_main, found_extern_crate, found_macro)
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
let (already_has_main, already_has_extern_crate, found_macro) = match result {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(ErrorReported) => {
|
||||||
|
// If the parser panicked due to a fatal error, pass the test code through unchanged.
|
||||||
|
// The error will be reported during compilation.
|
||||||
|
return (s.to_owned(), 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't
|
// 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
|
// see it. In that case, run the old text-based scan to see if they at least have a main
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#![crate_name = "foo"]
|
||||||
|
#![doc(html_playground_url = "https://play.rust-lang.org/")]
|
||||||
|
|
||||||
|
/// bar docs
|
||||||
|
///
|
||||||
|
/// ```edition2015
|
||||||
|
/// use std::future::Future;
|
||||||
|
/// use std::pin::Pin;
|
||||||
|
/// fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||||
|
/// Box::pin(async move {
|
||||||
|
/// if n > 0 {
|
||||||
|
/// foo_recursive(n - 1).await;
|
||||||
|
/// }
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn bar() {}
|
||||||
|
|
||||||
|
// @has foo/fn.bar.html
|
||||||
|
// @has - '//a[@class="test-arrow"]' "Run"
|
||||||
|
// @has - '//*[@class="docblock"]' 'foo_recursive'
|
Loading…
Reference in New Issue