]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libcpp/lex.c
PR preprocessor/84517 allow double-underscore macros after string literals
[thirdparty/gcc.git] / libcpp / lex.c
index 0c47e2980701ace96e576ffdb05cce50e4cd3977..37c365a3560bcd758ccf69375b43a26f52d38a41 100644 (file)
@@ -1,5 +1,5 @@
 /* CPP Library - lexical analysis.
-   Copyright (C) 2000-2016 Free Software Foundation, Inc.
+   Copyright (C) 2000-2018 Free Software Foundation, Inc.
    Contributed by Per Bothner, 1994-95.
    Based on CCCP program by Paul Rubin, June 1986
    Adapted to ANSI C, Richard Stallman, Jan 1987
@@ -568,7 +568,7 @@ search_line_fast (const uchar *s, const uchar *end ATTRIBUTE_UNUSED)
     {
       vc m_nl, m_cr, m_bs, m_qm;
 
-      data = *((const vc *)s);
+      data = __builtin_vec_vsx_ld (0, s);
       s += 16;
 
       m_nl = (vc) __builtin_vec_cmpeq(data, repl_nl);
@@ -733,6 +733,7 @@ search_line_fast (const uchar *s, const uchar *end ATTRIBUTE_UNUSED)
        if (l != 0)
          break;
        s += sizeof(unsigned long);
+       /* FALLTHROUGH */
       case 2:
        l = u.l[i++];
        if (l != 0)
@@ -751,6 +752,101 @@ search_line_fast (const uchar *s, const uchar *end ATTRIBUTE_UNUSED)
   }
 }
 
+#elif defined (__ARM_NEON) && defined (__ARM_64BIT_STATE)
+#include "arm_neon.h"
+
+/* This doesn't have to be the exact page size, but no system may use
+   a size smaller than this.  ARMv8 requires a minimum page size of
+   4k.  The impact of being conservative here is a small number of
+   cases will take the slightly slower entry path into the main
+   loop.  */
+
+#define AARCH64_MIN_PAGE_SIZE 4096
+
+static const uchar *
+search_line_fast (const uchar *s, const uchar *end ATTRIBUTE_UNUSED)
+{
+  const uint8x16_t repl_nl = vdupq_n_u8 ('\n');
+  const uint8x16_t repl_cr = vdupq_n_u8 ('\r');
+  const uint8x16_t repl_bs = vdupq_n_u8 ('\\');
+  const uint8x16_t repl_qm = vdupq_n_u8 ('?');
+  const uint8x16_t xmask = (uint8x16_t) vdupq_n_u64 (0x8040201008040201ULL);
+
+#ifdef __ARM_BIG_ENDIAN
+  const int16x8_t shift = {8, 8, 8, 8, 0, 0, 0, 0};
+#else
+  const int16x8_t shift = {0, 0, 0, 0, 8, 8, 8, 8};
+#endif
+
+  unsigned int found;
+  const uint8_t *p;
+  uint8x16_t data;
+  uint8x16_t t;
+  uint16x8_t m;
+  uint8x16_t u, v, w;
+
+  /* Align the source pointer.  */
+  p = (const uint8_t *)((uintptr_t)s & -16);
+
+  /* Assuming random string start positions, with a 4k page size we'll take
+     the slow path about 0.37% of the time.  */
+  if (__builtin_expect ((AARCH64_MIN_PAGE_SIZE
+                        - (((uintptr_t) s) & (AARCH64_MIN_PAGE_SIZE - 1)))
+                       < 16, 0))
+    {
+      /* Slow path: the string starts near a possible page boundary.  */
+      uint32_t misalign, mask;
+
+      misalign = (uintptr_t)s & 15;
+      mask = (-1u << misalign) & 0xffff;
+      data = vld1q_u8 (p);
+      t = vceqq_u8 (data, repl_nl);
+      u = vceqq_u8 (data, repl_cr);
+      v = vorrq_u8 (t, vceqq_u8 (data, repl_bs));
+      w = vorrq_u8 (u, vceqq_u8 (data, repl_qm));
+      t = vorrq_u8 (v, w);
+      t = vandq_u8 (t, xmask);
+      m = vpaddlq_u8 (t);
+      m = vshlq_u16 (m, shift);
+      found = vaddvq_u16 (m);
+      found &= mask;
+      if (found)
+       return (const uchar*)p + __builtin_ctz (found);
+    }
+  else
+    {
+      data = vld1q_u8 ((const uint8_t *) s);
+      t = vceqq_u8 (data, repl_nl);
+      u = vceqq_u8 (data, repl_cr);
+      v = vorrq_u8 (t, vceqq_u8 (data, repl_bs));
+      w = vorrq_u8 (u, vceqq_u8 (data, repl_qm));
+      t = vorrq_u8 (v, w);
+      if (__builtin_expect (vpaddd_u64 ((uint64x2_t)t) != 0, 0))
+       goto done;
+    }
+
+  do
+    {
+      p += 16;
+      data = vld1q_u8 (p);
+      t = vceqq_u8 (data, repl_nl);
+      u = vceqq_u8 (data, repl_cr);
+      v = vorrq_u8 (t, vceqq_u8 (data, repl_bs));
+      w = vorrq_u8 (u, vceqq_u8 (data, repl_qm));
+      t = vorrq_u8 (v, w);
+    } while (!vpaddd_u64 ((uint64x2_t)t));
+
+done:
+  /* Now that we've found the terminating substring, work out precisely where
+     we need to stop.  */
+  t = vandq_u8 (t, xmask);
+  m = vpaddlq_u8 (t);
+  m = vshlq_u16 (m, shift);
+  found = vaddvq_u16 (m);
+  return (((((uintptr_t) p) < (uintptr_t) s) ? s : (const uchar *)p)
+         + __builtin_ctz (found));
+}
+
 #elif defined (__ARM_NEON)
 #include "arm_neon.h"
 
