[PATCH, stage1] Better error recovery for merge-conflict markers

David Malcolm dmalcolm@redhat.com
Fri Mar 20 15:38:00 GMT 2015


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.

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;
-- 
1.8.5.3



More information about the Gcc-patches mailing list