From 631238ac3f50b42dd55e87cd8bea02c7fbec0f53 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Tue, 9 May 2017 02:47:14 +0000 Subject: [PATCH] PR translation/80280 - Missing closing quote (%>) c/semantics.c and c/c-typeck.c gcc/c-family/ChangeLog: 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. gcc/testsuite/ChangeLog: PR translation/80280 * gcc.dg/format/gcc_diag-10.c: New test. From-SVN: r247778 --- gcc/c-family/ChangeLog | 16 + gcc/c-family/c-format.c | 422 +++++++++++++--------- gcc/c-family/c-format.h | 14 +- gcc/testsuite/ChangeLog | 5 + gcc/testsuite/gcc.dg/format/gcc_diag-10.c | 151 ++++++++ 5 files changed, 432 insertions(+), 176 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/format/gcc_diag-10.c diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index e228e84b73b..7e882e1cf24 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,19 @@ +2017-05-08 Martin Sebor + + 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 * 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 400eb666d51..2dba0629f5f 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 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 ("% 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 ("% 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 ("% 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 ("% 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 13ca8eadbe4..37fa3828485 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 597930c69b2..8930e384064 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-05-08 Martin Sebor + + PR translation/80280 + * gcc.dg/format/gcc_diag-10.c: New test. + 2017-05-08 Kelvin Nilsen 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 00000000000..b3be2773957 --- /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 ("%%>"); /* { 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 ("%%>"); /* { 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 ("%%>"); /* { 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); +} -- 2.30.2