@@ -816,7 +912,7 @@ search_line_fast (const uchar *s, const uchar *end ATTRIBUTE_UNUSED)
 
 #else
 
-/* We only have one accellerated alternative.  Use a direct call so that
+/* We only have one accelerated alternative.  Use a direct call so that
    we encourage inlining.  */
 
 #define search_line_fast  search_line_acc_char
@@ -1256,6 +1352,28 @@ forms_identifier_p (cpp_reader *pfile, int first,
   return false;
 }
 
+/* Helper function to issue error about improper __VA_OPT__ use.  */
+static void
+maybe_va_opt_error (cpp_reader *pfile)
+{
+  if (CPP_PEDANTIC (pfile) && !CPP_OPTION (pfile, va_opt))
+    {
+      /* __VA_OPT__ should not be accepted at all, but allow it in
+        system headers.  */
+      if (!cpp_in_system_header (pfile))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  "__VA_OPT__ is not available until C++2a");
+    }
+  else if (!pfile->state.va_args_ok)
+    {
+      /* __VA_OPT__ should only appear in the replacement list of a
+        variadic macro.  */
+      cpp_error (pfile, CPP_DL_PEDWARN,
+                "__VA_OPT__ can only appear in the expansion"
+                " of a C++2a variadic macro");
+    }
+}
+
 /* Helper function to get the cpp_hashnode of the identifier BASE.  */
 static cpp_hashnode *
 lex_identifier_intern (cpp_reader *pfile, const uchar *base)
@@ -1300,6 +1418,9 @@ lex_identifier_intern (cpp_reader *pfile, const uchar *base)
                       " of a C99 variadic macro");
        }
 
+      if (result == pfile->spec_nodes.n__VA_OPT__)
+       maybe_va_opt_error (pfile);
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
        cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
@@ -1389,6 +1510,11 @@ lex_identifier (cpp_reader *pfile, const uchar *base, bool starts_ucn,
                       " of a C99 variadic macro");
        }
 
+      /* __VA_OPT__ should only appear in the replacement list of a
+        variadic macro.  */
+      if (result == pfile->spec_nodes.n__VA_OPT__)
+       maybe_va_opt_error (pfile);
+
       /* For -Wc++-compat, warn about use of C++ named operators.  */
       if (result->flags & NODE_WARN_OPERATOR)
        cpp_warning (pfile, CPP_W_CXX_OPERATOR_NAMES,
@@ -1504,6 +1630,21 @@ is_macro(cpp_reader *pfile, const uchar *base)
   return !result ? false : (result->type == NT_MACRO);
 }
 
