diff mbox

[stage1] Better error recovery for merge-conflict markers

Message ID 1426866606-65042-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm March 20, 2015, 3:50 p.m. UTC
Various tools that operate on source code files will inject markers
into them when an unfixable conflict occurs in a merger.

There appears to be no blessed standard for these conflict markers,
but an ad-hoc convention is for 7 '<' , '=', or '>' characters at
the start of a line, followed optionally by a space and optional
text

e.g.:
<<<<<<< HEAD
extern int some_var;

Comments

Joseph Myers March 20, 2015, 5:50 p.m. UTC | #1
On Fri, 20 Mar 2015, David Malcolm wrote:

> I believe that the presense of these markers in source code is almost
> always a bug (are there any GCC frontends in which the markers are
> parsable as something valid?)

Well, obviously they are valid inside #if 0, strings (where you have a 
test, though not one at start of line "\
<<<<<<<") and comments (where you don't have a test).  They are also valid 
when stringized:

#define str(s) #s
const char *s = str(
<<<<<<<
);

must be accepted.  They are also valid in the expansion of a macro that 
doesn't get expanded.

#define foo \
<<<<<<<

That is, in general, the invalidity only occurs when preprocessing tokens 
are converted to tokens.

In C++ (C++11 and later), >>>>>>> can also close a sequence of nested 
template argument lists, thanks to the rule about replacing >> by > > in 
that context.  And of course it's OK, if odd, to put that at the start of 
a line.  So in that case the preprocessing tokens do get converted to 
tokens, and that token sequence (interpreted as >> >> >> > and then 
contextually adjusted to > > > > > > >) is valid.
Bert Wesarg April 9, 2015, 8:29 a.m. UTC | #2
Hi David,

> Various tools that operate on source code files will inject markers
> into them when an unfixable conflict occurs in a merger.
>
> There appears to be no blessed standard for these conflict markers,
> but an ad-hoc convention is for 7 '<' , '=', or '>' characters at
> the start of a line, followed optionally by a space and optional
> text
>
> e.g.:
> <<<<<<< HEAD
> extern int some_var;
> =======
> extern short some_var;
> >>>>>>> Some other branch
>
> This convention is followed by GNU patch:
>   http://git.savannah.gnu.org/cgit/patch.git/tree/src/merge.c
> by git:
>   http://git.kernel.org/cgit/git/git.git/tree/Documentation/merge-config.txt
> and by various other tools.

if you read both of these tools carefully, you will notice an 
alternative conflict style (named 'diff3' in both of them), that 
includes a third section, the common pre-image. Here is an example:

<<<<<<< HEAD
extern int some_var;
||||||| merge base
extern int var;
=======
extern short var;
>>>>>>> Some other branch

Additionally, git supports a custom conflict-marker-size to change the 
default of 7 on a per file name (the conflict-marker-size attribute). So 
it may be worthwhile to support other sizes than 7 in this patch too.

Bert
Bert Wesarg Feb. 8, 2016, 9:07 a.m. UTC | #3
David,

On Thu, Apr 9, 2015 at 10:29 AM, Bert Wesarg <bert.wesarg@googlemail.com> wrote:
> Hi David,
>
>> Various tools that operate on source code files will inject markers
>> into them when an unfixable conflict occurs in a merger.
>>
>> There appears to be no blessed standard for these conflict markers,
>> but an ad-hoc convention is for 7 '<' , '=', or '>' characters at
>> the start of a line, followed optionally by a space and optional
>> text
>>
>> e.g.:
>> <<<<<<< HEAD
>> extern int some_var;
>> =======
>> extern short some_var;
>> >>>>>>> Some other branch
>>
>> This convention is followed by GNU patch:
>>   http://git.savannah.gnu.org/cgit/patch.git/tree/src/merge.c
>> by git:
>>
>> http://git.kernel.org/cgit/git/git.git/tree/Documentation/merge-config.txt
>> and by various other tools.
>
>
> if you read both of these tools carefully, you will notice an alternative
> conflict style (named 'diff3' in both of them), that includes a third
> section, the common pre-image. Here is an example:
>
> <<<<<<< HEAD
> extern int some_var;
> ||||||| merge base
> extern int var;
> =======
> extern short var;
>>>>>>>>
>>>>>>>> Some other branch
>
>
> Additionally, git supports a custom conflict-marker-size to change the
> default of 7 on a per file name (the conflict-marker-size attribute). So it
> may be worthwhile to support other sizes than 7 in this patch too.

you never commentewd on my mail, but I saw this now in trunk. I can
only repeat myself here.

Thanks.

Bert

>
> Bert
>
David Malcolm Feb. 10, 2016, 5:42 p.m. UTC | #4
On Mon, 2016-02-08 at 10:07 +0100, Bert Wesarg wrote:
> David,
> 
> On Thu, Apr 9, 2015 at 10:29 AM, Bert Wesarg <
> bert.wesarg@googlemail.com> wrote:
> > Hi David,
> > 
> > > Various tools that operate on source code files will inject
> > > markers
> > > into them when an unfixable conflict occurs in a merger.
> > > 
> > > There appears to be no blessed standard for these conflict
> > > markers,
> > > but an ad-hoc convention is for 7 '<' , '=', or '>' characters at
> > > the start of a line, followed optionally by a space and optional
> > > text
> > > 
> > > e.g.:
> > > <<<<<<< HEAD
> > > extern int some_var;
> > > =======
> > > extern short some_var;
> > > > > > > > > > Some other branch
> > > 
> > > This convention is followed by GNU patch:
> > >   http://git.savannah.gnu.org/cgit/patch.git/tree/src/merge.c
> > > by git:
> > > 
> > > http://git.kernel.org/cgit/git/git.git/tree/Documentation/merge-c
> > > onfig.txt
> > > and by various other tools.
> > 
> > 
> > if you read both of these tools carefully, you will notice an
> > alternative
> > conflict style (named 'diff3' in both of them), that includes a
> > third
> > section, the common pre-image. Here is an example:
> > 
> > <<<<<<< HEAD
> > extern int some_var;
> > > > > > > > > merge base
> > extern int var;
> > =======
> > extern short var;
> > > > > > > > > 
> > > > > > > > > Some other branch
> > 
> > 
> > Additionally, git supports a custom conflict-marker-size to change
> > the
> > default of 7 on a per file name (the conflict-marker-size
> > attribute). So it
> > may be worthwhile to support other sizes than 7 in this patch too.
> 
> you never commentewd on my mail, but I saw this now in trunk. I can
> only repeat myself here.

Thanks.  FWIW I did read your mail, and it was a factor in me proposing
this alternate implementation which would be more flexible:
https://gcc.gnu.org/ml/gcc-patches/2015-12/msg01515.html
but that implementation had the drawback of not working with .i files
from -save-temps, so we went with the original approach.
diff mbox

Patch

=======
extern short some_var;
>>>>>>> Some other branch

This convention is followed by GNU patch:
  http://git.savannah.gnu.org/cgit/patch.git/tree/src/merge.c
by git:
  http://git.kernel.org/cgit/git/git.git/tree/Documentation/merge-config.txt
and by various other tools.

I believe that the presense of these markers in source code is almost
always a bug (are there any GCC frontends in which the markers are
parsable as something valid?)

Currently the above leads to cryptic error messages from GCC:

foo.c:1:1: error: expected identifier or ‘(’ before ‘<<’ token
 <<<<<<< HEAD
 ^
foo.c:3:1: error: expected identifier or ‘(’ before ‘==’ token
 =======
 ^
foo.c:5:1: error: expected identifier or ‘(’ before ‘>>’ token
 >>>>>>> Some other branch
 ^

This is typically followed by a cascade of other error messages.

The attached patch implements detection of such patch conflict markers,
issuing errors about each one that it encounters:

foo.c:1:1: error: source file contains patch conflict marker
 <<<<<<< HEAD
 ^
foo.c:3:1: error: source file contains patch conflict marker
 =======
 ^
foo.c:5:1: error: source file contains patch conflict marker
 >>>>>>> Some other branch
 ^

It attempts to continue the failure more gracefully by only parsing
the first hunk of the change and ignoring the second (I arbitrarily
picked the top hunk; I don't know if there's a better way to deal with
this, but it's already a failure mode).

It also attempts to handle nested conflicts.  This nesting works
somewhat analogously to a:
  #error ...warn about the conflict marker
  #if 1  /* accept input at <<<<<<< */
  #else  /* start suppressing input at ======= */
  #endif /* finish suppressing input at >>>>>>> */
but without imposing any assumptions that the conflict markers are
balanced.

The patch is implemented within libcpp: any such conflict markers were
typically injected by tools that work on raw lines of unpreprocessed
text, so it seemed fitting to do it there.

The error can be suppressed with -fno-detect-conflict-markers for
the case where you're using the compiler just for the C preprocessor
and some of the input files legitimately contain such character
sequences.

Successfully bootstrapped&regrtested on x86_64-unknown-linux-gnu
(Fedora 20), with 25 new "PASS" results in gcc.sum
(relative to a control build of r221492), for the new test cases.

OK for next stage1?

gcc/c-family/ChangeLog:
	* c-opts.c (sanitize_cpp_opts): Set up cpp_opts
	detect_conflict_markers based on flag_detect_conflict_markers.

gcc/ChangeLog:
	* common.opt (fdetect-conflict-markers): New option.

gcc/testsuite/ChangeLog:
	* gcc.dg/patch-conflict-markers-1.c: New test case.
	* gcc.dg/patch-conflict-markers-2.c: Likewise.
	* gcc.dg/patch-conflict-markers-3.c: Likewise.
	* gcc.dg/patch-conflict-markers-4.c: Likewise.
	* gcc.dg/patch-conflict-markers-5.c: Likewise.
	* gcc.dg/patch-conflict-markers-6.c: Likewise.
	* gcc.dg/patch-conflict-markers-7.c: Likewise.

libcpp/ChangeLog:
	* Makefile.in (libcpp_a_OBJS): Add conflict-markers.o.
	(libcpp_a_SOURCES): Add conflict-markers.c.
	* conflict-markers.c: New file.
	* include/cpplib.h (TTYPE_TABLE): Add CONFLICT_MARKER_BEGIN,
	CONFLICT_MARKER_MIDDLE, CONFLICT_MARKER_END.
	(struct cpp_options): Add field "detect_conflict_markers".
	* init.c (cpp_create_reader): Initialize detect_conflict_markers
	field.
	* internal.h (struct lexer_state): Add field
	"on_conflict_marker_line".
	(struct cpp_buffer): Add field "cm_stack".
	(_cpp_report_conflict_marker): New function decl.
	(_cpp_handle_conflict_marker): New function decl.
	* lex.c (_cpp_lex_token): When encountering a conflict marker
	token, call _cpp_handle_conflict_marker and skip it.
	(_cpp_get_fresh_line): Update logic for injecting CPP_EOF to
	also do it when handling conflict markers.
	(_cpp_lex_direct): Update comment to reflect above change to
	_cpp_get_fresh_line.  Update handling of '<', '=' and '>' to
	implement detection of conflict markers.
---
 gcc/c-family/c-opts.c                           |   2 +
 gcc/common.opt                                  |   4 +
 gcc/testsuite/gcc.dg/patch-conflict-markers-1.c |  10 +++
 gcc/testsuite/gcc.dg/patch-conflict-markers-2.c |   2 +
 gcc/testsuite/gcc.dg/patch-conflict-markers-3.c |  20 +++++
 gcc/testsuite/gcc.dg/patch-conflict-markers-4.c |  11 +++
 gcc/testsuite/gcc.dg/patch-conflict-markers-5.c |  11 +++
 gcc/testsuite/gcc.dg/patch-conflict-markers-6.c |  11 +++
 gcc/testsuite/gcc.dg/patch-conflict-markers-7.c |  13 +++
 libcpp/Makefile.in                              |   8 +-
 libcpp/conflict-markers.c                       | 112 ++++++++++++++++++++++++
 libcpp/include/cpplib.h                         |   9 +-
 libcpp/init.c                                   |   3 +
 libcpp/internal.h                               |  12 +++
 libcpp/lex.c                                    |  84 ++++++++++++++++--
 15 files changed, 301 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-1.c
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-2.c
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-3.c
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-4.c
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-5.c
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-6.c
 create mode 100644 gcc/testsuite/gcc.dg/patch-conflict-markers-7.c
 create mode 100644 libcpp/conflict-markers.c

diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index 1a67b5a..09a22d1 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -1265,6 +1265,8 @@  sanitize_cpp_opts (void)
       if (cpp_opts->traditional)
 	error ("-fdirectives-only is incompatible with -traditional");
     }
+
+  cpp_opts->detect_conflict_markers = flag_detect_conflict_markers;
 }
 
 /* Add include path with a prefix at the front of its name.  */
diff --git a/gcc/common.opt b/gcc/common.opt
index b49ac46..5336297 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1068,6 +1068,10 @@  fdelete-null-pointer-checks
 Common Report Var(flag_delete_null_pointer_checks) Init(1) Optimization
 Delete useless null pointer checks
 
+fdetect-conflict-markers
+Common Report Var(flag_detect_conflict_markers) Init(1)
+Detect patch conflict markers in the preprocessor and report them as errors
+
 fdevirtualize-at-ltrans
 Common Report Var(flag_ltrans_devirtualize)
 Stream extra data to support more aggressive devirtualization in LTO local transformation mode
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-1.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-1.c
new file mode 100644
index 0000000..dcb8835
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-1.c
@@ -0,0 +1,10 @@ 
+int p;
+
+<<<<<<< HEAD /* { dg-error "patch conflict marker" } */
+extern int some_var;
+=======      /* { dg-error "patch conflict marker" } */
+extern short some_var; /* this line would lead to a warning */
+#error this directive should not be processed
+>>>>>>> Some commit message  /* { dg-error "patch conflict marker" } */
+
+int q;
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-2.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-2.c
new file mode 100644
index 0000000..79030ee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-2.c
@@ -0,0 +1,2 @@ 
+/* This should not be flagged as a patch conflict marker.  */
+const char *msg = "<<<<<<< ";
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-3.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-3.c
new file mode 100644
index 0000000..bd661af
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-3.c
@@ -0,0 +1,20 @@ 
+/* Ensure we can handle nested conflict markers.  */
+int p;
+
+<<<<<<< HEAD /* { dg-error "patch conflict marker" } */
+extern int some_var;
+=======      /* { dg-error "patch conflict marker" } */
+
+#error this directive should not be processed
+
+<<<<<<< HEAD /* { dg-error "patch conflict marker" } */
+#error this directive should not be processed
+=======      /* { dg-error "patch conflict marker" } */
+#error this directive should not be processed
+>>>>>>> Some commit message  /* { dg-error "patch conflict marker" } */
+
+#error this directive should not be processed
+
+>>>>>>> Some other commit message  /* { dg-error "patch conflict marker" } */
+
+int q;
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-4.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-4.c
new file mode 100644
index 0000000..ec3730c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-4.c
@@ -0,0 +1,11 @@ 
+/* Ensure we can handle mismatched conflict markers.  */
+
+int p;
+
+>>>>>>> Some commit message  /* { dg-error "patch conflict marker" } */
+
+int q;
+
+>>>>>>> Some other commit message  /* { dg-error "patch conflict marker" } */
+
+int r;
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-5.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-5.c
new file mode 100644
index 0000000..816a97e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-5.c
@@ -0,0 +1,11 @@ 
+/* Ensure we can handle mismatched conflict markers.  */
+
+int p;
+
+=======  /* { dg-error "patch conflict marker" } */
+
+int q;
+
+=======  /* { dg-error "patch conflict marker" } */
+
+int r;
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-6.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-6.c
new file mode 100644
index 0000000..be956b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-6.c
@@ -0,0 +1,11 @@ 
+/* Ensure we can handle unterminated conflict markers.  */
+
+int p;
+
+<<<<<<< HEAD  /* { dg-error "patch conflict marker" } */
+
+int q;
+
+<<<<<<< HEAD  /* { dg-error "patch conflict marker" } */
+
+int r;
diff --git a/gcc/testsuite/gcc.dg/patch-conflict-markers-7.c b/gcc/testsuite/gcc.dg/patch-conflict-markers-7.c
new file mode 100644
index 0000000..22eeac0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/patch-conflict-markers-7.c
@@ -0,0 +1,13 @@ 
+/* Ensure that we can turn off detection of patch conflict markers.  */
+
+/* { dg-options "-fno-detect-conflict-markers" } */
+
+int p;
+
+<<<<<<< HEAD /* { dg-error "1: expected identifier or '\\(' before '<<' token" } */
+int q;
+=======      /* { dg-error "1: expected identifier or '\\(' before '==' token" } */
+int r;
+>>>>>>> Some commit message  /* { dg-error "1: expected identifier or '\\(' before '>>' token" } */
+
+int s;
diff --git a/libcpp/Makefile.in b/libcpp/Makefile.in
index ad35563..27e8b70 100644
--- a/libcpp/Makefile.in
+++ b/libcpp/Makefile.in
@@ -83,12 +83,12 @@  COMPILER_FLAGS = $(ALL_CXXFLAGS)
 DEPMODE = $(CXXDEPMODE)
 
 
-libcpp_a_OBJS = charset.o directives.o directives-only.o errors.o \
-	expr.o files.o identifiers.o init.o lex.o line-map.o macro.o \
+libcpp_a_OBJS = charset.o conflict-markers.o directives.o directives-only.o \
+	errors.o expr.o files.o identifiers.o init.o lex.o line-map.o macro.o \
 	mkdeps.o pch.o symtab.o traditional.o
 
-libcpp_a_SOURCES = charset.c directives.c directives-only.c errors.c \
-	expr.c files.c identifiers.c init.c lex.c line-map.c macro.c \
+libcpp_a_SOURCES = charset.c conflict-markers.c directives.c directives-only.c \
+	errors.c expr.c files.c identifiers.c init.c lex.c line-map.c macro.c \
 	mkdeps.c pch.c symtab.c traditional.c
 
 all: libcpp.a $(USED_CATALOGS)
diff --git a/libcpp/conflict-markers.c b/libcpp/conflict-markers.c
new file mode 100644
index 0000000..d25a690
--- /dev/null
+++ b/libcpp/conflict-markers.c
@@ -0,0 +1,112 @@ 
+/* CPP Library. (Handling of patch conflict markers.)
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   Contributed by David Malcolm
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "cpplib.h"
+#include "internal.h"
+#include "mkdeps.h"
+#include "obstack.h"
+
+/* Stack of conflict markers currently in progress.  */
+struct cm_stack
+{
+  struct cm_stack *next;
+  bool seen_middle;
+  bool was_skipping;
+};
+
+static struct cm_stack *
+push_conflict_marker (cpp_reader *pfile)
+{
+  struct cm_stack *cm;
+  cpp_buffer *buffer = pfile->buffer;
+  cm = XNEW (struct cm_stack);
+  buffer->cm_stack = cm;
+  cm->next = buffer->cm_stack;
+  cm->seen_middle = 0;
+  cm->was_skipping = 0;
+  return cm;
+}
+
+void
+_cpp_report_conflict_marker (cpp_reader *pfile)
+{
+  cpp_error_with_line (pfile, CPP_DL_ERROR,
+		       pfile->line_table->highest_line, 1,
+		       "source file contains patch conflict marker");
+}
+
+void
+_cpp_handle_conflict_marker (cpp_reader *pfile, cpp_token *token)
+{
+  enum cpp_ttype tok_type = token->type;
+
+  /* Skip to the end of the line.  */
+  pfile->state.prevent_expansion++;
+  pfile->state.on_conflict_marker_line = 1;
+  while (_cpp_lex_token (pfile)->type != CPP_EOF)
+    ;
+  pfile->state.on_conflict_marker_line = 0;
+  pfile->state.prevent_expansion--;
+
+  cpp_buffer *buffer = pfile->buffer;
+  if (!buffer)
+    return;
+
+  switch (tok_type)
+    {
+    default:
+      /* unreachable.  */
+      abort ();
+      break;
+
+    case CPP_CONFLICT_MARKER_BEGIN:
+      push_conflict_marker (pfile);
+      break;
+
+    case CPP_CONFLICT_MARKER_MIDDLE:
+      {
+	struct cm_stack *cm = buffer->cm_stack;
+	if (!cm)
+	  cm = push_conflict_marker (pfile);
+
+	cm->seen_middle = 1;
+	/* Begin skipping (until we see a conflict marker end).  */
+	cm->was_skipping = pfile->state.skipping;
+	pfile->state.skipping = 1;
+      }
+      break;
+
+    case CPP_CONFLICT_MARKER_END:
+      {
+	struct cm_stack *cm = buffer->cm_stack;
+	if (cm)
+	  {
+	    /* Restore skipping state to that when middle marker was seen.  */
+	    if (cm->seen_middle)
+	      {
+		pfile->state.skipping = buffer->cm_stack->was_skipping;
+	      }
+	    buffer->cm_stack = cm->next;
+	    free (buffer->cm_stack);
+	  }
+      }
+      break;
+    }
+}
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 5e08014..88d6e2d 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -144,7 +144,11 @@  struct _cpp_file;
   TK(MACRO_ARG,		NONE)	 /* Macro argument.  */			\
   TK(PRAGMA,		NONE)	 /* Only for deferred pragmas.  */	\
   TK(PRAGMA_EOL,	NONE)	 /* End-of-line for deferred pragmas.  */ \
-  TK(PADDING,		NONE)	 /* Whitespace for -E.	*/
+  TK(PADDING,		NONE)	 /* Whitespace for -E. */		\
+									\
+  OP(CONFLICT_MARKER_BEGIN,  "<<<<<<<")				\
+  OP(CONFLICT_MARKER_MIDDLE, "=======")				\
+  OP(CONFLICT_MARKER_END,    ">>>>>>>")
 
 #define OP(e, s) CPP_ ## e,
 #define TK(e, s) CPP_ ## e,
@@ -526,6 +530,9 @@  struct cpp_options
 
   /* True enables canonicalization of system header file paths. */
   bool canonical_system_headers;
+
+  /* True to detect patch conflict markers (and issue errors for them).  */
+  bool detect_conflict_markers;
 };
 
 /* Callback for header lookup for HEADER, which is the name of a
diff --git a/libcpp/init.c b/libcpp/init.c
index 45a4d13..d73b3b7 100644
--- a/libcpp/init.c
+++ b/libcpp/init.c
@@ -216,6 +216,9 @@  cpp_create_reader (enum c_lang lang, cpp_hash_table *table,
   /* Default the input character set to UTF-8.  */
   CPP_OPTION (pfile, input_charset) = _cpp_default_encoding ();
 
+  /* Default to detecting patch conflict markers.  */
+  CPP_OPTION (pfile, detect_conflict_markers) = 1;
+
   /* A fake empty "directory" used as the starting point for files
      looked up without a search path.  Name cannot be '/' because we
      don't want to prepend anything at all to filenames using it.  All
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 96ccc19..06541e5 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -273,6 +273,10 @@  struct lexer_state
 
   /* Nonzero if the deferred pragma being handled allows macro expansion.  */
   unsigned char pragma_allow_expansion;
+
+  /* Nonzero when skipping to the end of a line when handling a patch
+     conflict marker.  */
+  unsigned char on_conflict_marker_line;
 };
 
 /* Special nodes - identifiers with predefined significance.  */
@@ -330,6 +334,9 @@  struct cpp_buffer
      Used to prohibit unmatched #endif (etc) in an include file.  */
   struct if_stack *if_stack;
 
+  /* Stack of conflict marker information.  */
+  struct cm_stack *cm_stack;
+
   /* True if we need to get the next clean line.  */
   bool need_line;
 
@@ -682,6 +689,11 @@  extern void _cpp_init_lexer (void);
 extern void _cpp_maybe_push_include_file (cpp_reader *);
 extern const char *cpp_named_operator2name (enum cpp_ttype type);
 
+/* In conflict-markers.c */
+extern void _cpp_report_conflict_marker (cpp_reader *pfile);
+extern void _cpp_handle_conflict_marker (cpp_reader *pfile,
+					 cpp_token *token);
+
 /* In directives.c */
 extern int _cpp_test_assertion (cpp_reader *, unsigned int *);
 extern int _cpp_handle_directive (cpp_reader *, int);
diff --git a/libcpp/lex.c b/libcpp/lex.c
index bca5629..b14f7df 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -2182,6 +2182,13 @@  _cpp_lex_token (cpp_reader *pfile)
 		  result = &pfile->directive_result;
 		}
 	    }
