diff options
author | jsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-07-01 08:52:33 +0000 |
---|---|---|
committer | jsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4> | 2004-07-01 08:52:33 +0000 |
commit | 31266980975ce05c3f41e92e112f873c9cfa9a05 (patch) | |
tree | 5e14fad8701c2b2a0d71a2e08df216891b59d315 /gcc/c-format.c | |
parent | 52d6bee4af7c047290440bcef095b2b11b5280f4 (diff) | |
download | gcc-31266980975ce05c3f41e92e112f873c9cfa9a05.tar.gz |
PR c/1027
* c-lang.c (c_initialize_diagnostics): Move from here ...
* c-objc-common.c: ... to here. Include "c-pretty-print.h".
(c_tree_printer): Use pretty-printer to format %T.
* c-pretty-print.c (pp_c_specifier_qualifier_list): Include space
before '*' if not C++.
(pp_c_direct_abstract_declarator): Don't try to print array upper
bound for flexible array members.
* c-tree.h: Include "diagnostic.h".
(c_initialize_diagnostics): Declare.
* objc/objc-lang.c (LANG_HOOKS_INITIALIZE_DIAGNOSTICS): Define.
* c-format.c (format_type_warning): New function. Improve
diagnostics for incorrect format argument types.
(check_format_types): Use it. Add two parameters. Use the
TYPE_MAIN_VARIANT of wanted_type.
(check_format_info_main): Pass new parameters to
check_format_types.
(struct format_wanted_type): Update comment.
testsuite:
* gcc.dg/Wswitch-enum.c, gcc.dg/Wswitch.c,
gcc.dg/format/branch-1.c, gcc.dg/format/diag-1.c,
gcc.dg/format/multattr-3.c, gcc.dg/format/xopen-1.c: Update
expected warning text.
* gcc.dg/format/diag-2.c: New test.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@83965 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/c-format.c')
-rw-r--r-- | gcc/c-format.c | 158 |
1 files changed, 88 insertions, 70 deletions
diff --git a/gcc/c-format.c b/gcc/c-format.c index 5551ad135c4..98274ee649d 100644 --- a/gcc/c-format.c +++ b/gcc/c-format.c @@ -488,12 +488,10 @@ typedef struct format_wanted_type /* Whether the argument, dereferenced once, is read from and so must not be a NULL pointer. */ int reading_from_flag; - /* If warnings should be of the form "field precision is not type int", - the name to use (in this case "field precision"), otherwise NULL, - for "%s format, %s arg" type messages. If (in an extension), this - is a pointer type, wanted_type_name should be set to include the - terminating '*' characters of the type name to give a correct - message. */ + /* If warnings should be of the form "field precision should have + type 'int'", the name to use (in this case "field precision"), + otherwise NULL, for "format expects type 'long'" type + messages. */ const char *name; /* The actual parameter to check against the wanted type. */ tree param; @@ -1031,7 +1029,9 @@ static void finish_dollar_format_checking (format_check_results *, int); static const format_flag_spec *get_flag_spec (const format_flag_spec *, int, const char *); -static void check_format_types (format_wanted_type *); +static void check_format_types (format_wanted_type *, const char *, int); +static void format_type_warning (const char *, const char *, int, tree, + int, const char *, tree, int); /* Decode a format type from a string, returning the type, or format_type_error if not valid, in which case the caller should print an @@ -1667,6 +1667,7 @@ check_format_info_main (format_check_results *res, const format_char_info *fci = NULL; char flag_chars[256]; int aflag = 0; + const char *format_start = format_chars; if (*format_chars == 0) { if (format_chars - orig_format_chars != format_length) @@ -2255,7 +2256,8 @@ check_format_info_main (format_check_results *res, } if (first_wanted_type != 0) - check_format_types (first_wanted_type); + check_format_types (first_wanted_type, format_start, + format_chars - format_start); } } @@ -2264,7 +2266,8 @@ check_format_info_main (format_check_results *res, /* Check the argument types from a single format conversion (possibly including width and precision arguments). */ static void -check_format_types (format_wanted_type *types) +check_format_types (format_wanted_type *types, const char *format_start, + int format_length) { for (; types != 0; types = types->next) { @@ -2279,6 +2282,7 @@ check_format_types (format_wanted_type *types) cur_type = TREE_TYPE (cur_param); if (cur_type == error_mark_node) continue; + orig_cur_type = cur_type; char_type_flag = 0; wanted_type = types->wanted_type; arg_num = types->arg_num; @@ -2292,6 +2296,8 @@ check_format_types (format_wanted_type *types) if (types->pointer_count == 0) wanted_type = lang_hooks.types.type_promotes_to (wanted_type); + wanted_type = TYPE_MAIN_VARIANT (wanted_type); + STRIP_NOPS (cur_param); /* Check the types of any additional pointer arguments @@ -2353,10 +2359,10 @@ check_format_types (format_wanted_type *types) } else { - if (types->pointer_count == 1) - warning ("format argument is not a pointer (arg %d)", arg_num); - else - warning ("format argument is not a pointer to a pointer (arg %d)", arg_num); + format_type_warning (types->name, format_start, format_length, + wanted_type, types->pointer_count, + types->wanted_type_name, orig_cur_type, + arg_num); break; } } @@ -2364,7 +2370,6 @@ check_format_types (format_wanted_type *types) if (i < types->pointer_count) continue; - orig_cur_type = cur_type; cur_type = TYPE_MAIN_VARIANT (cur_type); /* Check whether the argument type is a character type. This leniency @@ -2403,67 +2408,80 @@ check_format_types (format_wanted_type *types) && char_type_flag) continue; /* Now we have a type mismatch. */ - { - const char *this; - const char *that; - tree tmp; - - tmp = TYPE_NAME (wanted_type); - if (TREE_CODE (tmp) == TYPE_DECL) - tmp = DECL_NAME (tmp); - this = IDENTIFIER_POINTER (tmp); - - that = 0; - if (TYPE_NAME (orig_cur_type) != 0 - && TREE_CODE (orig_cur_type) != INTEGER_TYPE - && !(TREE_CODE (orig_cur_type) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (orig_cur_type)) == INTEGER_TYPE)) - { - tmp = TYPE_NAME (orig_cur_type); - if (TREE_CODE (tmp) == TYPE_DECL) - tmp = DECL_NAME (tmp); - if (tmp) - that = IDENTIFIER_POINTER (tmp); - } - - /* A nameless type can't possibly match what the format wants. - So there will be a warning for it. - Make up a string to describe vaguely what it is. */ - if (that == 0) - { - if (TREE_CODE (orig_cur_type) == POINTER_TYPE) - that = _("pointer"); - else - that = _("different type"); - } + format_type_warning (types->name, format_start, format_length, + wanted_type, types->pointer_count, + types->wanted_type_name, orig_cur_type, arg_num); + } +} - /* Make the warning better in case of mismatch of int vs long. */ - if (TREE_CODE (orig_cur_type) == INTEGER_TYPE - && TREE_CODE (wanted_type) == INTEGER_TYPE - && TYPE_PRECISION (orig_cur_type) == TYPE_PRECISION (wanted_type) - && TYPE_NAME (orig_cur_type) != 0 - && TREE_CODE (TYPE_NAME (orig_cur_type)) == TYPE_DECL) - that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (orig_cur_type))); - if (strcmp (this, that) != 0) - { - /* 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 (types->wanted_type_name != 0 - && strcmp (types->wanted_type_name, that) != 0) - this = types->wanted_type_name; - if (types->name != 0) - warning ("%s is not type %s (arg %d)", types->name, this, - arg_num); - else - warning ("%s format, %s arg (arg %d)", this, that, arg_num); - } - } +/* Give a warning about a format argument of different type from that + expected. DESCR is a description such as "field precision", or + NULL for an ordinary format. For an ordinary format, FORMAT_START + points to where the format starts in the format string and + FORMAT_LENGTH is its length. WANTED_TYPE is the type the argument + should have after POINTER_COUNT pointer dereferences. + WANTED_NAME_NAME is a possibly more friendly name of WANTED_TYPE, + or NULL if the ordinary name of the type should be used. ARG_TYPE + is the type of the actual argument. ARG_NUM is the number of that + argument. */ +static void +format_type_warning (const char *descr, const char *format_start, + int format_length, tree wanted_type, int pointer_count, + const char *wanted_type_name, tree arg_type, int arg_num) +{ + char *p; + /* If ARG_TYPE is a typedef with a misleading name (for example, + size_t but not the standard size_t expected by printf %zu), avoid + printing the typedef name. */ + if (wanted_type_name + && TYPE_NAME (arg_type) + && TREE_CODE (TYPE_NAME (arg_type)) == TYPE_DECL + && DECL_NAME (TYPE_NAME (arg_type)) + && !strcmp (wanted_type_name, + lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2))) + arg_type = TYPE_MAIN_VARIANT (arg_type); + /* The format type and name exclude any '*' for pointers, so those + must be formatted manually. For all the types we currently have, + this is adequate, but formats taking pointers to functions or + arrays would require the full type to be built up in order to + print it with %T. */ + p = alloca (pointer_count + 2); + if (pointer_count == 0) + p[0] = 0; + else if (c_dialect_cxx ()) + { + memset (p, '*', pointer_count); + p[pointer_count] = 0; + } + else + { + p[0] = ' '; + memset (p + 1, '*', pointer_count); + p[pointer_count + 1] = 0; + } + if (wanted_type_name) + { + if (descr) + warning ("%s should have type %<%s%s%>, but argument %d has type %qT", + descr, wanted_type_name, p, arg_num, arg_type); + else + warning ("format %q.*s expects type %<%s%s%>, but argument %d has type %qT", + format_length, format_start, wanted_type_name, p, + arg_num, arg_type); + } + else + { + if (descr) + warning ("%s should have type %<%T%s%>, but argument %d has type %qT", + descr, wanted_type, p, arg_num, arg_type); + else + warning ("format %q.*s expects type %<%T%s%>, but argument %d has type %qT", + format_length, format_start, wanted_type, p, arg_num, arg_type); } } + /* Given a format_char_info array FCI, and a character C, this function returns the index into the conversion_specs where that specifier's data is located. If the character isn't found it aborts. */ |