summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>2000-09-01 22:09:55 +0000
committerjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>2000-09-01 22:09:55 +0000
commit6659e606b71ad1cd94cb3bbb1fb8e6afdf753e04 (patch)
tree90f159e42be0a2896231846a7d740d3767007896
parent9f37edbeb0ccfe97d11850334de98b25ab34bc09 (diff)
downloadgcc-6659e606b71ad1cd94cb3bbb1fb8e6afdf753e04.tar.gz
* c-common.h (enum c_tree_index): Add CTI_C_SIZE_TYPE.
(c_size_type_node): Define. * c-decl.c (init_decl_processing): Initialize c_size_type_node. * c-common.c (enum format_lengths, enum format_std_version, format_length_info, format_type_detail, BADLEN, NOLENGTHS, format_kind_info, printf_length_specs, scanf_length_specs, T89_I, T99_I, T89_L, T99_LL, TEX_LL, T89_S, T89_UI, T99_UI, T89_UL, T99_ULL, TEX_ULL, T89_US, T89_F, T99_F, T89_D, T99_D, T89_LD, T99_LD, T89_C, T99_SC, T99_UC, T89_V, T94_W, TEX_W, T94_WI, TEX_WI, T99_ST, T99_SST, T99_PD, T99_UPD, T99_IM, T99_UIM, format_types): Define. (format_char_info, print_char_table, scan_char_table, time_char_table): Rearrange for new organization of information about format length modifiers and standard versions. (T_ST): Redefine to use c_size_type_node. (check_format_info): Obtain information about length modifiers and standard versions from tables. Adjust warning message wordings. Use the name from the user's program for `ll' and `hh' length modifiers in warning messages. Use more informative names for wanted types where available (for wchar_t, wint_t, size_t, signed size_t, ptrdiff_t, unsigned ptrdiff_t, intmax_t and uintmax_t). testsuite: * gcc.dg/format-diag-1.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@36106 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog24
-rw-r--r--gcc/c-common.c508
-rw-r--r--gcc/c-common.h2
-rw-r--r--gcc/c-decl.c1
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.dg/format-diag-1.c18
6 files changed, 359 insertions, 198 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 8e0b3878a49..52204553f1c 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,27 @@
+2000-09-01 Joseph S. Myers <jsm28@cam.ac.uk>
+
+ * c-common.h (enum c_tree_index): Add CTI_C_SIZE_TYPE.
+ (c_size_type_node): Define.
+ * c-decl.c (init_decl_processing): Initialize c_size_type_node.
+ * c-common.c (enum format_lengths, enum format_std_version,
+ format_length_info, format_type_detail, BADLEN, NOLENGTHS,
+ format_kind_info, printf_length_specs, scanf_length_specs, T89_I,
+ T99_I, T89_L, T99_LL, TEX_LL, T89_S, T89_UI, T99_UI, T89_UL,
+ T99_ULL, TEX_ULL, T89_US, T89_F, T99_F, T89_D, T99_D, T89_LD,
+ T99_LD, T89_C, T99_SC, T99_UC, T89_V, T94_W, TEX_W, T94_WI,
+ TEX_WI, T99_ST, T99_SST, T99_PD, T99_UPD, T99_IM, T99_UIM,
+ format_types): Define.
+ (format_char_info, print_char_table, scan_char_table,
+ time_char_table): Rearrange for new organization of information
+ about format length modifiers and standard versions.
+ (T_ST): Redefine to use c_size_type_node.
+ (check_format_info): Obtain information about length modifiers and
+ standard versions from tables. Adjust warning message wordings.
+ Use the name from the user's program for `ll' and `hh' length
+ modifiers in warning messages. Use more informative names for
+ wanted types where available (for wchar_t, wint_t, size_t, signed
+ size_t, ptrdiff_t, unsigned ptrdiff_t, intmax_t and uintmax_t).
+
2000-09-01 Jim Wilson <wilson@cygnus.com>
* loop.c (check_final_value): Check for biv use before checking for
diff --git a/gcc/c-common.c b/gcc/c-common.c
index 7fcdbebfcc7..9c6564721b3 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -1191,128 +1191,268 @@ strip_attrs (specs_attrs)
/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
a parameter list. */
+/* The meaningfully distinct length modifiers for format checking recognised
+ by GCC. */
+enum format_lengths
+{
+ FMT_LEN_none,
+ FMT_LEN_hh,
+ FMT_LEN_h,
+ FMT_LEN_l,
+ FMT_LEN_ll,
+ FMT_LEN_L,
+ FMT_LEN_z,
+ FMT_LEN_t,
+ FMT_LEN_j,
+ FMT_LEN_MAX
+};
+
+
+/* The standard versions in which various format features appeared. */
+enum format_std_version
+{
+ STD_C89,
+ STD_C94,
+ STD_C99,
+ STD_EXT
+};
+
+
+/* Structure describing a length modifier supported in format checking, and
+ possibly a doubled version such as "hh". */
+typedef struct
+{
+ /* Name of the single-character length modifier. */
+ const char *name;
+ /* Index into a format_char_info.types array. */
+ enum format_lengths index;
+ /* Standard version this length appears in. */
+ enum format_std_version std;
+ /* Same, if the modifier can be repeated, or NULL if it can't. */
+ const char *double_name;
+ enum format_lengths double_index;
+ enum format_std_version double_std;
+} format_length_info;
+
+
+/* Structure desribing the combination of a conversion specifier
+ (or a set of specifiers which act identically) and a length modifier. */
+typedef struct
+{
+ /* The standard version this combination of length and type appeared in.
+ This is only relevant if greater than those for length and type
+ individually; otherwise it is ignored. */
+ enum format_std_version std;
+ /* The name to use for the type, if different from that generated internally
+ (e.g., "signed size_t"). */
+ const char *name;
+ /* The type itself. */
+ tree *type;
+} format_type_detail;
+
+
+/* Macros to fill out tables of these. */
+#define BADLEN { 0, NULL, NULL }
+#define NOLENGTHS { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
+
+
+/* Structure desribing a format conversion specifier (or a set of specifiers
+ which act identically), and the length modifiers used with it. */
+typedef struct
+{
+ const char *format_chars;
+ int pointer_count;
+ enum format_std_version std;
+ /* Types accepted for each length modifier. */
+ format_type_detail types[FMT_LEN_MAX];
+ /* List of other modifier characters allowed with these options.
+ This lists flags, and additionally "w" for width, "p" for precision,
+ "c" for generic character pointers being allowed, "a" for scanf
+ "a" allocation extension (not applicable in C99 mode), "*" for
+ scanf suppression, "2" for strftime two digit year formats, "3"
+ for strftime formats giving two digit years in some locales, "E"
+ and "O" for those strftime modifiers, and "o" if use of strftime "O"
+ is a GNU extension beyond C99. */
+ const char *flag_chars;
+} format_char_info;
+
+
+/* Structure describing a particular kind of format processed by GCC. */
+typedef struct
+{
+ /* The name of this kind of format, for use in diagnostics. */
+ const char *name;
+ /* Specifications of the length modifiers accepted; possibly NULL. */
+ const format_length_info *length_char_specs;
+ /* Details of the conversion specification characters accepted. */
+ const format_char_info *conversion_specs;
+} format_kind_info;
+
+
+static const format_length_info printf_length_specs[] =
+{
+ { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
+ { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C99 },
+ { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
+ { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
+ { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
+ { "Z", FMT_LEN_z, STD_EXT, NULL, 0, 0 },
+ { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
+ { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
+ { NULL, 0, 0, NULL, 0, 0 }
+};
+
+
+/* This differs from printf_length_specs only in that "Z" is not accepted. */
+static const format_length_info scanf_length_specs[] =
+{
+ { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
+ { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C99 },
+ { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
+ { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
+ { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
+ { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
+ { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
+ { NULL, 0, 0, NULL, 0, 0 }
+};
+
+
#define T_I &integer_type_node
+#define T89_I { STD_C89, NULL, T_I }
+#define T99_I { STD_C99, NULL, T_I }
#define T_L &long_integer_type_node
+#define T89_L { STD_C89, NULL, T_L }
#define T_LL &long_long_integer_type_node
+#define T99_LL { STD_C99, NULL, T_LL }
+#define TEX_LL { STD_EXT, NULL, T_LL }
#define T_S &short_integer_type_node
+#define T89_S { STD_C89, NULL, T_S }
#define T_UI &unsigned_type_node
+#define T89_UI { STD_C89, NULL, T_UI }
+#define T99_UI { STD_C99, NULL, T_UI }
#define T_UL &long_unsigned_type_node
+#define T89_UL { STD_C89, NULL, T_UL }
#define T_ULL &long_long_unsigned_type_node
+#define T99_ULL { STD_C99, NULL, T_ULL }
+#define TEX_ULL { STD_EXT, NULL, T_ULL }
#define T_US &short_unsigned_type_node
+#define T89_US { STD_C89, NULL, T_US }
#define T_F &float_type_node
+#define T89_F { STD_C89, NULL, T_F }
+#define T99_F { STD_C99, NULL, T_F }
#define T_D &double_type_node
+#define T89_D { STD_C89, NULL, T_D }
+#define T99_D { STD_C99, NULL, T_D }
#define T_LD &long_double_type_node
+#define T89_LD { STD_C89, NULL, T_LD }
+#define T99_LD { STD_C99, NULL, T_LD }
#define T_C &char_type_node
+#define T89_C { STD_C89, NULL, T_C }
#define T_SC &signed_char_type_node
+#define T99_SC { STD_C99, NULL, T_SC }
#define T_UC &unsigned_char_type_node
+#define T99_UC { STD_C99, NULL, T_UC }
#define T_V &void_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 }
+#define TEX_W { STD_EXT, "wchar_t", T_W }
#define T_WI &wint_type_node
-#define T_ST &sizetype
+#define T94_WI { STD_C94, "wint_t", T_WI }
+#define TEX_WI { STD_EXT, "wint_t", T_WI }
+#define T_ST &c_size_type_node
+#define T99_ST { STD_C99, "size_t", T_ST }
#define T_SST &signed_size_type_node
+#define T99_SST { STD_C99, "signed size_t", T_SST }
#define T_PD &ptrdiff_type_node
+#define T99_PD { STD_C99, "ptrdiff_t", T_PD }
#define T_UPD &unsigned_ptrdiff_type_node
+#define T99_UPD { STD_C99, "unsigned ptrdiff_t", T_UPD }
#define T_IM NULL /* intmax_t not yet implemented. */
+#define T99_IM { STD_C99, "intmax_t", T_IM }
#define T_UIM NULL /* uintmax_t not yet implemented. */
+#define T99_UIM { STD_C99, "uintmax_t", T_UIM }
-typedef struct {
- const char *format_chars;
- int pointer_count;
- /* Type of argument if no length modifier is used. */
- tree *nolen;
- /* Type of argument if length modifier for shortening to byte is used.
- If NULL, then this modifier is not allowed. */
- tree *hhlen;
- /* Type of argument if length modifier for shortening is used.
- If NULL, then this modifier is not allowed. */
- tree *hlen;
- /* Type of argument if length modifier `l' is used.
- If NULL, then this modifier is not allowed. */
- tree *llen;
- /* Type of argument if length modifier `q' or `ll' is used.
- If NULL, then this modifier is not allowed. */
- tree *qlen;
- /* Type of argument if length modifier `L' is used.
- If NULL, then this modifier is not allowed. */
- tree *bigllen;
- /* Type of argument if length modifiers 'z' or `Z' is used.
- If NULL, then this modifier is not allowed. */
- tree *zlen;
- /* Type of argument if length modifier 't' is used.
- If NULL, then this modifier is not allowed. */
- tree *tlen;
- /* Type of argument if length modifier 'j' is used.
- If NULL, then this modifier is not allowed. */
- tree *jlen;
- /* List of other modifier characters allowed with these options. */
- const char *flag_chars;
-} format_char_info;
+static const format_char_info print_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "di", 0, STD_C89, { T89_I, T99_I, T89_I, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "-wp0 +'I" },
+ { "oxX", 0, STD_C89, { T89_UI, T99_UI, T89_UI, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0#" },
+ { "u", 0, STD_C89, { T89_UI, T99_UI, T89_UI, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "-wp0'I" },
+ { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#'" },
+ { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#" },
+ { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w" },
+ { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wpc" },
+ { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wc" },
+ { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "" },
+ /* C99 conversion specifiers. */
+ { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#'" },
+ { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "-wp0 +#" },
+ /* X/Open conversion specifiers. */
+ { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w" },
+ { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp" },
+ /* GNU conversion specifiers. */
+ { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp" },
+ { NULL, 0, 0, NOLENGTHS, NULL }
+};
-static format_char_info print_char_table[] = {
- { "di", 0, T_I, T_I, T_I, T_L, T_LL, T_LL, T_SST, T_PD, T_IM, "-wp0 +'I" },
- { "oxX", 0, T_UI, T_UI, T_UI, T_UL, T_ULL, T_ULL, T_ST, T_UPD, T_UIM, "-wp0#" },
- { "u", 0, T_UI, T_UI, T_UI, T_UL, T_ULL, T_ULL, T_ST, T_UPD, T_UIM, "-wp0'I" },
-/* A GNU extension. */
- { "m", 0, T_V, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-wp" },
- { "fFgG", 0, T_D, NULL, NULL, T_D, NULL, T_LD, NULL, NULL, NULL, "-wp0 +#'" },
- { "eEaA", 0, T_D, NULL, NULL, T_D, NULL, T_LD, NULL, NULL, NULL, "-wp0 +#" },
- { "c", 0, T_I, NULL, NULL, T_WI, NULL, NULL, NULL, NULL, NULL, "-w" },
- { "C", 0, T_WI, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-w" },
- { "s", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, NULL, NULL, "-wpc" },
- { "S", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-wp" },
- { "p", 1, T_V, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-wc" },
- { "n", 1, T_I, T_SC, T_S, T_L, T_LL, NULL, T_SST, T_PD, T_IM, "" },
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+static const format_char_info scan_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "di", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, TEX_LL, T99_SST, T99_PD, T99_IM }, "*w" },
+ { "ouxX", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T99_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM }, "*w" },
+ { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "*w" },
+ { "c", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*cw" },
+ { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*acw" },
+ { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*acw" },
+ { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w" },
+ { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T99_LL, BADLEN, T99_SST, T99_PD, T99_IM }, "" },
+ /* C99 conversion specifiers. */
+ { "FaA", 1, STD_C99, { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN }, "*w" },
+ /* X/Open conversion specifiers. */
+ { "C", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w" },
+ { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw" },
+ { NULL, 0, 0, NOLENGTHS, NULL }
};
-static format_char_info scan_char_table[] = {
- { "di", 1, T_I, T_SC, T_S, T_L, T_LL, T_LL, T_SST, T_PD, T_IM, "*w" },
- { "ouxX", 1, T_UI, T_UC, T_US, T_UL, T_ULL, T_ULL, T_ST, T_UPD, T_UIM, "*w" },
- { "efFgEGaA", 1, T_F, NULL, NULL, T_D, NULL, T_LD, NULL, NULL, NULL, "*w" },
- { "c", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, NULL, NULL, "*cw" },
- { "s", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, NULL, NULL, "*acw" },
- { "[", 1, T_C, NULL, NULL, T_W, NULL, NULL, NULL, NULL, NULL, "*acw" },
- { "C", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "*w" },
- { "S", 1, T_W, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "*aw" },
- { "p", 2, T_V, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "*w" },
- { "n", 1, T_I, T_SC, T_S, T_L, T_LL, NULL, T_SST, T_PD, T_IM, "" },
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+static format_char_info time_char_table[] =
+{
+ /* C89 conversion specifiers. */
+ { "ABZa", 0, STD_C89, NOLENGTHS, "^#" },
+ { "b", 0, STD_C89, NOLENGTHS, "^" },
+ { "cx", 0, STD_C89, NOLENGTHS, "3E" },
+ { "HIMSUWdmw", 0, STD_C89, NOLENGTHS, "-_0Ow" },
+ { "j", 0, STD_C89, NOLENGTHS, "-_0Oow" },
+ { "p", 0, STD_C89, NOLENGTHS, "#" },
+ { "X", 0, STD_C89, NOLENGTHS, "E" },
+ { "y", 0, STD_C89, NOLENGTHS, "2EO-_0w" },
+ { "Y", 0, STD_C89, NOLENGTHS, "-_0EOow" },
+ { "%", 0, STD_C89, NOLENGTHS, "" },
+ /* C99 conversion specifiers. */
+ { "C", 0, STD_C99, NOLENGTHS, "-_0EOow" },
+ { "D", 0, STD_C99, NOLENGTHS, "2" },
+ { "eVu", 0, STD_C99, NOLENGTHS, "-_0Ow" },
+ { "FRTnrt", 0, STD_C99, NOLENGTHS, "" },
+ { "g", 0, STD_C99, NOLENGTHS, "2Oo-_0w" },
+ { "G", 0, STD_C99, NOLENGTHS, "-_0Oow" },
+ { "h", 0, STD_C99, NOLENGTHS, "^" },
+ { "z", 0, STD_C99, NOLENGTHS, "Oo" },
+ /* GNU conversion specifiers. */
+ { "kls", 0, STD_EXT, NOLENGTHS, "-_0Ow" },
+ { "P", 0, STD_EXT, NOLENGTHS, "" },
+ { NULL, 0, 0, NOLENGTHS, NULL }
};
-/* Handle format characters recognized by glibc's strftime.c.
- '2' - MUST do years as only two digits
- '3' - MAY do years as only two digits (depending on locale)
- 'E' - E modifier is acceptable
- 'O' - O modifier is acceptable to Standard C
- 'o' - O modifier is acceptable as a GNU extension
- '9' - added to the C standard in C99
- 'G' - other GNU extensions */
-
-static format_char_info time_char_table[] = {
- { "y", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
- { "D", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "29" },
- { "g", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2Oo-_0w9" },
- { "cx", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
- { "%", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "" },
- { "X", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "E" },
- { "FRTnrt", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "9" },
- { "P", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
- { "HIMSUWdmw", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
- { "e", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow9" },
- { "j", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
- { "Vu", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow9" },
- { "G", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow9" },
- { "z", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Oo9" },
- { "kls", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
- { "ABZa", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
- { "p", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
- { "b", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
- { "h", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^9" },
- { "Y", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOow" },
- { "C", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOow9" },
- { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+
+/* This must be in the same order as enum format_type. */
+static const format_kind_info format_types[] =
+{
+ { "printf", printf_length_specs, print_char_table },
+ { "scanf", scanf_length_specs, scan_char_table },
+ { "strftime", NULL, time_char_table }
};
+
typedef struct function_format_info
{
struct function_format_info *next; /* next structure on the list */
@@ -1694,16 +1834,22 @@ check_format_info (info, params)
int i;
int arg_num;
int suppressed, wide, precise;
- int length_char = 0;
+ const char *length_chars = NULL;
+ enum format_lengths length_chars_val = FMT_LEN_none;
+ enum format_std_version length_chars_std = STD_C89;
int format_char;
int format_length;
tree format_tree;
tree cur_param;
tree cur_type;
tree wanted_type;
+ enum format_std_version wanted_type_std;
+ const char *wanted_type_name;
tree first_fillin_param;
const char *format_chars;
- format_char_info *fci = NULL;
+ const format_kind_info *fki = NULL;
+ const format_length_info *fli = NULL;
+ const format_char_info *fci = NULL;
char flag_chars[8];
/* -1 if no conversions taking an operand have been found; 0 if one has
and it didn't use $; 1 if $ formats are in use. */
@@ -1808,6 +1954,7 @@ check_format_info (info, params)
first_fillin_param = params;
init_dollar_format_checking (info->first_arg_num, first_fillin_param);
+ fki = &format_types[info->format_type];
while (1)
{
int aflag;
@@ -2056,52 +2203,44 @@ check_format_info (info, params)
aflag = 0;
- if (info->format_type != strftime_format_type)
+ fli = fki->length_char_specs;
+ if (fli)
{
- if (*format_chars == 'h' || *format_chars == 'l')
- length_char = *format_chars++;
- else if (*format_chars == 'q' || *format_chars == 'L')
- {
- length_char = *format_chars++;
- if (length_char == 'q' && pedantic)
- warning ("ISO C does not support the `%c' length modifier",
- length_char);
- }
- else if (*format_chars == 'z'
- || (*format_chars == 'Z'
- && info->format_type == printf_format_type))
+ while (fli->name != 0 && fli->name[0] != *format_chars)
+ fli++;
+ if (fli->name != 0)
{
- length_char = *format_chars++;
- if (pedantic)
+ format_chars++;
+ if (fli->double_name != 0 && fli->name[0] == *format_chars)
{
- if (length_char == 'Z')
- warning ("ISO C does not support the `%c' length modifier",
- length_char);
- else if (!flag_isoc99)
- warning ("ISO C89 does not support the `%c' length modifier",
- length_char);
+ format_chars++;
+ length_chars = fli->double_name;
+ length_chars_val = fli->double_index;
+ length_chars_std = fli->double_std;
+ }
+ else
+ {
+ length_chars = fli->name;
+ length_chars_val = fli->index;
+ length_chars_std = fli->std;
}
- }
- else if (*format_chars == 't' || *format_chars == 'j')
- {
- length_char = *format_chars++;
- if (pedantic && !flag_isoc99)
- warning ("ISO C89 does not support the `%c' length modifier",
- length_char);
}
else
- length_char = 0;
- if (length_char == 'l' && *format_chars == 'l')
{
- length_char = 'q', format_chars++;
- if (pedantic && !flag_isoc99)
- warning ("ISO C89 does not support the `ll' length modifier");
+ length_chars = NULL;
+ length_chars_val = FMT_LEN_none;
+ length_chars_std = STD_C89;
}
- else if (length_char == 'h' && *format_chars == 'h')
+ if (pedantic)
{
- length_char = 'H', format_chars++;
- if (pedantic && !flag_isoc99)
- warning ("ISO C89 does not support the `hh' length modifier");
+ /* Warn if the length modifier is non-standard. */
+ if (length_chars_std == STD_EXT)
+ warning ("ISO C does not support the `%s' %s length modifier",
+ length_chars, fki->name);
+ else if ((length_chars_std == STD_C99 && !flag_isoc99)
+ || (length_chars_std == STD_C94 && !flag_isoc94))
+ warning ("ISO C89 does not support the `%s' %s length modifier",
+ length_chars, fki->name);
}
if (*format_chars == 'a' && info->format_type == scanf_format_type
&& !flag_isoc99)
@@ -2114,8 +2253,8 @@ check_format_info (info, params)
format_chars++;
}
}
- if (suppressed && length_char != 0)
- warning ("use of `*' and `%c' together in format", length_char);
+ if (suppressed && length_chars_val != FMT_LEN_none)
+ warning ("use of `*' and `%s' together in format", length_chars);
}
format_char = *format_chars;
if (format_char == 0
@@ -2124,30 +2263,8 @@ check_format_info (info, params)
warning ("conversion lacks type at end of format");
continue;
}
- /* The m, C, and S formats are GNU extensions. */
- if (pedantic && info->format_type != strftime_format_type
- && (format_char == 'm' || format_char == 'C' || format_char == 'S'))
- warning ("ISO C does not support the `%c' format", format_char);
- /* The a, A and F formats are C99 extensions. */
- if (pedantic && info->format_type != strftime_format_type
- && (format_char == 'a' || format_char == 'A' || format_char == 'F')
- && !flag_isoc99)
- warning ("ISO C89 does not support the `%c' format", format_char);
format_chars++;
- switch (info->format_type)
- {
- case printf_format_type:
- fci = print_char_table;
- break;
- case scanf_format_type:
- fci = scan_char_table;
- break;
- case strftime_format_type:
- fci = time_char_table;
- break;
- default:
- abort ();
- }
+ fci = fki->conversion_specs;
while (fci->format_chars != 0
&& index (fci->format_chars, format_char) == 0)
++fci;
@@ -2163,10 +2280,13 @@ check_format_info (info, params)
}
if (pedantic)
{
- if (index (fci->flag_chars, 'G') != 0)
- warning ("ISO C does not support `%%%c'", format_char);
- if (index (fci->flag_chars, '9') != 0 && !flag_isoc99)
- warning ("ISO C89 does not support `%%%c'", format_char);
+ if (fci->std == STD_EXT)
+ warning ("ISO C does not support the `%%%c' %s format",
+ format_char, fki->name);
+ else if ((fci->std == STD_C99 && !flag_isoc99)
+ || (fci->std == STD_C94 && !flag_isoc94))
+ warning ("ISO C89 does not support the `%%%c' %s format",
+ format_char, fki->name);
if (index (flag_chars, 'O') != 0)
{
if (index (fci->flag_chars, 'o') != 0)
@@ -2231,45 +2351,28 @@ check_format_info (info, params)
|| format_char == 'x' || format_char == 'X'))
warning ("`0' flag ignored with precision specifier and `%c' format",
format_char);
- switch (length_char)
+ wanted_type = (fci->types[length_chars_val].type
+ ? *fci->types[length_chars_val].type : 0);
+ wanted_type_name = fci->types[length_chars_val].name;
+ wanted_type_std = fci->types[length_chars_val].std;
+ if (wanted_type == 0)
+ warning ("use of `%s' length modifier with `%c' type character",
+ length_chars, format_char);
+ else if (pedantic
+ /* Warn if non-standard, provided it is more non-standard
+ than the length and type characters that may already
+ have been warned for. */
+ && wanted_type_std > length_chars_std
+ && wanted_type_std > fci->std)
{
- default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
- case 'H': wanted_type = fci->hhlen ? *(fci->hhlen) : 0; break;
- case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
- case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
- case 'q': wanted_type = fci->qlen ? *(fci->qlen) : 0; break;
- case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
- case 'z': case 'Z': wanted_type = (fci->zlen
- ? (TYPE_DOMAIN (*fci->zlen)
- ? TYPE_DOMAIN (*fci->zlen)
- : *fci->zlen)
- : 0); break;
- case 't': wanted_type = fci->tlen ? *(fci->tlen) : 0; break;
- case 'j': wanted_type = fci->jlen ? *(fci->jlen) : 0; break;
+ if (wanted_type_std == STD_EXT)
+ warning ("ISO C does not support the `%%%s%c' %s format",
+ length_chars, format_char, fki->name);
+ else if ((wanted_type_std == STD_C99 && !flag_isoc99)
+ || (wanted_type_std == STD_C94 && !flag_isoc94))
+ warning ("ISO C89 does not support the `%%%s%c' %s format",
+ length_chars, format_char, fki->name);
}
- if (wanted_type == 0)
- warning ("use of `%c' length character with `%c' type character",
- length_char, format_char);
- else if (length_char == 'L' && pedantic
- && !(format_char == 'a' || format_char == 'A'
- || format_char == 'e' || format_char == 'E'
- || format_char == 'f' || format_char == 'F'
- || format_char == 'g' || format_char == 'G'))
- warning ("ISO C does not support the `L' length modifier with the `%c' type character",
- format_char);
- else if (length_char == 'l'
- && (format_char == 'c' || format_char == 's'
- || format_char == '[')
- && pedantic && !flag_isoc94)
- warning ("ISO C89 does not support the `l' length modifier with the `%c' type character",
- format_char);
- else if (info->format_type == printf_format_type && pedantic
- && !flag_isoc99 && length_char == 'l'
- && (format_char == 'f' || format_char == 'e'
- || format_char == 'E' || format_char == 'g'
- || format_char == 'G'))
- warning ("ISO C89 does not support the `l' length modifier with the `%c' type character",
- format_char);
/* Finally. . .check type of argument against desired type! */
if (info->first_arg_num == 0)
@@ -2424,7 +2527,16 @@ check_format_info (info, params)
that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
if (strcmp (this, that) != 0)
- warning ("%s format, %s arg (arg %d)", this, that, arg_num);
+ {
+ /* There may be a better name for the format, e.g. size_t,
+ but we should allow for programs with a perverse typedef
+ making size_t something other than what the compiler
+ thinks. */
+ if (wanted_type_name != 0
+ && strcmp (wanted_type_name, that) != 0)
+ this = wanted_type_name;
+ warning ("%s format, %s arg (arg %d)", this, that, arg_num);
+ }
}
}
}
diff --git a/gcc/c-common.h b/gcc/c-common.h
index 2d9e7b0daf0..bec52140efe 100644
--- a/gcc/c-common.h
+++ b/gcc/c-common.h
@@ -91,6 +91,7 @@ enum c_tree_index
CTI_SIGNED_WCHAR_TYPE,
CTI_UNSIGNED_WCHAR_TYPE,
CTI_WINT_TYPE,
+ CTI_C_SIZE_TYPE, /* For format checking only. */
CTI_SIGNED_SIZE_TYPE, /* For format checking only. */
CTI_UNSIGNED_PTRDIFF_TYPE, /* For format checking only. */
CTI_WIDEST_INT_LIT_TYPE,
@@ -130,6 +131,7 @@ enum c_tree_index
#define signed_wchar_type_node c_global_trees[CTI_SIGNED_WCHAR_TYPE]
#define unsigned_wchar_type_node c_global_trees[CTI_UNSIGNED_WCHAR_TYPE]
#define wint_type_node c_global_trees[CTI_WINT_TYPE]
+#define c_size_type_node c_global_trees[CTI_C_SIZE_TYPE]
#define signed_size_type_node c_global_trees[CTI_SIGNED_SIZE_TYPE]
#define unsigned_ptrdiff_type_node c_global_trees[CTI_UNSIGNED_PTRDIFF_TYPE]
#define widest_integer_literal_type_node c_global_trees[CTI_WIDEST_INT_LIT_TYPE]
diff --git a/gcc/c-decl.c b/gcc/c-decl.c
index 6820cedcfcc..64823591edc 100644
--- a/gcc/c-decl.c
+++ b/gcc/c-decl.c
@@ -3016,6 +3016,7 @@ init_decl_processing ()
if (flag_traditional && TREE_UNSIGNED (t))
t = signed_type (t);
+ c_size_type_node = t;
set_sizetype (t);
/* Create the widest literal types. */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index bc59d1f5a9a..73aea90f07f 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,9 @@
2000-09-01 Joseph S. Myers <jsm28@cam.ac.uk>
+ * gcc.dg/format-diag-1.c: New test.
+
+2000-09-01 Joseph S. Myers <jsm28@cam.ac.uk>
+
* gcc.dg/c90-printf-1.c, gcc.dg/c90-printf-2.c,
gcc.dg/c90-scanf-1.c, gcc.dg/c90-scanf-2.c, gcc.dg/c99-printf-1.c,
gcc.dg/c99-scanf-1.c, gcc.dg/format-ext-1.c,
diff --git a/gcc/testsuite/gcc.dg/format-diag-1.c b/gcc/testsuite/gcc.dg/format-diag-1.c
new file mode 100644
index 00000000000..33364d75159
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format-diag-1.c
@@ -0,0 +1,18 @@
+/* Test for format diagnostics. */
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+extern int printf (const char *, ...);
+
+void
+foo (double d)
+{
+ /* This should get a message referring to `hh', not to `H'. */
+ printf ("%hhf", d); /* { dg-warning "hh" "%hhf warning" } */
+ /* This should get a message referring to `ll', not to `q'. */
+ printf ("%llf", d); /* { dg-warning "ll" "%llf warning" } */
+ /* This should get a message referring to `size_t format', not to
+ `unsigned int format' or similar. */
+ printf ("%zu", d); /* { dg-warning "size_t format" "size_t format warning" } */
+}