When encountering unclosed delimiters during parsing, check for diff markers
Fix #116252.
This commit is contained in:
parent
608e9682f0
commit
50ca5ef07f
@ -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,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)
|
||||
}
|
||||
|
@ -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>,
|
||||
@ -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) => {
|
||||
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
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
@ -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 \
|
||||
<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
|
||||
|
9
tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs
Normal file
9
tests/ui/parser/diff-markers/unclosed-delims-in-macro.rs
Normal file
@ -0,0 +1,9 @@
|
||||
macro_rules! foo {
|
||||
<<<<<<< HEAD
|
||||
//~^ ERROR encountered diff marker
|
||||
() {
|
||||
=======
|
||||
() { //
|
||||
>>>>>>> 7a4f13c blah blah blah
|
||||
}
|
||||
}
|
18
tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr
Normal file
18
tests/ui/parser/diff-markers/unclosed-delims-in-macro.stderr
Normal file
@ -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
tests/ui/parser/diff-markers/unclosed-delims.rs
Normal file
14
tests/ui/parser/diff-markers/unclosed-delims.rs
Normal file
@ -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
tests/ui/parser/diff-markers/unclosed-delims.stderr
Normal file
18
tests/ui/parser/diff-markers/unclosed-delims.stderr
Normal file
@ -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…
x
Reference in New Issue
Block a user