Skip to content

Commit 7035c3d

Browse files
authored
Rollup merge of #116712 - estebank:issue-116252, r=petrochenkov
When encountering unclosed delimiters during lexing, check for diff markers Fix #116252.
2 parents d0833c4 + 50ca5ef commit 7035c3d

File tree

7 files changed

+122
-27
lines changed

7 files changed

+122
-27
lines changed

Diff for: compiler/rustc_parse/src/lexer/mod.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ pub(crate) fn parse_token_trees<'a>(
6464
override_span,
6565
nbsp_is_whitespace: false,
6666
};
67-
let (token_trees, unmatched_delims) =
67+
let (stream, res, unmatched_delims) =
6868
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader);
69-
match token_trees {
70-
Ok(stream) if unmatched_delims.is_empty() => Ok(stream),
69+
match res {
70+
Ok(()) if unmatched_delims.is_empty() => Ok(stream),
7171
_ => {
7272
// Return error if there are unmatched delimiters or unclosed delimiters.
7373
// We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
@@ -79,9 +79,11 @@ pub(crate) fn parse_token_trees<'a>(
7979
err.buffer(&mut buffer);
8080
}
8181
}
82-
if let Err(err) = token_trees {
83-
// Add unclosing delimiter error
84-
err.buffer(&mut buffer);
82+
if let Err(errs) = res {
83+
// Add unclosing delimiter or diff marker errors
84+
for err in errs {
85+
err.buffer(&mut buffer);
86+
}
8587
}
8688
Err(buffer)
8789
}

Diff for: compiler/rustc_parse/src/lexer/tokentrees.rs

+46-18
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::{StringReader, UnmatchedDelim};
55
use rustc_ast::token::{self, Delimiter, Token};
66
use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
77
use rustc_ast_pretty::pprust::token_to_string;
8-
use rustc_errors::{PErr, PResult};
8+
use rustc_errors::PErr;
99

1010
pub(super) struct TokenTreesReader<'a> {
1111
string_reader: StringReader<'a>,
@@ -18,36 +18,42 @@ pub(super) struct TokenTreesReader<'a> {
1818
impl<'a> TokenTreesReader<'a> {
1919
pub(super) fn parse_all_token_trees(
2020
string_reader: StringReader<'a>,
21-
) -> (PResult<'a, TokenStream>, Vec<UnmatchedDelim>) {
21+
) -> (TokenStream, Result<(), Vec<PErr<'a>>>, Vec<UnmatchedDelim>) {
2222
let mut tt_reader = TokenTreesReader {
2323
string_reader,
2424
token: Token::dummy(),
2525
diag_info: TokenTreeDiagInfo::default(),
2626
};
27-
let res = tt_reader.parse_token_trees(/* is_delimited */ false);
28-
(res, tt_reader.diag_info.unmatched_delims)
27+
let (stream, res) = tt_reader.parse_token_trees(/* is_delimited */ false);
28+
(stream, res, tt_reader.diag_info.unmatched_delims)
2929
}
3030

3131
// Parse a stream of tokens into a list of `TokenTree`s.
32-
fn parse_token_trees(&mut self, is_delimited: bool) -> PResult<'a, TokenStream> {
32+
fn parse_token_trees(
33+
&mut self,
34+
is_delimited: bool,
35+
) -> (TokenStream, Result<(), Vec<PErr<'a>>>) {
3336
self.token = self.string_reader.next_token().0;
3437
let mut buf = Vec::new();
3538
loop {
3639
match self.token.kind {
37-
token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)?),
40+
token::OpenDelim(delim) => {
41+
buf.push(match self.parse_token_tree_open_delim(delim) {
42+
Ok(val) => val,
43+
Err(errs) => return (TokenStream::new(buf), Err(errs)),
44+
})
45+
}
3846
token::CloseDelim(delim) => {
39-
return if is_delimited {
40-
Ok(TokenStream::new(buf))
41-
} else {
42-
Err(self.close_delim_err(delim))
43-
};
47+
return (
48+
TokenStream::new(buf),
49+
if is_delimited { Ok(()) } else { Err(vec![self.close_delim_err(delim)]) },
50+
);
4451
}
4552
token::Eof => {
46-
return if is_delimited {
47-
Err(self.eof_err())
48-
} else {
49-
Ok(TokenStream::new(buf))
50-
};
53+
return (
54+
TokenStream::new(buf),
55+
if is_delimited { Err(vec![self.eof_err()]) } else { Ok(()) },
56+
);
5157
}
5258
_ => {
5359
// Get the next normal token. This might require getting multiple adjacent
@@ -97,7 +103,10 @@ impl<'a> TokenTreesReader<'a> {
97103
err
98104
}
99105

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

@@ -106,7 +115,26 @@ impl<'a> TokenTreesReader<'a> {
106115
// Parse the token trees within the delimiters.
107116
// We stop at any delimiter so we can try to recover if the user
108117
// uses an incorrect delimiter.
109-
let tts = self.parse_token_trees(/* is_delimited */ true)?;
118+
let (tts, res) = self.parse_token_trees(/* is_delimited */ true);
119+
if let Err(mut errs) = res {
120+
// If there are unclosed delims, see if there are diff markers and if so, point them
121+
// out instead of complaining about the unclosed delims.
122+
let mut parser = crate::stream_to_parser(self.string_reader.sess, tts, None);
123+
let mut diff_errs = vec![];
124+
while parser.token != token::Eof {
125+
if let Err(diff_err) = parser.err_diff_marker() {
126+
diff_errs.push(diff_err);
127+
}
128+
parser.bump();
129+
}
130+
if !diff_errs.is_empty() {
131+
errs.iter_mut().for_each(|err| {
132+
err.delay_as_bug();
133+
});
134+
return Err(diff_errs);
135+
}
136+
return Err(errs);
137+
}
110138

111139
// Expand to cover the entire delimited token tree
112140
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);

Diff for: compiler/rustc_parse/src/parser/diagnostics.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -2808,8 +2808,15 @@ impl<'a> Parser<'a> {
28082808
}
28092809

28102810
pub fn recover_diff_marker(&mut self) {
2811+
if let Err(mut err) = self.err_diff_marker() {
2812+
err.emit();
2813+
FatalError.raise();
2814+
}
2815+
}
2816+
2817+
pub fn err_diff_marker(&mut self) -> PResult<'a, ()> {
28112818
let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else {
2812-
return;
2819+
return Ok(());
28132820
};
28142821
let mut spans = Vec::with_capacity(3);
28152822
spans.push(start);
@@ -2856,8 +2863,7 @@ impl<'a> Parser<'a> {
28562863
"for an explanation on these markers from the `git` documentation, visit \
28572864
<https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
28582865
);
2859-
err.emit();
2860-
FatalError.raise()
2866+
Err(err)
28612867
}
28622868

28632869
/// Parse and throw away a parenthesized comma separated
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
macro_rules! foo {
2+
<<<<<<< HEAD
3+
//~^ ERROR encountered diff marker
4+
() {
5+
=======
6+
() { //
7+
>>>>>>> 7a4f13c blah blah blah
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: encountered diff marker
2+
--> $DIR/unclosed-delims-in-macro.rs:2:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
...
7+
LL | =======
8+
| -------
9+
LL | () { //
10+
LL | >>>>>>> 7a4f13c blah blah blah
11+
| ^^^^^^^ above this are the incoming code changes
12+
|
13+
= 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
14+
= 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
15+
= 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>
16+
17+
error: aborting due to previous error
18+

Diff for: tests/ui/parser/diff-markers/unclosed-delims.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
mod tests {
2+
#[test]
3+
<<<<<<< HEAD
4+
//~^ ERROR encountered diff marker
5+
//~| NOTE after this is the code before the merge
6+
//~| NOTE for an explanation on these markers
7+
fn test1() {
8+
=======
9+
//~^ NOTE
10+
fn test2() {
11+
>>>>>>> 7a4f13c blah blah blah
12+
//~^ NOTE above this are the incoming code changes
13+
}
14+
}

Diff for: tests/ui/parser/diff-markers/unclosed-delims.stderr

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: encountered diff marker
2+
--> $DIR/unclosed-delims.rs:3:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
...
7+
LL | =======
8+
| -------
9+
...
10+
LL | >>>>>>> 7a4f13c blah blah blah
11+
| ^^^^^^^ above this are the incoming code changes
12+
|
13+
= 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
14+
= 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
15+
= 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>
16+
17+
error: aborting due to previous error
18+

0 commit comments

Comments
 (0)