From ebd4bc9224a5de37ce151745b00cb8039fef4385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 27 Dec 2025 20:56:37 +0000 Subject: [PATCH] Support long diff conflict markers git can be configured to use more than 7 characters for conflict markers, and jj automatically uses longer conflict markers when the text contains any char sequence that could be confused with conflict markers. Ensure that we only point at markers that are consistent with the start marker's length. Ensure that we only consider char sequences at the beginning of a line as a diff marker. --- .../rustc_parse/src/parser/diagnostics.rs | 52 +++++++++++++++---- .../diff-markers/long-conflict-markers.rs | 11 ++++ .../diff-markers/long-conflict-markers.stderr | 20 +++++++ 3 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 tests/ui/parser/diff-markers/long-conflict-markers.rs create mode 100644 tests/ui/parser/diff-markers/long-conflict-markers.stderr diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 70ec80a508126..9c4895e391d23 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -3037,27 +3037,53 @@ impl<'a> Parser<'a> { 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) + if long_kind == short_kind { + // For conflict marker chars like `%` and `\`. + (0..7).all(|i| self.look_ahead(i, |tok| tok == long_kind)) + } else { + // For conflict marker chars like `<` and `|`. + (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind)) + && self.look_ahead(3, |tok| tok == short_kind || tok == long_kind) + } } - fn conflict_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option { + fn conflict_marker( + &mut self, + long_kind: &TokenKind, + short_kind: &TokenKind, + expected: Option, + ) -> Option<(Span, usize)> { if self.is_vcs_conflict_marker(long_kind, short_kind) { let lo = self.token.span; - for _ in 0..4 { + if self.psess.source_map().span_to_margin(lo) != Some(0) { + return None; + } + let mut len = 0; + while self.token.kind == *long_kind || self.token.kind == *short_kind { + if self.token.kind.break_two_token_op(1).is_some() { + len += 2; + } else { + len += 1; + } self.bump(); + if expected == Some(len) { + break; + } + } + if expected.is_some() && expected != Some(len) { + return None; } - return Some(lo.to(self.prev_token.span)); + return Some((lo.to(self.prev_token.span), len)); } None } pub(super) fn recover_vcs_conflict_marker(&mut self) { // <<<<<<< - let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else { + let Some((start, len)) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt, None) else { return; }; - let mut spans = Vec::with_capacity(3); + let mut spans = Vec::with_capacity(2); spans.push(start); // ||||||| let mut middlediff3 = None; @@ -3069,13 +3095,19 @@ impl<'a> Parser<'a> { if self.token == TokenKind::Eof { break; } - if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or, Some(len)) + { middlediff3 = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq, Some(len)) + { middle = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt, Some(len)) + { spans.push(span); end = Some(span); break; diff --git a/tests/ui/parser/diff-markers/long-conflict-markers.rs b/tests/ui/parser/diff-markers/long-conflict-markers.rs new file mode 100644 index 0000000000000..f92615fb75b47 --- /dev/null +++ b/tests/ui/parser/diff-markers/long-conflict-markers.rs @@ -0,0 +1,11 @@ +enum E { + Foo { +<<<<<<<<< HEAD //~ ERROR encountered diff marker + x: u8, +||||||| + z: (), +========= + y: i8, +>>>>>>>>> branch + } +} diff --git a/tests/ui/parser/diff-markers/long-conflict-markers.stderr b/tests/ui/parser/diff-markers/long-conflict-markers.stderr new file mode 100644 index 0000000000000..c83aaf16b7418 --- /dev/null +++ b/tests/ui/parser/diff-markers/long-conflict-markers.stderr @@ -0,0 +1,20 @@ +error: encountered diff marker + --> $DIR/long-conflict-markers.rs:3:1 + | +LL | <<<<<<<<< HEAD + | ^^^^^^^^^ between this marker and `=======` is the code that you are merging into +... +LL | ========= + | --------- between this marker and `>>>>>>>` is the incoming code +LL | y: i8, +LL | >>>>>>>>> branch + | ^^^^^^^^^ this marker concludes the conflict region + | + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you are in a merge, the top section is the code you already had checked out and the bottom section is the new code + if you are in a rebase, the top section is the code being rebased onto and the bottom section is the code you had checked out which is being rebased + = note: for an explanation on these markers from the `git` documentation, visit + +error: aborting due to 1 previous error +