Implement -Wimplicit-fallthrough.

Co-Authored-By: Jakub Jelinek <jakub@redhat.com>

From-SVN: r240485
diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog
index 61304cb..94ea99d 100644
--- a/libcpp/ChangeLog
+++ b/libcpp/ChangeLog
@@ -1,3 +1,13 @@
+2016-09-26  Marek Polacek  <polacek@redhat.com>
+	    Jakub Jelinek  <jakub@redhat.com>
+
+	PR c/7652
+	* include/cpplib.h (PREV_FALLTHROUGH): Define.
+	* internal.h (CPP_FALLTHRU): Define.
+	* lex.c (fallthrough_comment_p): New function.
+	(_cpp_lex_direct): Set PREV_FALLTHROUGH on tokens succeeding a falls
+	through comment.
+
 2016-09-23  David Malcolm  <dmalcolm@redhat.com>
 
 	PR preprocessor/77672
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index cfc6ccd..6352ac5 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -185,7 +185,8 @@
 #define STRINGIFY_ARG	(1 << 2) /* If macro argument to be stringified.  */
 #define PASTE_LEFT	(1 << 3) /* If on LHS of a ## operator.  */
 #define NAMED_OP	(1 << 4) /* C++ named operators.  */
-#define NO_EXPAND	(1 << 5) /* Do not macro-expand this token.  */
+#define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH
+				     comment.  */
 #define BOL		(1 << 6) /* Token at beginning of line.  */
 #define PURE_ZERO	(1 << 7) /* Single 0 digit, used by the C++ frontend,
 				    set in c-lex.c.  */
@@ -193,6 +194,7 @@
 #define SP_PREV_WHITE	(1 << 9) /* If whitespace before a ##
 				    operator, or before this token
 				    after a # operator.  */
+#define NO_EXPAND	(1 << 10) /* Do not macro-expand this token.  */
 
 /* Specify which field, if any, of the cpp_token union is used.  */
 
diff --git a/libcpp/lex.c b/libcpp/lex.c
index 6254ed6..0c47e29 100644
--- a/libcpp/lex.c
+++ b/libcpp/lex.c
@@ -2032,6 +2032,94 @@
   store_comment (pfile, token);
 }
 
+/* Returns true if comment at COMMENT_START is a recognized FALLTHROUGH
+   comment.  */
+
+static bool
+fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start)
+{
+  const unsigned char *from = comment_start + 1;
+  /* Whole comment contents:
+     -fallthrough
+     @fallthrough@
+   */
+  if (*from == '-' || *from == '@')
+    {
+      size_t len = sizeof "fallthrough" - 1;
+      if ((size_t) (pfile->buffer->cur - from - 1) < len)
+	return false;
+      if (memcmp (from + 1, "fallthrough", len))
+	return false;
+      if (*from == '@')
+	{
+	  if (from[len + 1] != '@')
+	    return false;
+	  len++;
+	}
+      from += 1 + len;
+    }
+  /* Whole comment contents (regex):
+     [ \t]*FALL(S | |-)?THR(OUGH|U)\.?[ \t]*
+     [ \t]*Fall(s | |-)?[Tt]hr(ough|u)\.?[ \t]*
+     [ \t]*fall(s | |-)?thr(ough|u)\.?[ \t]*
+   */
+  else
+    {
+      while (*from == ' ' || *from == '\t')
+	from++;
+      unsigned char f = *from;
+      if (f != 'F' && f != 'f')
+	return false;
+      if ((size_t) (pfile->buffer->cur - from) < sizeof "fallthrough")
+	return false;
+      bool all_upper = false;
+      if (f == 'F' && memcmp (from + 1, "ALL", sizeof "ALL" - 1) == 0)
+	all_upper = true;
+      else if (memcmp (from + 1, "all", sizeof "all" - 1))
+	return false;
+      if (from[sizeof "fall" - 1] == (all_upper ? 'S' : 's')
+	  && from[sizeof "falls" - 1] == ' ')
+	from += sizeof "falls " - 1;
+      else if (from[sizeof "fall" - 1] == ' '
+	       || from[sizeof "fall" - 1] == '-')
+	from += sizeof "fall " - 1;
+      else if (from[sizeof "fall" - 1] != (all_upper ? 'T' : 't'))
+	return false;
+      else
+	from += sizeof "fall" - 1;
+      if ((f == 'f' || *from != 'T') && (all_upper || *from != 't'))
+	return false;
+      if ((size_t) (pfile->buffer->cur - from) < sizeof "thru")
+	return false;
+      if (memcmp (from + 1, all_upper ? "HRU" : "hru", sizeof "hru" - 1))
+	{
+	  if ((size_t) (pfile->buffer->cur - from) < sizeof "through")
+	    return false;
+	  if (memcmp (from + 1, all_upper ? "HROUGH" : "hrough",
+		      sizeof "hrough" - 1))
+	    return false;
+	  from += sizeof "through" - 1;
+	}
+      else
+	from += sizeof "thru" - 1;
+      if (*from == '.')
+	from++;
+      while (*from == ' ' || *from == '\t')
+	from++;
+    }
+  /* C block comment.  */
+  if (*comment_start == '*')
+    {
+      if (*from != '*' || from[1] != '/')
+	return false;
+    }
+  /* C++ line comment.  */
+  else if (*from != '\n')
+    return false;
+
+  return true;
+}
+
 /* Allocate COUNT tokens for RUN.  */
 void
 _cpp_init_tokenrun (tokenrun *run, unsigned int count)
@@ -2310,7 +2398,7 @@
 {
   cppchar_t c;
   cpp_buffer *buffer;
-  const unsigned char *comment_start;
+  const unsigned char *comment_start = NULL;
   cpp_token *result = pfile->cur_token++;
 
  fresh_line:
@@ -2337,6 +2425,8 @@
 	    }
 	  return result;
 	}
+      if (buffer != pfile->buffer)
+	comment_start = NULL;
       if (!pfile->keep_tokens)
 	{
 	  pfile->cur_run = &pfile->base_run;
@@ -2443,6 +2533,11 @@
 	  result->flags |= NAMED_OP;
 	  result->type = (enum cpp_ttype) result->val.node.node->directive_index;
 	}
+
+      /* Signal FALLTHROUGH comment followed by another token.  */
+      if (comment_start
+	  && fallthrough_comment_p (pfile, comment_start))
+	result->flags |= PREV_FALLTHROUGH;
       break;
 
     case '\'':
@@ -2534,6 +2629,9 @@
 	  goto update_tokens_line;
 	}
 
+      if (fallthrough_comment_p (pfile, comment_start))
+	result->flags |= PREV_FALLTHROUGH;
+
       /* Save the comment as a token in its own right.  */
       save_comment (pfile, result, comment_start, c);
       break;