diff options
author | law <law@138bc75d-0d04-0410-961f-82ee72b054a4> | 1998-04-12 19:52:16 +0000 |
---|---|---|
committer | law <law@138bc75d-0d04-0410-961f-82ee72b054a4> | 1998-04-12 19:52:16 +0000 |
commit | d1f11193560a12cb37145c741e07491e902d3078 (patch) | |
tree | 4a100f9474ef02453ea6bf947858e68b2128c340 /gcc/c-common.c | |
parent | aa45319d8c2fd77782949f742e577f16e74062fb (diff) | |
download | gcc-d1f11193560a12cb37145c741e07491e902d3078.tar.gz |
This change is from an idea suggested by Arthur David Olson.
* c-common.c (decl_attributes, record_function_format,
check_format_info, init_function_format_info):
Add support for strftime format checking.
(enum format_type): New type.
(record_function_format): Now static, and takes value of type
enum format_type instead of int.
(time_char_table): New constant.
(struct function_format_info): format_type member renamed from is_scan.
(check_format_info): Use `warning' rather than sprintf followed by
`warning', to avoid mishandling `%' in warnings.
Change `pedwarn' to `warning', since these warnings do not necessarily
mean the program does not conform to the C Standard, as the code
need not be executed.
* c-tree.h (record_function_format): Remove decl; no longer extern.
* extend.texi: Add documentation for strftime format checking.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@19151 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/c-common.c')
-rw-r--r-- | gcc/c-common.c | 358 |
1 files changed, 210 insertions, 148 deletions
diff --git a/gcc/c-common.c b/gcc/c-common.c index 2422741ccee..c84e1957c33 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -44,10 +44,15 @@ enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION, A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED, A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS}; +enum format_type { printf_format_type, scanf_format_type, + strftime_format_type }; + static void declare_hidden_char_array PROTO((char *, char *)); static void add_attribute PROTO((enum attrs, char *, int, int, int)); static void init_attributes PROTO((void)); +static void record_function_format PROTO((tree, tree, enum format_type, + int, int)); static void record_international_format PROTO((tree, tree, int)); /* Keep a stack of if statements. We record the number of compound @@ -649,13 +654,13 @@ decl_attributes (node, attributes, prefix_attributes) case A_FORMAT: { - tree format_type = TREE_VALUE (args); + tree format_type_id = TREE_VALUE (args); tree format_num_expr = TREE_VALUE (TREE_CHAIN (args)); tree first_arg_num_expr = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); int format_num; int first_arg_num; - int is_scan; + enum format_type format_type; tree argument; int arg_num; @@ -666,26 +671,27 @@ decl_attributes (node, attributes, prefix_attributes) continue; } - if (TREE_CODE (format_type) == IDENTIFIER_NODE - && (!strcmp (IDENTIFIER_POINTER (format_type), "printf") - || !strcmp (IDENTIFIER_POINTER (format_type), - "__printf__"))) - is_scan = 0; - else if (TREE_CODE (format_type) == IDENTIFIER_NODE - && (!strcmp (IDENTIFIER_POINTER (format_type), "scanf") - || !strcmp (IDENTIFIER_POINTER (format_type), - "__scanf__"))) - is_scan = 1; - else if (TREE_CODE (format_type) == IDENTIFIER_NODE) + if (TREE_CODE (format_type_id) != IDENTIFIER_NODE) { - warning ("`%s' is an unrecognized format function type", - IDENTIFIER_POINTER (format_type)); + error ("unrecognized format specifier"); continue; } else { - error ("unrecognized format specifier"); - continue; + char *p = IDENTIFIER_POINTER (format_type_id); + + if (!strcmp (p, "printf") || !strcmp (p, "__printf__")) + format_type = printf_format_type; + else if (!strcmp (p, "scanf") || !strcmp (p, "__scanf__")) + format_type = scanf_format_type; + else if (!strcmp (p, "strftime") + || !strcmp (p, "__strftime__")) + format_type = strftime_format_type; + else + { + error ("`%s' is an unrecognized format function type", p); + continue; + } } /* Strip any conversions from the string index and first arg number @@ -751,7 +757,7 @@ decl_attributes (node, attributes, prefix_attributes) record_function_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl), - is_scan, format_num, first_arg_num); + format_type, format_num, first_arg_num); break; } @@ -1011,12 +1017,37 @@ static format_char_info scan_char_table[] = { { 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 + 'G' - other GNU extensions */ + +static format_char_info time_char_table[] = { + { "y", 0, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" }, + { "D", 0, NULL, NULL, NULL, NULL, NULL, NULL, "2" }, + { "g", 0, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" }, + { "cx", 0, NULL, NULL, NULL, NULL, NULL, NULL, "3E" }, + { "%RTXnrt", 0, NULL, NULL, NULL, NULL, NULL, NULL, "" }, + { "P", 0, NULL, NULL, NULL, NULL, NULL, NULL, "G" }, + { "HIMSUWdemw", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" }, + { "Vju", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" }, + { "Gklsz", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" }, + { "ABZa", 0, NULL, NULL, NULL, NULL, NULL, NULL, "^#" }, + { "p", 0, NULL, NULL, NULL, NULL, NULL, NULL, "#" }, + { "bh", 0, NULL, NULL, NULL, NULL, NULL, NULL, "^" }, + { "CY", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" }, + { NULL } +}; + typedef struct function_format_info { struct function_format_info *next; /* next structure on the list */ tree name; /* identifier such as "printf" */ tree assembler_name; /* optional mangled identifier (for C++) */ - int is_scan; /* TRUE if *scanf */ + enum format_type format_type; /* type of format (printf, scanf, etc.) */ int format_num; /* number of format argument */ int first_arg_num; /* number of first arg (zero for varargs) */ } function_format_info; @@ -1048,15 +1079,26 @@ static void check_format_info PROTO((function_format_info *, tree)); void init_function_format_info () { - record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2); - record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3); - record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3); - record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2); - record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3); - record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3); - record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0); - record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0); - record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0); + record_function_format (get_identifier ("printf"), NULL_TREE, + printf_format_type, 1, 2); + record_function_format (get_identifier ("fprintf"), NULL_TREE, + printf_format_type, 2, 3); + record_function_format (get_identifier ("sprintf"), NULL_TREE, + printf_format_type, 2, 3); + record_function_format (get_identifier ("scanf"), NULL_TREE, + scanf_format_type, 1, 2); + record_function_format (get_identifier ("fscanf"), NULL_TREE, + scanf_format_type, 2, 3); + record_function_format (get_identifier ("sscanf"), NULL_TREE, + scanf_format_type, 2, 3); + record_function_format (get_identifier ("vprintf"), NULL_TREE, + printf_format_type, 1, 0); + record_function_format (get_identifier ("vfprintf"), NULL_TREE, + printf_format_type, 2, 0); + record_function_format (get_identifier ("vsprintf"), NULL_TREE, + printf_format_type, 2, 0); + record_function_format (get_identifier ("strftime"), NULL_TREE, + strftime_format_type, 3, 0); record_international_format (get_identifier ("gettext"), NULL_TREE, 1); record_international_format (get_identifier ("dgettext"), NULL_TREE, 2); @@ -1065,19 +1107,19 @@ init_function_format_info () /* Record information for argument format checking. FUNCTION_IDENT is the identifier node for the name of the function to check (its decl - need not exist yet). IS_SCAN is true for scanf-type format checking; - false indicates printf-style format checking. FORMAT_NUM is the number + need not exist yet). + FORMAT_TYPE specifies the type of format checking. FORMAT_NUM is the number of the argument which is the format control string (starting from 1). FIRST_ARG_NUM is the number of the first actual argument to check against the format string, or zero if no checking is not be done (e.g. for varargs such as vfprintf). */ -void -record_function_format (name, assembler_name, is_scan, +static void +record_function_format (name, assembler_name, format_type, format_num, first_arg_num) tree name; tree assembler_name; - int is_scan; + enum format_type format_type; int format_num; int first_arg_num; { @@ -1100,7 +1142,7 @@ record_function_format (name, assembler_name, is_scan, info->assembler_name = assembler_name; } - info->is_scan = is_scan; + info->format_type = format_type; info->format_num = format_num; info->first_arg_num = first_arg_num; } @@ -1195,7 +1237,6 @@ check_format_info (info, params) tree first_fillin_param; char *format_chars; format_char_info *fci; - static char message[132]; char flag_chars[8]; int has_operand_number = 0; @@ -1304,7 +1345,7 @@ check_format_info (info, params) } flag_chars[0] = 0; suppressed = wide = precise = FALSE; - if (info->is_scan) + if (info->format_type == scanf_format_type) { suppressed = *format_chars == '*'; if (suppressed) @@ -1312,7 +1353,47 @@ check_format_info (info, params) while (isdigit (*format_chars)) ++format_chars; } - else + else if (info->format_type == strftime_format_type) + { + while (*format_chars != 0 && index ("_-0^#", *format_chars) != 0) + { + if (pedantic) + warning ("ANSI C does not support the strftime `%c' flag", + *format_chars); + if (index (flag_chars, *format_chars) != 0) + { + warning ("repeated `%c' flag in format", + *format_chars); + ++format_chars; + } + else + { + i = strlen (flag_chars); + flag_chars[i++] = *format_chars++; + flag_chars[i] = 0; + } + } + while (isdigit ((unsigned char) *format_chars)) + { + wide = TRUE; + ++format_chars; + } + if (wide && pedantic) + warning ("ANSI C does not support strftime format width"); + if (*format_chars == 'E' || *format_chars == 'O') + { + i = strlen (flag_chars); + flag_chars[i++] = *format_chars++; + flag_chars[i] = 0; + if (*format_chars == 'E' || *format_chars == 'O') + { + warning ("multiple E/O modifiers in format"); + while (*format_chars == 'E' || *format_chars == 'O') + ++format_chars; + } + } + } + else if (info->format_type == printf_format_type) { /* See if we have a number followed by a dollar sign. If we do, it is an operand number, so set PARAMS to that operand. */ @@ -1345,11 +1426,7 @@ check_format_info (info, params) while (*format_chars != 0 && index (" +#0-", *format_chars) != 0) { if (index (flag_chars, *format_chars) != 0) - { - sprintf (message, "repeated `%c' flag in format", - *format_chars++); - warning (message); - } + warning ("repeated `%c' flag in format", *format_chars++); else { i = strlen (flag_chars); @@ -1392,12 +1469,7 @@ check_format_info (info, params) && (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param)) != unsigned_type_node)) - { - sprintf (message, - "field width is not type int (arg %d)", - arg_num); - warning (message); - } + warning ("field width is not type int (arg %d)", arg_num); } } else @@ -1431,12 +1503,8 @@ check_format_info (info, params) ++arg_num; if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param)) != integer_type_node) - { - sprintf (message, - "field width is not type int (arg %d)", - arg_num); - warning (message); - } + warning ("field width is not type int (arg %d)", + arg_num); } } else @@ -1446,92 +1514,106 @@ check_format_info (info, params) } } } - if (*format_chars == 'h' || *format_chars == 'l') - length_char = *format_chars++; - else if (*format_chars == 'q' || *format_chars == 'L') - { - length_char = *format_chars++; - if (pedantic && length_char == 'q') - pedwarn ("ANSI C does not support the `%c' length modifier", - length_char); - } - else if (*format_chars == 'Z') - { - length_char = *format_chars++; - if (pedantic) - pedwarn ("ANSI C does not support the `Z' length modifier"); - } - else - length_char = 0; - if (length_char == 'l' && *format_chars == 'l') - { - length_char = 'q', format_chars++; - if (pedantic) - pedwarn ("ANSI C does not support the `ll' length modifier"); - } + aflag = 0; - if (*format_chars == 'a' && info->is_scan) + + if (info->format_type != strftime_format_type) { - if (format_chars[1] == 's' || format_chars[1] == 'S' - || format_chars[1] == '[') + if (*format_chars == 'h' || *format_chars == 'l') + length_char = *format_chars++; + else if (*format_chars == 'q' || *format_chars == 'L') { - /* `a' is used as a flag. */ - aflag = 1; - format_chars++; + length_char = *format_chars++; + if (pedantic && length_char == 'q') + warning ("ANSI C does not support the `%c' length modifier", + length_char); } - } - if (suppressed && length_char != 0) - { - sprintf (message, - "use of `*' and `%c' together in format", - length_char); - warning (message); + else if (*format_chars == 'Z') + { + length_char = *format_chars++; + if (pedantic) + warning ("ANSI C does not support the `Z' length modifier"); + } + else + length_char = 0; + if (length_char == 'l' && *format_chars == 'l') + { + length_char = 'q', format_chars++; + if (pedantic) + warning ("ANSI C does not support the `ll' length modifier"); + } + if (*format_chars == 'a' && info->format_type == scanf_format_type) + { + if (format_chars[1] == 's' || format_chars[1] == 'S' + || format_chars[1] == '[') + { + /* `a' is used as a flag. */ + aflag = 1; + format_chars++; + } + } + if (suppressed && length_char != 0) + warning ("use of `*' and `%c' together in format", length_char); } format_char = *format_chars; - if (format_char == 0 || format_char == '%') + if (format_char == 0 + || (info->format_type != strftime_format_type && format_char == '%')) { warning ("conversion lacks type at end of format"); continue; } format_chars++; - fci = info->is_scan ? scan_char_table : print_char_table; + 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 (); + } while (fci->format_chars != 0 && index (fci->format_chars, format_char) == 0) ++fci; if (fci->format_chars == 0) { if (format_char >= 040 && format_char < 0177) - sprintf (message, - "unknown conversion type character `%c' in format", + warning ("unknown conversion type character `%c' in format", format_char); else - sprintf (message, - "unknown conversion type character 0x%x in format", + warning ("unknown conversion type character 0x%x in format", format_char); - warning (message); continue; } - if (wide && index (fci->flag_chars, 'w') == 0) + if (pedantic) { - sprintf (message, "width used with `%c' format", - format_char); - warning (message); + if (index (fci->flag_chars, 'G') != 0) + warning ("ANSI C does not support `%%%c'", format_char); + if (index (fci->flag_chars, 'o') != 0 + && index (flag_chars, 'O') != 0) + warning ("ANSI C does not support `%%O%c'", format_char); } + if (wide && index (fci->flag_chars, 'w') == 0) + warning ("width used with `%c' format", format_char); + if (index (fci->flag_chars, '2') != 0) + warning ("`%%%c' yields only last 2 digits of year", format_char); + else if (index (fci->flag_chars, '3') != 0) + warning ("`%%%c' yields only last 2 digits of year in some locales", + format_char); if (precise && index (fci->flag_chars, 'p') == 0) - { - sprintf (message, "precision used with `%c' format", - format_char); - warning (message); - } + warning ("precision used with `%c' format", format_char); if (aflag && index (fci->flag_chars, 'a') == 0) { - sprintf (message, "`a' flag used with `%c' format", - format_char); - warning (message); + warning ("`a' flag used with `%c' format", format_char); /* To simplify the following code. */ aflag = 0; } - if (info->is_scan && format_char == '[') + if (info->format_type == scanf_format_type && format_char == '[') { /* Skip over scan set, in case it happens to have '%' in it. */ if (*format_chars == '^') @@ -1543,39 +1625,29 @@ check_format_info (info, params) while (*format_chars && *format_chars != ']') ++format_chars; if (*format_chars != ']') - /* The end of the format string was reached. */ - warning ("no closing `]' for `%%[' format"); + /* The end of the format string was reached. */ + warning ("no closing `]' for `%%[' format"); } if (suppressed) { if (index (fci->flag_chars, '*') == 0) - { - sprintf (message, - "suppression of `%c' conversion in format", - format_char); - warning (message); - } + warning ("suppression of `%c' conversion in format", format_char); continue; } for (i = 0; flag_chars[i] != 0; ++i) { if (index (fci->flag_chars, flag_chars[i]) == 0) - { - sprintf (message, "flag `%c' used with type `%c'", - flag_chars[i], format_char); - warning (message); - } + warning ("flag `%c' used with type `%c'", + flag_chars[i], format_char); } + if (info->format_type == strftime_format_type) + continue; integral_format = (format_char == 'd' || format_char == 'i' || format_char == 'o' || format_char == 'u' || format_char == 'x' || format_char == 'x'); if (precise && index (flag_chars, '0') != 0 && integral_format) - { - sprintf (message, - "`0' flag ignored with precision specifier and `%c' format", - format_char); - warning (message); - } + warning ("`0' flag ignored with precision specifier and `%c' format", + format_char); switch (length_char) { default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break; @@ -1637,20 +1709,17 @@ check_format_info (info, params) continue; } if (TREE_CODE (cur_type) != ERROR_MARK) - { - sprintf (message, - "format argument is not a %s (arg %d)", - ((fci->pointer_count + aflag == 1) - ? "pointer" : "pointer to a pointer"), - arg_num); - warning (message); - } + warning ("format argument is not a %s (arg %d)", + ((fci->pointer_count + aflag == 1) + ? "pointer" : "pointer to a pointer"), + arg_num); break; } /* See if this is an attempt to write into a const type with scanf. */ - if (info->is_scan && i == fci->pointer_count + aflag + if (info->format_type == scanf_format_type + && i == fci->pointer_count + aflag && wanted_type != 0 && TREE_CODE (cur_type) != ERROR_MARK && (TYPE_READONLY (cur_type) @@ -1658,10 +1727,7 @@ check_format_info (info, params) && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c' || (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'd' && TREE_READONLY (cur_param)))))) - { - sprintf (message, "writing into constant object (arg %d)", arg_num); - warning (message); - } + warning ("writing into constant object (arg %d)", arg_num); /* Check the type of the "real" argument, if there's a type we want. */ if (i == fci->pointer_count + aflag && wanted_type != 0 @@ -1721,11 +1787,7 @@ check_format_info (info, params) that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type))); if (strcmp (this, that) != 0) - { - sprintf (message, "%s format, %s arg (arg %d)", - this, that, arg_num); - warning (message); - } + warning ("%s format, %s arg (arg %d)", this, that, arg_num); } } } @@ -1829,7 +1891,7 @@ convert_and_check (type, expr) || TREE_UNSIGNED (type) || ! int_fits_type_p (expr, unsigned_type (type))) && skip_evaluation == 0) - warning ("overflow in implicit constant conversion"); + warning ("overflow in implicit constant conversion"); } else unsigned_conversion_warning (t, expr); |