aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/c-family/ChangeLog16
-rw-r--r--gcc/c-family/c-format.c422
-rw-r--r--gcc/c-family/c-format.h14
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.dg/format/gcc_diag-10.c151
5 files changed, 432 insertions, 176 deletions
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index e228e84b..7e882e1 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,19 @@
+2017-05-08 Martin Sebor <msebor@redhat.com>
+
+ PR translation/80280
+ * c-format.h (struct format_flag_spec): Add new member.
+ (T89_T): New macro.
+ * c-format.c (local_tree_type_node): New global.
+ (printf_flag_specs, asm_fprintf_flag_spec): Initialize new data.
+ (gcc_diag_flag_specs, scanf_flag_specs, strftime_flag_specs): Ditto.
+ (strfmon_flag_specs): Likewise.
+ (gcc_diag_char_table, gcc_cdiag_char_table): Split up specifiers
+ with distinct quoting properties.
+ (gcc_tdiag_char_table, gcc_cxxdiag_char_table): Same.
+ (flag_chars_t::validate): Add argument and handle bad quoting.
+ (check_format_info_main): Handle quoting problems.
+ (init_dynamic_diag_info): Simplify.
+
2017-05-08 Jason Merrill <jason@redhat.com>
* c-opts.c (c_common_post_options): Update defaults for
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 400eb66..2dba062 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -53,6 +53,9 @@ struct function_format_info
unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */
};
+/* Initialized in init_dynamic_diag_info. */
+static tree local_tree_type_node;
+
static bool decode_format_attr (tree, function_format_info *, int);
static int decode_format_type (const char *);
@@ -492,17 +495,17 @@ static const format_length_info gcc_gfc_length_specs[] =
static const format_flag_spec printf_flag_specs[] =
{
- { ' ', 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 },
- { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
- { '#', 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 },
- { '0', 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 },
- { '-', 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 },
- { '\'', 0, 0, N_("''' flag"), N_("the ''' printf flag"), STD_EXT },
- { 'I', 0, 0, N_("'I' flag"), N_("the 'I' printf flag"), STD_EXT },
- { 'w', 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 },
- { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 },
- { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
- { 0, 0, 0, NULL, NULL, STD_C89 }
+ { ' ', 0, 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 },
+ { '+', 0, 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
+ { '#', 0, 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 },
+ { '0', 0, 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 },
+ { '-', 0, 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 },
+ { '\'', 0, 0, 0, N_("''' flag"), N_("the ''' printf flag"), STD_EXT },
+ { 'I', 0, 0, 0, N_("'I' flag"), N_("the 'I' printf flag"), STD_EXT },
+ { 'w', 0, 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 },
+ { 'p', 0, 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 },
+ { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+ { 0, 0, 0, 0, NULL, NULL, STD_C89 }
};
@@ -516,15 +519,15 @@ static const format_flag_pair printf_flag_pairs[] =
static const format_flag_spec asm_fprintf_flag_specs[] =
{
- { ' ', 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 },
- { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
- { '#', 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 },
- { '0', 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 },
- { '-', 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 },
- { 'w', 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 },
- { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 },
- { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
- { 0, 0, 0, NULL, NULL, STD_C89 }
+ { ' ', 0, 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 },
+ { '+', 0, 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
+ { '#', 0, 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 },
+ { '0', 0, 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 },
+ { '-', 0, 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 },
+ { 'w', 0, 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 },
+ { 'p', 0, 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 },
+ { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+ { 0, 0, 0, 0, NULL, NULL, STD_C89 }
};
static const format_flag_pair asm_fprintf_flag_pairs[] =
@@ -547,12 +550,12 @@ static const format_flag_pair gcc_diag_flag_pairs[] =
static const format_flag_spec gcc_diag_flag_specs[] =
{
- { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
- { '#', 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 },
- { 'q', 0, 0, N_("'q' flag"), N_("the 'q' diagnostic flag"), STD_C89 },
- { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 },
- { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
- { 0, 0, 0, NULL, NULL, STD_C89 }
+ { '+', 0, 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 },
+ { '#', 0, 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 },
+ { 'q', 0, 0, 1, N_("'q' flag"), N_("the 'q' diagnostic flag"), STD_C89 },
+ { 'p', 0, 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 },
+ { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+ { 0, 0, 0, 0, NULL, NULL, STD_C89 }
};
#define gcc_tdiag_flag_specs gcc_diag_flag_specs
@@ -562,14 +565,14 @@ static const format_flag_spec gcc_diag_flag_specs[] =
static const format_flag_spec scanf_flag_specs[] =
{
- { '*', 0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
- { 'a', 0, 0, N_("'a' flag"), N_("the 'a' scanf flag"), STD_EXT },
- { 'm', 0, 0, N_("'m' flag"), N_("the 'm' scanf flag"), STD_EXT },
- { 'w', 0, 0, N_("field width"), N_("field width in scanf format"), STD_C89 },
- { 'L', 0, 0, N_("length modifier"), N_("length modifier in scanf format"), STD_C89 },
- { '\'', 0, 0, N_("''' flag"), N_("the ''' scanf flag"), STD_EXT },
- { 'I', 0, 0, N_("'I' flag"), N_("the 'I' scanf flag"), STD_EXT },
- { 0, 0, 0, NULL, NULL, STD_C89 }
+ { '*', 0, 0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
+ { 'a', 0, 0, 0, N_("'a' flag"), N_("the 'a' scanf flag"), STD_EXT },
+ { 'm', 0, 0, 0, N_("'m' flag"), N_("the 'm' scanf flag"), STD_EXT },
+ { 'w', 0, 0, 0, N_("field width"), N_("field width in scanf format"), STD_C89 },
+ { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in scanf format"), STD_C89 },
+ { '\'', 0, 0, 0, N_("''' flag"), N_("the ''' scanf flag"), STD_EXT },
+ { 'I', 0, 0, 0, N_("'I' flag"), N_("the 'I' scanf flag"), STD_EXT },
+ { 0, 0, 0, 0, NULL, NULL, STD_C89 }
};
@@ -583,16 +586,16 @@ static const format_flag_pair scanf_flag_pairs[] =
static const format_flag_spec strftime_flag_specs[] =
{
- { '_', 0, 0, N_("'_' flag"), N_("the '_' strftime flag"), STD_EXT },
- { '-', 0, 0, N_("'-' flag"), N_("the '-' strftime flag"), STD_EXT },
- { '0', 0, 0, N_("'0' flag"), N_("the '0' strftime flag"), STD_EXT },
- { '^', 0, 0, N_("'^' flag"), N_("the '^' strftime flag"), STD_EXT },
- { '#', 0, 0, N_("'#' flag"), N_("the '#' strftime flag"), STD_EXT },
- { 'w', 0, 0, N_("field width"), N_("field width in strftime format"), STD_EXT },
- { 'E', 0, 0, N_("'E' modifier"), N_("the 'E' strftime modifier"), STD_C99 },
- { 'O', 0, 0, N_("'O' modifier"), N_("the 'O' strftime modifier"), STD_C99 },
- { 'O', 'o', 0, NULL, N_("the 'O' modifier"), STD_EXT },
- { 0, 0, 0, NULL, NULL, STD_C89 }
+ { '_', 0, 0, 0, N_("'_' flag"), N_("the '_' strftime flag"), STD_EXT },
+ { '-', 0, 0, 0, N_("'-' flag"), N_("the '-' strftime flag"), STD_EXT },
+ { '0', 0, 0, 0, N_("'0' flag"), N_("the '0' strftime flag"), STD_EXT },
+ { '^', 0, 0, 0, N_("'^' flag"), N_("the '^' strftime flag"), STD_EXT },
+ { '#', 0, 0, 0, N_("'#' flag"), N_("the '#' strftime flag"), STD_EXT },
+ { 'w', 0, 0, 0, N_("field width"), N_("field width in strftime format"), STD_EXT },
+ { 'E', 0, 0, 0, N_("'E' modifier"), N_("the 'E' strftime modifier"), STD_C99 },
+ { 'O', 0, 0, 0, N_("'O' modifier"), N_("the 'O' strftime modifier"), STD_C99 },
+ { 'O', 'o', 0, 0, NULL, N_("the 'O' modifier"), STD_EXT },
+ { 0, 0, 0, 0, NULL, NULL, STD_C89 }
};
@@ -609,17 +612,17 @@ static const format_flag_pair strftime_flag_pairs[] =
static const format_flag_spec strfmon_flag_specs[] =
{
- { '=', 0, 1, N_("fill character"), N_("fill character in strfmon format"), STD_C89 },
- { '^', 0, 0, N_("'^' flag"), N_("the '^' strfmon flag"), STD_C89 },
- { '+', 0, 0, N_("'+' flag"), N_("the '+' strfmon flag"), STD_C89 },
- { '(', 0, 0, N_("'(' flag"), N_("the '(' strfmon flag"), STD_C89 },
- { '!', 0, 0, N_("'!' flag"), N_("the '!' strfmon flag"), STD_C89 },
- { '-', 0, 0, N_("'-' flag"), N_("the '-' strfmon flag"), STD_C89 },
- { 'w', 0, 0, N_("field width"), N_("field width in strfmon format"), STD_C89 },
- { '#', 0, 0, N_("left precision"), N_("left precision in strfmon format"), STD_C89 },
- { 'p', 0, 0, N_("right precision"), N_("right precision in strfmon format"), STD_C89 },
- { 'L', 0, 0, N_("length modifier"), N_("length modifier in strfmon format"), STD_C89 },
- { 0, 0, 0, NULL, NULL, STD_C89 }
+ { '=', 0, 1, 0, N_("fill character"), N_("fill character in strfmon format"), STD_C89 },
+ { '^', 0, 0, 0, N_("'^' flag"), N_("the '^' strfmon flag"), STD_C89 },
+ { '+', 0, 0, 0, N_("'+' flag"), N_("the '+' strfmon flag"), STD_C89 },
+ { '(', 0, 0, 0, N_("'(' flag"), N_("the '(' strfmon flag"), STD_C89 },
+ { '!', 0, 0, 0, N_("'!' flag"), N_("the '!' strfmon flag"), STD_C89 },
+ { '-', 0, 0, 0, N_("'-' flag"), N_("the '-' strfmon flag"), STD_C89 },
+ { 'w', 0, 0, 0, N_("field width"), N_("field width in strfmon format"), STD_C89 },
+ { '#', 0, 0, 0, N_("left precision"), N_("left precision in strfmon format"), STD_C89 },
+ { 'p', 0, 0, 0, N_("right precision"), N_("right precision in strfmon format"), STD_C89 },
+ { 'L', 0, 0, 0, N_("length modifier"), N_("length modifier in strfmon format"), STD_C89 },
+ { 0, 0, 0, 0, NULL, NULL, STD_C89 }
};
static const format_flag_pair strfmon_flag_pairs[] =
@@ -685,10 +688,13 @@ static const format_char_info gcc_diag_char_table[] =
/* Custom conversion specifiers. */
/* These will require a "tree" at runtime. */
- { "K", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL },
+ { "K", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "\"", NULL },
- { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL },
- { "<>'R",0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "//cR", NULL },
+ { "<", 0, STD_C89, NOARGUMENTS, "", "<", NULL },
+ { ">", 0, STD_C89, NOARGUMENTS, "", ">", NULL },
+ { "'" , 0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "R", 0, STD_C89, NOARGUMENTS, "", "\\", NULL },
{ "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL },
{ NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
};
@@ -706,12 +712,17 @@ static const format_char_info gcc_tdiag_char_table[] =
/* Custom conversion specifiers. */
/* These will require a "tree" at runtime. */
- { "DFKTEV", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "", NULL },
+ { "DFTV", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "'", NULL },
+ { "E", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "", NULL },
+ { "K", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "\"", NULL },
{ "v", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q#", "", NULL },
- { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL },
- { "<>'R",0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "/cR", NULL },
+ { "<", 0, STD_C89, NOARGUMENTS, "", "<", NULL },
+ { ">", 0, STD_C89, NOARGUMENTS, "", ">", NULL },
+ { "'", 0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "R", 0, STD_C89, NOARGUMENTS, "", "\\", NULL },
{ "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL },
{ "Z", 1, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", &gcc_tdiag_char_table[0] },
{ NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
@@ -730,12 +741,17 @@ static const format_char_info gcc_cdiag_char_table[] =
/* Custom conversion specifiers. */
/* These will require a "tree" at runtime. */
- { "DEFKTV", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "", NULL },
+ { "DFTV", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "'", NULL },
+ { "E", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "", NULL },
+ { "K", 1, STD_C89, { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "\"", NULL },
{ "v", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q#", "", NULL },
- { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL },
- { "<>'R",0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "/cR", NULL },
+ { "<", 0, STD_C89, NOARGUMENTS, "", "<", NULL },
+ { ">", 0, STD_C89, NOARGUMENTS, "", ">", NULL },
+ { "'", 0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "R", 0, STD_C89, NOARGUMENTS, "", "\\", NULL },
{ "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL },
{ "Z", 1, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", &gcc_tdiag_char_table[0] },
{ NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
@@ -754,15 +770,19 @@ static const format_char_info gcc_cxxdiag_char_table[] =
/* Custom conversion specifiers. */
/* These will require a "tree" at runtime. */
- { "ADEFKSTVX",0,STD_C89,{ T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+#", "", NULL },
-
+ { "ADFSTVX",1,STD_C89,{ T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+#", "'", NULL },
+ { "E", 1,STD_C89,{ T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+#", "", NULL },
+ { "K", 1, STD_C89,{ T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "\"", NULL },
{ "v", 0,STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q#", "", NULL },
/* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.) */
{ "CLOPQ",0,STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL },
- { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL },
- { "<>'R",0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "/cR", NULL },
+ { "<", 0, STD_C89, NOARGUMENTS, "", "<", NULL },
+ { ">", 0, STD_C89, NOARGUMENTS, "", ">", NULL },
+ { "'", 0, STD_C89, NOARGUMENTS, "", "", NULL },
+ { "R", 0, STD_C89, NOARGUMENTS, "", "\\", NULL },
{ "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL },
{ "Z", 1, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", &gcc_tdiag_char_table[0] },
{ NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
@@ -1689,7 +1709,8 @@ class flag_chars_t
tree format_string_cst,
location_t format_string_loc,
const char * const orig_format_chars,
- char format_char);
+ char format_char,
+ bool quoted);
int get_alloc_flag (const format_kind_info *fki);
int assignment_suppression_p (const format_kind_info *fki);
@@ -1849,10 +1870,13 @@ flag_chars_t::validate (const format_kind_info *fki,
tree format_string_cst,
location_t format_string_loc,
const char * const orig_format_chars,
- char format_char)
+ char format_char,
+ bool quoted)
{
int i;
int d = 0;
+ bool quotflag = false;
+
for (i = 0; m_flag_chars[i] != 0; i++)
{
const format_flag_spec *s = get_flag_spec (flag_specs,
@@ -1860,6 +1884,10 @@ flag_chars_t::validate (const format_kind_info *fki,
m_flag_chars[i - d] = m_flag_chars[i];
if (m_flag_chars[i] == fki->length_code_char)
continue;
+
+ /* Remember if a quoting flag is seen. */
+ quotflag |= s->quoting;
+
if (strchr (fci->flag_chars, m_flag_chars[i]) == 0)
{
format_warning_at_char (format_string_loc, format_string_cst,
@@ -1891,8 +1919,30 @@ flag_chars_t::validate (const format_kind_info *fki,
format_char, fki->name);
}
}
+
+ /* Detect quoting directives used within a quoted sequence, such
+ as GCC's "%<...%qE". */
+ if (quoted && s->quoting)
+ {
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars - 1,
+ OPT_Wformat_,
+ "%s used within a quoted sequence",
+ _(s->name));
+ }
}
m_flag_chars[i - d] = 0;
+
+ if (!quoted
+ && !quotflag
+ && strchr (fci->flags2, '\''))
+ {
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars,
+ OPT_Wformat_,
+ "%qc conversion used unquoted",
+ format_char);
+ }
}
/* Determine if an assignment-allocation has been set, requiring
@@ -2704,6 +2754,16 @@ check_format_info_main (format_check_results *res,
and it didn't use $; 1 if $ formats are in use. */
int has_operand_number = -1;
+ /* Vector of pointers to opening quoting directives (like GCC "%<"). */
+ auto_vec<const char*> quotdirs;
+
+ /* Pointers to the most recent color directives (like GCC's "%r or %R").
+ A starting color directive much be terminated before the end of
+ the format string. A terminating directive makes no sense without
+ a prior starting directive. */
+ const char *color_begin = NULL;
+ const char *color_end = NULL;
+
init_dollar_format_checking (info->first_arg_num, first_fillin_param);
while (*format_chars != 0)
@@ -2785,11 +2845,72 @@ check_format_info_main (format_check_results *res,
flag_chars.validate (fki, fci, flag_specs, format_chars,
format_string_cst,
- format_string_loc, orig_format_chars, format_char);
+ format_string_loc, orig_format_chars, format_char,
+ quotdirs.length () > 0);
const int alloc_flag = flag_chars.get_alloc_flag (fki);
const bool suppressed = flag_chars.assignment_suppression_p (fki);
+ /* Diagnose nested or unmatched quoting directives such as GCC's
+ "%<...%<" and "%>...%>". */
+ bool quot_begin_p = strchr (fci->flags2, '<');
+ bool quot_end_p = strchr (fci->flags2, '>');
+
+ if (quot_begin_p && !quot_end_p)
+ {
+ if (quotdirs.length ())
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars,
+ OPT_Wformat_,
+ "nested quoting directive");
+ quotdirs.safe_push (format_chars);
+ }
+ else if (!quot_begin_p && quot_end_p)
+ {
+ if (quotdirs.length ())
+ quotdirs.pop ();
+ else
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars,
+ OPT_Wformat_,
+ "unmatched quoting directive");
+ }
+
+ bool color_begin_p = strchr (fci->flags2, '/');
+ if (color_begin_p)
+ {
+ color_begin = format_chars;
+ color_end = NULL;
+ }
+ else if (strchr (fci->flags2, '\\'))
+ {
+ if (color_end)
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars,
+ OPT_Wformat_,
+ "%qc directive redundant after prior "
+ "occurence of the same", format_char);
+ else if (!color_begin)
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars,
+ OPT_Wformat_,
+ "unmatched color reset directive");
+ color_end = format_chars;
+ }
+
+ /* Diagnose directives that shouldn't appear in a quoted sequence.
+ (They are denoted by a double quote in FLAGS2.) */
+ if (quotdirs.length ())
+ {
+ if (strchr (fci->flags2, '"'))
+ format_warning_at_char (format_string_loc, format_string_cst,
+ format_chars - orig_format_chars,
+ OPT_Wformat_,
+ "%qc conversion used within a quoted "
+ "sequence",
+ format_char);
+ }
+
/* Validate the pairs of flags used. */
arg_parser.validate_flag_pairs (fci, format_char);
@@ -2834,6 +2955,15 @@ check_format_info_main (format_check_results *res,
}
if (has_operand_number > 0)
finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK);
+
+ if (quotdirs.length ())
+ format_warning_at_char (format_string_loc, format_string_cst,
+ quotdirs.pop () - orig_format_chars,
+ OPT_Wformat_, "unterminated quoting directive");
+ if (color_begin && !color_end)
+ format_warning_at_char (format_string_loc, format_string_cst,
+ color_begin - orig_format_chars,
+ OPT_Wformat_, "unterminated color directive");
}
/* Check the argument types from a single format conversion (possibly
@@ -3654,58 +3784,58 @@ init_dynamic_gfc_info (void)
static void
init_dynamic_diag_info (void)
{
- static tree t, loc, hwi;
-
- if (!loc || !t || !hwi)
+ /* For the GCC-diagnostics custom format specifiers to work, one
+ must have declared 'tree' and 'location_t' prior to using those
+ attributes. If we haven't seen these declarations then
+ the specifiers requiring these types shouldn't be used.
+ However we don't force a hard ICE because we may see only one
+ or the other type. */
+ if (tree loc = maybe_get_identifier ("location_t"))
{
- static format_char_info *diag_fci, *tdiag_fci, *cdiag_fci, *cxxdiag_fci;
- static format_length_info *diag_ls;
- unsigned int i;
-
- /* For the GCC-diagnostics custom format specifiers to work, one
- must have declared 'tree' and/or 'location_t' prior to using
- those attributes. If we haven't seen these declarations then
- you shouldn't use the specifiers requiring these types.
- However we don't force a hard ICE because we may see only one
- or the other type. */
- if ((loc = maybe_get_identifier ("location_t")))
- {
- loc = identifier_global_value (loc);
- if (loc)
- {
- if (TREE_CODE (loc) != TYPE_DECL)
- {
- error ("%<location_t%> is not defined as a type");
- loc = 0;
- }
- else
- loc = TREE_TYPE (loc);
- }
- }
+ loc = identifier_global_value (loc);
+ if (loc && TREE_CODE (loc) != TYPE_DECL)
+ error ("%<location_t%> is not defined as a type");
+ }
+ /* Initialize the global tree node type local to this file. */
+ if (!local_tree_type_node
+ || local_tree_type_node == void_type_node)
+ {
/* We need to grab the underlying 'union tree_node' so peek into
an extra type level. */
- if ((t = maybe_get_identifier ("tree")))
+ if ((local_tree_type_node = maybe_get_identifier ("tree")))
{
- t = identifier_global_value (t);
- if (t)
+ local_tree_type_node = identifier_global_value (local_tree_type_node);
+ if (local_tree_type_node)
{
- if (TREE_CODE (t) != TYPE_DECL)
+ if (TREE_CODE (local_tree_type_node) != TYPE_DECL)
{
error ("%<tree%> is not defined as a type");
- t = 0;
+ local_tree_type_node = 0;
}
- else if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
+ else if (TREE_CODE (TREE_TYPE (local_tree_type_node))
+ != POINTER_TYPE)
{
error ("%<tree%> is not defined as a pointer type");
- t = 0;
+ local_tree_type_node = 0;
}
else
- t = TREE_TYPE (TREE_TYPE (t));
+ local_tree_type_node =
+ TREE_TYPE (TREE_TYPE (local_tree_type_node));
}
}
+ else
+ local_tree_type_node = void_type_node;
+ }
- /* Find the underlying type for HOST_WIDE_INT. For the %w
+ static tree hwi;
+
+ if (!hwi)
+ {
+ static format_length_info *diag_ls;
+ unsigned int i;
+
+ /* Find the underlying type for HOST_WIDE_INT. For the 'w'
length modifier to work, one must have issued: "typedef
HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code
prior to using that modifier. */
@@ -3757,75 +3887,17 @@ init_dynamic_diag_info (void)
else
gcc_unreachable ();
}
-
- /* Handle the __gcc_diag__ format specifics. */
- if (!diag_fci)
- dynamic_format_types[gcc_diag_format_type].conversion_specs =
- diag_fci = (format_char_info *)
- xmemdup (gcc_diag_char_table,
- sizeof (gcc_diag_char_table),
- sizeof (gcc_diag_char_table));
- if (t)
- {
- i = find_char_info_specifier_index (diag_fci, 'K');
- diag_fci[i].types[0].type = &t;
- diag_fci[i].pointer_count = 1;
- }
-
- /* Handle the __gcc_tdiag__ format specifics. */
- if (!tdiag_fci)
- dynamic_format_types[gcc_tdiag_format_type].conversion_specs =
- tdiag_fci = (format_char_info *)
- xmemdup (gcc_tdiag_char_table,
- sizeof (gcc_tdiag_char_table),
- sizeof (gcc_tdiag_char_table));
- if (t)
- {
- /* All specifiers taking a tree share the same struct. */
- i = find_char_info_specifier_index (tdiag_fci, 'D');
- tdiag_fci[i].types[0].type = &t;
- tdiag_fci[i].pointer_count = 1;
- i = find_char_info_specifier_index (tdiag_fci, 'K');
- tdiag_fci[i].types[0].type = &t;
- tdiag_fci[i].pointer_count = 1;
- }
-
- /* Handle the __gcc_cdiag__ format specifics. */
- if (!cdiag_fci)
- dynamic_format_types[gcc_cdiag_format_type].conversion_specs =
- cdiag_fci = (format_char_info *)
- xmemdup (gcc_cdiag_char_table,
- sizeof (gcc_cdiag_char_table),
- sizeof (gcc_cdiag_char_table));
- if (t)
- {
- /* All specifiers taking a tree share the same struct. */
- i = find_char_info_specifier_index (cdiag_fci, 'D');
- cdiag_fci[i].types[0].type = &t;
- cdiag_fci[i].pointer_count = 1;
- i = find_char_info_specifier_index (cdiag_fci, 'K');
- cdiag_fci[i].types[0].type = &t;
- cdiag_fci[i].pointer_count = 1;
- }
-
- /* Handle the __gcc_cxxdiag__ format specifics. */
- if (!cxxdiag_fci)
- dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs =
- cxxdiag_fci = (format_char_info *)
- xmemdup (gcc_cxxdiag_char_table,
- sizeof (gcc_cxxdiag_char_table),
- sizeof (gcc_cxxdiag_char_table));
- if (t)
- {
- /* All specifiers taking a tree share the same struct. */
- i = find_char_info_specifier_index (cxxdiag_fci, 'D');
- cxxdiag_fci[i].types[0].type = &t;
- cxxdiag_fci[i].pointer_count = 1;
- i = find_char_info_specifier_index (cxxdiag_fci, 'K');
- cxxdiag_fci[i].types[0].type = &t;
- cxxdiag_fci[i].pointer_count = 1;
- }
}
+
+ /* It's safe to "re-initialize these to the same values. */
+ dynamic_format_types[gcc_diag_format_type].conversion_specs =
+ gcc_diag_char_table;
+ dynamic_format_types[gcc_tdiag_format_type].conversion_specs =
+ gcc_tdiag_char_table;
+ dynamic_format_types[gcc_cdiag_format_type].conversion_specs =
+ gcc_cdiag_char_table;
+ dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs =
+ gcc_cxxdiag_char_table;
}
#ifdef TARGET_FORMAT_TYPES
diff --git a/gcc/c-family/c-format.h b/gcc/c-family/c-format.h
index 13ca8ea..37fa382 100644
--- a/gcc/c-family/c-format.h
+++ b/gcc/c-family/c-format.h
@@ -151,7 +151,16 @@ struct format_char_info
"W" if the argument is a pointer which is dereferenced and written into,
"R" if the argument is a pointer which is dereferenced and read from,
"i" for printf integer formats where the '0' flag is ignored with
- precision, and "[" for the starting character of a scanf scanset. */
+ precision, and "[" for the starting character of a scanf scanset,
+ "<" if the specifier introduces a quoted sequence (such as "%<"),
+ ">" if the specifier terminates a quoted sequence (such as "%>"),
+ "[" if the specifier introduces a color sequence (such as "%r"),
+ "]" if the specifier terminates a color sequence (such as "%R"),
+ "'" (single quote) if the specifier is expected to be quoted when
+ it appears outside a quoted sequence and unquoted otherwise (such
+ as the GCC internal printf format directive "%T"), and
+ "\"" (double quote) if the specifier is not expected to appear in
+ a quoted sequence (such as the GCC internal format directive "%K". */
const char *flags2;
/* If this format conversion character consumes more than one argument,
CHAIN points to information about the next argument. For later
@@ -178,6 +187,8 @@ struct format_flag_spec
/* Nonzero if the next character after this flag in the format should
be skipped ('=' in strfmon), zero otherwise. */
int skip_next_char;
+ /* True if the flag introduces quoting (as in GCC's %qE). */
+ bool quoting;
/* The name to use for this flag in diagnostic messages. For example,
N_("'0' flag"), N_("field width"). */
const char *name;
@@ -287,6 +298,7 @@ struct format_kind_info
#define T_UC &unsigned_char_type_node
#define T99_UC { STD_C99, NULL, T_UC }
#define T_V &void_type_node
+#define T89_T { STD_C89, NULL, &local_tree_type_node }
#define T89_V { STD_C89, NULL, T_V }
#define T_W &wchar_type_node
#define T94_W { STD_C94, "wchar_t", T_W }
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 597930c..8930e38 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2017-05-08 Martin Sebor <msebor@redhat.com>
+
+ PR translation/80280
+ * gcc.dg/format/gcc_diag-10.c: New test.
+
2017-05-08 Kelvin Nilsen <kelvin@gcc.gnu.org>
PR target/80101
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
new file mode 100644
index 0000000..b3be277
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
@@ -0,0 +1,151 @@
+/* Test for GCC internal format directives.
+ { dg-do compile }
+ { dg-options "-std=gnu99 -Wformat" } */
+
+/* Magic identifiers must be set before the attribute is used. */
+
+typedef long long __gcc_host_wide_int__;
+
+typedef struct location_s
+{
+ const char *file;
+ int line;
+} location_t;
+
+union tree_node;
+typedef union tree_node *tree;
+
+
+#define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__, 1, 2)))
+
+void diag (const char*, ...) FORMAT (diag);
+void cdiag (const char*, ...) FORMAT (cdiag);
+void tdiag (const char*, ...) FORMAT (tdiag);
+void cxxdiag (const char*, ...) FORMAT (cxxdiag);
+
+void test_diag (tree t)
+{
+ diag ("%<"); /* { dg-warning "unterminated quoting directive" } */
+ diag ("%>"); /* { dg-warning "unmatched quoting directive " } */
+ diag ("%<foo%<bar%>%>"); /* { dg-warning "nested quoting directive" } */
+
+ diag ("%K", t);
+
+ diag ("%R"); /* { dg-warning "unmatched color reset directive" } */
+ diag ("%r", ""); /* { dg-warning "unterminated color directive" } */
+ diag ("%r%r", "", ""); /* { dg-warning "unterminated color directive" } */
+ diag ("%r%R", "");
+ diag ("%r%r%R", "", "");
+ diag ("%r%R%r%R", "", "");
+
+ diag ("%<%K%>", t); /* { dg-warning ".K. conversion used within a quoted sequence" } */
+
+ diag ("%<%R%>"); /* { dg-warning "unmatched color reset directive" } */
+ diag ("%<%r%>", ""); /* { dg-warning "unterminated color directive" } */
+ diag ("%<%r%R%>", "");
+}
+
+void test_cdiag (tree t)
+{
+ cdiag ("%<"); /* { dg-warning "unterminated quoting directive" } */
+ cdiag ("%>"); /* { dg-warning "unmatched quoting directive " } */
+ cdiag ("%<foo%<bar%>%>"); /* { dg-warning "nested quoting directive" } */
+
+ cdiag ("%D", t); /* { dg-warning ".D. conversion used unquoted" } */
+ cdiag ("%E", t);
+ cdiag ("%F", t); /* { dg-warning ".F. conversion used unquoted" } */
+ cdiag ("%K", t);
+
+ cdiag ("%R"); /* { dg-warning "unmatched color reset directive" } */
+ cdiag ("%r", ""); /* { dg-warning "unterminated color directive" } */
+ cdiag ("%r%r", "", ""); /* { dg-warning "unterminated color directive" } */
+ cdiag ("%r%R", "");
+ cdiag ("%r%r%R", "", "");
+ cdiag ("%r%R%r%R", "", "");
+
+ cdiag ("%T", t); /* { dg-warning ".T. conversion used unquoted" } */
+ cdiag ("%V", t); /* { dg-warning ".V. conversion used unquoted" } */
+
+ cdiag ("%<%D%>", t);
+ cdiag ("%<%E%>", t);
+ cdiag ("%<%F%>", t);
+ cdiag ("%<%K%>", t); /* { dg-warning ".K. conversion used within a quoted sequence" } */
+
+ cdiag ("%<%R%>"); /* { dg-warning "unmatched color reset directive" } */
+ cdiag ("%<%r%>", ""); /* { dg-warning "unterminated color directive" } */
+ cdiag ("%<%r%R%>", "");
+
+ cdiag ("%<%T%>", t);
+ cdiag ("%<%V%>", t);
+
+ cdiag ("%<%qD%>", t); /* { dg-warning ".q. flag used within a quoted sequence" } */
+ cdiag ("%<%qE%>", t); /* { dg-warning ".q. flag used within a quoted sequence" } */
+ cdiag ("%<%qT%>", t); /* { dg-warning ".q. flag used within a quoted sequence" } */
+}
+
+void test_tdiag (tree t)
+{
+ tdiag ("%<"); /* { dg-warning "unterminated quoting directive" } */
+ tdiag ("%>"); /* { dg-warning "unmatched quoting directive " } */
+ tdiag ("%<foo%<bar%>%>"); /* { dg-warning "nested quoting directive" } */
+
+ tdiag ("%D", t); /* { dg-warning ".D. conversion used unquoted" } */
+ tdiag ("%E", t);
+ tdiag ("%K", t);
+
+ tdiag ("%R"); /* { dg-warning "unmatched color reset directive" } */
+ tdiag ("%r", ""); /* { dg-warning "unterminated color directive" } */
+ tdiag ("%r%r", "", ""); /* { dg-warning "unterminated color directive" } */
+ tdiag ("%r%R", "");
+ tdiag ("%r%R", "");
+ tdiag ("%r%r%R", "", "");
+ tdiag ("%r%R%r%R", "", "");
+
+ tdiag ("%T", t); /* { dg-warning ".T. conversion used unquoted" } */
+
+ tdiag ("%<%D%>", t);
+ tdiag ("%<%E%>", t);
+ tdiag ("%<%K%>", t); /* { dg-warning ".K. conversion used within a quoted sequence" } */
+
+ tdiag ("%<%R%>"); /* { dg-warning "unmatched color reset directive" } */
+ tdiag ("%<%r%>", ""); /* { dg-warning "unterminated color directive" } */
+ tdiag ("%<%r%R%>", "");
+
+ tdiag ("%<%T%>", t);
+
+ tdiag ("%<%qD%>", t); /* { dg-warning ".q. flag used within a quoted sequence" } */
+ tdiag ("%<%qE%>", t); /* { dg-warning ".q. flag used within a quoted sequence" } */
+ tdiag ("%<%qT%>", t); /* { dg-warning ".q. flag used within a quoted sequence" } */
+}
+
+void test_cxxdiag (tree t)
+{
+ cxxdiag ("%A", t); /* { dg-warning ".A. conversion used unquoted" } */
+ cxxdiag ("%D", t); /* { dg-warning ".D. conversion used unquoted" } */
+ cxxdiag ("%E", t);
+ cxxdiag ("%F", t); /* { dg-warning ".F. conversion used unquoted" } */
+
+ cxxdiag ("%R"); /* { dg-warning "unmatched color reset directive" } */
+ cxxdiag ("%r", ""); /* { dg-warning "unterminated color directive" } */
+ cxxdiag ("%r%r", "", ""); /* { dg-warning "unterminated color directive" } */
+ cxxdiag ("%r%R", "");
+ cxxdiag ("%r%R", "");
+ cxxdiag ("%r%r%R", "", "");
+ cxxdiag ("%r%R%r%R", "", "");
+
+ cxxdiag ("%S", t); /* { dg-warning ".S. conversion used unquoted" } */
+ cxxdiag ("%T", t); /* { dg-warning ".T. conversion used unquoted" } */
+ cxxdiag ("%V", t); /* { dg-warning ".V. conversion used unquoted" } */
+ cxxdiag ("%X", t); /* { dg-warning ".X. conversion used unquoted" } */
+
+ cxxdiag ("%<%A%>", t);
+ cxxdiag ("%<%D%>", t);
+ cxxdiag ("%<%E%>", t);
+ cxxdiag ("%<%F%>", t);
+ cxxdiag ("%<%R%>"); /* { dg-warning "unmatched color reset" } */
+ cxxdiag ("%<%r%R%>", "");
+ cxxdiag ("%<%S%>", t);
+ cxxdiag ("%<%T%>", t);
+ cxxdiag ("%<%V%>", t);
+ cxxdiag ("%<%X%>", t);
+}