+/* Returns true if a literal suffix does not have the expected form
+   and is defined as a macro.  */
+
+static bool
+is_macro_not_literal_suffix(cpp_reader *pfile, const uchar *base)
+{
+  /* User-defined literals outside of namespace std must start with a single
+     underscore, so assume anything of that form really is a UDL suffix.
+     We don't need to worry about UDLs defined inside namespace std because
+     their names are reserved, so cannot be used as macro names in valid
+     programs.  */
+  if (base[0] == '_' && base[1] != '_')
+    return false;
+  return is_macro (pfile, base);
+}
 
 /* Lexes a raw string.  The stored string contains the spelling, including
    double quotes, delimiter string, '(' and ')', any leading
@@ -1551,7 +1692,7 @@ lex_raw_string (cpp_reader *pfile, cpp_token *token, const uchar *base,
                    (const uchar *)(STR), (LEN));               \
            temp_buffer_len += (LEN);                           \
          }                                                     \
-      } while (0);
+      } while (0)
 
   orig_base = base;
   ++cur;
@@ -1774,9 +1915,8 @@ lex_raw_string (cpp_reader *pfile, cpp_token *token, const uchar *base,
     {
       /* If a string format macro, say from inttypes.h, is placed touching
         a string literal it could be parsed as a C++11 user-defined string
-        literal thus breaking the program.
-        Try to identify macros with is_macro. A warning is issued. */
-      if (is_macro (pfile, cur))
+        literal thus breaking the program.  */
+      if (is_macro_not_literal_suffix (pfile, cur))
        {
          /* Raise a warning, but do not consume subsequent tokens.  */
          if (CPP_OPTION (pfile, warn_literal_suffix) && !pfile->state.skipping)
@@ -1904,9 +2044,8 @@ lex_string (cpp_reader *pfile, cpp_token *token, const uchar *base)
     {
       /* If a string format macro, say from inttypes.h, is placed touching
         a string literal it could be parsed as a C++11 user-defined string
-        literal thus breaking the program.
-        Try to identify macros with is_macro. A warning is issued. */
-      if (is_macro (pfile, cur))
+        literal thus breaking the program.  */
+      if (is_macro_not_literal_suffix (pfile, cur))
        {
          /* Raise a warning, but do not consume subsequent tokens.  */
          if (CPP_OPTION (pfile, warn_literal_suffix) && !pfile->state.skipping)
@@ -2039,6 +2178,64 @@ static bool
 fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start)
 {
   const unsigned char *from = comment_start + 1;
+
+  switch (CPP_OPTION (pfile, cpp_warn_implicit_fallthrough))
+    {
+      /* For both -Wimplicit-fallthrough=0 and -Wimplicit-fallthrough=5 we
+        don't recognize any comments.  The latter only checks attributes,
+        the former doesn't warn.  */
+    case 0:
+    default:
+      return false;
+      /* -Wimplicit-fallthrough=1 considers any comment, no matter what
+        content it has.  */
+    case 1:
+      return true;
+    case 2:
+      /* -Wimplicit-fallthrough=2 looks for (case insensitive)
+        .*falls?[ \t-]*thr(u|ough).* regex.  */
+      for (; (size_t) (pfile->buffer->cur - from) >= sizeof "fallthru" - 1;
+          from++)
+       {
+         /* Is there anything like strpbrk with upper boundary, or
+            memchr looking for 2 characters rather than just one?  */
+         if (from[0] != 'f' && from[0] != 'F')
+           continue;
+         if (from[1] != 'a' && from[1] != 'A')
+           continue;
+         if (from[2] != 'l' && from[2] != 'L')
+           continue;
+         if (from[3] != 'l' && from[3] != 'L')
+           continue;
+         from += sizeof "fall" - 1;
+         if (from[0] == 's' || from[0] == 'S')
+           from++;
+         while (*from == ' ' || *from == '\t' || *from == '-')
+           from++;
+         if (from[0] != 't' && from[0] != 'T')
+           continue;
+         if (from[1] != 'h' && from[1] != 'H')
+           continue;
+         if (from[2] != 'r' && from[2] != 'R')
+           continue;
+         if (from[3] == 'u' || from[3] == 'U')
+           return true;
+         if (from[3] != 'o' && from[3] != 'O')
+           continue;
+         if (from[4] != 'u' && from[4] != 'U')
+           continue;
+         if (from[5] != 'g' && from[5] != 'G')
+           continue;
+         if (from[6] != 'h' && from[6] != 'H')
+           continue;
+         return true;
+       }
+      return false;
+    case 3:
+    case 4:
+      break;
+    }
+
   /* Whole comment contents:
      -fallthrough
      @fallthrough@
@@ -2059,41 +2256,132 @@ fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start)
       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]*
+     lint -fallthrough[ \t]*
    */
-  else
+  else if (*from == 'l')
+    {
+      size_t len = sizeof "int -fallthrough" - 1;
+      if ((size_t) (pfile->buffer->cur - from - 1) < len)
+       return false;
+      if (memcmp (from + 1, "int -fallthrough", len))
+       return false;
+      from += 1 + len;
+      while (*from == ' ' || *from == '\t')
+       from++;
+    }
+  /* Whole comment contents (regex):
+     [ \t]*FALLTHR(U|OUGH)[ \t]*
+   */
+  else if (CPP_OPTION (pfile, cpp_warn_implicit_fallthrough) == 4)
     {
       while (*from == ' ' || *from == '\t')
        from++;
+      if ((size_t) (pfile->buffer->cur - from)  < sizeof "FALLTHRU" - 1)
+       return false;
+      if (memcmp (from, "FALLTHR", sizeof "FALLTHR" - 1))
+       return false;
+      from += sizeof "FALLTHR" - 1;
+      if (*from == 'U')
+       from++;
+      else if ((size_t) (pfile->buffer->cur - from)  < sizeof "OUGH" - 1)
+       return false;
+      else if (memcmp (from, "OUGH", sizeof "OUGH" - 1))
+       return false;
+      else
+       from += sizeof "OUGH" - 1;
+      while (*from == ' ' || *from == '\t')
+       from++;
+    }
+  /* Whole comment contents (regex):
+     [ \t.!]*(ELSE,? |INTENTIONAL(LY)? )?FALL(S | |-)?THR(OUGH|U)[ \t.!]*(-[^\n\r]*)?
+     [ \t.!]*(Else,? |Intentional(ly)? )?Fall((s | |-)[Tt]|t)hr(ough|u)[ \t.!]*(-[^\n\r]*)?
+     [ \t.!]*([Ee]lse,? |[Ii]ntentional(ly)? )?fall(s | |-)?thr(ough|u)[ \t.!]*(-[^\n\r]*)?
+   */
+  else
+    {
+      while (*from == ' ' || *from == '\t' || *from == '.' || *from == '!')
+       from++;
       unsigned char f = *from;
+      bool all_upper = false;
+      if (f == 'E' || f == 'e')
+       {
+         if ((size_t) (pfile->buffer->cur - from)
+             < sizeof "else fallthru" - 1)
+           return false;
+         if (f == 'E' && memcmp (from + 1, "LSE", sizeof "LSE" - 1) == 0)
+           all_upper = true;
+         else if (memcmp (from + 1, "lse", sizeof "lse" - 1))
+           return false;
+         from += sizeof "else" - 1;
+         if (*from == ',')
+           from++;
+         if (*from != ' ')
+           return false;
+         from++;
+         if (all_upper && *from == 'f')
+           return false;
+         if (f == 'e' && *from == 'F')
+           return false;
+         f = *from;
+       }
+      else if (f == 'I' || f == 'i')
+       {
+         if ((size_t) (pfile->buffer->cur - from)
+             < sizeof "intentional fallthru" - 1)
+           return false;
+         if (f == 'I' && memcmp (from + 1, "NTENTIONAL",
+                                 sizeof "NTENTIONAL" - 1) == 0)
+           all_upper = true;
+         else if (memcmp (from + 1, "ntentional",
+                          sizeof "ntentional" - 1))
+           return false;
+         from += sizeof "intentional" - 1;
+         if (*from == ' ')
+           {
+             from++;
+             if (all_upper && *from == 'f')
+               return false;
+           }
+         else if (all_upper)
+           {
+             if (memcmp (from, "LY F", sizeof "LY F" - 1))
+               return false;
+             from += sizeof "LY " - 1;
+           }
+         else
+           {
+             if (memcmp (from, "ly ", sizeof "ly " - 1))
+               return false;
+             from += sizeof "ly " - 1;
+           }
+         if (f == 'i' && *from == 'F')
+           return false;
+         f = *from;
+       }
       if (f != 'F' && f != 'f')
        return false;
-      if ((size_t) (pfile->buffer->cur - from) < sizeof "fallthrough")
+      if ((size_t) (pfile->buffer->cur - from) < sizeof "fallthru" - 1)
        return false;
-      bool all_upper = false;
       if (f == 'F' && memcmp (from + 1, "ALL", sizeof "ALL" - 1) == 0)
        all_upper = true;
+      else if (all_upper)
+       return false;
       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'))
+      from += sizeof "fall" - 1;
+      if (*from == (all_upper ? 'S' : 's') && from[1] == ' ')
+       from += 2;
+      else if (*from == ' ' || *from == '-')
+       from++;
+      else if (*from != (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")
+      if ((size_t) (pfile->buffer->cur - from) < sizeof "thru" - 1)
        return false;
       if (memcmp (from + 1, all_upper ? "HRU" : "hru", sizeof "hru" - 1))
        {
-         if ((size_t) (pfile->buffer->cur - from) < sizeof "through")
+         if ((size_t) (pfile->buffer->cur - from) < sizeof "through" - 1)
            return false;
          if (memcmp (from + 1, all_upper ? "HROUGH" : "hrough",
                      sizeof "hrough" - 1))
@@ -2102,10 +2390,28 @@ fallthrough_comment_p (cpp_reader *pfile, const unsigned char *comment_start)
        }
       else
        from += sizeof "thru" - 1;
-      if (*from == '.')
-       from++;
-      while (*from == ' ' || *from == '\t')
+      while (*from == ' ' || *from == '\t' || *from == '.' || *from == '!')
        from++;
+      if (*from == '-')
+       {
+         from++;
+         if (*comment_start == '*')
+           {
+             do
+               {
+                 while (*from && *from != '*'
+                        && *from != '\n' && *from != '\r')
+                   from++;
+                 if (*from != '*' || from[1] == '/')
+                   break;
+                 from++;
+               }
+             while (1);
+           }
+         else
+           while (*from && *from != '\n' && *from != '\r')
+             from++;
+       }
     }
   /* C block comment.  */
   if (*comment_start == '*')
@@ -2398,7 +2704,8 @@ _cpp_lex_direct (cpp_reader *pfile)
 {
   cppchar_t c;
   cpp_buffer *buffer;
-  const unsigned char *comment_start = NULL;
+  const unsigned char *comment_start;
+  bool fallthrough_comment = false;
   cpp_token *result = pfile->cur_token++;
 
  fresh_line:
@@ -2426,7 +2733,7 @@ _cpp_lex_direct (cpp_reader *pfile)
          return result;
        }
       if (buffer != pfile->buffer)
-       comment_start = NULL;
+       fallthrough_comment = false;
       if (!pfile->keep_tokens)
        {
          pfile->cur_run = &pfile->base_run;
@@ -2535,8 +2842,7 @@ _cpp_lex_direct (cpp_reader *pfile)
        }
 
       /* Signal FALLTHROUGH comment followed by another token.  */
-      if (comment_start
-         && fallthrough_comment_p (pfile, comment_start))
+      if (fallthrough_comment)
        result->flags |= PREV_FALLTHROUGH;
       break;
 
@@ -2623,13 +2929,23 @@ _cpp_lex_direct (cpp_reader *pfile)
          break;
        }
 
+      if (fallthrough_comment_p (pfile, comment_start))
+       fallthrough_comment = true;
+
+      if (pfile->cb.comment)
+       {
+         size_t len = pfile->buffer->cur - comment_start;
+         pfile->cb.comment (pfile, result->src_loc, comment_start - 1,
+                            len + 1);
+       }
+
       if (!pfile->state.save_comments)
        {
          result->flags |= PREV_WHITE;
          goto update_tokens_line;
        }
 
-      if (fallthrough_comment_p (pfile, comment_start))
+      if (fallthrough_comment)
        result->flags |= PREV_FALLTHROUGH;
 
       /* Save the comment as a token in its own right.  */
@@ -2823,18 +3139,27 @@ _cpp_lex_direct (cpp_reader *pfile)
       break;
     }
 
-  source_range tok_range;
-  tok_range.m_start = result->src_loc;
-  if (result->src_loc >= RESERVED_LOCATION_COUNT)
-    tok_range.m_finish
-      = linemap_position_for_column (pfile->line_table,
-                                    CPP_BUF_COLUMN (buffer, buffer->cur));
-  else
-    tok_range.m_finish = tok_range.m_start;
+  /* Potentially convert the location of the token to a range.  */
+  if (result->src_loc >= RESERVED_LOCATION_COUNT
+      && result->type != CPP_EOF)
+    {
+      /* Ensure that any line notes are processed, so that we have the
+        correct physical line/column for the end-point of the token even
+        when a logical line is split via one or more backslashes.  */
+      if (buffer->cur >= buffer->notes[buffer->cur_note].pos
+         && !pfile->overlaid_buffer)
+       _cpp_process_line_notes (pfile, false);
 
-  result->src_loc = COMBINE_LOCATION_DATA (pfile->line_table,
-                                          result->src_loc,
-                                          tok_range, NULL);
+      source_range tok_range;
+      tok_range.m_start = result->src_loc;
+      tok_range.m_finish
+       = linemap_position_for_column (pfile->line_table,
+                                      CPP_BUF_COLUMN (buffer, buffer->cur));
+
+      result->src_loc = COMBINE_LOCATION_DATA (pfile->line_table,
+                                              result->src_loc,
+                                              tok_range, NULL);
+    }
 
   return result;
 }