Ensure that control characters in user supplied error and warning messages are escaped.
authorNick Clifton <nickc@redhat.com>
Mon, 18 Jun 2018 10:39:01 +0000 (10:39 +0000)
committerNick Clifton <nickc@gcc.gnu.org>
Mon, 18 Jun 2018 10:39:01 +0000 (10:39 +0000)
PR 84195
* tree.c (escaped_string): New class.  Converts an unescaped
string into its escaped equivalent.
(warn_deprecated_use): Use the new class to convert the
deprecation message, if present.
(test_escaped_strings): New self test.
(test_c_tests): Add test_escaped_strings.

From-SVN: r261697

gcc/ChangeLog
gcc/tree.c

index 13e5c10..4134a2c 100644 (file)
@@ -1,3 +1,21 @@
+2018-06-18  Nick Clifton  <nickc@redhat.com>
+
+       PR 84195
+       * tree.c (escaped_string): New class.  Converts an unescaped
+       string into its escaped equivalent.
+       (warn_deprecated_use): Use the new class to convert the
+       deprecation message, if present.
+       (test_escaped_strings): New self test.
+       (test_c_tests): Add test_escaped_strings.
+       * doc/extend.texi (deprecated): Add a note that the
+       deprecation message is affected by the -fmessage-length
+       option, and that control characters will be escaped.
+       (#pragma GCC error): Document this pragma.
+       (#pragma GCC warning): Likewise.
+       * doc/invoke.texi (-fmessage-length): Document this option's
+       effect on the #warning and #error preprocessor directives and
+       the deprecated attribute.
+
 2018-06-18  Eric Botcazou  <ebotcazou@adacore.com>
 
        * tree.c (decl_value_expr_lookup): Revert latest change.
index 2d3b26e..6728f1c 100644 (file)
@@ -12423,13 +12423,103 @@ typedef_variant_p (const_tree type)
   return is_typedef_decl (TYPE_NAME (type));
 }
 
+/* A class to handle converting a string that might contain
+   control characters, (eg newline, form-feed, etc), into one
+   in which contains escape sequences instead.  */
+
+class escaped_string
+{
+ public:
+  escaped_string () { m_owned = false; m_str = NULL; };
+  ~escaped_string () { if (m_owned) free (m_str); }
+  operator const char *() const { return (const char *) m_str; }
+  void escape (const char *);
+ private:
+  char *m_str;
+  bool  m_owned;
+};
+
+/* PR 84195: Replace control characters in "unescaped" with their
+   escaped equivalents.  Allow newlines if -fmessage-length has
+   been set to a non-zero value.  This is done here, rather than
+   where the attribute is recorded as the message length can
+   change between these two locations.  */
+
+void
+escaped_string::escape (const char *unescaped)
+{
+  char *escaped;
+  size_t i, new_i, len;
+
+  if (m_owned)
+    free (m_str);
+
+  m_str = (char *) unescaped;
+  m_owned = false;
+
+  if (unescaped == NULL || *unescaped == 0)
+    return;
+
+  len = strlen (unescaped);
+  escaped = NULL;
+  new_i = 0;
+
+  for (i = 0; i < len; i++)
+    {
+      char c = unescaped[i];
+
+      if (!ISCNTRL (c))
+       {
+         if (escaped)
+           escaped[new_i++] = c;
+         continue;
+       }
+
+      if (c != '\n' || !pp_is_wrapping_line (global_dc->printer))
+       {
+         if (escaped == NULL)
+           {
+             /* We only allocate space for a new string if we
+                actually encounter a control character that
+                needs replacing.  */
+             escaped = (char *) xmalloc (len * 2 + 1);
+             strncpy (escaped, unescaped, i);
+             new_i = i;
+           }
+
+         escaped[new_i++] = '\\';
+
+         switch (c)
+           {
+           case '\a': escaped[new_i++] = 'a'; break;
+           case '\b': escaped[new_i++] = 'b'; break;
+           case '\f': escaped[new_i++] = 'f'; break;
+           case '\n': escaped[new_i++] = 'n'; break;
+           case '\r': escaped[new_i++] = 'r'; break;
+           case '\t': escaped[new_i++] = 't'; break;
+           case '\v': escaped[new_i++] = 'v'; break;
+           default:   escaped[new_i++] = '?'; break;
+           }
+       }
+      else if (escaped)
+       escaped[new_i++] = c;
+    }
+
+  if (escaped)
+    {
+      escaped[new_i] = 0;
+      m_str = escaped;
+      m_owned = true;
+    }
+}
+
 /* Warn about a use of an identifier which was marked deprecated.  Returns
    whether a warning was given.  */
 
 bool
 warn_deprecated_use (tree node, tree attr)
 {
-  const char *msg;
+  escaped_string msg;
 
   if (node == 0 || !warn_deprecated_decl)
     return false;
@@ -12451,16 +12541,14 @@ warn_deprecated_use (tree node, tree attr)
     attr = lookup_attribute ("deprecated", attr);
 
   if (attr)
-    msg = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
-  else
-    msg = NULL;
+    msg.escape (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
 
   bool w = false;
   if (DECL_P (node))
     {
       if (msg)
        w = warning (OPT_Wdeprecated_declarations,
-                    "%qD is deprecated: %s", node, msg);
+                    "%qD is deprecated: %s", node, (const char *) msg);
       else
        w = warning (OPT_Wdeprecated_declarations,
                     "%qD is deprecated", node);
@@ -12485,7 +12573,7 @@ warn_deprecated_use (tree node, tree attr)
        {
          if (msg)
            w = warning (OPT_Wdeprecated_declarations,
-                        "%qE is deprecated: %s", what, msg);
+                        "%qE is deprecated: %s", what, (const char *) msg);
          else
            w = warning (OPT_Wdeprecated_declarations,
                         "%qE is deprecated", what);
@@ -12494,11 +12582,12 @@ warn_deprecated_use (tree node, tree attr)
        {
          if (msg)
            w = warning (OPT_Wdeprecated_declarations,
-                        "type is deprecated: %s", msg);
+                        "type is deprecated: %s", (const char *) msg);
          else
            w = warning (OPT_Wdeprecated_declarations,
                         "type is deprecated");
        }
+
       if (w && decl)
        inform (DECL_SOURCE_LOCATION (decl), "declared here");
     }
@@ -14537,6 +14626,49 @@ test_location_wrappers ()
   check_strip_nops (wrapped_int_var, int_var);
 }
 
