Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When encountering unclosed delimiters during lexing, check for diff markers #116712

Merged
merged 1 commit into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions compiler/rustc_parse/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -79,9 +79,11 @@ pub(crate) fn parse_token_trees<'a>(
err.buffer(&mut buffer);
}
}
if let Err(err) = token_trees {
// Add unclosing delimiter error
err.buffer(&mut buffer);
if let Err(errs) = res {
// Add unclosing delimiter or diff marker errors
for err in errs {
err.buffer(&mut buffer);
}
}
Err(buffer)
}
Expand Down
64 changes: 46 additions & 18 deletions compiler/rustc_parse/src/lexer/tokentrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::{StringReader, UnmatchedDelim};
use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_ast_pretty::pprust::token_to_string;
use rustc_errors::{PErr, PResult};
use rustc_errors::PErr;

pub(super) struct TokenTreesReader<'a> {
string_reader: StringReader<'a>,
Expand All @@ -18,36 +18,42 @@ pub(super) struct TokenTreesReader<'a> {
impl<'a> TokenTreesReader<'a> {
pub(super) fn parse_all_token_trees(
string_reader: StringReader<'a>,
) -> (PResult<'a, TokenStream>, Vec<UnmatchedDelim>) {
) -> (TokenStream, Result<(), Vec<PErr<'a>>>, Vec<UnmatchedDelim>) {
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, Result<(), Vec<PErr<'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) => {
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
buf.push(match self.parse_token_tree_open_delim(delim) {
Ok(val) => val,
Err(errs) => return (TokenStream::new(buf), Err(errs)),
})
}
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(vec![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(vec![self.eof_err()]) } else { Ok(()) },
);
}
_ => {
// Get the next normal token. This might require getting multiple adjacent
Expand Down Expand Up @@ -97,7 +103,10 @@ impl<'a> TokenTreesReader<'a> {
err
}

fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> PResult<'a, TokenTree> {
fn parse_token_tree_open_delim(
&mut self,
open_delim: Delimiter,
) -> Result<TokenTree, Vec<PErr<'a>>> {
// The span for beginning of the delimited section
let pre_span = self.token.span;

Expand All @@ -106,7 +115,26 @@ 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 errs) = 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);
let mut diff_errs = vec![];
while parser.token != token::Eof {
if let Err(diff_err) = parser.err_diff_marker() {
diff_errs.push(diff_err);
}
parser.bump();
}
if !diff_errs.is_empty() {
errs.iter_mut().for_each(|err| {
err.delay_as_bug();
});
return Err(diff_errs);
}
return Err(errs);
}

// Expand to cover the entire delimited token tree
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
Expand Down
12 changes: 9 additions & 3 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -2856,8 +2863,7 @@ impl<'a> Parser<'a> {
"for an explanation on these markers from the `git` documentation, visit \
<https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
);
err.emit();
FatalError.raise()
Err(err)
}

/// Parse and throw away a parenthesized comma separated
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
macro_rules! foo {
<<<<<<< HEAD
//~^ ERROR encountered diff marker
() {
=======
() { //
>>>>>>> 7a4f13c blah blah blah
}
}
18 changes: 18 additions & 0 deletions tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr
Original file line number Diff line number Diff line change
@@ -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 <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>

error: aborting due to previous error

14 changes: 14 additions & 0 deletions tests/ui/parser/diff-markers/unclosed-delims.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
18 changes: 18 additions & 0 deletions tests/ui/parser/diff-markers/unclosed-delims.stderr
Original file line number Diff line number Diff line change
@@ -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 <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>

error: aborting due to previous error

Loading