]>
Commit | Line | Data |
---|---|---|
0144d886 | 1 | /* Check calls to formatted I/O functions (-Wformat). |
c53485bb | 2 | Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, |
c429745f | 3 | 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. |
0144d886 | 4 | |
f12b58b3 | 5 | This file is part of GCC. |
0144d886 | 6 | |
f12b58b3 | 7 | GCC is free software; you can redistribute it and/or modify it under |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 2, or (at your option) any later | |
10 | version. | |
0144d886 | 11 | |
f12b58b3 | 12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
0144d886 | 16 | |
17 | You should have received a copy of the GNU General Public License | |
f12b58b3 | 18 | along with GCC; see the file COPYING. If not, write to the Free |
67ce556b | 19 | Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA |
20 | 02110-1301, USA. */ | |
0144d886 | 21 | |
22 | #include "config.h" | |
23 | #include "system.h" | |
805e22b2 | 24 | #include "coretypes.h" |
25 | #include "tm.h" | |
0144d886 | 26 | #include "tree.h" |
27 | #include "flags.h" | |
0144d886 | 28 | #include "c-common.h" |
7781aa77 | 29 | #include "toplev.h" |
0144d886 | 30 | #include "intl.h" |
31 | #include "diagnostic.h" | |
63c62881 | 32 | #include "langhooks.h" |
1e9af880 | 33 | #include "c-format.h" |
0144d886 | 34 | \f |
0144d886 | 35 | /* Set format warning options according to a -Wformat=n option. */ |
36 | ||
37 | void | |
1cae46be | 38 | set_Wformat (int setting) |
0144d886 | 39 | { |
40 | warn_format = setting; | |
0144d886 | 41 | warn_format_extra_args = setting; |
959f435b | 42 | warn_format_zero_length = setting; |
0144d886 | 43 | if (setting != 1) |
44 | { | |
45 | warn_format_nonliteral = setting; | |
46 | warn_format_security = setting; | |
af35025e | 47 | warn_format_y2k = setting; |
0144d886 | 48 | } |
dbf6c367 | 49 | /* Make sure not to disable -Wnonnull if -Wformat=0 is specified. */ |
50 | if (setting) | |
51 | warn_nonnull = setting; | |
0144d886 | 52 | } |
53 | ||
54 | \f | |
55 | /* Handle attributes associated with format checking. */ | |
56 | ||
1e9af880 | 57 | /* This must be in the same order as format_types, except for |
58 | format_type_error. Target-specific format types do not have | |
59 | matching enum values. */ | |
c54d077b | 60 | enum format_type { printf_format_type, asm_fprintf_format_type, |
7781aa77 | 61 | gcc_diag_format_type, gcc_tdiag_format_type, |
62 | gcc_cdiag_format_type, | |
bbec950d | 63 | gcc_cxxdiag_format_type, gcc_gfc_format_type, |
c54d077b | 64 | scanf_format_type, strftime_format_type, |
1e9af880 | 65 | strfmon_format_type, format_type_error = -1}; |
0144d886 | 66 | |
e9a0f285 | 67 | typedef struct function_format_info |
68 | { | |
1e9af880 | 69 | int format_type; /* type of format (printf, scanf, etc.) */ |
e9a0f285 | 70 | unsigned HOST_WIDE_INT format_num; /* number of format argument */ |
71 | unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */ | |
72 | } function_format_info; | |
73 | ||
1cae46be | 74 | static bool decode_format_attr (tree, function_format_info *, int); |
1e9af880 | 75 | static int decode_format_type (const char *); |
0144d886 | 76 | |
0a1f0a0f | 77 | static bool check_format_string (tree argument, |
78 | unsigned HOST_WIDE_INT format_num, | |
79 | int flags, bool *no_add_attrs); | |
80 | static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value, | |
81 | int validated_p); | |
0144d886 | 82 | |
83 | ||
e9a0f285 | 84 | /* Handle a "format_arg" attribute; arguments as in |
e3c541f0 | 85 | struct attribute_spec.handler. */ |
86 | tree | |
9a03a746 | 87 | handle_format_arg_attribute (tree *node, tree ARG_UNUSED (name), |
d19a6ea0 | 88 | tree args, int flags, bool *no_add_attrs) |
0144d886 | 89 | { |
e9a0f285 | 90 | tree type = *node; |
0144d886 | 91 | tree format_num_expr = TREE_VALUE (args); |
4ee9c684 | 92 | unsigned HOST_WIDE_INT format_num = 0; |
0144d886 | 93 | tree argument; |
94 | ||
0a1f0a0f | 95 | if (!get_constant (format_num_expr, &format_num, 0)) |
0144d886 | 96 | { |
97 | error ("format string has invalid operand number"); | |
e3c541f0 | 98 | *no_add_attrs = true; |
99 | return NULL_TREE; | |
0144d886 | 100 | } |
101 | ||
0144d886 | 102 | argument = TYPE_ARG_TYPES (type); |
103 | if (argument) | |
104 | { | |
0a1f0a0f | 105 | if (!check_format_string (argument, format_num, flags, no_add_attrs)) |
106 | return NULL_TREE; | |
0144d886 | 107 | } |
108 | ||
e9a0f285 | 109 | if (TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE |
110 | || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) | |
0144d886 | 111 | != char_type_node)) |
112 | { | |
7d3b509a | 113 | if (!(flags & (int) ATTR_FLAG_BUILT_IN)) |
114 | error ("function does not return string type"); | |
e3c541f0 | 115 | *no_add_attrs = true; |
116 | return NULL_TREE; | |
0144d886 | 117 | } |
118 | ||
e3c541f0 | 119 | return NULL_TREE; |
0144d886 | 120 | } |
121 | ||
0a1f0a0f | 122 | /* Verify that the format_num argument is actually a string, in case |
123 | the format attribute is in error. */ | |
9140e457 | 124 | static bool |
0a1f0a0f | 125 | check_format_string (tree argument, unsigned HOST_WIDE_INT format_num, |
126 | int flags, bool *no_add_attrs) | |
127 | { | |
128 | unsigned HOST_WIDE_INT i; | |
129 | ||
130 | for (i = 1; i != format_num; i++) | |
131 | { | |
132 | if (argument == 0) | |
133 | break; | |
134 | argument = TREE_CHAIN (argument); | |
135 | } | |
136 | ||
137 | if (!argument | |
138 | || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE | |
139 | || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument))) | |
140 | != char_type_node)) | |
141 | { | |
142 | if (!(flags & (int) ATTR_FLAG_BUILT_IN)) | |
07e3a3d2 | 143 | error ("format string argument not a string type"); |
0a1f0a0f | 144 | *no_add_attrs = true; |
145 | return false; | |
146 | } | |
147 | ||
148 | return true; | |
149 | } | |
150 | ||
67409385 | 151 | /* Verify EXPR is a constant, and store its value. |
89f18f73 | 152 | If validated_p is true there should be no errors. |
6473f3f4 | 153 | Returns true on success, false otherwise. */ |
9140e457 | 154 | static bool |
231bd014 | 155 | get_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p) |
0a1f0a0f | 156 | { |
0a1f0a0f | 157 | if (TREE_CODE (expr) != INTEGER_CST || TREE_INT_CST_HIGH (expr) != 0) |
158 | { | |
231bd014 | 159 | gcc_assert (!validated_p); |
0a1f0a0f | 160 | return false; |
161 | } | |
162 | ||
163 | *value = TREE_INT_CST_LOW (expr); | |
164 | ||
165 | return true; | |
166 | } | |
0144d886 | 167 | |
89f18f73 | 168 | /* Decode the arguments to a "format" attribute into a |
169 | function_format_info structure. It is already known that the list | |
170 | is of the right length. If VALIDATED_P is true, then these | |
171 | attributes have already been validated and must not be erroneous; | |
172 | if false, it will give an error message. Returns true if the | |
173 | attributes are successfully decoded, false otherwise. */ | |
0144d886 | 174 | |
e9a0f285 | 175 | static bool |
1cae46be | 176 | decode_format_attr (tree args, function_format_info *info, int validated_p) |
0144d886 | 177 | { |
e9a0f285 | 178 | tree format_type_id = TREE_VALUE (args); |
179 | tree format_num_expr = TREE_VALUE (TREE_CHAIN (args)); | |
180 | tree first_arg_num_expr | |
181 | = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); | |
0144d886 | 182 | |
e9a0f285 | 183 | if (TREE_CODE (format_type_id) != IDENTIFIER_NODE) |
0144d886 | 184 | { |
231bd014 | 185 | gcc_assert (!validated_p); |
e9a0f285 | 186 | error ("unrecognized format specifier"); |
187 | return false; | |
0144d886 | 188 | } |
e9a0f285 | 189 | else |
0144d886 | 190 | { |
e9a0f285 | 191 | const char *p = IDENTIFIER_POINTER (format_type_id); |
0144d886 | 192 | |
e9a0f285 | 193 | info->format_type = decode_format_type (p); |
0144d886 | 194 | |
e9a0f285 | 195 | if (info->format_type == format_type_error) |
196 | { | |
231bd014 | 197 | gcc_assert (!validated_p); |
6bf97f82 | 198 | warning (OPT_Wformat, "%qE is an unrecognized format function type", |
782858b8 | 199 | format_type_id); |
e9a0f285 | 200 | return false; |
201 | } | |
202 | } | |
0144d886 | 203 | |
0a1f0a0f | 204 | if (!get_constant (format_num_expr, &info->format_num, validated_p)) |
0144d886 | 205 | { |
e9a0f285 | 206 | error ("format string has invalid operand number"); |
207 | return false; | |
0144d886 | 208 | } |
209 | ||
0a1f0a0f | 210 | if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p)) |
211 | { | |
1e5fcbe2 | 212 | error ("%<...%> has invalid operand number"); |
0a1f0a0f | 213 | return false; |
214 | } | |
215 | ||
e9a0f285 | 216 | if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num) |
0144d886 | 217 | { |
231bd014 | 218 | gcc_assert (!validated_p); |
07e3a3d2 | 219 | error ("format string argument follows the args to be formatted"); |
e9a0f285 | 220 | return false; |
0144d886 | 221 | } |
222 | ||
e9a0f285 | 223 | return true; |
0144d886 | 224 | } |
0144d886 | 225 | \f |
226 | /* Check a call to a format function against a parameter list. */ | |
227 | ||
0144d886 | 228 | /* The C standard version C++ is treated as equivalent to |
229 | or inheriting from, for the purpose of format features supported. */ | |
7c2404e1 | 230 | #define CPLUSPLUS_STD_VER STD_C94 |
0144d886 | 231 | /* The C standard version we are checking formats against when pedantic. */ |
84166705 | 232 | #define C_STD_VER ((int) (c_dialect_cxx () \ |
233 | ? CPLUSPLUS_STD_VER \ | |
234 | : (flag_isoc99 \ | |
235 | ? STD_C99 \ | |
0fd4500a | 236 | : (flag_isoc94 ? STD_C94 : STD_C89)))) |
0144d886 | 237 | /* The name to give to the standard version we are warning about when |
238 | pedantic. FEATURE_VER is the version in which the feature warned out | |
239 | appeared, which is higher than C_STD_VER. */ | |
c0f19401 | 240 | #define C_STD_NAME(FEATURE_VER) (c_dialect_cxx () \ |
0144d886 | 241 | ? "ISO C++" \ |
242 | : ((FEATURE_VER) == STD_EXT \ | |
243 | ? "ISO C" \ | |
ba059ac0 | 244 | : "ISO C90")) |
0144d886 | 245 | /* Adjust a C standard version, which may be STD_C9L, to account for |
246 | -Wno-long-long. Returns other standard versions unchanged. */ | |
84166705 | 247 | #define ADJ_STD(VER) ((int) ((VER) == STD_C9L \ |
0144d886 | 248 | ? (warn_long_long ? STD_C99 : STD_C89) \ |
249 | : (VER))) | |
250 | ||
0144d886 | 251 | /* Structure describing details of a type expected in format checking, |
252 | and the type to check against it. */ | |
253 | typedef struct format_wanted_type | |
254 | { | |
255 | /* The type wanted. */ | |
256 | tree wanted_type; | |
257 | /* The name of this type to use in diagnostics. */ | |
258 | const char *wanted_type_name; | |
259 | /* The level of indirection through pointers at which this type occurs. */ | |
260 | int pointer_count; | |
261 | /* Whether, when pointer_count is 1, to allow any character type when | |
262 | pedantic, rather than just the character or void type specified. */ | |
263 | int char_lenient_flag; | |
264 | /* Whether the argument, dereferenced once, is written into and so the | |
265 | argument must not be a pointer to a const-qualified type. */ | |
266 | int writing_in_flag; | |
267 | /* Whether the argument, dereferenced once, is read from and so | |
268 | must not be a NULL pointer. */ | |
269 | int reading_from_flag; | |
31266980 | 270 | /* If warnings should be of the form "field precision should have |
271 | type 'int'", the name to use (in this case "field precision"), | |
272 | otherwise NULL, for "format expects type 'long'" type | |
273 | messages. */ | |
0144d886 | 274 | const char *name; |
275 | /* The actual parameter to check against the wanted type. */ | |
276 | tree param; | |
277 | /* The argument number of that parameter. */ | |
278 | int arg_num; | |
279 | /* The next type to check for this format conversion, or NULL if none. */ | |
280 | struct format_wanted_type *next; | |
281 | } format_wanted_type; | |
282 | ||
283 | ||
284 | static const format_length_info printf_length_specs[] = | |
285 | { | |
286 | { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 }, | |
287 | { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L }, | |
288 | { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 }, | |
289 | { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 }, | |
290 | { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 }, | |
291 | { "Z", FMT_LEN_z, STD_EXT, NULL, 0, 0 }, | |
292 | { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 }, | |
293 | { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 }, | |
c4503c0a | 294 | { "H", FMT_LEN_H, STD_EXT, NULL, 0, 0 }, |
295 | { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT }, | |
0144d886 | 296 | { NULL, 0, 0, NULL, 0, 0 } |
297 | }; | |
298 | ||
c54d077b | 299 | /* Length specifiers valid for asm_fprintf. */ |
300 | static const format_length_info asm_fprintf_length_specs[] = | |
301 | { | |
302 | { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 }, | |
303 | { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 }, | |
304 | { NULL, 0, 0, NULL, 0, 0 } | |
305 | }; | |
0144d886 | 306 | |
3e038b9d | 307 | /* Length specifiers valid for GCC diagnostics. */ |
308 | static const format_length_info gcc_diag_length_specs[] = | |
309 | { | |
310 | { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 }, | |
311 | { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 }, | |
312 | { NULL, 0, 0, NULL, 0, 0 } | |
313 | }; | |
314 | ||
315 | /* The custom diagnostics all accept the same length specifiers. */ | |
7781aa77 | 316 | #define gcc_tdiag_length_specs gcc_diag_length_specs |
3e038b9d | 317 | #define gcc_cdiag_length_specs gcc_diag_length_specs |
318 | #define gcc_cxxdiag_length_specs gcc_diag_length_specs | |
319 | ||
0144d886 | 320 | /* This differs from printf_length_specs only in that "Z" is not accepted. */ |
321 | static const format_length_info scanf_length_specs[] = | |
322 | { | |
323 | { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 }, | |
324 | { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L }, | |
325 | { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 }, | |
326 | { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 }, | |
327 | { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 }, | |
328 | { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 }, | |
329 | { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 }, | |
c4503c0a | 330 | { "H", FMT_LEN_H, STD_EXT, NULL, 0, 0 }, |
331 | { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT }, | |
0144d886 | 332 | { NULL, 0, 0, NULL, 0, 0 } |
333 | }; | |
334 | ||
335 | ||
336 | /* All tables for strfmon use STD_C89 everywhere, since -pedantic warnings | |
337 | make no sense for a format type not part of any C standard version. */ | |
338 | static const format_length_info strfmon_length_specs[] = | |
339 | { | |
340 | /* A GNU extension. */ | |
341 | { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 }, | |
342 | { NULL, 0, 0, NULL, 0, 0 } | |
343 | }; | |
344 | ||
345 | static const format_flag_spec printf_flag_specs[] = | |
346 | { | |
eb586f2c | 347 | { ' ', 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 }, |
348 | { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 }, | |
349 | { '#', 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 }, | |
350 | { '0', 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 }, | |
351 | { '-', 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 }, | |
352 | { '\'', 0, 0, N_("''' flag"), N_("the ''' printf flag"), STD_EXT }, | |
353 | { 'I', 0, 0, N_("'I' flag"), N_("the 'I' printf flag"), STD_EXT }, | |
0144d886 | 354 | { 'w', 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 }, |
355 | { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 }, | |
356 | { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 }, | |
357 | { 0, 0, 0, NULL, NULL, 0 } | |
358 | }; | |
359 | ||
360 | ||
361 | static const format_flag_pair printf_flag_pairs[] = | |
362 | { | |
363 | { ' ', '+', 1, 0 }, | |
364 | { '0', '-', 1, 0 }, | |
365 | { '0', 'p', 1, 'i' }, | |
366 | { 0, 0, 0, 0 } | |
367 | }; | |
368 | ||
c54d077b | 369 | static const format_flag_spec asm_fprintf_flag_specs[] = |
370 | { | |
eb586f2c | 371 | { ' ', 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 }, |
372 | { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 }, | |
373 | { '#', 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 }, | |
374 | { '0', 0, 0, N_("'0' flag"), N_("the '0' printf flag"), STD_C89 }, | |
375 | { '-', 0, 0, N_("'-' flag"), N_("the '-' printf flag"), STD_C89 }, | |
c54d077b | 376 | { 'w', 0, 0, N_("field width"), N_("field width in printf format"), STD_C89 }, |
377 | { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 }, | |
378 | { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 }, | |
379 | { 0, 0, 0, NULL, NULL, 0 } | |
380 | }; | |
381 | ||
382 | static const format_flag_pair asm_fprintf_flag_pairs[] = | |
383 | { | |
384 | { ' ', '+', 1, 0 }, | |
385 | { '0', '-', 1, 0 }, | |
386 | { '0', 'p', 1, 'i' }, | |
387 | { 0, 0, 0, 0 } | |
388 | }; | |
0144d886 | 389 | |
3e038b9d | 390 | static const format_flag_pair gcc_diag_flag_pairs[] = |
391 | { | |
392 | { 0, 0, 0, 0 } | |
393 | }; | |
394 | ||
7781aa77 | 395 | #define gcc_tdiag_flag_pairs gcc_diag_flag_pairs |
3e038b9d | 396 | #define gcc_cdiag_flag_pairs gcc_diag_flag_pairs |
397 | #define gcc_cxxdiag_flag_pairs gcc_diag_flag_pairs | |
398 | ||
bbec950d | 399 | static const format_flag_pair gcc_gfc_flag_pairs[] = |
400 | { | |
401 | { 0, 0, 0, 0 } | |
402 | }; | |
403 | ||
3e038b9d | 404 | static const format_flag_spec gcc_diag_flag_specs[] = |
405 | { | |
3cf8b391 | 406 | { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 }, |
eb586f2c | 407 | { 'q', 0, 0, N_("'q' flag"), N_("the 'q' diagnostic flag"), STD_C89 }, |
3e038b9d | 408 | { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 }, |
409 | { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 }, | |
410 | { 0, 0, 0, NULL, NULL, 0 } | |
411 | }; | |
412 | ||
7781aa77 | 413 | #define gcc_tdiag_flag_specs gcc_diag_flag_specs |
3e038b9d | 414 | #define gcc_cdiag_flag_specs gcc_diag_flag_specs |
415 | ||
416 | static const format_flag_spec gcc_cxxdiag_flag_specs[] = | |
417 | { | |
eb586f2c | 418 | { '+', 0, 0, N_("'+' flag"), N_("the '+' printf flag"), STD_C89 }, |
419 | { '#', 0, 0, N_("'#' flag"), N_("the '#' printf flag"), STD_C89 }, | |
420 | { 'q', 0, 0, N_("'q' flag"), N_("the 'q' diagnostic flag"), STD_C89 }, | |
3e038b9d | 421 | { 'p', 0, 0, N_("precision"), N_("precision in printf format"), STD_C89 }, |
422 | { 'L', 0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 }, | |
423 | { 0, 0, 0, NULL, NULL, 0 } | |
424 | }; | |
425 | ||
0144d886 | 426 | static const format_flag_spec scanf_flag_specs[] = |
427 | { | |
1a5a5cd8 | 428 | { '*', 0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 }, |
eb586f2c | 429 | { 'a', 0, 0, N_("'a' flag"), N_("the 'a' scanf flag"), STD_EXT }, |
1a5a5cd8 | 430 | { 'w', 0, 0, N_("field width"), N_("field width in scanf format"), STD_C89 }, |
431 | { 'L', 0, 0, N_("length modifier"), N_("length modifier in scanf format"), STD_C89 }, | |
eb586f2c | 432 | { '\'', 0, 0, N_("''' flag"), N_("the ''' scanf flag"), STD_EXT }, |
433 | { 'I', 0, 0, N_("'I' flag"), N_("the 'I' scanf flag"), STD_EXT }, | |
0144d886 | 434 | { 0, 0, 0, NULL, NULL, 0 } |
435 | }; | |
436 | ||
437 | ||
438 | static const format_flag_pair scanf_flag_pairs[] = | |
439 | { | |
440 | { '*', 'L', 0, 0 }, | |
441 | { 0, 0, 0, 0 } | |
442 | }; | |
443 | ||
444 | ||
445 | static const format_flag_spec strftime_flag_specs[] = | |
446 | { | |
eb586f2c | 447 | { '_', 0, 0, N_("'_' flag"), N_("the '_' strftime flag"), STD_EXT }, |
448 | { '-', 0, 0, N_("'-' flag"), N_("the '-' strftime flag"), STD_EXT }, | |
449 | { '0', 0, 0, N_("'0' flag"), N_("the '0' strftime flag"), STD_EXT }, | |
450 | { '^', 0, 0, N_("'^' flag"), N_("the '^' strftime flag"), STD_EXT }, | |
451 | { '#', 0, 0, N_("'#' flag"), N_("the '#' strftime flag"), STD_EXT }, | |
0144d886 | 452 | { 'w', 0, 0, N_("field width"), N_("field width in strftime format"), STD_EXT }, |
eb586f2c | 453 | { 'E', 0, 0, N_("'E' modifier"), N_("the 'E' strftime modifier"), STD_C99 }, |
454 | { 'O', 0, 0, N_("'O' modifier"), N_("the 'O' strftime modifier"), STD_C99 }, | |
455 | { 'O', 'o', 0, NULL, N_("the 'O' modifier"), STD_EXT }, | |
0144d886 | 456 | { 0, 0, 0, NULL, NULL, 0 } |
457 | }; | |
458 | ||
459 | ||
460 | static const format_flag_pair strftime_flag_pairs[] = | |
461 | { | |
462 | { 'E', 'O', 0, 0 }, | |
463 | { '_', '-', 0, 0 }, | |
464 | { '_', '0', 0, 0 }, | |
465 | { '-', '0', 0, 0 }, | |
466 | { '^', '#', 0, 0 }, | |
467 | { 0, 0, 0, 0 } | |
468 | }; | |
469 | ||
470 | ||
471 | static const format_flag_spec strfmon_flag_specs[] = | |
472 | { | |
473 | { '=', 0, 1, N_("fill character"), N_("fill character in strfmon format"), STD_C89 }, | |
eb586f2c | 474 | { '^', 0, 0, N_("'^' flag"), N_("the '^' strfmon flag"), STD_C89 }, |
475 | { '+', 0, 0, N_("'+' flag"), N_("the '+' strfmon flag"), STD_C89 }, | |
476 | { '(', 0, 0, N_("'(' flag"), N_("the '(' strfmon flag"), STD_C89 }, | |
477 | { '!', 0, 0, N_("'!' flag"), N_("the '!' strfmon flag"), STD_C89 }, | |
478 | { '-', 0, 0, N_("'-' flag"), N_("the '-' strfmon flag"), STD_C89 }, | |
0144d886 | 479 | { 'w', 0, 0, N_("field width"), N_("field width in strfmon format"), STD_C89 }, |
480 | { '#', 0, 0, N_("left precision"), N_("left precision in strfmon format"), STD_C89 }, | |
481 | { 'p', 0, 0, N_("right precision"), N_("right precision in strfmon format"), STD_C89 }, | |
482 | { 'L', 0, 0, N_("length modifier"), N_("length modifier in strfmon format"), STD_C89 }, | |
483 | { 0, 0, 0, NULL, NULL, 0 } | |
484 | }; | |
485 | ||
486 | static const format_flag_pair strfmon_flag_pairs[] = | |
487 | { | |
488 | { '+', '(', 0, 0 }, | |
489 | { 0, 0, 0, 0 } | |
490 | }; | |
491 | ||
492 | ||
0144d886 | 493 | static const format_char_info print_char_table[] = |
494 | { | |
495 | /* C89 conversion specifiers. */ | |
c4503c0a | 496 | { "di", 0, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_LL, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "-wp0 +'I", "i", NULL }, |
497 | { "oxX", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL }, | |
498 | { "u", 0, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "-wp0'I", "i", NULL }, | |
499 | { "fgG", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL }, | |
500 | { "eE", 0, STD_C89, { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#I", "", NULL }, | |
501 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, | |
502 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL }, | |
503 | { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "c", NULL }, | |
504 | { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, | |
0144d886 | 505 | /* C99 conversion specifiers. */ |
c4503c0a | 506 | { "F", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "", NULL }, |
507 | { "aA", 0, STD_C99, { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +#", "", NULL }, | |
0144d886 | 508 | /* X/Open conversion specifiers. */ |
c4503c0a | 509 | { "C", 0, STD_EXT, { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, |
510 | { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "R", NULL }, | |
0144d886 | 511 | /* GNU conversion specifiers. */ |
c4503c0a | 512 | { "m", 0, STD_EXT, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "", NULL }, |
1e9af880 | 513 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } |
0144d886 | 514 | }; |
515 | ||
c54d077b | 516 | static const format_char_info asm_fprintf_char_table[] = |
517 | { | |
518 | /* C89 conversion specifiers. */ | |
c4503c0a | 519 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0 +", "i", NULL }, |
520 | { "oxX", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0#", "i", NULL }, | |
521 | { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp0", "i", NULL }, | |
522 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-w", "", NULL }, | |
523 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "-wp", "cR", NULL }, | |
c54d077b | 524 | |
525 | /* asm_fprintf conversion specifiers. */ | |
1e9af880 | 526 | { "O", 0, STD_C89, NOARGUMENTS, "", "", NULL }, |
527 | { "R", 0, STD_C89, NOARGUMENTS, "", "", NULL }, | |
528 | { "I", 0, STD_C89, NOARGUMENTS, "", "", NULL }, | |
529 | { "L", 0, STD_C89, NOARGUMENTS, "", "", NULL }, | |
530 | { "U", 0, STD_C89, NOARGUMENTS, "", "", NULL }, | |
c4503c0a | 531 | { "r", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL }, |
1e9af880 | 532 | { "@", 0, STD_C89, NOARGUMENTS, "", "", NULL }, |
533 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
c54d077b | 534 | }; |
535 | ||
3e038b9d | 536 | static const format_char_info gcc_diag_char_table[] = |
537 | { | |
538 | /* C89 conversion specifiers. */ | |
c4503c0a | 539 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
540 | { "ox", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
541 | { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
542 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
543 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "pq", "cR", NULL }, | |
544 | { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "c", NULL }, | |
3e038b9d | 545 | |
546 | /* Custom conversion specifiers. */ | |
547 | ||
548 | /* %H will require "location_t" at runtime. */ | |
c4503c0a | 549 | { "H", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
3e038b9d | 550 | |
9bc3739f | 551 | /* These will require a "tree" at runtime. */ |
c4503c0a | 552 | { "J", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
9bc3739f | 553 | |
1e9af880 | 554 | { "<>'", 0, STD_C89, NOARGUMENTS, "", "", NULL }, |
555 | { "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL }, | |
556 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
3e038b9d | 557 | }; |
558 | ||
7781aa77 | 559 | static const format_char_info gcc_tdiag_char_table[] = |
560 | { | |
561 | /* C89 conversion specifiers. */ | |
562 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
563 | { "ox", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
564 | { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
565 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
566 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "pq", "cR", NULL }, | |
567 | { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "c", NULL }, | |
568 | ||
569 | /* Custom conversion specifiers. */ | |
570 | ||
571 | /* %H will require "location_t" at runtime. */ | |
572 | { "H", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
573 | ||
574 | /* These will require a "tree" at runtime. */ | |
575 | { "DFJT", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "", NULL }, | |
576 | ||
577 | { "<>'", 0, STD_C89, NOARGUMENTS, "", "", NULL }, | |
578 | { "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL }, | |
579 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
580 | }; | |
581 | ||
3e038b9d | 582 | static const format_char_info gcc_cdiag_char_table[] = |
583 | { | |
584 | /* C89 conversion specifiers. */ | |
c4503c0a | 585 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
586 | { "ox", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
587 | { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
588 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
589 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "pq", "cR", NULL }, | |
590 | { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "c", NULL }, | |
3e038b9d | 591 | |
592 | /* Custom conversion specifiers. */ | |
593 | ||
594 | /* %H will require "location_t" at runtime. */ | |
c4503c0a | 595 | { "H", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
3e038b9d | 596 | |
597 | /* These will require a "tree" at runtime. */ | |
c4503c0a | 598 | { "DEFJT", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+", "", NULL }, |
3e038b9d | 599 | |
1e9af880 | 600 | { "<>'", 0, STD_C89, NOARGUMENTS, "", "", NULL }, |
601 | { "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL }, | |
602 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
3e038b9d | 603 | }; |
604 | ||
605 | static const format_char_info gcc_cxxdiag_char_table[] = | |
606 | { | |
607 | /* C89 conversion specifiers. */ | |
c4503c0a | 608 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
609 | { "ox", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
610 | { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
611 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, | |
612 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "pq", "cR", NULL }, | |
613 | { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "c", NULL }, | |
3e038b9d | 614 | |
615 | /* Custom conversion specifiers. */ | |
616 | ||
617 | /* %H will require "location_t" at runtime. */ | |
c4503c0a | 618 | { "H", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
3e038b9d | 619 | |
620 | /* These will require a "tree" at runtime. */ | |
c4503c0a | 621 | { "ADEFJTV",0,STD_C89,{ T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q+#", "", NULL }, |
3e038b9d | 622 | |
eb586f2c | 623 | /* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.) */ |
c4503c0a | 624 | { "CLOPQ",0,STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, |
3e038b9d | 625 | |
1e9af880 | 626 | { "<>'", 0, STD_C89, NOARGUMENTS, "", "", NULL }, |
627 | { "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL }, | |
628 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
3e038b9d | 629 | }; |
630 | ||
bbec950d | 631 | static const format_char_info gcc_gfc_char_table[] = |
632 | { | |
633 | /* C89 conversion specifiers. */ | |
634 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL }, | |
635 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL }, | |
636 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL }, | |
637 | ||
638 | /* gfc conversion specifiers. */ | |
639 | ||
640 | { "C", 0, STD_C89, NOARGUMENTS, "", "", NULL }, | |
641 | ||
642 | /* This will require a "locus" at runtime. */ | |
643 | { "L", 0, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "R", NULL }, | |
644 | ||
645 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
646 | }; | |
647 | ||
0144d886 | 648 | static const format_char_info scan_char_table[] = |
649 | { | |
650 | /* C89 conversion specifiers. */ | |
c4503c0a | 651 | { "di", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_LL, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "*w'I", "W", NULL }, |
652 | { "u", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "*w'I", "W", NULL }, | |
653 | { "oxX", 1, STD_C89, { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, | |
654 | { "efgEG", 1, STD_C89, { T89_F, BADLEN, BADLEN, T89_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "*w'", "W", NULL }, | |
655 | { "c", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "cW", NULL }, | |
656 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW", NULL }, | |
657 | { "[", 1, STD_C89, { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "cW[", NULL }, | |
658 | { "p", 2, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, | |
659 | { "n", 1, STD_C89, { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN }, "", "W", NULL }, | |
0144d886 | 660 | /* C99 conversion specifiers. */ |
c4503c0a | 661 | { "F", 1, STD_C99, { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, TEX_D32, TEX_D64, TEX_D128 }, "*w'", "W", NULL }, |
662 | { "aA", 1, STD_C99, { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w'", "W", NULL }, | |
0144d886 | 663 | /* X/Open conversion specifiers. */ |
c4503c0a | 664 | { "C", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*w", "W", NULL }, |
665 | { "S", 1, STD_EXT, { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "*aw", "W", NULL }, | |
1e9af880 | 666 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } |
0144d886 | 667 | }; |
668 | ||
669 | static const format_char_info time_char_table[] = | |
670 | { | |
671 | /* C89 conversion specifiers. */ | |
1e9af880 | 672 | { "ABZab", 0, STD_C89, NOLENGTHS, "^#", "", NULL }, |
673 | { "cx", 0, STD_C89, NOLENGTHS, "E", "3", NULL }, | |
674 | { "HIMSUWdmw", 0, STD_C89, NOLENGTHS, "-_0Ow", "", NULL }, | |
675 | { "j", 0, STD_C89, NOLENGTHS, "-_0Ow", "o", NULL }, | |
676 | { "p", 0, STD_C89, NOLENGTHS, "#", "", NULL }, | |
677 | { "X", 0, STD_C89, NOLENGTHS, "E", "", NULL }, | |
678 | { "y", 0, STD_C89, NOLENGTHS, "EO-_0w", "4", NULL }, | |
679 | { "Y", 0, STD_C89, NOLENGTHS, "-_0EOw", "o", NULL }, | |
680 | { "%", 0, STD_C89, NOLENGTHS, "", "", NULL }, | |
0144d886 | 681 | /* C99 conversion specifiers. */ |
1e9af880 | 682 | { "C", 0, STD_C99, NOLENGTHS, "-_0EOw", "o", NULL }, |
683 | { "D", 0, STD_C99, NOLENGTHS, "", "2", NULL }, | |
684 | { "eVu", 0, STD_C99, NOLENGTHS, "-_0Ow", "", NULL }, | |
685 | { "FRTnrt", 0, STD_C99, NOLENGTHS, "", "", NULL }, | |
686 | { "g", 0, STD_C99, NOLENGTHS, "O-_0w", "2o", NULL }, | |
687 | { "G", 0, STD_C99, NOLENGTHS, "-_0Ow", "o", NULL }, | |
688 | { "h", 0, STD_C99, NOLENGTHS, "^#", "", NULL }, | |
689 | { "z", 0, STD_C99, NOLENGTHS, "O", "o", NULL }, | |
0144d886 | 690 | /* GNU conversion specifiers. */ |
1e9af880 | 691 | { "kls", 0, STD_EXT, NOLENGTHS, "-_0Ow", "", NULL }, |
692 | { "P", 0, STD_EXT, NOLENGTHS, "", "", NULL }, | |
693 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } | |
0144d886 | 694 | }; |
695 | ||
696 | static const format_char_info monetary_char_table[] = | |
697 | { | |
c4503c0a | 698 | { "in", 0, STD_C89, { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "=^+(!-w#p", "", NULL }, |
1e9af880 | 699 | { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL } |
0144d886 | 700 | }; |
701 | ||
0144d886 | 702 | /* This must be in the same order as enum format_type. */ |
c54d077b | 703 | static const format_kind_info format_types_orig[] = |
0144d886 | 704 | { |
705 | { "printf", printf_length_specs, print_char_table, " +#0-'I", NULL, | |
706 | printf_flag_specs, printf_flag_pairs, | |
707 | FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK, | |
708 | 'w', 0, 'p', 0, 'L', | |
709 | &integer_type_node, &integer_type_node | |
710 | }, | |
c54d077b | 711 | { "asm_fprintf", asm_fprintf_length_specs, asm_fprintf_char_table, " +#0-", NULL, |
712 | asm_fprintf_flag_specs, asm_fprintf_flag_pairs, | |
713 | FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK, | |
714 | 'w', 0, 'p', 0, 'L', | |
207e0c7d | 715 | NULL, NULL |
c54d077b | 716 | }, |
3cf8b391 | 717 | { "gcc_diag", gcc_diag_length_specs, gcc_diag_char_table, "q+", NULL, |
3e038b9d | 718 | gcc_diag_flag_specs, gcc_diag_flag_pairs, |
719 | FMT_FLAG_ARG_CONVERT, | |
720 | 0, 0, 'p', 0, 'L', | |
721 | NULL, &integer_type_node | |
722 | }, | |
7781aa77 | 723 | { "gcc_tdiag", gcc_tdiag_length_specs, gcc_tdiag_char_table, "q+", NULL, |
724 | gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs, | |
725 | FMT_FLAG_ARG_CONVERT, | |
726 | 0, 0, 'p', 0, 'L', | |
727 | NULL, &integer_type_node | |
728 | }, | |
3cf8b391 | 729 | { "gcc_cdiag", gcc_cdiag_length_specs, gcc_cdiag_char_table, "q+", NULL, |
3e038b9d | 730 | gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs, |
731 | FMT_FLAG_ARG_CONVERT, | |
732 | 0, 0, 'p', 0, 'L', | |
733 | NULL, &integer_type_node | |
734 | }, | |
89e7e005 | 735 | { "gcc_cxxdiag", gcc_cxxdiag_length_specs, gcc_cxxdiag_char_table, "q+#", NULL, |
3e038b9d | 736 | gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs, |
737 | FMT_FLAG_ARG_CONVERT, | |
738 | 0, 0, 'p', 0, 'L', | |
739 | NULL, &integer_type_node | |
740 | }, | |
bbec950d | 741 | { "gcc_gfc", NULL, gcc_gfc_char_table, "", NULL, |
742 | NULL, gcc_gfc_flag_pairs, | |
743 | FMT_FLAG_ARG_CONVERT, | |
744 | 0, 0, 0, 0, 0, | |
745 | NULL, NULL | |
746 | }, | |
0144d886 | 747 | { "scanf", scanf_length_specs, scan_char_table, "*'I", NULL, |
748 | scanf_flag_specs, scanf_flag_pairs, | |
b2306c7f | 749 | FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK, |
0144d886 | 750 | 'w', 0, 0, '*', 'L', |
751 | NULL, NULL | |
752 | }, | |
753 | { "strftime", NULL, time_char_table, "_-0^#", "EO", | |
754 | strftime_flag_specs, strftime_flag_pairs, | |
755 | FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0, | |
756 | NULL, NULL | |
757 | }, | |
758 | { "strfmon", strfmon_length_specs, monetary_char_table, "=^+(!-", NULL, | |
759 | strfmon_flag_specs, strfmon_flag_pairs, | |
760 | FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L', | |
761 | NULL, NULL | |
762 | } | |
763 | }; | |
764 | ||
c54d077b | 765 | /* This layer of indirection allows GCC to reassign format_types with |
766 | new data if necessary, while still allowing the original data to be | |
767 | const. */ | |
768 | static const format_kind_info *format_types = format_types_orig; | |
1e9af880 | 769 | /* We can modify this one. We also add target-specific format types |
770 | to the end of the array. */ | |
3dc4d00f | 771 | static format_kind_info *dynamic_format_types; |
0144d886 | 772 | |
1e9af880 | 773 | static int n_format_types = ARRAY_SIZE (format_types_orig); |
774 | ||
0144d886 | 775 | /* Structure detailing the results of checking a format function call |
776 | where the format expression may be a conditional expression with | |
777 | many leaves resulting from nested conditional expressions. */ | |
778 | typedef struct | |
779 | { | |
780 | /* Number of leaves of the format argument that could not be checked | |
781 | as they were not string literals. */ | |
782 | int number_non_literal; | |
783 | /* Number of leaves of the format argument that were null pointers or | |
784 | string literals, but had extra format arguments. */ | |
785 | int number_extra_args; | |
786 | /* Number of leaves of the format argument that were null pointers or | |
787 | string literals, but had extra format arguments and used $ operand | |
788 | numbers. */ | |
789 | int number_dollar_extra_args; | |
790 | /* Number of leaves of the format argument that were wide string | |
791 | literals. */ | |
792 | int number_wide; | |
793 | /* Number of leaves of the format argument that were empty strings. */ | |
794 | int number_empty; | |
795 | /* Number of leaves of the format argument that were unterminated | |
796 | strings. */ | |
797 | int number_unterminated; | |
798 | /* Number of leaves of the format argument that were not counted above. */ | |
799 | int number_other; | |
800 | } format_check_results; | |
801 | ||
dbf6c367 | 802 | typedef struct |
803 | { | |
804 | format_check_results *res; | |
805 | function_format_info *info; | |
806 | tree params; | |
dbf6c367 | 807 | } format_check_context; |
808 | ||
1b1f2444 | 809 | static void check_format_info (function_format_info *, tree); |
1cae46be | 810 | static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); |
1b1f2444 | 811 | static void check_format_info_main (format_check_results *, |
1cae46be | 812 | function_format_info *, |
813 | const char *, int, tree, | |
814 | unsigned HOST_WIDE_INT); | |
0144d886 | 815 | |
1cae46be | 816 | static void init_dollar_format_checking (int, tree); |
1b1f2444 | 817 | static int maybe_read_dollar_number (const char **, int, |
1cae46be | 818 | tree, tree *, const format_kind_info *); |
1b1f2444 | 819 | static bool avoid_dollar_number (const char *); |
820 | static void finish_dollar_format_checking (format_check_results *, int); | |
0144d886 | 821 | |
1cae46be | 822 | static const format_flag_spec *get_flag_spec (const format_flag_spec *, |
823 | int, const char *); | |
0144d886 | 824 | |
31266980 | 825 | static void check_format_types (format_wanted_type *, const char *, int); |
826 | static void format_type_warning (const char *, const char *, int, tree, | |
827 | int, const char *, tree, int); | |
0144d886 | 828 | |
829 | /* Decode a format type from a string, returning the type, or | |
830 | format_type_error if not valid, in which case the caller should print an | |
831 | error message. */ | |
1e9af880 | 832 | static int |
1cae46be | 833 | decode_format_type (const char *s) |
0144d886 | 834 | { |
835 | int i; | |
836 | int slen; | |
837 | slen = strlen (s); | |
1e9af880 | 838 | for (i = 0; i < n_format_types; i++) |
0144d886 | 839 | { |
840 | int alen; | |
841 | if (!strcmp (s, format_types[i].name)) | |
1e9af880 | 842 | return i; |
0144d886 | 843 | alen = strlen (format_types[i].name); |
844 | if (slen == alen + 4 && s[0] == '_' && s[1] == '_' | |
845 | && s[slen - 1] == '_' && s[slen - 2] == '_' | |
846 | && !strncmp (s + 2, format_types[i].name, alen)) | |
1e9af880 | 847 | return i; |
0144d886 | 848 | } |
1e9af880 | 849 | return format_type_error; |
0144d886 | 850 | } |
851 | ||
852 | \f | |
853 | /* Check the argument list of a call to printf, scanf, etc. | |
e9a0f285 | 854 | ATTRS are the attributes on the function type. |
0144d886 | 855 | PARAMS is the list of argument values. Also, if -Wmissing-format-attribute, |
856 | warn for calls to vprintf or vscanf in functions with no such format | |
857 | attribute themselves. */ | |
858 | ||
859 | void | |
1b1f2444 | 860 | check_function_format (tree attrs, tree params) |
0144d886 | 861 | { |
e9a0f285 | 862 | tree a; |
0144d886 | 863 | |
e9a0f285 | 864 | /* See if this function has any format attributes. */ |
865 | for (a = attrs; a; a = TREE_CHAIN (a)) | |
0144d886 | 866 | { |
e9a0f285 | 867 | if (is_attribute_p ("format", TREE_PURPOSE (a))) |
0144d886 | 868 | { |
869 | /* Yup; check it. */ | |
e9a0f285 | 870 | function_format_info info; |
871 | decode_format_attr (TREE_VALUE (a), &info, 1); | |
95c90e04 | 872 | if (warn_format) |
873 | check_format_info (&info, params); | |
e9a0f285 | 874 | if (warn_missing_format_attribute && info.first_arg_num == 0 |
875 | && (format_types[info.format_type].flags | |
0fd4500a | 876 | & (int) FMT_FLAG_ARG_CONVERT)) |
0144d886 | 877 | { |
e9a0f285 | 878 | tree c; |
879 | for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); | |
880 | c; | |
881 | c = TREE_CHAIN (c)) | |
882 | if (is_attribute_p ("format", TREE_PURPOSE (c)) | |
883 | && (decode_format_type (IDENTIFIER_POINTER | |
884 | (TREE_VALUE (TREE_VALUE (c)))) | |
885 | == info.format_type)) | |
0144d886 | 886 | break; |
e9a0f285 | 887 | if (c == NULL_TREE) |
0144d886 | 888 | { |
889 | /* Check if the current function has a parameter to which | |
890 | the format attribute could be attached; if not, it | |
891 | can't be a candidate for a format attribute, despite | |
892 | the vprintf-like or vscanf-like call. */ | |
893 | tree args; | |
894 | for (args = DECL_ARGUMENTS (current_function_decl); | |
895 | args != 0; | |
896 | args = TREE_CHAIN (args)) | |
897 | { | |
898 | if (TREE_CODE (TREE_TYPE (args)) == POINTER_TYPE | |
899 | && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (args))) | |
900 | == char_type_node)) | |
901 | break; | |
902 | } | |
903 | if (args != 0) | |
8f7040d4 | 904 | warning (OPT_Wmissing_format_attribute, "function might " |
905 | "be possible candidate for %qs format attribute", | |
e9a0f285 | 906 | format_types[info.format_type].name); |
0144d886 | 907 | } |
908 | } | |
0144d886 | 909 | } |
910 | } | |
911 | } | |
912 | ||
0144d886 | 913 | |
914 | /* Variables used by the checking of $ operand number formats. */ | |
915 | static char *dollar_arguments_used = NULL; | |
b2306c7f | 916 | static char *dollar_arguments_pointer_p = NULL; |
0144d886 | 917 | static int dollar_arguments_alloc = 0; |
918 | static int dollar_arguments_count; | |
919 | static int dollar_first_arg_num; | |
920 | static int dollar_max_arg_used; | |
921 | static int dollar_format_warned; | |
922 | ||
923 | /* Initialize the checking for a format string that may contain $ | |
924 | parameter number specifications; we will need to keep track of whether | |
925 | each parameter has been used. FIRST_ARG_NUM is the number of the first | |
926 | argument that is a parameter to the format, or 0 for a vprintf-style | |
927 | function; PARAMS is the list of arguments starting at this argument. */ | |
928 | ||
929 | static void | |
1cae46be | 930 | init_dollar_format_checking (int first_arg_num, tree params) |
0144d886 | 931 | { |
b2306c7f | 932 | tree oparams = params; |
933 | ||
0144d886 | 934 | dollar_first_arg_num = first_arg_num; |
935 | dollar_arguments_count = 0; | |
936 | dollar_max_arg_used = 0; | |
937 | dollar_format_warned = 0; | |
938 | if (first_arg_num > 0) | |
939 | { | |
940 | while (params) | |
941 | { | |
942 | dollar_arguments_count++; | |
943 | params = TREE_CHAIN (params); | |
944 | } | |
945 | } | |
946 | if (dollar_arguments_alloc < dollar_arguments_count) | |
947 | { | |
948 | if (dollar_arguments_used) | |
949 | free (dollar_arguments_used); | |
b2306c7f | 950 | if (dollar_arguments_pointer_p) |
951 | free (dollar_arguments_pointer_p); | |
0144d886 | 952 | dollar_arguments_alloc = dollar_arguments_count; |
4fd61bc6 | 953 | dollar_arguments_used = XNEWVEC (char, dollar_arguments_alloc); |
954 | dollar_arguments_pointer_p = XNEWVEC (char, dollar_arguments_alloc); | |
0144d886 | 955 | } |
956 | if (dollar_arguments_alloc) | |
b2306c7f | 957 | { |
958 | memset (dollar_arguments_used, 0, dollar_arguments_alloc); | |
959 | if (first_arg_num > 0) | |
960 | { | |
961 | int i = 0; | |
962 | params = oparams; | |
963 | while (params) | |
964 | { | |
965 | dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params))) | |
966 | == POINTER_TYPE); | |
967 | params = TREE_CHAIN (params); | |
968 | i++; | |
969 | } | |
970 | } | |
971 | } | |
0144d886 | 972 | } |
973 | ||
974 | ||
975 | /* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED | |
976 | is set, it is an error if one is not found; otherwise, it is OK. If | |
977 | such a number is found, check whether it is within range and mark that | |
978 | numbered operand as being used for later checking. Returns the operand | |
979 | number if found and within range, zero if no such number was found and | |
980 | this is OK, or -1 on error. PARAMS points to the first operand of the | |
981 | format; PARAM_PTR is made to point to the parameter referred to. If | |
982 | a $ format is found, *FORMAT is updated to point just after it. */ | |
983 | ||
984 | static int | |
1b1f2444 | 985 | maybe_read_dollar_number (const char **format, |
1cae46be | 986 | int dollar_needed, tree params, tree *param_ptr, |
987 | const format_kind_info *fki) | |
0144d886 | 988 | { |
989 | int argnum; | |
990 | int overflow_flag; | |
991 | const char *fcp = *format; | |
84166705 | 992 | if (!ISDIGIT (*fcp)) |
0144d886 | 993 | { |
994 | if (dollar_needed) | |
995 | { | |
6bf97f82 | 996 | warning (OPT_Wformat, "missing $ operand number in format"); |
0144d886 | 997 | return -1; |
998 | } | |
999 | else | |
1000 | return 0; | |
1001 | } | |
1002 | argnum = 0; | |
1003 | overflow_flag = 0; | |
66a33570 | 1004 | while (ISDIGIT (*fcp)) |
0144d886 | 1005 | { |
1006 | int nargnum; | |
1007 | nargnum = 10 * argnum + (*fcp - '0'); | |
1008 | if (nargnum < 0 || nargnum / 10 != argnum) | |
1009 | overflow_flag = 1; | |
1010 | argnum = nargnum; | |
1011 | fcp++; | |
1012 | } | |
1013 | if (*fcp != '$') | |
1014 | { | |
1015 | if (dollar_needed) | |
1016 | { | |
6bf97f82 | 1017 | warning (OPT_Wformat, "missing $ operand number in format"); |
0144d886 | 1018 | return -1; |
1019 | } | |
1020 | else | |
1021 | return 0; | |
1022 | } | |
1023 | *format = fcp + 1; | |
1024 | if (pedantic && !dollar_format_warned) | |
1025 | { | |
6bf97f82 | 1026 | warning (OPT_Wformat, "%s does not support %%n$ operand number formats", |
1b1f2444 | 1027 | C_STD_NAME (STD_EXT)); |
0144d886 | 1028 | dollar_format_warned = 1; |
1029 | } | |
1030 | if (overflow_flag || argnum == 0 | |
1031 | || (dollar_first_arg_num && argnum > dollar_arguments_count)) | |
1032 | { | |
6bf97f82 | 1033 | warning (OPT_Wformat, "operand number out of range in format"); |
0144d886 | 1034 | return -1; |
1035 | } | |
1036 | if (argnum > dollar_max_arg_used) | |
1037 | dollar_max_arg_used = argnum; | |
1038 | /* For vprintf-style functions we may need to allocate more memory to | |
1039 | track which arguments are used. */ | |
1040 | while (dollar_arguments_alloc < dollar_max_arg_used) | |
1041 | { | |
1042 | int nalloc; | |
1043 | nalloc = 2 * dollar_arguments_alloc + 16; | |
4fd61bc6 | 1044 | dollar_arguments_used = XRESIZEVEC (char, dollar_arguments_used, |
1045 | nalloc); | |
1046 | dollar_arguments_pointer_p = XRESIZEVEC (char, dollar_arguments_pointer_p, | |
1047 | nalloc); | |
0144d886 | 1048 | memset (dollar_arguments_used + dollar_arguments_alloc, 0, |
1049 | nalloc - dollar_arguments_alloc); | |
1050 | dollar_arguments_alloc = nalloc; | |
1051 | } | |
0fd4500a | 1052 | if (!(fki->flags & (int) FMT_FLAG_DOLLAR_MULTIPLE) |
0144d886 | 1053 | && dollar_arguments_used[argnum - 1] == 1) |
1054 | { | |
1055 | dollar_arguments_used[argnum - 1] = 2; | |
6bf97f82 | 1056 | warning (OPT_Wformat, "format argument %d used more than once in %s format", |
1b1f2444 | 1057 | argnum, fki->name); |
0144d886 | 1058 | } |
1059 | else | |
1060 | dollar_arguments_used[argnum - 1] = 1; | |
1061 | if (dollar_first_arg_num) | |
1062 | { | |
1063 | int i; | |
1064 | *param_ptr = params; | |
1065 | for (i = 1; i < argnum && *param_ptr != 0; i++) | |
1066 | *param_ptr = TREE_CHAIN (*param_ptr); | |
1067 | ||
231bd014 | 1068 | /* This case shouldn't be caught here. */ |
1069 | gcc_assert (*param_ptr); | |
0144d886 | 1070 | } |
1071 | else | |
1072 | *param_ptr = 0; | |
1073 | return argnum; | |
1074 | } | |
1075 | ||
448748a4 | 1076 | /* Ensure that FORMAT does not start with a decimal number followed by |
1077 | a $; give a diagnostic and return true if it does, false otherwise. */ | |
1078 | ||
1079 | static bool | |
1b1f2444 | 1080 | avoid_dollar_number (const char *format) |
448748a4 | 1081 | { |
1082 | if (!ISDIGIT (*format)) | |
1083 | return false; | |
1084 | while (ISDIGIT (*format)) | |
1085 | format++; | |
1086 | if (*format == '$') | |
1087 | { | |
6bf97f82 | 1088 | warning (OPT_Wformat, "$ operand number used after format without operand number"); |
448748a4 | 1089 | return true; |
1090 | } | |
1091 | return false; | |
1092 | } | |
1093 | ||
0144d886 | 1094 | |
1095 | /* Finish the checking for a format string that used $ operand number formats | |
1096 | instead of non-$ formats. We check for unused operands before used ones | |
1097 | (a serious error, since the implementation of the format function | |
1098 | can't know what types to pass to va_arg to find the later arguments). | |
1099 | and for unused operands at the end of the format (if we know how many | |
1100 | arguments the format had, so not for vprintf). If there were operand | |
1101 | numbers out of range on a non-vprintf-style format, we won't have reached | |
b2306c7f | 1102 | here. If POINTER_GAP_OK, unused arguments are OK if all arguments are |
1103 | pointers. */ | |
0144d886 | 1104 | |
1105 | static void | |
1b1f2444 | 1106 | finish_dollar_format_checking (format_check_results *res, int pointer_gap_ok) |
0144d886 | 1107 | { |
1108 | int i; | |
b2306c7f | 1109 | bool found_pointer_gap = false; |
0144d886 | 1110 | for (i = 0; i < dollar_max_arg_used; i++) |
1111 | { | |
1112 | if (!dollar_arguments_used[i]) | |
b2306c7f | 1113 | { |
1114 | if (pointer_gap_ok && (dollar_first_arg_num == 0 | |
1115 | || dollar_arguments_pointer_p[i])) | |
1116 | found_pointer_gap = true; | |
1117 | else | |
6bf97f82 | 1118 | warning (OPT_Wformat, |
1119 | "format argument %d unused before used argument %d in $-style format", | |
1b1f2444 | 1120 | i + 1, dollar_max_arg_used); |
b2306c7f | 1121 | } |
0144d886 | 1122 | } |
b2306c7f | 1123 | if (found_pointer_gap |
1124 | || (dollar_first_arg_num | |
1125 | && dollar_max_arg_used < dollar_arguments_count)) | |
0144d886 | 1126 | { |
1127 | res->number_other--; | |
1128 | res->number_dollar_extra_args++; | |
1129 | } | |
1130 | } | |
1131 | ||
1132 | ||
1133 | /* Retrieve the specification for a format flag. SPEC contains the | |
1134 | specifications for format flags for the applicable kind of format. | |
1135 | FLAG is the flag in question. If PREDICATES is NULL, the basic | |
89f18f73 | 1136 | spec for that flag must be retrieved and must exist. If |
1137 | PREDICATES is not NULL, it is a string listing possible predicates | |
1138 | for the spec entry; if an entry predicated on any of these is | |
1139 | found, it is returned, otherwise NULL is returned. */ | |
0144d886 | 1140 | |
1141 | static const format_flag_spec * | |
1cae46be | 1142 | get_flag_spec (const format_flag_spec *spec, int flag, const char *predicates) |
0144d886 | 1143 | { |
1144 | int i; | |
1145 | for (i = 0; spec[i].flag_char != 0; i++) | |
1146 | { | |
1147 | if (spec[i].flag_char != flag) | |
1148 | continue; | |
1149 | if (predicates != NULL) | |
1150 | { | |
1151 | if (spec[i].predicate != 0 | |
1152 | && strchr (predicates, spec[i].predicate) != 0) | |
1153 | return &spec[i]; | |
1154 | } | |
1155 | else if (spec[i].predicate == 0) | |
1156 | return &spec[i]; | |
1157 | } | |
231bd014 | 1158 | gcc_assert (predicates); |
1159 | return NULL; | |
0144d886 | 1160 | } |
1161 | ||
1162 | ||
1163 | /* Check the argument list of a call to printf, scanf, etc. | |
1164 | INFO points to the function_format_info structure. | |
1165 | PARAMS is the list of argument values. */ | |
1166 | ||
1167 | static void | |
1b1f2444 | 1168 | check_format_info (function_format_info *info, tree params) |
0144d886 | 1169 | { |
dbf6c367 | 1170 | format_check_context format_ctx; |
e9a0f285 | 1171 | unsigned HOST_WIDE_INT arg_num; |
0144d886 | 1172 | tree format_tree; |
1173 | format_check_results res; | |
1174 | /* Skip to format argument. If the argument isn't available, there's | |
1175 | no work for us to do; prototype checking will catch the problem. */ | |
1176 | for (arg_num = 1; ; ++arg_num) | |
1177 | { | |
1178 | if (params == 0) | |
1179 | return; | |
1180 | if (arg_num == info->format_num) | |
1181 | break; | |
1182 | params = TREE_CHAIN (params); | |
1183 | } | |
1184 | format_tree = TREE_VALUE (params); | |
1185 | params = TREE_CHAIN (params); | |
1186 | if (format_tree == 0) | |
1187 | return; | |
1188 | ||
1189 | res.number_non_literal = 0; | |
1190 | res.number_extra_args = 0; | |
1191 | res.number_dollar_extra_args = 0; | |
1192 | res.number_wide = 0; | |
1193 | res.number_empty = 0; | |
1194 | res.number_unterminated = 0; | |
1195 | res.number_other = 0; | |
1196 | ||
dbf6c367 | 1197 | format_ctx.res = &res; |
1198 | format_ctx.info = info; | |
1199 | format_ctx.params = params; | |
dbf6c367 | 1200 | |
1201 | check_function_arguments_recurse (check_format_arg, &format_ctx, | |
1202 | format_tree, arg_num); | |
0144d886 | 1203 | |
1204 | if (res.number_non_literal > 0) | |
1205 | { | |
1206 | /* Functions taking a va_list normally pass a non-literal format | |
1207 | string. These functions typically are declared with | |
1208 | first_arg_num == 0, so avoid warning in those cases. */ | |
0fd4500a | 1209 | if (!(format_types[info->format_type].flags & (int) FMT_FLAG_ARG_CONVERT)) |
0144d886 | 1210 | { |
1211 | /* For strftime-like formats, warn for not checking the format | |
1212 | string; but there are no arguments to check. */ | |
8b6866af | 1213 | warning (OPT_Wformat_nonliteral, |
1214 | "format not a string literal, format string not checked"); | |
0144d886 | 1215 | } |
1216 | else if (info->first_arg_num != 0) | |
1217 | { | |
1218 | /* If there are no arguments for the format at all, we may have | |
1219 | printf (foo) which is likely to be a security hole. */ | |
1220 | while (arg_num + 1 < info->first_arg_num) | |
1221 | { | |
1222 | if (params == 0) | |
1223 | break; | |
1224 | params = TREE_CHAIN (params); | |
1225 | ++arg_num; | |
1226 | } | |
8b6866af | 1227 | if (params == 0 && warn_format_security) |
1228 | warning (OPT_Wformat_security, | |
1229 | "format not a string literal and no format arguments"); | |
1230 | else if (params == 0 && warn_format_nonliteral) | |
1231 | warning (OPT_Wformat_nonliteral, | |
1232 | "format not a string literal and no format arguments"); | |
1233 | else | |
1234 | warning (OPT_Wformat_nonliteral, | |
1235 | "format not a string literal, argument types not checked"); | |
0144d886 | 1236 | } |
1237 | } | |
1238 | ||
1239 | /* If there were extra arguments to the format, normally warn. However, | |
1240 | the standard does say extra arguments are ignored, so in the specific | |
1241 | case where we have multiple leaves (conditional expressions or | |
1242 | ngettext) allow extra arguments if at least one leaf didn't have extra | |
1243 | arguments, but was otherwise OK (either non-literal or checked OK). | |
1244 | If the format is an empty string, this should be counted similarly to the | |
1245 | case of extra format arguments. */ | |
1246 | if (res.number_extra_args > 0 && res.number_non_literal == 0 | |
6bf97f82 | 1247 | && res.number_other == 0) |
1248 | warning (OPT_Wformat_extra_args, "too many arguments for format"); | |
0144d886 | 1249 | if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0 |
6bf97f82 | 1250 | && res.number_other == 0) |
1251 | warning (OPT_Wformat_extra_args, "unused arguments in $-style format"); | |
0144d886 | 1252 | if (res.number_empty > 0 && res.number_non_literal == 0 |
6bf97f82 | 1253 | && res.number_other == 0) |
1254 | warning (OPT_Wformat_zero_length, "zero-length %s format string", | |
1b1f2444 | 1255 | format_types[info->format_type].name); |
0144d886 | 1256 | |
1257 | if (res.number_wide > 0) | |
6bf97f82 | 1258 | warning (OPT_Wformat, "format is a wide character string"); |
0144d886 | 1259 | |
1260 | if (res.number_unterminated > 0) | |
6bf97f82 | 1261 | warning (OPT_Wformat, "unterminated format string"); |
0144d886 | 1262 | } |
1263 | ||
dbf6c367 | 1264 | /* Callback from check_function_arguments_recurse to check a |
1265 | format string. FORMAT_TREE is the format parameter. ARG_NUM | |
1266 | is the number of the format argument. CTX points to a | |
1267 | format_check_context. */ | |
0144d886 | 1268 | |
1269 | static void | |
1cae46be | 1270 | check_format_arg (void *ctx, tree format_tree, |
1271 | unsigned HOST_WIDE_INT arg_num) | |
0144d886 | 1272 | { |
4fd61bc6 | 1273 | format_check_context *format_ctx = (format_check_context *) ctx; |
dbf6c367 | 1274 | format_check_results *res = format_ctx->res; |
1275 | function_format_info *info = format_ctx->info; | |
1276 | tree params = format_ctx->params; | |
dbf6c367 | 1277 | |
0144d886 | 1278 | int format_length; |
1fe57f24 | 1279 | HOST_WIDE_INT offset; |
0144d886 | 1280 | const char *format_chars; |
1281 | tree array_size = 0; | |
1282 | tree array_init; | |
1283 | ||
0144d886 | 1284 | if (integer_zerop (format_tree)) |
1285 | { | |
0144d886 | 1286 | /* Skip to first argument to check, so we can see if this format |
1287 | has any arguments (it shouldn't). */ | |
1288 | while (arg_num + 1 < info->first_arg_num) | |
1289 | { | |
1290 | if (params == 0) | |
1291 | return; | |
1292 | params = TREE_CHAIN (params); | |
1293 | ++arg_num; | |
1294 | } | |
1295 | ||
1296 | if (params == 0) | |
1297 | res->number_other++; | |
1298 | else | |
1299 | res->number_extra_args++; | |
1300 | ||
1301 | return; | |
1302 | } | |
1303 | ||
1fe57f24 | 1304 | offset = 0; |
1305 | if (TREE_CODE (format_tree) == PLUS_EXPR) | |
1306 | { | |
1307 | tree arg0, arg1; | |
1308 | ||
1309 | arg0 = TREE_OPERAND (format_tree, 0); | |
1310 | arg1 = TREE_OPERAND (format_tree, 1); | |
1311 | STRIP_NOPS (arg0); | |
1312 | STRIP_NOPS (arg1); | |
1313 | if (TREE_CODE (arg1) == INTEGER_CST) | |
1314 | format_tree = arg0; | |
1315 | else if (TREE_CODE (arg0) == INTEGER_CST) | |
1316 | { | |
1317 | format_tree = arg1; | |
1318 | arg1 = arg0; | |
1319 | } | |
1320 | else | |
1321 | { | |
1322 | res->number_non_literal++; | |
1323 | return; | |
1324 | } | |
c53485bb | 1325 | if (!host_integerp (arg1, 0) |
1326 | || (offset = tree_low_cst (arg1, 0)) < 0) | |
1fe57f24 | 1327 | { |
1328 | res->number_non_literal++; | |
1329 | return; | |
1330 | } | |
1fe57f24 | 1331 | } |
0144d886 | 1332 | if (TREE_CODE (format_tree) != ADDR_EXPR) |
1333 | { | |
1334 | res->number_non_literal++; | |
1335 | return; | |
1336 | } | |
1337 | format_tree = TREE_OPERAND (format_tree, 0); | |
b02995ae | 1338 | if (TREE_CODE (format_tree) == ARRAY_REF |
1339 | && host_integerp (TREE_OPERAND (format_tree, 1), 0) | |
1340 | && (offset += tree_low_cst (TREE_OPERAND (format_tree, 1), 0)) >= 0) | |
1341 | format_tree = TREE_OPERAND (format_tree, 0); | |
0144d886 | 1342 | if (TREE_CODE (format_tree) == VAR_DECL |
1343 | && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE | |
1344 | && (array_init = decl_constant_value (format_tree)) != format_tree | |
1345 | && TREE_CODE (array_init) == STRING_CST) | |
1346 | { | |
1347 | /* Extract the string constant initializer. Note that this may include | |
1348 | a trailing NUL character that is not in the array (e.g. | |
1349 | const char a[3] = "foo";). */ | |
1350 | array_size = DECL_SIZE_UNIT (format_tree); | |
1351 | format_tree = array_init; | |
1352 | } | |
1353 | if (TREE_CODE (format_tree) != STRING_CST) | |
1354 | { | |
1355 | res->number_non_literal++; | |
1356 | return; | |
1357 | } | |
1358 | if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))) != char_type_node) | |
1359 | { | |
1360 | res->number_wide++; | |
1361 | return; | |
1362 | } | |
1363 | format_chars = TREE_STRING_POINTER (format_tree); | |
1364 | format_length = TREE_STRING_LENGTH (format_tree); | |
1365 | if (array_size != 0) | |
1366 | { | |
1367 | /* Variable length arrays can't be initialized. */ | |
231bd014 | 1368 | gcc_assert (TREE_CODE (array_size) == INTEGER_CST); |
1369 | ||
0144d886 | 1370 | if (host_integerp (array_size, 0)) |
1371 | { | |
1372 | HOST_WIDE_INT array_size_value = TREE_INT_CST_LOW (array_size); | |
1373 | if (array_size_value > 0 | |
1374 | && array_size_value == (int) array_size_value | |
1375 | && format_length > array_size_value) | |
1376 | format_length = array_size_value; | |
1377 | } | |
1378 | } | |
1fe57f24 | 1379 | if (offset) |
1380 | { | |
1381 | if (offset >= format_length) | |
1382 | { | |
1383 | res->number_non_literal++; | |
1384 | return; | |
1385 | } | |
1386 | format_chars += offset; | |
1387 | format_length -= offset; | |
1388 | } | |
0144d886 | 1389 | if (format_length < 1) |
1390 | { | |
1391 | res->number_unterminated++; | |
1392 | return; | |
1393 | } | |
1394 | if (format_length == 1) | |
1395 | { | |
1396 | res->number_empty++; | |
1397 | return; | |
1398 | } | |
1399 | if (format_chars[--format_length] != 0) | |
1400 | { | |
1401 | res->number_unterminated++; | |
1402 | return; | |
1403 | } | |
1404 | ||
1405 | /* Skip to first argument to check. */ | |
1406 | while (arg_num + 1 < info->first_arg_num) | |
1407 | { | |
1408 | if (params == 0) | |
1409 | return; | |
1410 | params = TREE_CHAIN (params); | |
1411 | ++arg_num; | |
1412 | } | |
1413 | /* Provisionally increment res->number_other; check_format_info_main | |
1414 | will decrement it if it finds there are extra arguments, but this way | |
1415 | need not adjust it for every return. */ | |
1416 | res->number_other++; | |
1b1f2444 | 1417 | check_format_info_main (res, info, format_chars, format_length, |
0144d886 | 1418 | params, arg_num); |
1419 | } | |
1420 | ||
1421 | ||
1422 | /* Do the main part of checking a call to a format function. FORMAT_CHARS | |
1423 | is the NUL-terminated format string (which at this point may contain | |
1424 | internal NUL characters); FORMAT_LENGTH is its length (excluding the | |
1425 | terminating NUL character). ARG_NUM is one less than the number of | |
1426 | the first format argument to check; PARAMS points to that format | |
1427 | argument in the list of arguments. */ | |
1428 | ||
1429 | static void | |
1b1f2444 | 1430 | check_format_info_main (format_check_results *res, |
1cae46be | 1431 | function_format_info *info, const char *format_chars, |
1432 | int format_length, tree params, | |
1433 | unsigned HOST_WIDE_INT arg_num) | |
0144d886 | 1434 | { |
1435 | const char *orig_format_chars = format_chars; | |
1436 | tree first_fillin_param = params; | |
1437 | ||
1438 | const format_kind_info *fki = &format_types[info->format_type]; | |
1439 | const format_flag_spec *flag_specs = fki->flag_specs; | |
1440 | const format_flag_pair *bad_flag_pairs = fki->bad_flag_pairs; | |
1441 | ||
1442 | /* -1 if no conversions taking an operand have been found; 0 if one has | |
1443 | and it didn't use $; 1 if $ formats are in use. */ | |
1444 | int has_operand_number = -1; | |
1445 | ||
1446 | init_dollar_format_checking (info->first_arg_num, first_fillin_param); | |
1447 | ||
1448 | while (1) | |
1449 | { | |
1450 | int i; | |
1451 | int suppressed = FALSE; | |
1452 | const char *length_chars = NULL; | |
1453 | enum format_lengths length_chars_val = FMT_LEN_none; | |
1454 | enum format_std_version length_chars_std = STD_C89; | |
1455 | int format_char; | |
1456 | tree cur_param; | |
1457 | tree wanted_type; | |
1458 | int main_arg_num = 0; | |
1459 | tree main_arg_params = 0; | |
1460 | enum format_std_version wanted_type_std; | |
1461 | const char *wanted_type_name; | |
1462 | format_wanted_type width_wanted_type; | |
1463 | format_wanted_type precision_wanted_type; | |
1464 | format_wanted_type main_wanted_type; | |
1465 | format_wanted_type *first_wanted_type = NULL; | |
1466 | format_wanted_type *last_wanted_type = NULL; | |
1467 | const format_length_info *fli = NULL; | |
1468 | const format_char_info *fci = NULL; | |
1469 | char flag_chars[256]; | |
1470 | int aflag = 0; | |
31266980 | 1471 | const char *format_start = format_chars; |
0144d886 | 1472 | if (*format_chars == 0) |
1473 | { | |
1474 | if (format_chars - orig_format_chars != format_length) | |
6bf97f82 | 1475 | warning (OPT_Wformat, "embedded %<\\0%> in format"); |
0144d886 | 1476 | if (info->first_arg_num != 0 && params != 0 |
1477 | && has_operand_number <= 0) | |
1478 | { | |
1479 | res->number_other--; | |
1480 | res->number_extra_args++; | |
1481 | } | |
1482 | if (has_operand_number > 0) | |
1b1f2444 | 1483 | finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); |
0144d886 | 1484 | return; |
1485 | } | |
1486 | if (*format_chars++ != '%') | |
1487 | continue; | |
1488 | if (*format_chars == 0) | |
1489 | { | |
6bf97f82 | 1490 | warning (OPT_Wformat, "spurious trailing %<%%%> in format"); |
0144d886 | 1491 | continue; |
1492 | } | |
1493 | if (*format_chars == '%') | |
1494 | { | |
1495 | ++format_chars; | |
1496 | continue; | |
1497 | } | |
1498 | flag_chars[0] = 0; | |
1499 | ||
0fd4500a | 1500 | if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0) |
0144d886 | 1501 | { |
1502 | /* Possibly read a $ operand number at the start of the format. | |
1503 | If one was previously used, one is required here. If one | |
1504 | is not used here, we can't immediately conclude this is a | |
1505 | format without them, since it could be printf %m or scanf %*. */ | |
1506 | int opnum; | |
1b1f2444 | 1507 | opnum = maybe_read_dollar_number (&format_chars, 0, |
0144d886 | 1508 | first_fillin_param, |
1509 | &main_arg_params, fki); | |
1510 | if (opnum == -1) | |
1511 | return; | |
1512 | else if (opnum > 0) | |
1513 | { | |
1514 | has_operand_number = 1; | |
1515 | main_arg_num = opnum + info->first_arg_num - 1; | |
1516 | } | |
1517 | } | |
448748a4 | 1518 | else if (fki->flags & FMT_FLAG_USE_DOLLAR) |
1519 | { | |
1b1f2444 | 1520 | if (avoid_dollar_number (format_chars)) |
448748a4 | 1521 | return; |
1522 | } | |
0144d886 | 1523 | |
1524 | /* Read any format flags, but do not yet validate them beyond removing | |
1525 | duplicates, since in general validation depends on the rest of | |
1526 | the format. */ | |
1527 | while (*format_chars != 0 | |
1528 | && strchr (fki->flag_chars, *format_chars) != 0) | |
1529 | { | |
1530 | const format_flag_spec *s = get_flag_spec (flag_specs, | |
1531 | *format_chars, NULL); | |
1532 | if (strchr (flag_chars, *format_chars) != 0) | |
1533 | { | |
6bf97f82 | 1534 | warning (OPT_Wformat, "repeated %s in format", _(s->name)); |
0144d886 | 1535 | } |
1536 | else | |
1537 | { | |
1538 | i = strlen (flag_chars); | |
1539 | flag_chars[i++] = *format_chars; | |
1540 | flag_chars[i] = 0; | |
1541 | } | |
1542 | if (s->skip_next_char) | |
1543 | { | |
1544 | ++format_chars; | |
1545 | if (*format_chars == 0) | |
1546 | { | |
6bf97f82 | 1547 | warning (OPT_Wformat, "missing fill character at end of strfmon format"); |
0144d886 | 1548 | return; |
1549 | } | |
1550 | } | |
1551 | ++format_chars; | |
1552 | } | |
1553 | ||
1554 | /* Read any format width, possibly * or *m$. */ | |
1555 | if (fki->width_char != 0) | |
1556 | { | |
1557 | if (fki->width_type != NULL && *format_chars == '*') | |
1558 | { | |
1559 | i = strlen (flag_chars); | |
1560 | flag_chars[i++] = fki->width_char; | |
1561 | flag_chars[i] = 0; | |
1562 | /* "...a field width...may be indicated by an asterisk. | |
1563 | In this case, an int argument supplies the field width..." */ | |
1564 | ++format_chars; | |
0144d886 | 1565 | if (has_operand_number != 0) |
1566 | { | |
1567 | int opnum; | |
1b1f2444 | 1568 | opnum = maybe_read_dollar_number (&format_chars, |
0144d886 | 1569 | has_operand_number == 1, |
1570 | first_fillin_param, | |
1571 | ¶ms, fki); | |
1572 | if (opnum == -1) | |
1573 | return; | |
1574 | else if (opnum > 0) | |
1575 | { | |
1576 | has_operand_number = 1; | |
1577 | arg_num = opnum + info->first_arg_num - 1; | |
1578 | } | |
1579 | else | |
1580 | has_operand_number = 0; | |
1581 | } | |
448748a4 | 1582 | else |
1583 | { | |
1b1f2444 | 1584 | if (avoid_dollar_number (format_chars)) |
448748a4 | 1585 | return; |
1586 | } | |
0144d886 | 1587 | if (info->first_arg_num != 0) |
1588 | { | |
136d950f | 1589 | if (params == 0) |
1590 | { | |
6bf97f82 | 1591 | warning (OPT_Wformat, "too few arguments for format"); |
136d950f | 1592 | return; |
1593 | } | |
0144d886 | 1594 | cur_param = TREE_VALUE (params); |
1595 | if (has_operand_number <= 0) | |
1596 | { | |
1597 | params = TREE_CHAIN (params); | |
1598 | ++arg_num; | |
1599 | } | |
1600 | width_wanted_type.wanted_type = *fki->width_type; | |
1601 | width_wanted_type.wanted_type_name = NULL; | |
1602 | width_wanted_type.pointer_count = 0; | |
1603 | width_wanted_type.char_lenient_flag = 0; | |
1604 | width_wanted_type.writing_in_flag = 0; | |
1605 | width_wanted_type.reading_from_flag = 0; | |
1606 | width_wanted_type.name = _("field width"); | |
1607 | width_wanted_type.param = cur_param; | |
1608 | width_wanted_type.arg_num = arg_num; | |
1609 | width_wanted_type.next = NULL; | |
1610 | if (last_wanted_type != 0) | |
1611 | last_wanted_type->next = &width_wanted_type; | |
1612 | if (first_wanted_type == 0) | |
1613 | first_wanted_type = &width_wanted_type; | |
1614 | last_wanted_type = &width_wanted_type; | |
1615 | } | |
1616 | } | |
1617 | else | |
1618 | { | |
1619 | /* Possibly read a numeric width. If the width is zero, | |
1620 | we complain if appropriate. */ | |
1621 | int non_zero_width_char = FALSE; | |
1622 | int found_width = FALSE; | |
1623 | while (ISDIGIT (*format_chars)) | |
1624 | { | |
1625 | found_width = TRUE; | |
1626 | if (*format_chars != '0') | |
1627 | non_zero_width_char = TRUE; | |
1628 | ++format_chars; | |
1629 | } | |
1630 | if (found_width && !non_zero_width_char && | |
0fd4500a | 1631 | (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD)) |
6bf97f82 | 1632 | warning (OPT_Wformat, "zero width in %s format", fki->name); |
0144d886 | 1633 | if (found_width) |
1634 | { | |
1635 | i = strlen (flag_chars); | |
1636 | flag_chars[i++] = fki->width_char; | |
1637 | flag_chars[i] = 0; | |
1638 | } | |
1639 | } | |
1640 | } | |
1641 | ||
1642 | /* Read any format left precision (must be a number, not *). */ | |
1643 | if (fki->left_precision_char != 0 && *format_chars == '#') | |
1644 | { | |
1645 | ++format_chars; | |
1646 | i = strlen (flag_chars); | |
1647 | flag_chars[i++] = fki->left_precision_char; | |
1648 | flag_chars[i] = 0; | |
1649 | if (!ISDIGIT (*format_chars)) | |
6bf97f82 | 1650 | warning (OPT_Wformat, "empty left precision in %s format", fki->name); |
0144d886 | 1651 | while (ISDIGIT (*format_chars)) |
1652 | ++format_chars; | |
1653 | } | |
1654 | ||
1655 | /* Read any format precision, possibly * or *m$. */ | |
1656 | if (fki->precision_char != 0 && *format_chars == '.') | |
1657 | { | |
1658 | ++format_chars; | |
1659 | i = strlen (flag_chars); | |
1660 | flag_chars[i++] = fki->precision_char; | |
1661 | flag_chars[i] = 0; | |
1662 | if (fki->precision_type != NULL && *format_chars == '*') | |
1663 | { | |
1664 | /* "...a...precision...may be indicated by an asterisk. | |
1665 | In this case, an int argument supplies the...precision." */ | |
1666 | ++format_chars; | |
1667 | if (has_operand_number != 0) | |
1668 | { | |
1669 | int opnum; | |
1b1f2444 | 1670 | opnum = maybe_read_dollar_number (&format_chars, |
0144d886 | 1671 | has_operand_number == 1, |
1672 | first_fillin_param, | |
1673 | ¶ms, fki); | |
1674 | if (opnum == -1) | |
1675 | return; | |
1676 | else if (opnum > 0) | |
1677 | { | |
1678 | has_operand_number = 1; | |
1679 | arg_num = opnum + info->first_arg_num - 1; | |
1680 | } | |
1681 | else | |
1682 | has_operand_number = 0; | |
1683 | } | |
448748a4 | 1684 | else |
1685 | { | |
1b1f2444 | 1686 | if (avoid_dollar_number (format_chars)) |
448748a4 | 1687 | return; |
1688 | } | |
0144d886 | 1689 | if (info->first_arg_num != 0) |
1690 | { | |
1691 | if (params == 0) | |
1692 | { | |
6bf97f82 | 1693 | warning (OPT_Wformat, "too few arguments for format"); |
0144d886 | 1694 | return; |
1695 | } | |
1696 | cur_param = TREE_VALUE (params); | |
1697 | if (has_operand_number <= 0) | |
1698 | { | |
1699 | params = TREE_CHAIN (params); | |
1700 | ++arg_num; | |
1701 | } | |
1702 | precision_wanted_type.wanted_type = *fki->precision_type; | |
1703 | precision_wanted_type.wanted_type_name = NULL; | |
1704 | precision_wanted_type.pointer_count = 0; | |
1705 | precision_wanted_type.char_lenient_flag = 0; | |
1706 | precision_wanted_type.writing_in_flag = 0; | |
1707 | precision_wanted_type.reading_from_flag = 0; | |
1708 | precision_wanted_type.name = _("field precision"); | |
1709 | precision_wanted_type.param = cur_param; | |
1710 | precision_wanted_type.arg_num = arg_num; | |
1711 | precision_wanted_type.next = NULL; | |
1712 | if (last_wanted_type != 0) | |
1713 | last_wanted_type->next = &precision_wanted_type; | |
1714 | if (first_wanted_type == 0) | |
1715 | first_wanted_type = &precision_wanted_type; | |
1716 | last_wanted_type = &precision_wanted_type; | |
1717 | } | |
1718 | } | |
1719 | else | |
1720 | { | |
0fd4500a | 1721 | if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) |
0144d886 | 1722 | && !ISDIGIT (*format_chars)) |
6bf97f82 | 1723 | warning (OPT_Wformat, "empty precision in %s format", fki->name); |
0144d886 | 1724 | while (ISDIGIT (*format_chars)) |
1725 | ++format_chars; | |
1726 | } | |
1727 | } | |
1728 | ||
1729 | /* Read any length modifier, if this kind of format has them. */ | |
1730 | fli = fki->length_char_specs; | |
1731 | length_chars = NULL; | |
1732 | length_chars_val = FMT_LEN_none; | |
1733 | length_chars_std = STD_C89; | |
1734 | if (fli) | |
1735 | { | |
1736 | while (fli->name != 0 && fli->name[0] != *format_chars) | |
1737 | fli++; | |
1738 | if (fli->name != 0) | |
1739 | { | |
1740 | format_chars++; | |
1741 | if (fli->double_name != 0 && fli->name[0] == *format_chars) | |
1742 | { | |
1743 | format_chars++; | |
1744 | length_chars = fli->double_name; | |
1745 | length_chars_val = fli->double_index; | |
1746 | length_chars_std = fli->double_std; | |
1747 | } | |
1748 | else | |
1749 | { | |
1750 | length_chars = fli->name; | |
1751 | length_chars_val = fli->index; | |
1752 | length_chars_std = fli->std; | |
1753 | } | |
1754 | i = strlen (flag_chars); | |
1755 | flag_chars[i++] = fki->length_code_char; | |
1756 | flag_chars[i] = 0; | |
1757 | } | |
1758 | if (pedantic) | |
1759 | { | |
1760 | /* Warn if the length modifier is non-standard. */ | |
1761 | if (ADJ_STD (length_chars_std) > C_STD_VER) | |
6bf97f82 | 1762 | warning (OPT_Wformat, |
1763 | "%s does not support the %qs %s length modifier", | |
1b1f2444 | 1764 | C_STD_NAME (length_chars_std), length_chars, |
1765 | fki->name); | |
0144d886 | 1766 | } |
1767 | } | |
1768 | ||
1769 | /* Read any modifier (strftime E/O). */ | |
1770 | if (fki->modifier_chars != NULL) | |
1771 | { | |
1772 | while (*format_chars != 0 | |
1773 | && strchr (fki->modifier_chars, *format_chars) != 0) | |
1774 | { | |
1775 | if (strchr (flag_chars, *format_chars) != 0) | |
1776 | { | |
1777 | const format_flag_spec *s = get_flag_spec (flag_specs, | |
1778 | *format_chars, NULL); | |
6bf97f82 | 1779 | warning (OPT_Wformat, "repeated %s in format", _(s->name)); |
0144d886 | 1780 | } |
1781 | else | |
1782 | { | |
1783 | i = strlen (flag_chars); | |
1784 | flag_chars[i++] = *format_chars; | |
1785 | flag_chars[i] = 0; | |
1786 | } | |
1787 | ++format_chars; | |
1788 | } | |
1789 | } | |
1790 | ||
1791 | /* Handle the scanf allocation kludge. */ | |
0fd4500a | 1792 | if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) |
0144d886 | 1793 | { |
1794 | if (*format_chars == 'a' && !flag_isoc99) | |
1795 | { | |
1796 | if (format_chars[1] == 's' || format_chars[1] == 'S' | |
1797 | || format_chars[1] == '[') | |
1798 | { | |
eb586f2c | 1799 | /* 'a' is used as a flag. */ |
0144d886 | 1800 | i = strlen (flag_chars); |
1801 | flag_chars[i++] = 'a'; | |
1802 | flag_chars[i] = 0; | |
1803 | format_chars++; | |
1804 | } | |
1805 | } | |
1806 | } | |
1807 | ||
1808 | format_char = *format_chars; | |
1809 | if (format_char == 0 | |
0fd4500a | 1810 | || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK) |
1811 | && format_char == '%')) | |
0144d886 | 1812 | { |
6bf97f82 | 1813 | warning (OPT_Wformat, "conversion lacks type at end of format"); |
0144d886 | 1814 | continue; |
1815 | } | |
1816 | format_chars++; | |
1817 | fci = fki->conversion_specs; | |
1818 | while (fci->format_chars != 0 | |
1819 | && strchr (fci->format_chars, format_char) == 0) | |
1820 | ++fci; | |
1821 | if (fci->format_chars == 0) | |
1822 | { | |
84166705 | 1823 | if (ISGRAPH (format_char)) |
6bf97f82 | 1824 | warning (OPT_Wformat, "unknown conversion type character %qc in format", |
0144d886 | 1825 | format_char); |
1826 | else | |
6bf97f82 | 1827 | warning (OPT_Wformat, "unknown conversion type character 0x%x in format", |
0144d886 | 1828 | format_char); |
1829 | continue; | |
1830 | } | |
1831 | if (pedantic) | |
1832 | { | |
1833 | if (ADJ_STD (fci->std) > C_STD_VER) | |
6bf97f82 | 1834 | warning (OPT_Wformat, "%s does not support the %<%%%c%> %s format", |
1b1f2444 | 1835 | C_STD_NAME (fci->std), format_char, fki->name); |
0144d886 | 1836 | } |
1837 | ||
1838 | /* Validate the individual flags used, removing any that are invalid. */ | |
1839 | { | |
1840 | int d = 0; | |
1841 | for (i = 0; flag_chars[i] != 0; i++) | |
1842 | { | |
1843 | const format_flag_spec *s = get_flag_spec (flag_specs, | |
1844 | flag_chars[i], NULL); | |
1845 | flag_chars[i - d] = flag_chars[i]; | |
1846 | if (flag_chars[i] == fki->length_code_char) | |
1847 | continue; | |
1848 | if (strchr (fci->flag_chars, flag_chars[i]) == 0) | |
1849 | { | |
6bf97f82 | 1850 | warning (OPT_Wformat, "%s used with %<%%%c%> %s format", |
1b1f2444 | 1851 | _(s->name), format_char, fki->name); |
0144d886 | 1852 | d++; |
1853 | continue; | |
1854 | } | |
1855 | if (pedantic) | |
1856 | { | |
1857 | const format_flag_spec *t; | |
1858 | if (ADJ_STD (s->std) > C_STD_VER) | |
6bf97f82 | 1859 | warning (OPT_Wformat, "%s does not support %s", |
1b1f2444 | 1860 | C_STD_NAME (s->std), _(s->long_name)); |
0144d886 | 1861 | t = get_flag_spec (flag_specs, flag_chars[i], fci->flags2); |
1862 | if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std)) | |
1863 | { | |
1864 | const char *long_name = (t->long_name != NULL | |
1865 | ? t->long_name | |
1866 | : s->long_name); | |
1867 | if (ADJ_STD (t->std) > C_STD_VER) | |
6bf97f82 | 1868 | warning (OPT_Wformat, |
1869 | "%s does not support %s with the %<%%%c%> %s format", | |
1b1f2444 | 1870 | C_STD_NAME (t->std), _(long_name), |
1871 | format_char, fki->name); | |
0144d886 | 1872 | } |
1873 | } | |
1874 | } | |
1875 | flag_chars[i - d] = 0; | |
1876 | } | |
1877 | ||
0fd4500a | 1878 | if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) |
0144d886 | 1879 | && strchr (flag_chars, 'a') != 0) |
1880 | aflag = 1; | |
1881 | ||
1882 | if (fki->suppression_char | |
1883 | && strchr (flag_chars, fki->suppression_char) != 0) | |
1884 | suppressed = 1; | |
1885 | ||
1886 | /* Validate the pairs of flags used. */ | |
1887 | for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++) | |
1888 | { | |
1889 | const format_flag_spec *s, *t; | |
1890 | if (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0) | |
1891 | continue; | |
1892 | if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0) | |
1893 | continue; | |
1894 | if (bad_flag_pairs[i].predicate != 0 | |
1895 | && strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0) | |
1896 | continue; | |
1897 | s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL); | |
1898 | t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL); | |
1899 | if (bad_flag_pairs[i].ignored) | |
1900 | { | |
1901 | if (bad_flag_pairs[i].predicate != 0) | |
6bf97f82 | 1902 | warning (OPT_Wformat, |
1903 | "%s ignored with %s and %<%%%c%> %s format", | |
1b1f2444 | 1904 | _(s->name), _(t->name), format_char, |
1905 | fki->name); | |
0144d886 | 1906 | else |
6bf97f82 | 1907 | warning (OPT_Wformat, "%s ignored with %s in %s format", |
1b1f2444 | 1908 | _(s->name), _(t->name), fki->name); |
0144d886 | 1909 | } |
1910 | else | |
1911 | { | |
1912 | if (bad_flag_pairs[i].predicate != 0) | |
6bf97f82 | 1913 | warning (OPT_Wformat, |
1914 | "use of %s and %s together with %<%%%c%> %s format", | |
1b1f2444 | 1915 | _(s->name), _(t->name), format_char, |
1916 | fki->name); | |
0144d886 | 1917 | else |
6bf97f82 | 1918 | warning (OPT_Wformat, "use of %s and %s together in %s format", |
1b1f2444 | 1919 | _(s->name), _(t->name), fki->name); |
0144d886 | 1920 | } |
1921 | } | |
1922 | ||
1923 | /* Give Y2K warnings. */ | |
1924 | if (warn_format_y2k) | |
1925 | { | |
1926 | int y2k_level = 0; | |
1927 | if (strchr (fci->flags2, '4') != 0) | |
1928 | if (strchr (flag_chars, 'E') != 0) | |
1929 | y2k_level = 3; | |
1930 | else | |
1931 | y2k_level = 2; | |
1932 | else if (strchr (fci->flags2, '3') != 0) | |
1933 | y2k_level = 3; | |
1934 | else if (strchr (fci->flags2, '2') != 0) | |
1935 | y2k_level = 2; | |
1936 | if (y2k_level == 3) | |
6bf97f82 | 1937 | warning (OPT_Wformat_y2k, "%<%%%c%> yields only last 2 digits of " |
1938 | "year in some locales", format_char); | |
0144d886 | 1939 | else if (y2k_level == 2) |
6bf97f82 | 1940 | warning (OPT_Wformat_y2k, "%<%%%c%> yields only last 2 digits of " |
1941 | "year", format_char); | |
0144d886 | 1942 | } |
1943 | ||
1944 | if (strchr (fci->flags2, '[') != 0) | |
1945 | { | |
1946 | /* Skip over scan set, in case it happens to have '%' in it. */ | |
1947 | if (*format_chars == '^') | |
1948 | ++format_chars; | |
1949 | /* Find closing bracket; if one is hit immediately, then | |
1950 | it's part of the scan set rather than a terminator. */ | |
1951 | if (*format_chars == ']') | |
1952 | ++format_chars; | |
1953 | while (*format_chars && *format_chars != ']') | |
1954 | ++format_chars; | |
1955 | if (*format_chars != ']') | |
1956 | /* The end of the format string was reached. */ | |
6bf97f82 | 1957 | warning (OPT_Wformat, "no closing %<]%> for %<%%[%> format"); |
0144d886 | 1958 | } |
1959 | ||
1960 | wanted_type = 0; | |
1961 | wanted_type_name = 0; | |
0fd4500a | 1962 | if (fki->flags & (int) FMT_FLAG_ARG_CONVERT) |
0144d886 | 1963 | { |
1964 | wanted_type = (fci->types[length_chars_val].type | |
1965 | ? *fci->types[length_chars_val].type : 0); | |
1966 | wanted_type_name = fci->types[length_chars_val].name; | |
1967 | wanted_type_std = fci->types[length_chars_val].std; | |
1968 | if (wanted_type == 0) | |
1969 | { | |
6bf97f82 | 1970 | warning (OPT_Wformat, |
1971 | "use of %qs length modifier with %qc type character", | |
1b1f2444 | 1972 | length_chars, format_char); |
0144d886 | 1973 | /* Heuristic: skip one argument when an invalid length/type |
1974 | combination is encountered. */ | |
1975 | arg_num++; | |
1976 | if (params == 0) | |
1977 | { | |
6bf97f82 | 1978 | warning (OPT_Wformat, "too few arguments for format"); |
0144d886 | 1979 | return; |
1980 | } | |
1981 | params = TREE_CHAIN (params); | |
1982 | continue; | |
1983 | } | |
1984 | else if (pedantic | |
1985 | /* Warn if non-standard, provided it is more non-standard | |
1986 | than the length and type characters that may already | |
1987 | have been warned for. */ | |
1988 | && ADJ_STD (wanted_type_std) > ADJ_STD (length_chars_std) | |
1989 | && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) | |
1990 | { | |
1991 | if (ADJ_STD (wanted_type_std) > C_STD_VER) | |
6bf97f82 | 1992 | warning (OPT_Wformat, |
1993 | "%s does not support the %<%%%s%c%> %s format", | |
1b1f2444 | 1994 | C_STD_NAME (wanted_type_std), length_chars, |
1995 | format_char, fki->name); | |
0144d886 | 1996 | } |
1997 | } | |
1998 | ||
1e9af880 | 1999 | main_wanted_type.next = NULL; |
2000 | ||
0144d886 | 2001 | /* Finally. . .check type of argument against desired type! */ |
2002 | if (info->first_arg_num == 0) | |
2003 | continue; | |
2004 | if ((fci->pointer_count == 0 && wanted_type == void_type_node) | |
2005 | || suppressed) | |
2006 | { | |
2007 | if (main_arg_num != 0) | |
2008 | { | |
2009 | if (suppressed) | |
6bf97f82 | 2010 | warning (OPT_Wformat, "operand number specified with " |
2011 | "suppressed assignment"); | |
0144d886 | 2012 | else |
6bf97f82 | 2013 | warning (OPT_Wformat, "operand number specified for format " |
2014 | "taking no argument"); | |
0144d886 | 2015 | } |
2016 | } | |
2017 | else | |
2018 | { | |
1e9af880 | 2019 | format_wanted_type *wanted_type_ptr; |
2020 | ||
0144d886 | 2021 | if (main_arg_num != 0) |
2022 | { | |
2023 | arg_num = main_arg_num; | |
2024 | params = main_arg_params; | |
2025 | } | |
2026 | else | |
2027 | { | |
2028 | ++arg_num; | |
2029 | if (has_operand_number > 0) | |
2030 | { | |
6bf97f82 | 2031 | warning (OPT_Wformat, "missing $ operand number in format"); |
0144d886 | 2032 | return; |
2033 | } | |
2034 | else | |
2035 | has_operand_number = 0; | |
1e9af880 | 2036 | } |
2037 | ||
2038 | wanted_type_ptr = &main_wanted_type; | |
2039 | while (fci) | |
2040 | { | |
0144d886 | 2041 | if (params == 0) |
2042 | { | |
6bf97f82 | 2043 | warning (OPT_Wformat, "too few arguments for format"); |
0144d886 | 2044 | return; |
2045 | } | |
1e9af880 | 2046 | |
2047 | cur_param = TREE_VALUE (params); | |
2048 | params = TREE_CHAIN (params); | |
2049 | ||
2050 | wanted_type_ptr->wanted_type = wanted_type; | |
2051 | wanted_type_ptr->wanted_type_name = wanted_type_name; | |
2052 | wanted_type_ptr->pointer_count = fci->pointer_count + aflag; | |
2053 | wanted_type_ptr->char_lenient_flag = 0; | |
2054 | if (strchr (fci->flags2, 'c') != 0) | |
2055 | wanted_type_ptr->char_lenient_flag = 1; | |
2056 | wanted_type_ptr->writing_in_flag = 0; | |
2057 | wanted_type_ptr->reading_from_flag = 0; | |
2058 | if (aflag) | |
2059 | wanted_type_ptr->writing_in_flag = 1; | |
2060 | else | |
2061 | { | |
2062 | if (strchr (fci->flags2, 'W') != 0) | |
2063 | wanted_type_ptr->writing_in_flag = 1; | |
2064 | if (strchr (fci->flags2, 'R') != 0) | |
2065 | wanted_type_ptr->reading_from_flag = 1; | |
2066 | } | |
2067 | wanted_type_ptr->name = NULL; | |
2068 | wanted_type_ptr->param = cur_param; | |
2069 | wanted_type_ptr->arg_num = arg_num; | |
2070 | wanted_type_ptr->next = NULL; | |
2071 | if (last_wanted_type != 0) | |
2072 | last_wanted_type->next = wanted_type_ptr; | |
2073 | if (first_wanted_type == 0) | |
2074 | first_wanted_type = wanted_type_ptr; | |
2075 | last_wanted_type = wanted_type_ptr; | |
2076 | ||
2077 | fci = fci->chain; | |
2078 | if (fci) | |
2079 | { | |
2080 | wanted_type_ptr = ggc_alloc (sizeof (main_wanted_type)); | |
2081 | arg_num++; | |
2082 | wanted_type = *fci->types[length_chars_val].type; | |
2083 | wanted_type_name = fci->types[length_chars_val].name; | |
2084 | } | |
0144d886 | 2085 | } |
0144d886 | 2086 | } |
2087 | ||
2088 | if (first_wanted_type != 0) | |
31266980 | 2089 | check_format_types (first_wanted_type, format_start, |
2090 | format_chars - format_start); | |
0144d886 | 2091 | |
1e9af880 | 2092 | if (main_wanted_type.next != NULL) |
2093 | { | |
2094 | format_wanted_type *wanted_type_ptr = main_wanted_type.next; | |
2095 | while (wanted_type_ptr) | |
2096 | { | |
2097 | format_wanted_type *next = wanted_type_ptr->next; | |
2098 | ggc_free (wanted_type_ptr); | |
2099 | wanted_type_ptr = next; | |
2100 | } | |
2101 | } | |
0144d886 | 2102 | } |
2103 | } | |
2104 | ||
2105 | ||
2106 | /* Check the argument types from a single format conversion (possibly | |
2107 | including width and precision arguments). */ | |
2108 | static void | |
31266980 | 2109 | check_format_types (format_wanted_type *types, const char *format_start, |
2110 | int format_length) | |
0144d886 | 2111 | { |
2112 | for (; types != 0; types = types->next) | |
2113 | { | |
2114 | tree cur_param; | |
2115 | tree cur_type; | |
2116 | tree orig_cur_type; | |
2117 | tree wanted_type; | |
0144d886 | 2118 | int arg_num; |
2119 | int i; | |
2120 | int char_type_flag; | |
2121 | cur_param = types->param; | |
2122 | cur_type = TREE_TYPE (cur_param); | |
2123 | if (cur_type == error_mark_node) | |
2124 | continue; | |
31266980 | 2125 | orig_cur_type = cur_type; |
0144d886 | 2126 | char_type_flag = 0; |
2127 | wanted_type = types->wanted_type; | |
2128 | arg_num = types->arg_num; | |
2129 | ||
2130 | /* The following should not occur here. */ | |
231bd014 | 2131 | gcc_assert (wanted_type); |
2132 | gcc_assert (wanted_type != void_type_node || types->pointer_count); | |
0144d886 | 2133 | |
2134 | if (types->pointer_count == 0) | |
dc24ddbd | 2135 | wanted_type = lang_hooks.types.type_promotes_to (wanted_type); |
0144d886 | 2136 | |
31266980 | 2137 | wanted_type = TYPE_MAIN_VARIANT (wanted_type); |
2138 | ||
0144d886 | 2139 | STRIP_NOPS (cur_param); |
2140 | ||
2141 | /* Check the types of any additional pointer arguments | |
2142 | that precede the "real" argument. */ | |
2143 | for (i = 0; i < types->pointer_count; ++i) | |
2144 | { | |
2145 | if (TREE_CODE (cur_type) == POINTER_TYPE) | |
2146 | { | |
2147 | cur_type = TREE_TYPE (cur_type); | |
2148 | if (cur_type == error_mark_node) | |
2149 | break; | |
2150 | ||
2151 | /* Check for writing through a NULL pointer. */ | |
2152 | if (types->writing_in_flag | |
2153 | && i == 0 | |
2154 | && cur_param != 0 | |
2155 | && integer_zerop (cur_param)) | |
6bf97f82 | 2156 | warning (OPT_Wformat, "writing through null pointer " |
2157 | "(argument %d)", arg_num); | |
0144d886 | 2158 | |
2159 | /* Check for reading through a NULL pointer. */ | |
2160 | if (types->reading_from_flag | |
2161 | && i == 0 | |
2162 | && cur_param != 0 | |
2163 | && integer_zerop (cur_param)) | |
6bf97f82 | 2164 | warning (OPT_Wformat, "reading through null pointer " |
2165 | "(argument %d)", arg_num); | |
0144d886 | 2166 | |
2167 | if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR) | |
2168 | cur_param = TREE_OPERAND (cur_param, 0); | |
2169 | else | |
2170 | cur_param = 0; | |
2171 | ||
2172 | /* See if this is an attempt to write into a const type with | |
2173 | scanf or with printf "%n". Note: the writing in happens | |
2174 | at the first indirection only, if for example | |
2175 | void * const * is passed to scanf %p; passing | |
2176 | const void ** is simply passing an incompatible type. */ | |
2177 | if (types->writing_in_flag | |
2178 | && i == 0 | |
2179 | && (TYPE_READONLY (cur_type) | |
2180 | || (cur_param != 0 | |
ce45a448 | 2181 | && (CONSTANT_CLASS_P (cur_param) |
0144d886 | 2182 | || (DECL_P (cur_param) |
2183 | && TREE_READONLY (cur_param)))))) | |
6bf97f82 | 2184 | warning (OPT_Wformat, "writing into constant object " |
2185 | "(argument %d)", arg_num); | |
0144d886 | 2186 | |
2187 | /* If there are extra type qualifiers beyond the first | |
2188 | indirection, then this makes the types technically | |
2189 | incompatible. */ | |
2190 | if (i > 0 | |
2191 | && pedantic | |
2192 | && (TYPE_READONLY (cur_type) | |
2193 | || TYPE_VOLATILE (cur_type) | |
2194 | || TYPE_RESTRICT (cur_type))) | |
6bf97f82 | 2195 | warning (OPT_Wformat, "extra type qualifiers in format " |
2196 | "argument (argument %d)", | |
0144d886 | 2197 | arg_num); |
2198 | ||
2199 | } | |
2200 | else | |
2201 | { | |
31266980 | 2202 | format_type_warning (types->name, format_start, format_length, |
2203 | wanted_type, types->pointer_count, | |
2204 | types->wanted_type_name, orig_cur_type, | |
2205 | arg_num); | |
0144d886 | 2206 | break; |
2207 | } | |
2208 | } | |
2209 | ||
2210 | if (i < types->pointer_count) | |
2211 | continue; | |
2212 | ||
0144d886 | 2213 | cur_type = TYPE_MAIN_VARIANT (cur_type); |
2214 | ||
2215 | /* Check whether the argument type is a character type. This leniency | |
2216 | only applies to certain formats, flagged with 'c'. | |
2217 | */ | |
2218 | if (types->char_lenient_flag) | |
2219 | char_type_flag = (cur_type == char_type_node | |
2220 | || cur_type == signed_char_type_node | |
2221 | || cur_type == unsigned_char_type_node); | |
2222 | ||
2223 | /* Check the type of the "real" argument, if there's a type we want. */ | |
f6f70456 | 2224 | if (lang_hooks.types_compatible_p (wanted_type, cur_type)) |
0144d886 | 2225 | continue; |
eb586f2c | 2226 | /* If we want 'void *', allow any pointer type. |
0144d886 | 2227 | (Anything else would already have got a warning.) |
2228 | With -pedantic, only allow pointers to void and to character | |
2229 | types. */ | |
2230 | if (wanted_type == void_type_node | |
2231 | && (!pedantic || (i == 1 && char_type_flag))) | |
2232 | continue; | |
2233 | /* Don't warn about differences merely in signedness, unless | |
2234 | -pedantic. With -pedantic, warn if the type is a pointer | |
2235 | target and not a character type, and for character types at | |
2236 | a second level of indirection. */ | |
2237 | if (TREE_CODE (wanted_type) == INTEGER_TYPE | |
2238 | && TREE_CODE (cur_type) == INTEGER_TYPE | |
84166705 | 2239 | && (!pedantic || i == 0 || (i == 1 && char_type_flag)) |
78a8ed03 | 2240 | && (TYPE_UNSIGNED (wanted_type) |
4070745f | 2241 | ? wanted_type == c_common_unsigned_type (cur_type) |
2242 | : wanted_type == c_common_signed_type (cur_type))) | |
0144d886 | 2243 | continue; |
2244 | /* Likewise, "signed char", "unsigned char" and "char" are | |
2245 | equivalent but the above test won't consider them equivalent. */ | |
2246 | if (wanted_type == char_type_node | |
84166705 | 2247 | && (!pedantic || i < 2) |
0144d886 | 2248 | && char_type_flag) |
2249 | continue; | |
2250 | /* Now we have a type mismatch. */ | |
31266980 | 2251 | format_type_warning (types->name, format_start, format_length, |
2252 | wanted_type, types->pointer_count, | |
2253 | types->wanted_type_name, orig_cur_type, arg_num); | |
2254 | } | |
2255 | } | |
0144d886 | 2256 | |
0144d886 | 2257 | |
31266980 | 2258 | /* Give a warning about a format argument of different type from that |
2259 | expected. DESCR is a description such as "field precision", or | |
2260 | NULL for an ordinary format. For an ordinary format, FORMAT_START | |
2261 | points to where the format starts in the format string and | |
2262 | FORMAT_LENGTH is its length. WANTED_TYPE is the type the argument | |
2263 | should have after POINTER_COUNT pointer dereferences. | |
2264 | WANTED_NAME_NAME is a possibly more friendly name of WANTED_TYPE, | |
2265 | or NULL if the ordinary name of the type should be used. ARG_TYPE | |
2266 | is the type of the actual argument. ARG_NUM is the number of that | |
2267 | argument. */ | |
2268 | static void | |
2269 | format_type_warning (const char *descr, const char *format_start, | |
2270 | int format_length, tree wanted_type, int pointer_count, | |
2271 | const char *wanted_type_name, tree arg_type, int arg_num) | |
2272 | { | |
2273 | char *p; | |
2274 | /* If ARG_TYPE is a typedef with a misleading name (for example, | |
2275 | size_t but not the standard size_t expected by printf %zu), avoid | |
2276 | printing the typedef name. */ | |
2277 | if (wanted_type_name | |
2278 | && TYPE_NAME (arg_type) | |
2279 | && TREE_CODE (TYPE_NAME (arg_type)) == TYPE_DECL | |
2280 | && DECL_NAME (TYPE_NAME (arg_type)) | |
2281 | && !strcmp (wanted_type_name, | |
2282 | lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2))) | |
2283 | arg_type = TYPE_MAIN_VARIANT (arg_type); | |
2284 | /* The format type and name exclude any '*' for pointers, so those | |
2285 | must be formatted manually. For all the types we currently have, | |
2286 | this is adequate, but formats taking pointers to functions or | |
2287 | arrays would require the full type to be built up in order to | |
2288 | print it with %T. */ | |
2289 | p = alloca (pointer_count + 2); | |
2290 | if (pointer_count == 0) | |
2291 | p[0] = 0; | |
2292 | else if (c_dialect_cxx ()) | |
2293 | { | |
2294 | memset (p, '*', pointer_count); | |
2295 | p[pointer_count] = 0; | |
2296 | } | |
2297 | else | |
2298 | { | |
2299 | p[0] = ' '; | |
2300 | memset (p + 1, '*', pointer_count); | |
2301 | p[pointer_count + 1] = 0; | |
2302 | } | |
2303 | if (wanted_type_name) | |
2304 | { | |
2305 | if (descr) | |
6bf97f82 | 2306 | warning (OPT_Wformat, "%s should have type %<%s%s%>, " |
2307 | "but argument %d has type %qT", | |
31266980 | 2308 | descr, wanted_type_name, p, arg_num, arg_type); |
2309 | else | |
6bf97f82 | 2310 | warning (OPT_Wformat, "format %q.*s expects type %<%s%s%>, " |
2311 | "but argument %d has type %qT", | |
31266980 | 2312 | format_length, format_start, wanted_type_name, p, |
2313 | arg_num, arg_type); | |
2314 | } | |
2315 | else | |
2316 | { | |
2317 | if (descr) | |
6bf97f82 | 2318 | warning (OPT_Wformat, "%s should have type %<%T%s%>, " |
2319 | "but argument %d has type %qT", | |
31266980 | 2320 | descr, wanted_type, p, arg_num, arg_type); |
2321 | else | |
6bf97f82 | 2322 | warning (OPT_Wformat, "format %q.*s expects type %<%T%s%>, " |
2323 | "but argument %d has type %qT", | |
31266980 | 2324 | format_length, format_start, wanted_type, p, arg_num, arg_type); |
0144d886 | 2325 | } |
2326 | } | |
c54d077b | 2327 | |
31266980 | 2328 | |
3e038b9d | 2329 | /* Given a format_char_info array FCI, and a character C, this function |
2330 | returns the index into the conversion_specs where that specifier's | |
89f18f73 | 2331 | data is located. The character must exist. */ |
3e038b9d | 2332 | static unsigned int |
2333 | find_char_info_specifier_index (const format_char_info *fci, int c) | |
2334 | { | |
231bd014 | 2335 | unsigned i; |
2336 | ||
2337 | for (i = 0; fci->format_chars; i++, fci++) | |
2338 | if (strchr (fci->format_chars, c)) | |
2339 | return i; | |
3e038b9d | 2340 | |
2341 | /* We shouldn't be looking for a non-existent specifier. */ | |
231bd014 | 2342 | gcc_unreachable (); |
3e038b9d | 2343 | } |
2344 | ||
3dc4d00f | 2345 | /* Given a format_length_info array FLI, and a character C, this |
2346 | function returns the index into the conversion_specs where that | |
89f18f73 | 2347 | modifier's data is located. The character must exist. */ |
3dc4d00f | 2348 | static unsigned int |
2349 | find_length_info_modifier_index (const format_length_info *fli, int c) | |
2350 | { | |
231bd014 | 2351 | unsigned i; |
2352 | ||
2353 | for (i = 0; fli->name; i++, fli++) | |
2354 | if (strchr (fli->name, c)) | |
2355 | return i; | |
3dc4d00f | 2356 | |
2357 | /* We shouldn't be looking for a non-existent modifier. */ | |
231bd014 | 2358 | gcc_unreachable (); |
3dc4d00f | 2359 | } |
2360 | ||
2361 | /* Determine the type of HOST_WIDE_INT in the code being compiled for | |
2362 | use in GCC's __asm_fprintf__ custom format attribute. You must | |
2363 | have set dynamic_format_types before calling this function. */ | |
2364 | static void | |
2365 | init_dynamic_asm_fprintf_info (void) | |
2366 | { | |
2367 | static tree hwi; | |
231bd014 | 2368 | |
3dc4d00f | 2369 | if (!hwi) |
2370 | { | |
2371 | format_length_info *new_asm_fprintf_length_specs; | |
2372 | unsigned int i; | |
2373 | ||
2374 | /* Find the underlying type for HOST_WIDE_INT. For the %w | |
2375 | length modifier to work, one must have issued: "typedef | |
2376 | HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code | |
2377 | prior to using that modifier. */ | |
231bd014 | 2378 | hwi = maybe_get_identifier ("__gcc_host_wide_int__"); |
58db2592 | 2379 | if (!hwi) |
2380 | { | |
2381 | error ("%<__gcc_host_wide_int__%> is not defined as a type"); | |
2382 | return; | |
2383 | } | |
2384 | hwi = identifier_global_value (hwi); | |
2385 | if (!hwi || TREE_CODE (hwi) != TYPE_DECL) | |
2386 | { | |
2387 | error ("%<__gcc_host_wide_int__%> is not defined as a type"); | |
2388 | return; | |
2389 | } | |
2390 | hwi = DECL_ORIGINAL_TYPE (hwi); | |
231bd014 | 2391 | gcc_assert (hwi); |
58db2592 | 2392 | if (hwi != long_integer_type_node && hwi != long_long_integer_type_node) |
2393 | { | |
2394 | error ("%<__gcc_host_wide_int__%> is not defined as %<long%>" | |
2395 | " or %<long long%>"); | |
2396 | return; | |
2397 | } | |
3dc4d00f | 2398 | |
2399 | /* Create a new (writable) copy of asm_fprintf_length_specs. */ | |
4fd61bc6 | 2400 | new_asm_fprintf_length_specs = (format_length_info *) |
2401 | xmemdup (asm_fprintf_length_specs, | |
3dc4d00f | 2402 | sizeof (asm_fprintf_length_specs), |
2403 | sizeof (asm_fprintf_length_specs)); | |
2404 | ||
2405 | /* HOST_WIDE_INT must be one of 'long' or 'long long'. */ | |
2406 | i = find_length_info_modifier_index (new_asm_fprintf_length_specs, 'w'); | |
2407 | if (hwi == long_integer_type_node) | |
2408 | new_asm_fprintf_length_specs[i].index = FMT_LEN_l; | |
2409 | else if (hwi == long_long_integer_type_node) | |
2410 | new_asm_fprintf_length_specs[i].index = FMT_LEN_ll; | |
2411 | else | |
231bd014 | 2412 | gcc_unreachable (); |
3dc4d00f | 2413 | |
2414 | /* Assign the new data for use. */ | |
2415 | dynamic_format_types[asm_fprintf_format_type].length_char_specs = | |
2416 | new_asm_fprintf_length_specs; | |
2417 | } | |
2418 | } | |
2419 | ||
bbec950d | 2420 | /* Determine the type of a "locus" in the code being compiled for use |
2421 | in GCC's __gcc_gfc__ custom format attribute. You must have set | |
2422 | dynamic_format_types before calling this function. */ | |
2423 | static void | |
2424 | init_dynamic_gfc_info (void) | |
2425 | { | |
2426 | static tree locus; | |
2427 | ||
2428 | if (!locus) | |
2429 | { | |
2430 | static format_char_info *gfc_fci; | |
2431 | ||
2432 | /* For the GCC __gcc_gfc__ custom format specifier to work, one | |
2433 | must have declared 'locus' prior to using this attribute. If | |
2434 | we haven't seen this declarations then you shouldn't use the | |
2435 | specifier requiring that type. */ | |
2436 | if ((locus = maybe_get_identifier ("locus"))) | |
2437 | { | |
2438 | locus = identifier_global_value (locus); | |
2439 | if (locus) | |
2440 | { | |
2441 | if (TREE_CODE (locus) != TYPE_DECL) | |
2442 | { | |
2443 | error ("%<locus%> is not defined as a type"); | |
2444 | locus = 0; | |
2445 | } | |
2446 | else | |
2447 | locus = TREE_TYPE (locus); | |
2448 | } | |
2449 | } | |
2450 | ||
2451 | /* Assign the new data for use. */ | |
2452 | ||
2453 | /* Handle the __gcc_gfc__ format specifics. */ | |
2454 | if (!gfc_fci) | |
2455 | dynamic_format_types[gcc_gfc_format_type].conversion_specs = | |
2456 | gfc_fci = (format_char_info *) | |
2457 | xmemdup (gcc_gfc_char_table, | |
2458 | sizeof (gcc_gfc_char_table), | |
2459 | sizeof (gcc_gfc_char_table)); | |
2460 | if (locus) | |
2461 | { | |
2462 | const unsigned i = find_char_info_specifier_index (gfc_fci, 'L'); | |
2463 | gfc_fci[i].types[0].type = &locus; | |
2464 | gfc_fci[i].pointer_count = 1; | |
2465 | } | |
2466 | } | |
2467 | } | |
2468 | ||
3e038b9d | 2469 | /* Determine the types of "tree" and "location_t" in the code being |
2470 | compiled for use in GCC's diagnostic custom format attributes. You | |
2471 | must have set dynamic_format_types before calling this function. */ | |
2472 | static void | |
2473 | init_dynamic_diag_info (void) | |
2474 | { | |
2475 | static tree t, loc, hwi; | |
4fd61bc6 | 2476 | |
3e038b9d | 2477 | if (!loc || !t || !hwi) |
2478 | { | |
7781aa77 | 2479 | static format_char_info *diag_fci, *tdiag_fci, *cdiag_fci, *cxxdiag_fci; |
3e038b9d | 2480 | static format_length_info *diag_ls; |
2481 | unsigned int i; | |
2482 | ||
2483 | /* For the GCC-diagnostics custom format specifiers to work, one | |
eb586f2c | 2484 | must have declared 'tree' and/or 'location_t' prior to using |
3e038b9d | 2485 | those attributes. If we haven't seen these declarations then |
2486 | you shouldn't use the specifiers requiring these types. | |
2487 | However we don't force a hard ICE because we may see only one | |
2488 | or the other type. */ | |
2489 | if ((loc = maybe_get_identifier ("location_t"))) | |
58db2592 | 2490 | { |
2491 | loc = identifier_global_value (loc); | |
2492 | if (loc) | |
2493 | { | |
2494 | if (TREE_CODE (loc) != TYPE_DECL) | |
2495 | { | |
2496 | error ("%<location_t%> is not defined as a type"); | |
2497 | loc = 0; | |
2498 | } | |
2499 | else | |
2500 | loc = TREE_TYPE (loc); | |
2501 | } | |
2502 | } | |
3e038b9d | 2503 | |
eb586f2c | 2504 | /* We need to grab the underlying 'union tree_node' so peek into |
3e038b9d | 2505 | an extra type level. */ |
2506 | if ((t = maybe_get_identifier ("tree"))) | |
58db2592 | 2507 | { |
2508 | t = identifier_global_value (t); | |
2509 | if (t) | |
2510 | { | |
2511 | if (TREE_CODE (t) != TYPE_DECL) | |
2512 | { | |
2513 | error ("%<tree%> is not defined as a type"); | |
2514 | t = 0; | |
2515 | } | |
2516 | else if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE) | |
2517 | { | |
2518 | error ("%<tree%> is not defined as a pointer type"); | |
2519 | t = 0; | |
2520 | } | |
2521 | else | |
2522 | t = TREE_TYPE (TREE_TYPE (t)); | |
2523 | } | |
2524 | } | |
3e038b9d | 2525 | |
2526 | /* Find the underlying type for HOST_WIDE_INT. For the %w | |
2527 | length modifier to work, one must have issued: "typedef | |
2528 | HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code | |
2529 | prior to using that modifier. */ | |
2530 | if ((hwi = maybe_get_identifier ("__gcc_host_wide_int__"))) | |
58db2592 | 2531 | { |
2532 | hwi = identifier_global_value (hwi); | |
2533 | if (hwi) | |
2534 | { | |
2535 | if (TREE_CODE (hwi) != TYPE_DECL) | |
2536 | { | |
2537 | error ("%<__gcc_host_wide_int__%> is not defined as a type"); | |
2538 | hwi = 0; | |
2539 | } | |
2540 | else | |
2541 | { | |
2542 | hwi = DECL_ORIGINAL_TYPE (hwi); | |
2543 | gcc_assert (hwi); | |
2544 | if (hwi != long_integer_type_node | |
2545 | && hwi != long_long_integer_type_node) | |
2546 | { | |
2547 | error ("%<__gcc_host_wide_int__%> is not defined" | |
2548 | " as %<long%> or %<long long%>"); | |
2549 | hwi = 0; | |
2550 | } | |
2551 | } | |
2552 | } | |
2553 | } | |
3e038b9d | 2554 | |
2555 | /* Assign the new data for use. */ | |
2556 | ||
2557 | /* All the GCC diag formats use the same length specs. */ | |
84166705 | 2558 | if (!diag_ls) |
3e038b9d | 2559 | dynamic_format_types[gcc_diag_format_type].length_char_specs = |
7781aa77 | 2560 | dynamic_format_types[gcc_tdiag_format_type].length_char_specs = |
3e038b9d | 2561 | dynamic_format_types[gcc_cdiag_format_type].length_char_specs = |
2562 | dynamic_format_types[gcc_cxxdiag_format_type].length_char_specs = | |
4fd61bc6 | 2563 | diag_ls = (format_length_info *) |
2564 | xmemdup (gcc_diag_length_specs, | |
3e038b9d | 2565 | sizeof (gcc_diag_length_specs), |
2566 | sizeof (gcc_diag_length_specs)); | |
2567 | if (hwi) | |
2568 | { | |
2569 | /* HOST_WIDE_INT must be one of 'long' or 'long long'. */ | |
2570 | i = find_length_info_modifier_index (diag_ls, 'w'); | |
2571 | if (hwi == long_integer_type_node) | |
2572 | diag_ls[i].index = FMT_LEN_l; | |
2573 | else if (hwi == long_long_integer_type_node) | |
2574 | diag_ls[i].index = FMT_LEN_ll; | |
2575 | else | |
231bd014 | 2576 | gcc_unreachable (); |
3e038b9d | 2577 | } |
2578 | ||
2579 | /* Handle the __gcc_diag__ format specifics. */ | |
84166705 | 2580 | if (!diag_fci) |
3e038b9d | 2581 | dynamic_format_types[gcc_diag_format_type].conversion_specs = |
4fd61bc6 | 2582 | diag_fci = (format_char_info *) |
2583 | xmemdup (gcc_diag_char_table, | |
84166705 | 2584 | sizeof (gcc_diag_char_table), |
2585 | sizeof (gcc_diag_char_table)); | |
3e038b9d | 2586 | if (loc) |
2587 | { | |
2588 | i = find_char_info_specifier_index (diag_fci, 'H'); | |
2589 | diag_fci[i].types[0].type = &loc; | |
2590 | diag_fci[i].pointer_count = 1; | |
2591 | } | |
9bc3739f | 2592 | if (t) |
2593 | { | |
2594 | i = find_char_info_specifier_index (diag_fci, 'J'); | |
2595 | diag_fci[i].types[0].type = &t; | |
2596 | diag_fci[i].pointer_count = 1; | |
2597 | } | |
3e038b9d | 2598 | |
7781aa77 | 2599 | /* Handle the __gcc_tdiag__ format specifics. */ |
2600 | if (!tdiag_fci) | |
2601 | dynamic_format_types[gcc_tdiag_format_type].conversion_specs = | |
2602 | tdiag_fci = (format_char_info *) | |
2603 | xmemdup (gcc_tdiag_char_table, | |
2604 | sizeof (gcc_tdiag_char_table), | |
2605 | sizeof (gcc_tdiag_char_table)); | |
2606 | if (loc) | |
2607 | { | |
2608 | i = find_char_info_specifier_index (tdiag_fci, 'H'); | |
2609 | tdiag_fci[i].types[0].type = &loc; | |
2610 | tdiag_fci[i].pointer_count = 1; | |
2611 | } | |
2612 | if (t) | |
2613 | { | |
2614 | /* All specifiers taking a tree share the same struct. */ | |
2615 | i = find_char_info_specifier_index (tdiag_fci, 'D'); | |
2616 | tdiag_fci[i].types[0].type = &t; | |
2617 | tdiag_fci[i].pointer_count = 1; | |
2618 | i = find_char_info_specifier_index (tdiag_fci, 'J'); | |
2619 | tdiag_fci[i].types[0].type = &t; | |
2620 | tdiag_fci[i].pointer_count = 1; | |
2621 | } | |
2622 | ||
3e038b9d | 2623 | /* Handle the __gcc_cdiag__ format specifics. */ |
84166705 | 2624 | if (!cdiag_fci) |
3e038b9d | 2625 | dynamic_format_types[gcc_cdiag_format_type].conversion_specs = |
4fd61bc6 | 2626 | cdiag_fci = (format_char_info *) |
2627 | xmemdup (gcc_cdiag_char_table, | |
84166705 | 2628 | sizeof (gcc_cdiag_char_table), |
2629 | sizeof (gcc_cdiag_char_table)); | |
3e038b9d | 2630 | if (loc) |
2631 | { | |
2632 | i = find_char_info_specifier_index (cdiag_fci, 'H'); | |
2633 | cdiag_fci[i].types[0].type = &loc; | |
2634 | cdiag_fci[i].pointer_count = 1; | |
2635 | } | |
2636 | if (t) | |
2637 | { | |
81d47035 | 2638 | /* All specifiers taking a tree share the same struct. */ |
3e038b9d | 2639 | i = find_char_info_specifier_index (cdiag_fci, 'D'); |
2640 | cdiag_fci[i].types[0].type = &t; | |
2641 | cdiag_fci[i].pointer_count = 1; | |
9bc3739f | 2642 | i = find_char_info_specifier_index (cdiag_fci, 'J'); |
2643 | cdiag_fci[i].types[0].type = &t; | |
2644 | cdiag_fci[i].pointer_count = 1; | |
3e038b9d | 2645 | } |
2646 | ||
2647 | /* Handle the __gcc_cxxdiag__ format specifics. */ | |
84166705 | 2648 | if (!cxxdiag_fci) |
3e038b9d | 2649 | dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs = |
4fd61bc6 | 2650 | cxxdiag_fci = (format_char_info *) |
2651 | xmemdup (gcc_cxxdiag_char_table, | |
84166705 | 2652 | sizeof (gcc_cxxdiag_char_table), |
2653 | sizeof (gcc_cxxdiag_char_table)); | |
3e038b9d | 2654 | if (loc) |
2655 | { | |
2656 | i = find_char_info_specifier_index (cxxdiag_fci, 'H'); | |
2657 | cxxdiag_fci[i].types[0].type = &loc; | |
2658 | cxxdiag_fci[i].pointer_count = 1; | |
2659 | } | |
2660 | if (t) | |
2661 | { | |
81d47035 | 2662 | /* All specifiers taking a tree share the same struct. */ |
3e038b9d | 2663 | i = find_char_info_specifier_index (cxxdiag_fci, 'D'); |
2664 | cxxdiag_fci[i].types[0].type = &t; | |
2665 | cxxdiag_fci[i].pointer_count = 1; | |
9bc3739f | 2666 | i = find_char_info_specifier_index (cxxdiag_fci, 'J'); |
2667 | cxxdiag_fci[i].types[0].type = &t; | |
2668 | cxxdiag_fci[i].pointer_count = 1; | |
3e038b9d | 2669 | } |
2670 | } | |
2671 | } | |
2672 | ||
1e9af880 | 2673 | #ifdef TARGET_FORMAT_TYPES |
2674 | extern const format_kind_info TARGET_FORMAT_TYPES[]; | |
2675 | #endif | |
2676 | ||
c54d077b | 2677 | /* Handle a "format" attribute; arguments as in |
2678 | struct attribute_spec.handler. */ | |
2679 | tree | |
9a03a746 | 2680 | handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args, |
d19a6ea0 | 2681 | int flags, bool *no_add_attrs) |
c54d077b | 2682 | { |
2683 | tree type = *node; | |
2684 | function_format_info info; | |
2685 | tree argument; | |
c54d077b | 2686 | |
1e9af880 | 2687 | #ifdef TARGET_FORMAT_TYPES |
2688 | /* If the target provides additional format types, we need to | |
2689 | add them to FORMAT_TYPES at first use. */ | |
2690 | if (TARGET_FORMAT_TYPES != NULL && !dynamic_format_types) | |
2691 | { | |
2692 | dynamic_format_types = xmalloc ((n_format_types + TARGET_N_FORMAT_TYPES) | |
2693 | * sizeof (dynamic_format_types[0])); | |
2694 | memcpy (dynamic_format_types, format_types_orig, | |
2695 | sizeof (format_types_orig)); | |
2696 | memcpy (&dynamic_format_types[n_format_types], TARGET_FORMAT_TYPES, | |
2697 | TARGET_N_FORMAT_TYPES * sizeof (dynamic_format_types[0])); | |
2698 | ||
2699 | format_types = dynamic_format_types; | |
2700 | n_format_types += TARGET_N_FORMAT_TYPES; | |
2701 | } | |
2702 | #endif | |
2703 | ||
c54d077b | 2704 | if (!decode_format_attr (args, &info, 0)) |
2705 | { | |
2706 | *no_add_attrs = true; | |
2707 | return NULL_TREE; | |
2708 | } | |
2709 | ||
c54d077b | 2710 | argument = TYPE_ARG_TYPES (type); |
2711 | if (argument) | |
2712 | { | |
0a1f0a0f | 2713 | if (!check_format_string (argument, info.format_num, flags, |
2714 | no_add_attrs)) | |
2715 | return NULL_TREE; | |
c54d077b | 2716 | |
0a1f0a0f | 2717 | if (info.first_arg_num != 0) |
c54d077b | 2718 | { |
0a1f0a0f | 2719 | unsigned HOST_WIDE_INT arg_num = 1; |
2720 | ||
c54d077b | 2721 | /* Verify that first_arg_num points to the last arg, |
2722 | the ... */ | |
2723 | while (argument) | |
2724 | arg_num++, argument = TREE_CHAIN (argument); | |
2725 | ||
2726 | if (arg_num != info.first_arg_num) | |
2727 | { | |
2728 | if (!(flags & (int) ATTR_FLAG_BUILT_IN)) | |
1e5fcbe2 | 2729 | error ("args to be formatted is not %<...%>"); |
c54d077b | 2730 | *no_add_attrs = true; |
2731 | return NULL_TREE; | |
2732 | } | |
2733 | } | |
2734 | } | |
2735 | ||
2736 | if (info.format_type == strftime_format_type && info.first_arg_num != 0) | |
2737 | { | |
2738 | error ("strftime formats cannot format arguments"); | |
2739 | *no_add_attrs = true; | |
2740 | return NULL_TREE; | |
2741 | } | |
2742 | ||
3e038b9d | 2743 | /* If this is a custom GCC-internal format type, we have to |
2744 | initialize certain bits a runtime. */ | |
2745 | if (info.format_type == asm_fprintf_format_type | |
bbec950d | 2746 | || info.format_type == gcc_gfc_format_type |
3e038b9d | 2747 | || info.format_type == gcc_diag_format_type |
7781aa77 | 2748 | || info.format_type == gcc_tdiag_format_type |
3e038b9d | 2749 | || info.format_type == gcc_cdiag_format_type |
2750 | || info.format_type == gcc_cxxdiag_format_type) | |
c54d077b | 2751 | { |
3dc4d00f | 2752 | /* Our first time through, we have to make sure that our |
2753 | format_type data is allocated dynamically and is modifiable. */ | |
2754 | if (!dynamic_format_types) | |
4fd61bc6 | 2755 | format_types = dynamic_format_types = (format_kind_info *) |
3dc4d00f | 2756 | xmemdup (format_types_orig, sizeof (format_types_orig), |
2757 | sizeof (format_types_orig)); | |
2758 | ||
3e038b9d | 2759 | /* If this is format __asm_fprintf__, we have to initialize |
2760 | GCC's notion of HOST_WIDE_INT for checking %wd. */ | |
2761 | if (info.format_type == asm_fprintf_format_type) | |
84166705 | 2762 | init_dynamic_asm_fprintf_info (); |
bbec950d | 2763 | /* If this is format __gcc_gfc__, we have to initialize GCC's |
2764 | notion of 'locus' at runtime for %L. */ | |
2765 | else if (info.format_type == gcc_gfc_format_type) | |
2766 | init_dynamic_gfc_info (); | |
3e038b9d | 2767 | /* If this is one of the diagnostic attributes, then we have to |
eb586f2c | 2768 | initialize 'location_t' and 'tree' at runtime. */ |
3e038b9d | 2769 | else if (info.format_type == gcc_diag_format_type |
7781aa77 | 2770 | || info.format_type == gcc_tdiag_format_type |
3e038b9d | 2771 | || info.format_type == gcc_cdiag_format_type |
2772 | || info.format_type == gcc_cxxdiag_format_type) | |
84166705 | 2773 | init_dynamic_diag_info (); |
3e038b9d | 2774 | else |
84166705 | 2775 | gcc_unreachable (); |
c54d077b | 2776 | } |
2777 | ||
2778 | return NULL_TREE; | |
2779 | } |