+/* Check that string escaping works correctly.  */
+
+static void
+test_escaped_strings (void)
+{
+  int saved_cutoff;
+  escaped_string msg;
+
+  msg.escape (NULL);
+  /* ASSERT_STREQ does not accept NULL as a valid test
+     result, so we have to use ASSERT_EQ instead.  */
+  ASSERT_EQ (NULL, (const char *) msg);
+
+  msg.escape ("");
+  ASSERT_STREQ ("", (const char *) msg);
+
+  msg.escape ("foobar");
+  ASSERT_STREQ ("foobar", (const char *) msg);
+
+  /* Ensure that we have -fmessage-length set to 0.  */
+  saved_cutoff = pp_line_cutoff (global_dc->printer);
+  pp_line_cutoff (global_dc->printer) = 0;
+
+  msg.escape ("foo\nbar");
+  ASSERT_STREQ ("foo\\nbar", (const char *) msg);
+
+  msg.escape ("\a\b\f\n\r\t\v");
+  ASSERT_STREQ ("\\a\\b\\f\\n\\r\\t\\v", (const char *) msg);
+
+  /* Now repeat the tests with -fmessage-length set to 5.  */
+  pp_line_cutoff (global_dc->printer) = 5;
+
+  /* Note that the newline is not translated into an escape.  */
+  msg.escape ("foo\nbar");
+  ASSERT_STREQ ("foo\nbar", (const char *) msg);
+
+  msg.escape ("\a\b\f\n\r\t\v");
+  ASSERT_STREQ ("\\a\\b\\f\n\\r\\t\\v", (const char *) msg);
+
+  /* Restore the original message length setting.  */
+  pp_line_cutoff (global_dc->printer) = saved_cutoff;
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -14547,6 +14679,7 @@ tree_c_tests ()
   test_labels ();
   test_vector_cst_patterns ();
   test_location_wrappers ();
+  test_escaped_strings ();
 }
 
 } // namespace selftest