Skip to content

Commit 1780d68

Browse files
committed
Catch panics in the parser before they crash rustfmt
Closes #753
1 parent c829875 commit 1780d68

File tree

1 file changed

+43
-32
lines changed

1 file changed

+43
-32
lines changed

src/lib.rs

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use std::collections::HashMap;
3333
use std::fmt;
3434
use std::io::{self, stdout, BufRead, Write};
3535
use std::iter::repeat;
36+
use std::panic::{catch_unwind, AssertUnwindSafe};
3637
use std::path::PathBuf;
3738
use std::rc::Rc;
3839
use std::time::Duration;
@@ -537,43 +538,48 @@ fn parse_input<'sess>(
537538
input: Input,
538539
parse_session: &'sess ParseSess,
539540
config: &Config,
540-
) -> Result<ast::Crate, Option<DiagnosticBuilder<'sess>>> {
541-
let result = match input {
542-
Input::File(file) => {
543-
let mut parser = parse::new_parser_from_file(parse_session, &file);
544-
parser.cfg_mods = false;
545-
if config.skip_children() {
546-
parser.recurse_into_file_modules = false;
547-
}
548-
parser.parse_crate_mod()
549-
}
550-
Input::Text(text) => {
551-
let mut parser = parse::new_parser_from_source_str(
552-
parse_session,
553-
FileName::Custom("stdin".to_owned()),
554-
text,
555-
);
556-
parser.cfg_mods = false;
557-
if config.skip_children() {
558-
parser.recurse_into_file_modules = false;
559-
}
560-
parser.parse_crate_mod()
561-
}
541+
) -> Result<ast::Crate, ParseError<'sess>> {
542+
let mut parser = match input {
543+
Input::File(file) => parse::new_parser_from_file(parse_session, &file),
544+
Input::Text(text) => parse::new_parser_from_source_str(
545+
parse_session,
546+
FileName::Custom("stdin".to_owned()),
547+
text,
548+
),
562549
};
563550

551+
parser.cfg_mods = false;
552+
if config.skip_children() {
553+
parser.recurse_into_file_modules = false;
554+
}
555+
556+
let mut parser = AssertUnwindSafe(parser);
557+
let result = catch_unwind(move || parser.0.parse_crate_mod());
558+
564559
match result {
565-
Ok(c) => {
560+
Ok(Ok(c)) => {
566561
if parse_session.span_diagnostic.has_errors() {
567562
// Bail out if the parser recovered from an error.
568-
Err(None)
563+
Err(ParseError::Recovered)
569564
} else {
570565
Ok(c)
571566
}
572567
}
573-
Err(e) => Err(Some(e)),
568+
Ok(Err(e)) => Err(ParseError::Error(e)),
569+
Err(_) => Err(ParseError::Panic),
574570
}
575571
}
576572

573+
/// All the ways that parsing can fail.
574+
enum ParseError<'sess> {
575+
/// There was an error, but the parser recovered.
576+
Recovered,
577+
/// There was an error (supplied) and parsing failed.
578+
Error(DiagnosticBuilder<'sess>),
579+
/// The parser panicked.
580+
Panic,
581+
}
582+
577583
/// Format the given snippet. The snippet is expected to be *complete* code.
578584
/// When we cannot parse the given snippet, this function returns `None`.
579585
pub fn format_snippet(snippet: &str, config: &Config) -> Option<String> {
@@ -682,9 +688,18 @@ pub fn format_input<T: Write>(
682688

683689
let krate = match parse_input(input, &parse_session, config) {
684690
Ok(krate) => krate,
685-
Err(diagnostic) => {
686-
if let Some(mut diagnostic) = diagnostic {
687-
diagnostic.emit();
691+
Err(err) => {
692+
match err {
693+
ParseError::Error(mut diagnostic) => diagnostic.emit(),
694+
ParseError::Panic => {
695+
// Note that if you see this message and want more information,
696+
// then go to `parse_input` and run the parse function without
697+
// `catch_unwind` so rustfmt panics and you can get a backtrace.
698+
should_emit_verbose(&main_file, config, || {
699+
println!("The Rust parser panicked")
700+
});
701+
}
702+
ParseError::Recovered => {}
688703
}
689704
summary.add_parsing_error();
690705
return Ok((summary, FileMap::new(), FormatReport::new()));
@@ -693,10 +708,6 @@ pub fn format_input<T: Write>(
693708

694709
summary.mark_parse_time();
695710

696-
if parse_session.span_diagnostic.has_errors() {
697-
summary.add_parsing_error();
698-
}
699-
700711
// Suppress error output after parsing.
701712
let silent_emitter = Box::new(EmitterWriter::new(
702713
Box::new(Vec::new()),

0 commit comments

Comments
 (0)