rustdoc: Fix doctest heuristic for main fn wrapping
This commit is contained in:
parent
25cdf1f674
commit
714ea10ea4
12 changed files with 183 additions and 89 deletions
|
|
@ -301,8 +301,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
|
||||
let filename = FileName::anon_source_code(&wrapped_source);
|
||||
|
||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||
// send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
|
||||
let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
|
||||
|
|
@ -311,7 +309,8 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
info.supports_color =
|
||||
HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone())
|
||||
.supports_color();
|
||||
|
||||
// Any errors in parsing should also appear when the doctest is compiled for real, so just
|
||||
// send all the errors that the parser emits directly into a `Sink` instead of stderr.
|
||||
let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle);
|
||||
|
||||
// FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser
|
||||
|
|
@ -339,9 +338,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
*prev_span_hi = hi;
|
||||
}
|
||||
|
||||
// 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, info: &mut ParseSourceInfo, crate_name: &Option<&str>) -> bool {
|
||||
let mut is_extern_crate = false;
|
||||
if !info.has_global_allocator
|
||||
|
|
@ -351,8 +347,6 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
}
|
||||
match item.kind {
|
||||
ast::ItemKind::Fn(ref fn_item) if !info.has_main_fn => {
|
||||
// We only push if it's the top item because otherwise, we would duplicate
|
||||
// its content since the top-level item was already added.
|
||||
if fn_item.ident.name == sym::main {
|
||||
info.has_main_fn = true;
|
||||
}
|
||||
|
|
@ -412,7 +406,32 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
let mut is_extern_crate = false;
|
||||
match stmt.kind {
|
||||
StmtKind::Item(ref item) => {
|
||||
is_extern_crate = check_item(&item, &mut info, crate_name);
|
||||
is_extern_crate = check_item(item, &mut info, crate_name);
|
||||
}
|
||||
// We assume that the macro calls will expand to item(s) even though they could
|
||||
// expand to statements and expressions.
|
||||
StmtKind::MacCall(ref mac_call) => {
|
||||
if !info.has_main_fn {
|
||||
// For backward compatibility, we look for the token sequence `fn main(…)`
|
||||
// in the macro input (!) to crudely detect main functions "masked by a
|
||||
// wrapper macro". For the record, this is a horrible heuristic!
|
||||
// See <https://github.com/rust-lang/rust/issues/56898>.
|
||||
let mut iter = mac_call.mac.args.tokens.iter();
|
||||
while let Some(token) = iter.next() {
|
||||
if let TokenTree::Token(token, _) = token
|
||||
&& let TokenKind::Ident(kw::Fn, _) = token.kind
|
||||
&& let Some(TokenTree::Token(ident, _)) = iter.peek()
|
||||
&& let TokenKind::Ident(sym::main, _) = ident.kind
|
||||
&& let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, _)) = {
|
||||
iter.next();
|
||||
iter.peek()
|
||||
}
|
||||
{
|
||||
info.has_main_fn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::Expr(ref expr) => {
|
||||
if matches!(expr.kind, ast::ExprKind::Err(_)) {
|
||||
|
|
@ -421,35 +440,7 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn
|
|||
}
|
||||
has_non_items = true;
|
||||
}
|
||||
// We assume that the macro calls will expand to item(s) even though they could
|
||||
// expand to statements and expressions. And the simple fact that we're trying
|
||||
// to retrieve a `main` function inside it is a terrible idea.
|
||||
StmtKind::MacCall(ref mac_call) => {
|
||||
if info.has_main_fn {
|
||||
continue;
|
||||
}
|
||||
let mut iter = mac_call.mac.args.tokens.iter();
|
||||
|
||||
while let Some(token) = iter.next() {
|
||||
if let TokenTree::Token(token, _) = token
|
||||
&& let TokenKind::Ident(name, _) = token.kind
|
||||
&& name == kw::Fn
|
||||
&& let Some(TokenTree::Token(fn_token, _)) = iter.peek()
|
||||
&& let TokenKind::Ident(fn_name, _) = fn_token.kind
|
||||
&& fn_name == sym::main
|
||||
&& let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) = {
|
||||
iter.next();
|
||||
iter.peek()
|
||||
}
|
||||
{
|
||||
info.has_main_fn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
has_non_items = true;
|
||||
}
|
||||
StmtKind::Let(_) | StmtKind::Semi(_) | StmtKind::Empty => has_non_items = true,
|
||||
}
|
||||
|
||||
// Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue