Skip to content

Commit 031a214

Browse files
authored
Rollup merge of rust-lang#106242 - estebank:diff-markers, r=jyn514
Detect diff markers in the parser Partly address rust-lang#32059.
2 parents 98f2409 + 62c8e31 commit 031a214

26 files changed

+438
-5
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+71-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ use rustc_ast::{
3131
use rustc_ast_pretty::pprust;
3232
use rustc_data_structures::fx::FxHashSet;
3333
use rustc_errors::{
34-
fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
34+
fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, FatalError, Handler, MultiSpan,
35+
PResult,
3536
};
3637
use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic};
3738
use rustc_session::errors::ExprParenthesesNeeded;
@@ -2556,6 +2557,75 @@ impl<'a> Parser<'a> {
25562557
Ok(())
25572558
}
25582559

2560+
pub fn is_diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> bool {
2561+
(0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
2562+
&& self.look_ahead(3, |tok| tok == short_kind)
2563+
}
2564+
2565+
fn diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option<Span> {
2566+
if self.is_diff_marker(long_kind, short_kind) {
2567+
let lo = self.token.span;
2568+
for _ in 0..4 {
2569+
self.bump();
2570+
}
2571+
return Some(lo.to(self.prev_token.span));
2572+
}
2573+
None
2574+
}
2575+
2576+
pub fn recover_diff_marker(&mut self) {
2577+
let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else {
2578+
return;
2579+
};
2580+
let mut spans = Vec::with_capacity(3);
2581+
spans.push(start);
2582+
let mut middlediff3 = None;
2583+
let mut middle = None;
2584+
let mut end = None;
2585+
loop {
2586+
if self.token.kind == TokenKind::Eof {
2587+
break;
2588+
}
2589+
if let Some(span) = self.diff_marker(&TokenKind::OrOr, &TokenKind::BinOp(token::Or)) {
2590+
middlediff3 = Some(span);
2591+
}
2592+
if let Some(span) = self.diff_marker(&TokenKind::EqEq, &TokenKind::Eq) {
2593+
middle = Some(span);
2594+
}
2595+
if let Some(span) = self.diff_marker(&TokenKind::BinOp(token::Shr), &TokenKind::Gt) {
2596+
spans.push(span);
2597+
end = Some(span);
2598+
break;
2599+
}
2600+
self.bump();
2601+
}
2602+
let mut err = self.struct_span_err(spans, "encountered diff marker");
2603+
err.span_label(start, "after this is the code before the merge");
2604+
if let Some(middle) = middlediff3 {
2605+
err.span_label(middle, "");
2606+
}
2607+
if let Some(middle) = middle {
2608+
err.span_label(middle, "");
2609+
}
2610+
if let Some(end) = end {
2611+
err.span_label(end, "above this are the incoming code changes");
2612+
}
2613+
err.help(
2614+
"if you're having merge conflicts after pulling new code, the top section is the code \
2615+
you already had and the bottom section is the remote code",
2616+
);
2617+
err.help(
2618+
"if you're in the middle of a rebase, the top section is the code being rebased onto \
2619+
and the bottom section is the code coming from the current commit being rebased",
2620+
);
2621+
err.note(
2622+
"for an explanation on these markers from the `git` documentation, visit \
2623+
<https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
2624+
);
2625+
err.emit();
2626+
FatalError.raise()
2627+
}
2628+
25592629
/// Parse and throw away a parenthesized comma separated
25602630
/// sequence of patterns until `)` is reached.
25612631
fn skip_pat_list(&mut self) -> PResult<'a, ()> {

compiler/rustc_parse/src/parser/expr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3039,6 +3039,7 @@ impl<'a> Parser<'a> {
30393039
/// Parses `ident (COLON expr)?`.
30403040
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
30413041
let attrs = self.parse_outer_attributes()?;
3042+
self.recover_diff_marker();
30423043
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
30433044
let lo = this.token.span;
30443045

compiler/rustc_parse/src/parser/item.rs

+38-4
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ impl<'a> Parser<'a> {
9898
fn_parse_mode: FnParseMode,
9999
force_collect: ForceCollect,
100100
) -> PResult<'a, Option<Item>> {
101+
self.recover_diff_marker();
101102
let attrs = self.parse_outer_attributes()?;
103+
self.recover_diff_marker();
102104
self.parse_item_common(attrs, true, false, fn_parse_mode, force_collect)
103105
}
104106

@@ -704,6 +706,7 @@ impl<'a> Parser<'a> {
704706
if self.recover_doc_comment_before_brace() {
705707
continue;
706708
}
709+
self.recover_diff_marker();
707710
match parse_item(self) {
708711
Ok(None) => {
709712
let mut is_unnecessary_semicolon = !items.is_empty()
@@ -1039,8 +1042,11 @@ impl<'a> Parser<'a> {
10391042
/// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`]
10401043
/// ```
10411044
fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> {
1042-
self.parse_delim_comma_seq(Delimiter::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
1043-
.map(|(r, _)| r)
1045+
self.parse_delim_comma_seq(Delimiter::Brace, |p| {
1046+
p.recover_diff_marker();
1047+
Ok((p.parse_use_tree()?, DUMMY_NODE_ID))
1048+
})
1049+
.map(|(r, _)| r)
10441050
}
10451051

10461052
fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
@@ -1379,7 +1385,9 @@ impl<'a> Parser<'a> {
13791385
}
13801386

13811387
fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
1388+
self.recover_diff_marker();
13821389
let variant_attrs = self.parse_outer_attributes()?;
1390+
self.recover_diff_marker();
13831391
self.collect_tokens_trailing_token(
13841392
variant_attrs,
13851393
ForceCollect::No,
@@ -1573,9 +1581,32 @@ impl<'a> Parser<'a> {
15731581
self.parse_paren_comma_seq(|p| {
15741582
let attrs = p.parse_outer_attributes()?;
15751583
p.collect_tokens_trailing_token(attrs, ForceCollect::No, |p, attrs| {
1584+
let mut snapshot = None;
1585+
if p.is_diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
1586+
// Account for `<<<<<<<` diff markers. We can't proactively error here because
1587+
// that can be a valid type start, so we snapshot and reparse only we've
1588+
// encountered another parse error.
1589+
snapshot = Some(p.create_snapshot_for_diagnostic());
1590+
}
15761591
let lo = p.token.span;
1577-
let vis = p.parse_visibility(FollowedByType::Yes)?;
1578-
let ty = p.parse_ty()?;
1592+
let vis = match p.parse_visibility(FollowedByType::Yes) {
1593+
Ok(vis) => vis,
1594+
Err(err) => {
1595+
if let Some(ref mut snapshot) = snapshot {
1596+
snapshot.recover_diff_marker();
1597+
}
1598+
return Err(err);
1599+
}
1600+
};
1601+
let ty = match p.parse_ty() {
1602+
Ok(ty) => ty,
1603+
Err(err) => {
1604+
if let Some(ref mut snapshot) = snapshot {
1605+
snapshot.recover_diff_marker();
1606+
}
1607+
return Err(err);
1608+
}
1609+
};
15791610

15801611
Ok((
15811612
FieldDef {
@@ -1596,7 +1627,9 @@ impl<'a> Parser<'a> {
15961627

15971628
/// Parses an element of a struct declaration.
15981629
fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
1630+
self.recover_diff_marker();
15991631
let attrs = self.parse_outer_attributes()?;
1632+
self.recover_diff_marker();
16001633
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
16011634
let lo = this.token.span;
16021635
let vis = this.parse_visibility(FollowedByType::No)?;
@@ -2427,6 +2460,7 @@ impl<'a> Parser<'a> {
24272460
let mut first_param = true;
24282461
// Parse the arguments, starting out with `self` being allowed...
24292462
let (mut params, _) = self.parse_paren_comma_seq(|p| {
2463+
p.recover_diff_marker();
24302464
let param = p.parse_param_general(req_name, first_param).or_else(|mut e| {
24312465
e.emit();
24322466
let lo = p.prev_token.span;

compiler/rustc_parse/src/parser/stmt.rs

+10
Original file line numberDiff line numberDiff line change
@@ -531,13 +531,23 @@ impl<'a> Parser<'a> {
531531
recover: AttemptLocalParseRecovery,
532532
) -> PResult<'a, P<Block>> {
533533
let mut stmts = vec![];
534+
let mut snapshot = None;
534535
while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
535536
if self.token == token::Eof {
536537
break;
537538
}
539+
if self.is_diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
540+
// Account for `<<<<<<<` diff markers. We can't proactively error here because
541+
// that can be a valid path start, so we snapshot and reparse only we've
542+
// encountered another parse error.
543+
snapshot = Some(self.create_snapshot_for_diagnostic());
544+
}
538545
let stmt = match self.parse_full_stmt(recover) {
539546
Err(mut err) if recover.yes() => {
540547
self.maybe_annotate_with_ascription(&mut err, false);
548+
if let Some(ref mut snapshot) = snapshot {
549+
snapshot.recover_diff_marker();
550+
}
541551
err.emit();
542552
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
543553
Some(self.mk_stmt_err(self.token.span))
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enum E {
2+
Foo {
3+
<<<<<<< HEAD //~ ERROR encountered diff marker
4+
x: u8,
5+
|||||||
6+
z: (),
7+
=======
8+
y: i8,
9+
>>>>>>> branch
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error: encountered diff marker
2+
--> $DIR/enum-2.rs:3:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
LL | x: u8,
7+
LL | |||||||
8+
| -------
9+
LL | z: (),
10+
LL | =======
11+
| -------
12+
LL | y: i8,
13+
LL | >>>>>>> branch
14+
| ^^^^^^^ above this are the incoming code changes
15+
|
16+
= 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
17+
= 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
18+
= 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>
19+
20+
error: aborting due to previous error
21+
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
enum E {
2+
<<<<<<< HEAD //~ ERROR encountered diff marker
3+
Foo(u8),
4+
=======
5+
Bar(i8),
6+
>>>>>>> branch
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: encountered diff marker
2+
--> $DIR/enum.rs:2:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
LL | Foo(u8),
7+
LL | =======
8+
| -------
9+
LL | Bar(i8),
10+
LL | >>>>>>> branch
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+
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trait T {
2+
fn foo(
3+
<<<<<<< HEAD //~ ERROR encountered diff marker
4+
x: u8,
5+
=======
6+
x: i8,
7+
>>>>>>> branch
8+
) {}
9+
}
10+
11+
struct S;
12+
impl T for S {}
13+
14+
fn main() {
15+
S::foo(42);
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: encountered diff marker
2+
--> $DIR/fn-arg.rs:3:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
LL | x: u8,
7+
LL | =======
8+
| -------
9+
LL | x: i8,
10+
LL | >>>>>>> branch
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+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#[attribute]
2+
<<<<<<< HEAD //~ ERROR encountered diff marker
3+
fn foo() {}
4+
=======
5+
fn bar() {}
6+
>>>>>>> branch
7+
8+
fn main() {
9+
foo();
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: encountered diff marker
2+
--> $DIR/item-with-attr.rs:2:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
LL | fn foo() {}
7+
LL | =======
8+
| -------
9+
LL | fn bar() {}
10+
LL | >>>>>>> branch
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+
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<<<<<<< HEAD //~ ERROR encountered diff marker
2+
fn foo() {}
3+
=======
4+
fn bar() {}
5+
>>>>>>> branch
6+
7+
fn main() {
8+
foo();
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: encountered diff marker
2+
--> $DIR/item.rs:1:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ after this is the code before the merge
6+
LL | fn foo() {}
7+
LL | =======
8+
| -------
9+
LL | fn bar() {}
10+
LL | >>>>>>> branch
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+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
trait T {
2+
fn foo() {}
3+
fn bar() {}
4+
}
5+
6+
struct S;
7+
impl T for S {}
8+
9+
fn main() {
10+
<<<<<<< HEAD //~ ERROR encountered diff marker
11+
S::foo();
12+
=======
13+
S::bar();
14+
>>>>>>> branch
15+
}

0 commit comments

Comments
 (0)