diff --git a/src/formatting/syntux/parser.rs b/src/formatting/syntux/parser.rs index a439ec61b0c..9fb50b00ff3 100644 --- a/src/formatting/syntux/parser.rs +++ b/src/formatting/syntux/parser.rs @@ -66,8 +66,11 @@ impl<'a> ParserBuilder<'a> { let parser = match Self::parser(sess.inner(), input) { Ok(p) => p, Err(db) => { - sess.emit_diagnostics(db); - return Err(ParserError::ParserCreationError); + if let Some(diagnostics) = db { + sess.emit_diagnostics(diagnostics); + return Err(ParserError::ParserCreationError); + } + return Err(ParserError::ParsePanicError); } }; @@ -77,14 +80,18 @@ impl<'a> ParserBuilder<'a> { fn parser( sess: &'a rustc_session::parse::ParseSess, input: Input, - ) -> Result, Vec> { + ) -> Result, Option>> { match input { - Input::File(ref file) => Ok(new_parser_from_file(sess, file, None)), + Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || { + new_parser_from_file(sess, file, None) + })) + .map_err(|_| None), Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str( sess, rustc_span::FileName::Custom("stdin".to_owned()), text, - ), + ) + .map_err(|db| Some(db)), } } } @@ -121,7 +128,7 @@ impl<'a> Parser<'a> { match parser.parse_mod(&TokenKind::Eof, ast::Unsafe::No) { Ok(result) => Some(result), Err(mut e) => { - e.cancel(); + sess.emit_or_cancel_diagnostic(&mut e); if sess.can_reset_errors() { sess.reset_errors(); } diff --git a/src/formatting/syntux/session.rs b/src/formatting/syntux/session.rs index d7d634a2e13..3c21a3f2425 100644 --- a/src/formatting/syntux/session.rs +++ b/src/formatting/syntux/session.rs @@ -219,6 +219,17 @@ impl ParseSess { } } + pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) { + self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic); + // The Handler will check whether the diagnostic should be emitted + // based on the user's rustfmt configuration and the originating file + // that caused the parser error. If the Handler determined it should skip + // emission then we need to ensure the diagnostic is cancelled. + if !diagnostic.cancelled() { + diagnostic.cancel(); + } + } + pub(super) fn can_reset_errors(&self) -> bool { *self.can_reset_errors.borrow() } diff --git a/src/result.rs b/src/result.rs index e8f123e39e2..dcc8c2078d4 100644 --- a/src/result.rs +++ b/src/result.rs @@ -118,7 +118,7 @@ pub enum OperationError { #[error("invalid glob pattern found in ignore list: {0}")] InvalidGlobPattern(ignore::Error), /// Parse error occurred while parsing the input. - #[error("failed to parse {input:?}")] + #[error("failed to parse {input}")] ParseError { input: FileName, is_panic: bool }, /// Io error. #[error("{0}")] diff --git a/src/test/mod.rs b/src/test/mod.rs index 6f66547524b..5f7f45f9a6a 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -545,6 +545,22 @@ fn parser_errors_in_submods_are_surfaced() { } } +#[test] +fn parser_creation_errors_on_entry_new_parser_from_file_panic() { + // See also https://github.com/rust-lang/rustfmt/issues/4418 + let filename = "tests/parser/issue_4418.rs"; + let file = PathBuf::from(filename); + let (config, operation, _) = read_config(&file); + if let Err(OperationError::ParseError { input, is_panic }) = + format_file(&file, operation, config) + { + assert_eq!(input.as_path().unwrap(), file); + assert!(is_panic); + } else { + panic!("Expected ParseError operation error"); + } +} + // For each file, run rustfmt and collect the output. // Returns the number of files checked and the number of failures. fn check_files(files: Vec, opt_config: &Option) -> (Vec, u32, u32) { diff --git a/tests/parser/issue_4418.rs b/tests/parser/issue_4418.rs new file mode 100644 index 00000000000..ff30235f076 --- /dev/null +++ b/tests/parser/issue_4418.rs @@ -0,0 +1 @@ +} \ No newline at end of file