diff options
author | jsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4> | 2000-09-01 22:09:55 +0000 |
---|---|---|
committer | jsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4> | 2000-09-01 22:09:55 +0000 |
commit | 6659e606b71ad1cd94cb3bbb1fb8e6afdf753e04 (patch) | |
tree | 90f159e42be0a2896231846a7d740d3767007896 | |
parent | 9f37edbeb0ccfe97d11850334de98b25ab34bc09 (diff) | |
download | gcc-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/ChangeLog | 24 | ||||
-rw-r--r-- | gcc/c-common.c | 508 | ||||
-rw-r--r-- | gcc/c-common.h | 2 | ||||
-rw-r--r-- | gcc/c-decl.c | 1 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/format-diag-1.c | 18 |
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" } */ +} |