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

Detect diff markers in the parser #106242

Merged
merged 4 commits into from
Dec 29, 2022
Merged
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
72 changes: 71 additions & 1 deletion compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -31,7 +31,8 @@ use rustc_ast::{
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, FatalError, Handler, MultiSpan,
PResult,
};
use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic};
use rustc_session::errors::ExprParenthesesNeeded;
@@ -2556,6 +2557,75 @@ impl<'a> Parser<'a> {
Ok(())
}

pub fn is_diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> bool {
(0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
&& self.look_ahead(3, |tok| tok == short_kind)
}

fn diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option<Span> {
if self.is_diff_marker(long_kind, short_kind) {
let lo = self.token.span;
for _ in 0..4 {
self.bump();
}
return Some(lo.to(self.prev_token.span));
}
None
}

pub fn recover_diff_marker(&mut self) {
let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else {
return;
};
let mut spans = Vec::with_capacity(3);
spans.push(start);
let mut middlediff3 = None;
let mut middle = None;
let mut end = None;
loop {
if self.token.kind == TokenKind::Eof {
break;
}
if let Some(span) = self.diff_marker(&TokenKind::OrOr, &TokenKind::BinOp(token::Or)) {
middlediff3 = Some(span);
}
if let Some(span) = self.diff_marker(&TokenKind::EqEq, &TokenKind::Eq) {
middle = Some(span);
}
if let Some(span) = self.diff_marker(&TokenKind::BinOp(token::Shr), &TokenKind::Gt) {
spans.push(span);
end = Some(span);
break;
}
self.bump();
}
let mut err = self.struct_span_err(spans, "encountered diff marker");
err.span_label(start, "after this is the code before the merge");
if let Some(middle) = middlediff3 {
err.span_label(middle, "");
}
if let Some(middle) = middle {
err.span_label(middle, "");
}
if let Some(end) = end {
err.span_label(end, "above this are the incoming code changes");
}
err.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",
);
err.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",
);
err.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>",
);
err.emit();
FatalError.raise()
}

