From 075cb61e4a0a03d8afa1b4ef62643a6544a03b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 13 Oct 2023 22:43:48 +0000 Subject: [PATCH] When encountering unclosed delimiters during parsing, check for diff markers Fix #116252. --- compiler/rustc_parse/src/lexer/mod.rs | 8 ++-- compiler/rustc_parse/src/lexer/tokentrees.rs | 48 ++++++++++++------- .../rustc_parse/src/parser/diagnostics.rs | 12 +++-- .../diff-markers/unclosed-delims-in-macro.rs | 9 ++++ .../unclosed-delims-in-macro.stderr | 18 +++++++ .../ui/parser/diff-markers/unclosed-delims.rs | 14 ++++++ .../diff-markers/unclosed-delims.stderr | 18 +++++++ 7 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs create mode 100644 tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr create mode 100644 tests/ui/parser/diff-markers/unclosed-delims.rs create mode 100644 tests/ui/parser/diff-markers/unclosed-delims.stderr diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index a375a1d69cdea..d69b74aac0006 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -64,10 +64,10 @@ pub(crate) fn parse_token_trees<'a>( override_span, nbsp_is_whitespace: false, }; - let (token_trees, unmatched_delims) = + let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); - match token_trees { - Ok(stream) if unmatched_delims.is_empty() => Ok(stream), + match res { + Ok(()) if unmatched_delims.is_empty() => Ok(stream), _ => { // Return error if there are unmatched delimiters or unclosed delimiters. // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch @@ -79,7 +79,7 @@ pub(crate) fn parse_token_trees<'a>( err.buffer(&mut buffer); } } - if let Err(err) = token_trees { + if let Err(err) = res { // Add unclosing delimiter error err.buffer(&mut buffer); } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 1d9dbfe4b89d0..cea29b28a543b 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -18,36 +18,39 @@ pub(super) struct TokenTreesReader<'a> { impl<'a> TokenTreesReader<'a> { pub(super) fn parse_all_token_trees( string_reader: StringReader<'a>, - ) -> (PResult<'a, TokenStream>, Vec) { + ) -> (TokenStream, PResult<'a, ()>, Vec) { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), diag_info: TokenTreeDiagInfo::default(), }; - let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.diag_info.unmatched_delims) + let (stream, res) = tt_reader.parse_token_trees(/* is_delimited */ false); + (stream, res, tt_reader.diag_info.unmatched_delims) } // Parse a stream of tokens into a list of `TokenTree`s. - fn parse_token_trees(&mut self, is_delimited: bool) -> PResult<'a, TokenStream> { + fn parse_token_trees(&mut self, is_delimited: bool) -> (TokenStream, PResult<'a, ()>) { self.token = self.string_reader.next_token().0; let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)?), + token::OpenDelim(delim) => { + buf.push(match self.parse_token_tree_open_delim(delim) { + Ok(val) => val, + Err(err) => return (TokenStream::new(buf), Err(err)), + }) + } token::CloseDelim(delim) => { - return if is_delimited { - Ok(TokenStream::new(buf)) - } else { - Err(self.close_delim_err(delim)) - }; + return ( + TokenStream::new(buf), + if is_delimited { Ok(()) } else { Err(self.close_delim_err(delim)) }, + ); } token::Eof => { - return if is_delimited { - Err(self.eof_err()) - } else { - Ok(TokenStream::new(buf)) - }; + return ( + TokenStream::new(buf), + if is_delimited { Err(self.eof_err()) } else { Ok(()) }, + ); } _ => { // Get the next normal token. This might require getting multiple adjacent @@ -106,7 +109,20 @@ impl<'a> TokenTreesReader<'a> { // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user // uses an incorrect delimiter. - let tts = self.parse_token_trees(/* is_delimited */ true)?; + let (tts, res) = self.parse_token_trees(/* is_delimited */ true); + if let Err(mut err) = res { + // If there are unclosed delims, see if there are diff markers and if so, point them + // out instead of complaining about the unclosed delims. + let mut parser = crate::stream_to_parser(self.string_reader.sess, tts, None); + while parser.token != token::Eof { + if let Err(mut diff_err) = parser.err_diff_marker() { + diff_err.emit(); + err.delay_as_bug(); + } + parser.bump(); + } + return Err(err); + } // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9f8361a4b1ee2..9a34c5a10ec43 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -2808,8 +2808,15 @@ impl<'a> Parser<'a> { } pub fn recover_diff_marker(&mut self) { + if let Err(mut err) = self.err_diff_marker() { + err.emit(); + FatalError.raise(); + } + } + + pub fn err_diff_marker(&mut self) -> PResult<'a, ()> { let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else { - return; + return Ok(()); }; let mut spans = Vec::with_capacity(3); spans.push(start); @@ -2856,8 +2863,7 @@ impl<'a> Parser<'a> { "for an explanation on these markers from the `git` documentation, visit \ ", ); - err.emit(); - FatalError.raise() + Err(err) } /// Parse and throw away a parenthesized comma separated diff --git a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs new file mode 100644 index 0000000000000..da1774acea542 --- /dev/null +++ b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs @@ -0,0 +1,9 @@ +macro_rules! foo { +<<<<<<< HEAD + //~^ ERROR encountered diff marker + () { +======= + () { // +>>>>>>> 7a4f13c blah blah blah + } +} diff --git a/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr new file mode 100644 index 0000000000000..e0b6f1b5eb805 --- /dev/null +++ b/tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr @@ -0,0 +1,18 @@ +error: encountered diff marker + --> $DIR/unclosed-delims-in-macro.rs:2:1 + | +LL | <<<<<<< HEAD + | ^^^^^^^ after this is the code before the merge +... +LL | ======= + | ------- +LL | () { // +LL | >>>>>>> 7a4f13c blah blah blah + | ^^^^^^^ above this are the incoming code changes + | + = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code + = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation, visit + +error: aborting due to previous error + diff --git a/tests/ui/parser/diff-markers/unclosed-delims.rs b/tests/ui/parser/diff-markers/unclosed-delims.rs new file mode 100644 index 0000000000000..653a605c28c60 --- /dev/null +++ b/tests/ui/parser/diff-markers/unclosed-delims.rs @@ -0,0 +1,14 @@ +mod tests { + #[test] +<<<<<<< HEAD +//~^ ERROR encountered diff marker +//~| NOTE after this is the code before the merge +//~| NOTE for an explanation on these markers + fn test1() { +======= +//~^ NOTE + fn test2() { +>>>>>>> 7a4f13c blah blah blah +//~^ NOTE above this are the incoming code changes + } +} diff --git a/tests/ui/parser/diff-markers/unclosed-delims.stderr b/tests/ui/parser/diff-markers/unclosed-delims.stderr new file mode 100644 index 0000000000000..67199179b3922 --- /dev/null +++ b/tests/ui/parser/diff-markers/unclosed-delims.stderr @@ -0,0 +1,18 @@ +error: encountered diff marker + --> $DIR/unclosed-delims.rs:3:1 + | +LL | <<<<<<< HEAD + | ^^^^^^^ after this is the code before the merge +... +LL | ======= + | ------- +... +LL | >>>>>>> 7a4f13c blah blah blah + | ^^^^^^^ above this are the incoming code changes + | + = help: if you're having merge conflicts after pulling new code, the top section is the code you already had and the bottom section is the remote code + = help: if you're in the middle of a rebase, the top section is the code being rebased onto and the bottom section is the code coming from the current commit being rebased + = note: for an explanation on these markers from the `git` documentation, visit + +error: aborting due to previous error +