On Fri, 2018-02-09 at 13:01 +0000, Nick Clifton wrote: > Hi David, Hi Martin, > > OK, take 3 of the patch is attached. In this version: > > * The escaping is handled by a new class. > * Self-tests have been added (and they helped find a bug - yay!) > * The documentation has been extended to mention -fmessage-length's > effect on the #-directives, and to add documentation for #pragma > GCC error and #pragma GCC warning. (Which appeared to be > missing). > > I did try to add backslash characters to the regexp in the new > testcase > but I just could not find a way to persuade dejagnu to accept them. > > OK to apply ? > > (Apologies for the poor C++ coding - it is not my strong point. I > am > an assembler level programmer at heart). > > Cheers > Nick
Thanks Nick, sorry for inflicting C++ on you. This looks much better. Out of interest, what bug did the selftests help find? Various nitpicks below > gcc/ChangeLog > 2018-02-09 Nick Clifton <ni...@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. This doesn't mention the changes to the docs. > gcc/testsuite/ChangeLog > 2018-02-07 Nick Clifton <ni...@redhat.com> > > * gcc.c-torture/compile/pr84195.c: New test. > Index: gcc/tree.c > =================================================================== > --- gcc/tree.c (revision 257519) > +++ gcc/tree.c (working copy) > @@ -12416,11 +12416,99 @@ > 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. We're still mostly using C-style comments for blocks, though I don't think we have an actual rule about this. > +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; > +}; I'd hoped that instead of this we could have an escape_string function return an instance of a class that has responsibility for the "if (ownership) free" dtor, but I don't think C++89 supports that (I had a go at implementing it, but I think we'd need C++11's move semantics, rather than just the NRVO). So the approach above is OK by me (which, given that I suggested it, may seem redundant :) ) > +void > +escaped_string::escape (const char * unescaped) > +{ > + /* 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. */ Move this comment to outside the function, as a descriptive comment for the function as a whole. > + 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; Is NULL a valid input here? If so, please add test coverage for that to the selftest. The "*unescaped == 0" is presumably a micro-optimization for the strlen(escaped) == 0 case, as it appears the code already handles this case. > + 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++] = '\\'; Some of the spacing in this function looks a bit odd to my eyes, but you're much more familiar with the GNU standards than me, and I don't want to be too nitpicky (e.g. the space before an array access, and the space after '!'). > + > + 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. */ > void > warn_deprecated_use (tree node, tree attr) > { > - const char *msg; > + class escaped_string msg; "class" is redundant here. > if (node == 0 || !warn_deprecated_decl) > return; > @@ -12442,9 +12530,7 @@ > 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; > if (DECL_P (node)) > @@ -12451,7 +12537,7 @@ > { > 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); > @@ -12478,7 +12564,7 @@ > { > 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); > @@ -12487,7 +12573,7 @@ > { > 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"); > @@ -12501,7 +12587,7 @@ > { > if (msg) > warning (OPT_Wdeprecated_declarations, "%qE is deprecated: %s", > - what, msg); > + what, (const char *) msg); > else > warning (OPT_Wdeprecated_declarations, "%qE is deprecated", > what); > } > @@ -12509,7 +12595,7 @@ > { > if (msg) > warning (OPT_Wdeprecated_declarations, "type is deprecated: %s", > - msg); > + (const char *) msg); > else > warning (OPT_Wdeprecated_declarations, "type is deprecated"); > } > @@ -14548,6 +14634,43 @@ > 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 (""); > + 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; > + > + 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 > @@ -14558,6 +14681,7 @@ > test_labels (); > test_vector_cst_patterns (); > test_location_wrappers (); > + test_escaped_strings (); > } > > } // namespace selftest > Index: gcc/doc/extend.texi > =================================================================== > --- gcc/doc/extend.texi (revision 257519) > +++ gcc/doc/extend.texi (working copy) > @@ -2563,6 +2563,9 @@ > The @code{deprecated} attribute can also be used for variables and > types (@pxref{Variable Attributes}, @pxref{Type Attributes}.) > > +The message attached to the attribute is affected by the setting of > +the @option{-fmessage-length} option. > + > @item error ("@var{message}") > @itemx warning ("@var{message}") > @cindex @code{error} function attribute > @@ -6077,6 +6080,9 @@ > types (@pxref{Common Function Attributes}, > @pxref{Common Type Attributes}). > > +The message attached to the attribute is affected by the setting of > +the @option{-fmessage-length} option. > + > @item nonstring > @cindex @code{nonstring} variable attribute > The @code{nonstring} variable attribute specifies that an object or member > @@ -7035,11 +7041,16 @@ > deprecated. Line 5 has no warning because T3 is explicitly > deprecated. Similarly for line 6. The optional @var{msg} > argument, which must be a string, is printed in the warning if > -present. > +present. Control characters in the string will be replaced with > +spaces, and if the @option{-fmessage-length} option is set to 0 (its > +default value) then any newline characters will also be replaced. > > The @code{deprecated} attribute can also be used for functions and > variables (@pxref{Function Attributes}, @pxref{Variable Attributes}.) > > +The message attached to the attribute is affected by the setting of > +the @option{-fmessage-length} option. > + > @item designated_init > @cindex @code{designated_init} type attribute > This attribute may only be applied to structure types. It indicates > @@ -22372,7 +22383,9 @@ > @cindex pragma, diagnostic > > Prints @var{string} as a compiler message on compilation. The message > -is informational only, and is neither a compilation warning nor an error. > +is informational only, and is neither a compilation warning nor an > +error. Newlines can be included in the string by using the @samp{\n} > +escape sequence. > > @smallexample > #pragma message "Compiling " __FILE__ "..." > @@ -22392,6 +22405,37 @@ > prints @samp{/tmp/file.c:4: note: #pragma message: > TODO - Remember to fix this}. > > +@item #pragma GCC error @var{message} > +@cindex pragma, diagnostic > +Generates an error message. This pragma @emph{is} considered to > +indicate an error in the compilation, and it will be treated as such. > + > +Newlines can be included in the string by using the @samp{\n} > +escape sequence. They will be displayed as newlines even if the > +@option{-fmessage-length} option is set to zero. > + > +The error is only generated if the pragma is present in the code after > +pre-processing has been completed. It does not matter however if the > +code containing the pragma is unreachable: > + > +@smallexample > +#if 0 > +#pragma GCC error "this error is not seen" > +#endif > +void foo (void) > +@{ > + return; > +#pragma GCC error "this error is seen" > +@} > +@end smallexample > + > +@item #pragma GCC warning @var{message} > +@cindex pragma, diagnostic > +This is just like @samp{pragma GCC error} except that a warning > +message is issued instead of an error message. Unless > +@option{-Werror} is in effect, in which case this pragma will generate > +an error as well. > + > @end table > > @node Visibility Pragmas > Index: gcc/doc/invoke.texi > =================================================================== > --- gcc/doc/invoke.texi (revision 257519) > +++ gcc/doc/invoke.texi (working copy) > @@ -3496,6 +3496,11 @@ > done; each error message appears on a single line. This is the > default for all front ends. > > +Note - this option also affects the display of the @samp{#error} and > +@samp{#warning} pre-processor directives, and the @samp{deprecated} > +function/type/variable attribute. It does not however affect the > +@samp{pragma GCC warning} and @samp{pragma GCC error} pragmas. > + > @item -fdiagnostics-show-location=once > @opindex fdiagnostics-show-location > Only meaningful in line-wrapping mode. Instructs the diagnostic messages > --- /dev/null 2018-02-08 08:21:34.475648704 +0000 > +++ gcc/testsuite/gcc.c-torture/compile/pr84195.c 2018-02-07 > 17:20:13.494587052 +0000 > @@ -0,0 +1,17 @@ > +/* { dg-options "-Wdeprecated-declarations" } */ > + > +/* Check that MSG is printed without the escape characters being interpreted. > + Especially the newlines. > + > + Note - gcc's behaviour is inconsistent in this regard as #error and > + #warning will also display control characters as escape sequences, > + whereas #pragma GCC error and #pragma GCC warning will perform the > + control operations of the control characters. */ > + > +#define MSG "foo\n\t\rbar" > + > +int f (int i __attribute__ ((deprecated (MSG)))) > +{ > + return 0 ? i : 0; /* { dg-warning "'i' is deprecated: foo.n.t.rbar" } */ > +} Thanks for updating/fixing the docs. Other than the nitpicks above, this looks good. I believe I can approve this with my "diagnostics messages" maintainer hat on, but given that we're deep in stage 4 and this isn't a regression, please queue it up for gcc 9 stage 1 (unless there's a pressing need to get this into gcc 8, in which case talk to the release managers!) Dave