Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 39bcf8e

Browse files
committed
Handle fatal errors when parsing doctests
1 parent 6c83e56 commit 39bcf8e

File tree

3 files changed

+68
-55
lines changed

3 files changed

+68
-55
lines changed

clippy_lints/src/doc.rs

Lines changed: 61 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -451,66 +451,72 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
451451
}
452452

453453
fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
454-
fn has_needless_main(code: &str) -> bool {
455-
let filename = FileName::anon_source_code(code);
456-
457-
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
458-
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
459-
let handler = Handler::with_emitter(false, None, box emitter);
460-
let sess = ParseSess::with_span_handler(handler, sm);
461-
462-
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
463-
Ok(p) => p,
464-
Err(errs) => {
465-
for mut err in errs {
466-
err.cancel();
467-
}
468-
return false;
469-
},
470-
};
471-
472-
let mut relevant_main_found = false;
473-
loop {
474-
match parser.parse_item() {
475-
Ok(Some(item)) => match &item.kind {
476-
// Tests with one of these items are ignored
477-
ItemKind::Static(..)
478-
| ItemKind::Const(..)
479-
| ItemKind::ExternCrate(..)
480-
| ItemKind::ForeignMod(..) => return false,
481-
// We found a main function ...
482-
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
483-
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
484-
let returns_nothing = match &sig.decl.output {
485-
FnRetTy::Default(..) => true,
486-
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
487-
_ => false,
488-
};
489-
490-
if returns_nothing && !is_async && !block.stmts.is_empty() {
491-
// This main function should be linted, but only if there are no other functions
492-
relevant_main_found = true;
493-
} else {
494-
// This main function should not be linted, we're done
495-
return false;
454+
fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool {
455+
rustc_driver::catch_fatal_errors(|| {
456+
rustc_span::with_session_globals(cx.tcx.sess.edition(), || {
457+
let filename = FileName::anon_source_code(code);
458+
459+
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
460+
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
461+
let handler = Handler::with_emitter(false, None, box emitter);
462+
let sess = ParseSess::with_span_handler(handler, sm);
463+
464+
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
465+
Ok(p) => p,
466+
Err(errs) => {
467+
for mut err in errs {
468+
err.cancel();
496469
}
470+
return false;
497471
},
498-
// Another function was found; this case is ignored too
499-
ItemKind::Fn(..) => return false,
500-
_ => {},
501-
},
502-
Ok(None) => break,
503-
Err(mut e) => {
504-
e.cancel();
505-
return false;
506-
},
507-
}
508-
}
472+
};
473+
474+
let mut relevant_main_found = false;
475+
loop {
476+
match parser.parse_item() {
477+
Ok(Some(item)) => match &item.kind {
478+
// Tests with one of these items are ignored
479+
ItemKind::Static(..)
480+
| ItemKind::Const(..)
481+
| ItemKind::ExternCrate(..)
482+
| ItemKind::ForeignMod(..) => return false,
483+
// We found a main function ...
484+
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => {
485+
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
486+
let returns_nothing = match &sig.decl.output {
487+
FnRetTy::Default(..) => true,
488+
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
489+
_ => false,
490+
};
491+
492+
if returns_nothing && !is_async && !block.stmts.is_empty() {
493+
// This main function should be linted, but only if there are no other functions
494+
relevant_main_found = true;
495+
} else {
496+
// This main function should not be linted, we're done
497+
return false;
498+
}
499+
},
500+
// Another function was found; this case is ignored too
501+
ItemKind::Fn(..) => return false,
502+
_ => {},
503+
},
504+
Ok(None) => break,
505+
Err(mut e) => {
506+
e.cancel();
507+
return false;
508+
},
509+
}
510+
}
509511

510-
relevant_main_found
512+
relevant_main_found
513+
})
514+
})
515+
.ok()
516+
.unwrap_or_default()
511517
}
512518

513-
if has_needless_main(text) {
519+
if has_needless_main(cx, text) {
514520
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
515521
}
516522
}

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ extern crate rustc_ast;
2727
extern crate rustc_ast_pretty;
2828
extern crate rustc_attr;
2929
extern crate rustc_data_structures;
30+
extern crate rustc_driver;
3031
extern crate rustc_errors;
3132
extern crate rustc_hir;
3233
extern crate rustc_hir_pretty;

tests/ui/needless_doc_main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ fn bad_doctests() {}
128128
/// ```
129129
fn no_false_positives() {}
130130

131+
/// Yields a parse error when interpreted as rust code:
132+
/// ```
133+
/// r#"hi"
134+
/// ```
135+
fn issue_6022() {}
136+
131137
fn main() {
132138
bad_doctests();
133139
no_false_positives();

0 commit comments

Comments
 (0)