summaryrefslogtreecommitdiff
path: root/gcc/c-common.c
diff options
context:
space:
mode:
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>1998-04-12 19:52:16 +0000
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>1998-04-12 19:52:16 +0000
commitd1f11193560a12cb37145c741e07491e902d3078 (patch)
tree4a100f9474ef02453ea6bf947858e68b2128c340 /gcc/c-common.c
parentaa45319d8c2fd77782949f742e577f16e74062fb (diff)
downloadgcc-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.c358
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);