/// Parse and throw away a parenthesized comma separated
/// sequence of patterns until `)` is reached.
fn skip_pat_list(&mut self) -> PResult<'a, ()> {
1 change: 1 addition & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
@@ -3039,6 +3039,7 @@ impl<'a> Parser<'a> {
/// Parses `ident (COLON expr)?`.
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
let attrs = self.parse_outer_attributes()?;
self.recover_diff_marker();
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span;

42 changes: 38 additions & 4 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
@@ -98,7 +98,9 @@ impl<'a> Parser<'a> {
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Item>> {
self.recover_diff_marker();
let attrs = self.parse_outer_attributes()?;
self.recover_diff_marker();
self.parse_item_common(attrs, true, false, fn_parse_mode, force_collect)
}

@@ -704,6 +706,7 @@ impl<'a> Parser<'a> {
if self.recover_doc_comment_before_brace() {
continue;
}
self.recover_diff_marker();
match parse_item(self) {
Ok(None) => {
let mut is_unnecessary_semicolon = !items.is_empty()
@@ -1039,8 +1042,11 @@ impl<'a> Parser<'a> {
/// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`]
/// ```
fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> {
self.parse_delim_comma_seq(Delimiter::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
.map(|(r, _)| r)
self.parse_delim_comma_seq(Delimiter::Brace, |p| {
p.recover_diff_marker();
Ok((p.parse_use_tree()?, DUMMY_NODE_ID))
})
.map(|(r, _)| r)
}

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

fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
self.recover_diff_marker();
let variant_attrs = self.parse_outer_attributes()?;
self.recover_diff_marker();
self.collect_tokens_trailing_token(
variant_attrs,
ForceCollect::No,
@@ -1573,9 +1581,32 @@ impl<'a> Parser<'a> {
self.parse_paren_comma_seq(|p| {
let attrs = p.parse_outer_attributes()?;
p.collect_tokens_trailing_token(attrs, ForceCollect::No, |p, attrs| {
let mut snapshot = None;
if p.is_diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
// Account for `<<<<<<<` diff markers. We can't proactively error here because
// that can be a valid type start, so we snapshot and reparse only we've
// encountered another parse error.
snapshot = Some(p.create_snapshot_for_diagnostic());
}
let lo = p.token.span;
let vis = p.parse_visibility(FollowedByType::Yes)?;
let ty = p.parse_ty()?;
let vis = match p.parse_visibility(FollowedByType::Yes) {
Ok(vis) => vis,
Err(err) => {
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_diff_marker();
}
return Err(err);
}
};
let ty = match p.parse_ty() {
Ok(ty) => ty,
Err(err) => {
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_diff_marker();
}
return Err(err);
}
};

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

/// Parses an element of a struct declaration.
fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
self.recover_diff_marker();
let attrs = self.parse_outer_attributes()?;
self.recover_diff_marker();
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span;
let vis = this.parse_visibility(FollowedByType::No)?;
@@ -2427,6 +2460,7 @@ impl<'a> Parser<'a> {
let mut first_param = true;
// Parse the arguments, starting out with `self` being allowed...
let (mut params, _) = self.parse_paren_comma_seq(|p| {
p.recover_diff_marker();
let param = p.parse_param_general(req_name, first_param).or_else(|mut e| {
e.emit();
let lo = p.prev_token.span;
10 changes: 10 additions & 0 deletions compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
@@ -531,13 +531,23 @@ impl<'a> Parser<'a> {
recover: AttemptLocalParseRecovery,
) -> PResult<'a, P<Block>> {
let mut stmts = vec![];
let mut snapshot = None;
while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
if self.token == token::Eof {
break;
}
if self.is_diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
// Account for `<<<<<<<` diff markers. We can't proactively error here because
// that can be a valid path start, so we snapshot and reparse only we've
// encountered another parse error.
snapshot = Some(self.create_snapshot_for_diagnostic());
}
let stmt = match self.parse_full_stmt(recover) {
Err(mut err) if recover.yes() => {
self.maybe_annotate_with_ascription(&mut err, false);
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_diff_marker();
}
err.emit();
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
Some(self.mk_stmt_err(self.token.span))
11 changes: 11 additions & 0 deletions src/test/ui/parser/diff-markers/enum-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
enum E {
Foo {
<<<<<<< HEAD //~ ERROR encountered diff marker
x: u8,
|||||||
z: (),
=======
y: i8,
>>>>>>> branch
}
}
21 changes: 21 additions & 0 deletions src/test/ui/parser/diff-markers/enum-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error: encountered diff marker
--> $DIR/enum-2.rs:3:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | x: u8,
LL | |||||||
| -------
LL | z: (),
LL | =======
| -------
LL | y: i8,
LL | >>>>>>> branch
| ^^^^^^^ 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

7 changes: 7 additions & 0 deletions src/test/ui/parser/diff-markers/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
enum E {
<<<<<<< HEAD //~ ERROR encountered diff marker
Foo(u8),
=======
Bar(i8),
>>>>>>> branch
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/enum.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/enum.rs:2:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | Foo(u8),
LL | =======
| -------
LL | Bar(i8),
LL | >>>>>>> branch
| ^^^^^^^ 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

16 changes: 16 additions & 0 deletions src/test/ui/parser/diff-markers/fn-arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
trait T {
fn foo(
<<<<<<< HEAD //~ ERROR encountered diff marker
x: u8,
=======
x: i8,
>>>>>>> branch
) {}
}

struct S;
impl T for S {}

fn main() {
S::foo(42);
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/fn-arg.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/fn-arg.rs:3:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | x: u8,
LL | =======
| -------
LL | x: i8,
LL | >>>>>>> branch
| ^^^^^^^ 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

10 changes: 10 additions & 0 deletions src/test/ui/parser/diff-markers/item-with-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[attribute]
<<<<<<< HEAD //~ ERROR encountered diff marker
fn foo() {}
=======
fn bar() {}
>>>>>>> branch

fn main() {
foo();
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/item-with-attr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/item-with-attr.rs:2:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | fn foo() {}
LL | =======
| -------
LL | fn bar() {}
LL | >>>>>>> branch
| ^^^^^^^ 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

9 changes: 9 additions & 0 deletions src/test/ui/parser/diff-markers/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<<<<<<< HEAD //~ ERROR encountered diff marker
fn foo() {}
=======
fn bar() {}
>>>>>>> branch

fn main() {
foo();
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/item.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/item.rs:1:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | fn foo() {}
LL | =======
| -------
LL | fn bar() {}
LL | >>>>>>> branch
| ^^^^^^^ 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

15 changes: 15 additions & 0 deletions src/test/ui/parser/diff-markers/statement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait T {
fn foo() {}
fn bar() {}
}

struct S;
impl T for S {}

fn main() {
<<<<<<< HEAD //~ ERROR encountered diff marker
S::foo();
=======
S::bar();
>>>>>>> branch
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/statement.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/statement.rs:10:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | S::foo();
LL | =======
| -------
LL | S::bar();
LL | >>>>>>> branch
| ^^^^^^^ 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

12 changes: 12 additions & 0 deletions src/test/ui/parser/diff-markers/struct-expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
struct S {
x: u8,
}
fn main() {
let _ = S {
<<<<<<< HEAD //~ ERROR encountered diff marker
x: 42,
=======
x: 0,
>>>>>>> branch
}
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/struct-expr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/struct-expr.rs:6:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | x: 42,
LL | =======
| -------
LL | x: 0,
LL | >>>>>>> branch
| ^^^^^^^ 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

7 changes: 7 additions & 0 deletions src/test/ui/parser/diff-markers/struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
struct S {
<<<<<<< HEAD //~ ERROR encountered diff marker
x: u8,
=======
x: i8,
>>>>>>> branch
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/struct.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/struct.rs:2:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | x: u8,
LL | =======
| -------
LL | x: i8,
LL | >>>>>>> branch
| ^^^^^^^ 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 src/test/ui/parser/diff-markers/trait-item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
trait T {
<<<<<<< HEAD //~ ERROR encountered diff marker
fn foo() {}
=======
fn bar() {}
>>>>>>> branch
}

struct S;
impl T for S {}

fn main() {
S::foo();
}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/trait-item.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/trait-item.rs:2:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | fn foo() {}
LL | =======
| -------
LL | fn bar() {}
LL | >>>>>>> branch
| ^^^^^^^ 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

7 changes: 7 additions & 0 deletions src/test/ui/parser/diff-markers/tuple-struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
struct S(
<<<<<<< HEAD //~ ERROR encountered diff marker
u8,
=======
i8,
>>>>>>> branch
);
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/tuple-struct.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/tuple-struct.rs:2:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | u8,
LL | =======
| -------
LL | i8,
LL | >>>>>>> branch
| ^^^^^^^ 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

9 changes: 9 additions & 0 deletions src/test/ui/parser/diff-markers/use-statement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use foo::{
<<<<<<< HEAD //~ ERROR encountered diff marker
bar,
=======
baz,
>>>>>>> branch
};

fn main() {}
18 changes: 18 additions & 0 deletions src/test/ui/parser/diff-markers/use-statement.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: encountered diff marker
--> $DIR/use-statement.rs:2:1
|
LL | <<<<<<< HEAD
| ^^^^^^^ after this is the code before the merge
LL | bar,
LL | =======
| -------
LL | baz,
LL | >>>>>>> branch
| ^^^^^^^ 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