+	  else if (result->type == CPP_CONFLICT_MARKER_BEGIN
+		   || result->type == CPP_CONFLICT_MARKER_MIDDLE
+		   || result->type == CPP_CONFLICT_MARKER_END)
+	    {
+	      _cpp_handle_conflict_marker (pfile, result);
+	      continue;
+	    }
 	  else if (pfile->state.in_deferred_pragma)
 	    result = &pfile->directive_result;
 
@@ -2211,8 +2218,12 @@  _cpp_get_fresh_line (cpp_reader *pfile)
 {
   int return_at_eof;
 
-  /* We can't get a new line until we leave the current directive.  */
-  if (pfile->state.in_directive)
+  /* We can't get a new line until we leave the current directive.
+     This will lead to a CPP_EOF token being seen for the line-ending.
+     Behave similarly when handling a patch conflict marker and skipping
+     to the end of the line, so that a CPP_EOF is similarly injected,
+     which we can detect.  */
+  if (pfile->state.in_directive || pfile->state.on_conflict_marker_line)
     return false;
 
   for (;;)
@@ -2265,7 +2276,8 @@  _cpp_get_fresh_line (cpp_reader *pfile)
    suitable for use by _cpp_lex_token, and in special cases like
    lex_expansion_token which doesn't care for any of these issues.
 
-   When meeting a newline, returns CPP_EOF if parsing a directive,
+   When meeting a newline, returns CPP_EOF if parsing a directive
+   or handling a line that began with a patch conflict marker,
    otherwise returns to the start of the token buffer if permissible.
    Returns the location of the lexed token.  */
 cpp_token *
@@ -2513,8 +2525,28 @@  _cpp_lex_direct (cpp_reader *pfile)
 	buffer->cur++, result->type = CPP_LESS_EQ;
       else if (*buffer->cur == '<')
 	{
+	  /* We have "<<".  This may be a "<<", the start of a "<<=",
+	     or the beginning of a patch conflict marker.  */
 	  buffer->cur++;
-	  IF_NEXT_IS ('=', CPP_LSHIFT_EQ, CPP_LSHIFT);
+	  if (*buffer->cur == '=')
+	    {
+	      buffer->cur++;
+	      result->type = CPP_LSHIFT_EQ;
+	    }
+	  else if (result->flags & BOL
+		   && buffer->cur[0] == '<' && buffer->cur[1] == '<'
+		   && buffer->cur[2] == '<' && buffer->cur[3] == '<'
+		   && buffer->cur[4] == '<'
+		   && CPP_OPTION (pfile, detect_conflict_markers))
+	    {
+	      /* We have a patch conflict marker, for a total of 7 '<'
+		 characters.  */
+	      result->type = CPP_CONFLICT_MARKER_BEGIN;
+	      buffer->cur += 5;
+	      _cpp_report_conflict_marker (pfile);
+	    }
+	  else
+	    result->type = CPP_LSHIFT;
 	}
       else if (CPP_OPTION (pfile, digraphs))
 	{
@@ -2550,8 +2582,28 @@  _cpp_lex_direct (cpp_reader *pfile)
 	buffer->cur++, result->type = CPP_GREATER_EQ;
       else if (*buffer->cur == '>')
 	{
+	  /* We have ">>".  This may be a ">>", the start of a ">>=",
+	     or the beginning of a patch conflict marker.  */
 	  buffer->cur++;
-	  IF_NEXT_IS ('=', CPP_RSHIFT_EQ, CPP_RSHIFT);
+	  if (*buffer->cur == '=')
+	    {
+	      buffer->cur++;
+	      result->type = CPP_RSHIFT_EQ;
+	    }
+	  else if (result->flags & BOL
+		   && buffer->cur[0] == '>' && buffer->cur[1] == '>'
+		   && buffer->cur[2] == '>' && buffer->cur[3] == '>'
+		   && buffer->cur[4] == '>'
+		   && CPP_OPTION (pfile, detect_conflict_markers))
+	    {
+	      /* We have a patch conflict marker, for a total of 7 '>'
+		 characters.  */
+	      result->type = CPP_CONFLICT_MARKER_END;
+	      buffer->cur += 5;
+	      _cpp_report_conflict_marker (pfile);
+	    }
+	  else
+	    result->type = CPP_RSHIFT;
 	}
       break;
 
@@ -2645,7 +2697,27 @@  _cpp_lex_direct (cpp_reader *pfile)
       break;
 
     case '*': IF_NEXT_IS ('=', CPP_MULT_EQ, CPP_MULT); break;
-    case '=': IF_NEXT_IS ('=', CPP_EQ_EQ, CPP_EQ); break;
+    case '=':
+      result->type = CPP_EQ;
+      if (*buffer->cur == '=')
+	{
+	  buffer->cur++;
+	  if (result->flags & BOL
+	      && buffer->cur[0] == '=' && buffer->cur[1] == '='
+	      && buffer->cur[2] == '=' && buffer->cur[3] == '='
+	      && buffer->cur[4] == '='
+	      && CPP_OPTION (pfile, detect_conflict_markers))
+	    {
+	      /* We have a patch conflict marker, for a total of 7 '='
+		 characters.  */
+	      result->type = CPP_CONFLICT_MARKER_MIDDLE;
+	      buffer->cur += 5;
+	      _cpp_report_conflict_marker (pfile);
+	    }
+	  else
+	    result->type = CPP_EQ_EQ;
+	}
+      break;
     case '!': IF_NEXT_IS ('=', CPP_NOT_EQ, CPP_NOT); break;
     case '^': IF_NEXT_IS ('=', CPP_XOR_EQ, CPP_XOR); break;
     case '#': IF_NEXT_IS ('#', CPP_PASTE, CPP_HASH); result->val.token_no = 0; break;