Ensure that control characters in user supplied error and warning messages are escaped.

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
This commit is contained in:
Nick Clifton 2018-06-18 10:39:01 +00:00 committed by Nick Clifton
parent a7fc274f87
commit eede1a6bf3
2 changed files with 158 additions and 7 deletions

View 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.

View 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