Skip to content

Commit 375f025

Browse files
committed
Detect diff markers in the parser
Partly address #32059.
1 parent 92c1937 commit 375f025

15 files changed

+229
-3
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+53-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,57 @@ 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 middle = None;
2583+
let mut end = None;
2584+
loop {
2585+
if self.token.kind == TokenKind::Eof {
2586+
break;
2587+
}
2588+
if let Some(span) = self.diff_marker(&TokenKind::EqEq, &TokenKind::Eq) {
2589+
spans.push(span);
2590+
middle = Some(span);
2591+
}
2592+
if let Some(span) = self.diff_marker(&TokenKind::BinOp(token::Shr), &TokenKind::Gt) {
2593+
spans.push(span);
2594+
end = Some(span);
2595+
break;
2596+
}
2597+
self.bump();
2598+
}
2599+
let mut err = self.struct_span_err(spans, "encountered diff marker");
2600+
err.span_label(start, "start");
2601+
if let Some(middle) = middle {
2602+
err.span_label(middle, "middle");
2603+
}
2604+
if let Some(end) = end {
2605+
err.span_label(end, "end");
2606+
}
2607+
err.emit();
2608+
FatalError.raise()
2609+
}
2610+
25592611
/// Parse and throw away a parenthesized comma separated
25602612
/// sequence of patterns until `)` is reached.
25612613
fn skip_pat_list(&mut self) -> PResult<'a, ()> {

compiler/rustc_parse/src/parser/item.rs

+9-2
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>> {
@@ -2427,6 +2433,7 @@ impl<'a> Parser<'a> {
24272433
let mut first_param = true;
24282434
// Parse the arguments, starting out with `self` being allowed...
24292435
let (mut params, _) = self.parse_paren_comma_seq(|p| {
2436+
p.recover_diff_marker();
24302437
let param = p.parse_param_general(req_name, first_param).or_else(|mut e| {
24312438
e.emit();
24322439
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 proactivelly 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))
+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,14 @@
1+
error: encountered diff marker
2+
--> $DIR/fn-arg.rs:3:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ start
6+
LL | x: u8,
7+
LL | =======
8+
| ^^^^^^^ middle
9+
LL | x: i8,
10+
LL | >>>>>>> branch
11+
| ^^^^^^^ end
12+
13+
error: aborting due to previous error
14+
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,14 @@
1+
error: encountered diff marker
2+
--> $DIR/item-with-attr.rs:2:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ start
6+
LL | fn foo() {}
7+
LL | =======
8+
| ^^^^^^^ middle
9+
LL | fn bar() {}
10+
LL | >>>>>>> branch
11+
| ^^^^^^^ end
12+
13+
error: aborting due to previous error
14+
+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,14 @@
1+
error: encountered diff marker
2+
--> $DIR/item.rs:1:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ start
6+
LL | fn foo() {}
7+
LL | =======
8+
| ^^^^^^^ middle
9+
LL | fn bar() {}
10+
LL | >>>>>>> branch
11+
| ^^^^^^^ end
12+
13+
error: aborting due to previous error
14+
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: encountered diff marker
2+
--> $DIR/statement.rs:10:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ start
6+
LL | S::foo();
7+
LL | =======
8+
| ^^^^^^^ middle
9+
LL | S::bar();
10+
LL | >>>>>>> branch
11+
| ^^^^^^^ end
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait T {
2+
<<<<<<< HEAD //~ ERROR encountered diff marker
3+
fn foo() {}
4+
=======
5+
fn bar() {}
6+
>>>>>>> branch
7+
}
8+
9+
struct S;
10+
impl T for S {}
11+
12+
fn main() {
13+
S::foo();
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: encountered diff marker
2+
--> $DIR/trait-item.rs:2:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ start
6+
LL | fn foo() {}
7+
LL | =======
8+
| ^^^^^^^ middle
9+
LL | fn bar() {}
10+
LL | >>>>>>> branch
11+
| ^^^^^^^ end
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use foo::{
2+
<<<<<<< HEAD //~ ERROR encountered diff marker
3+
bar,
4+
=======
5+
baz,
6+
>>>>>>> branch
7+
};
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: encountered diff marker
2+
--> $DIR/use-statement.rs:2:1
3+
|
4+
LL | <<<<<<< HEAD
5+
| ^^^^^^^ start
6+
LL | bar,
7+
LL | =======
8+
| ^^^^^^^ middle
9+
LL | baz,
10+
LL | >>>>>>> branch
11+
| ^^^^^^^ end
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)