1 | /* Check calls to formatted I/O functions (-Wformat). |
2 | Copyright (C) 1992-2023 Free Software Foundation, Inc. |
3 | |
4 | This file is part of GCC. |
5 | |
6 | GCC is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free |
8 | Software Foundation; either version 3, or (at your option) any later |
9 | version. |
10 | |
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with GCC; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | #include "config.h" |
21 | #include "system.h" |
22 | #include "coretypes.h" |
23 | #include "tm.h" |
24 | #include "c-target.h" |
25 | #include "c-common.h" |
26 | #include "alloc-pool.h" |
27 | #include "stringpool.h" |
28 | #include "c-objc.h" |
29 | #include "intl.h" |
30 | #include "langhooks.h" |
31 | #include "c-format.h" |
32 | #include "diagnostic.h" |
33 | #include "substring-locations.h" |
34 | #include "selftest.h" |
35 | #include "selftest-diagnostic.h" |
36 | #include "builtins.h" |
37 | #include "attribs.h" |
38 | #include "gcc-rich-location.h" |
39 | |
40 | /* Handle attributes associated with format checking. */ |
41 | |
42 | /* This must be in the same order as format_types, except for |
43 | format_type_error. Target-specific format types do not have |
44 | matching enum values. */ |
45 | enum format_type { printf_format_type, asm_fprintf_format_type, |
46 | gcc_diag_format_type, gcc_tdiag_format_type, |
47 | gcc_cdiag_format_type, |
48 | gcc_cxxdiag_format_type, gcc_gfc_format_type, |
49 | gcc_dump_printf_format_type, |
50 | gcc_objc_string_format_type, |
51 | format_type_error = -1}; |
52 | |
53 | struct function_format_info |
54 | { |
55 | enum format_type format_type; /* type of format (printf, scanf, etc.) */ |
56 | /* IS_RAW is relevant only for GCC diagnostic format functions. |
57 | It is set for "raw" formatting functions like pp_printf that |
58 | are not intended to produce complete diagnostics according to |
59 | GCC guidelines, and clear for others like error and warning |
60 | whose format string is checked for proper quoting and spelling. */ |
61 | bool is_raw; |
62 | unsigned HOST_WIDE_INT format_num; /* number of format argument */ |
63 | unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */ |
64 | }; |
65 | |
66 | /* Initialized in init_dynamic_diag_info. */ |
67 | static GTY(()) tree local_tree_type_node; |
68 | static GTY(()) tree local_event_ptr_node; |
69 | static GTY(()) tree local_gimple_ptr_node; |
70 | static GTY(()) tree local_cgraph_node_ptr_node; |
71 | static GTY(()) tree locus; |
72 | |
73 | static bool decode_format_attr (const_tree, tree, tree, function_format_info *, |
74 | bool); |
75 | static format_type decode_format_type (const char *, bool * = NULL); |
76 | |
77 | static bool check_format_string (const_tree argument, |
78 | unsigned HOST_WIDE_INT format_num, |
79 | int flags, bool *no_add_attrs, |
80 | int expected_format_type); |
81 | static bool validate_constant (const_tree fn, const_tree atname, tree &expr, |
82 | int argno, unsigned HOST_WIDE_INT *value, |
83 | int flags, bool validated_p); |
84 | static const char *convert_format_name_to_system_name (const char *attr_name); |
85 | |
86 | static int first_target_format_type; |
87 | static const char *format_name (int format_num); |
88 | static int format_flags (int format_num); |
89 | |
90 | /* Emit a warning as per format_warning_va, but construct the substring_loc |
91 | for the character at offset (CHAR_IDX - 1) within a string constant |
92 | FORMAT_STRING_CST at FMT_STRING_LOC. */ |
93 | |
94 | ATTRIBUTE_GCC_DIAG (5,6) |
95 | static bool |
96 | format_warning_at_char (location_t fmt_string_loc, tree format_string_cst, |
97 | int char_idx, int opt, const char *gmsgid, ...) |
98 | { |
99 | va_list ap; |
100 | va_start (ap, gmsgid); |
101 | tree string_type = TREE_TYPE (format_string_cst); |
102 | |
103 | /* The callers are of the form: |
104 | format_warning (format_string_loc, format_string_cst, |
105 | format_chars - orig_format_chars, |
106 | where format_chars has already been incremented, so that |
107 | CHAR_IDX is one character beyond where the warning should |
108 | be emitted. Fix it. */ |
109 | char_idx -= 1; |
110 | |
111 | substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx, |
112 | char_idx); |
113 | format_string_diagnostic_t diag (fmt_loc, NULL, UNKNOWN_LOCATION, NULL, |
114 | NULL); |
115 | bool warned = diag.emit_warning_va (opt, gmsgid, ap: &ap); |
116 | va_end (ap); |
117 | |
118 | return warned; |
119 | } |
120 | |
121 | |
122 | /* Emit a warning as per format_warning_va, but construct the substring_loc |
123 | for the substring at offset (POS1, POS2 - 1) within a string constant |
124 | FORMAT_STRING_CST at FMT_STRING_LOC. */ |
125 | |
126 | ATTRIBUTE_GCC_DIAG (6,7) |
127 | static bool |
128 | format_warning_substr (location_t fmt_string_loc, tree format_string_cst, |
129 | int pos1, int pos2, int opt, const char *gmsgid, ...) |
130 | { |
131 | va_list ap; |
132 | va_start (ap, gmsgid); |
133 | tree string_type = TREE_TYPE (format_string_cst); |
134 | |
135 | pos2 -= 1; |
136 | |
137 | substring_loc fmt_loc (fmt_string_loc, string_type, pos1, pos1, pos2); |
138 | format_string_diagnostic_t diag (fmt_loc, NULL, UNKNOWN_LOCATION, NULL, |
139 | NULL); |
140 | bool warned = diag.emit_warning_va (opt, gmsgid, ap: &ap); |
141 | va_end (ap); |
142 | |
143 | return warned; |
144 | } |
145 | |
146 | |
147 | /* Check that we have a pointer to a string suitable for use as a format. |
148 | The default is to check for a char type. |
149 | For objective-c dialects, this is extended to include references to string |
150 | objects validated by objc_string_ref_type_p (). |
151 | Targets may also provide a string object type that can be used within c and |
152 | c++ and shared with their respective objective-c dialects. In this case the |
153 | reference to a format string is checked for validity via a hook. |
154 | |
155 | The function returns true if strref points to any string type valid for the |
156 | language dialect and target. */ |
157 | |
158 | bool |
159 | valid_format_string_type_p (tree strref) |
160 | { |
161 | return (strref != NULL |
162 | && TREE_CODE (strref) == POINTER_TYPE |
163 | && (TYPE_MAIN_VARIANT (TREE_TYPE (strref)) == char_type_node |
164 | || objc_string_ref_type_p (strref) |
165 | || (*targetcm.string_object_ref_type_p) ((const_tree) strref))); |
166 | } |
167 | |
168 | /* Handle a "format_arg" attribute; arguments as in |
169 | struct attribute_spec.handler. */ |
170 | tree |
171 | handle_format_arg_attribute (tree *node, tree atname, |
172 | tree args, int flags, bool *no_add_attrs) |
173 | { |
174 | tree type = *node; |
175 | /* Note that TREE_VALUE (args) is changed in the validate_constant call. */ |
176 | tree *format_num_expr = &TREE_VALUE (args); |
177 | unsigned HOST_WIDE_INT format_num = 0; |
178 | |
179 | if (!validate_constant (fn: type, atname, expr&: *format_num_expr, argno: 0, value: &format_num, flags: 0, |
180 | validated_p: false)) |
181 | { |
182 | *no_add_attrs = true; |
183 | return NULL_TREE; |
184 | } |
185 | |
186 | if (prototype_p (type)) |
187 | { |
188 | /* The format arg can be any string reference valid for the language and |
189 | target. We cannot be more specific in this case. */ |
190 | if (!check_format_string (argument: type, format_num, flags, no_add_attrs, expected_format_type: -1)) |
191 | return NULL_TREE; |
192 | } |
193 | |
194 | if (!valid_format_string_type_p (TREE_TYPE (type))) |
195 | { |
196 | if (!(flags & (int) ATTR_FLAG_BUILT_IN)) |
197 | error ("function does not return string type" ); |
198 | *no_add_attrs = true; |
199 | return NULL_TREE; |
200 | } |
201 | |
202 | return NULL_TREE; |
203 | } |
204 | |
205 | /* Verify that the format_num argument is actually a string reference suitable, |
206 | for the language dialect and target (in case the format attribute is in |
207 | error). When we know the specific reference type expected, this is also |
208 | checked. */ |
209 | static bool |
210 | check_format_string (const_tree fntype, unsigned HOST_WIDE_INT format_num, |
211 | int flags, bool *no_add_attrs, int expected_format_type) |
212 | { |
213 | unsigned HOST_WIDE_INT i; |
214 | bool is_objc_sref, is_target_sref, is_char_ref; |
215 | tree ref; |
216 | int fmt_flags; |
217 | function_args_iterator iter; |
218 | |
219 | i = 1; |
220 | FOREACH_FUNCTION_ARGS (fntype, ref, iter) |
221 | { |
222 | if (i == format_num) |
223 | break; |
224 | i++; |
225 | } |
226 | |
227 | if (!ref |
228 | || !valid_format_string_type_p (strref: ref)) |
229 | { |
230 | if (!(flags & (int) ATTR_FLAG_BUILT_IN)) |
231 | error ("format string argument is not a string type" ); |
232 | *no_add_attrs = true; |
233 | return false; |
234 | } |
235 | |
236 | /* We only know that we want a suitable string reference. */ |
237 | if (expected_format_type < 0) |
238 | return true; |
239 | |
240 | /* Now check that the arg matches the expected type. */ |
241 | is_char_ref = |
242 | (TYPE_MAIN_VARIANT (TREE_TYPE (ref)) == char_type_node); |
243 | |
244 | fmt_flags = format_flags (format_num: expected_format_type); |
245 | is_objc_sref = is_target_sref = false; |
246 | if (!is_char_ref) |
247 | is_objc_sref = objc_string_ref_type_p (ref); |
248 | |
249 | if (!(fmt_flags & FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL)) |
250 | { |
251 | if (is_char_ref) |
252 | return true; /* OK, we expected a char and found one. */ |
253 | else |
254 | { |
255 | /* We expected a char but found an extended string type. */ |
256 | if (is_objc_sref) |
257 | error ("found a %qs reference but the format argument should" |
258 | " be a string" , format_name (format_num: gcc_objc_string_format_type)); |
259 | else |
260 | error ("found a %qT but the format argument should be a string" , |
261 | ref); |
262 | *no_add_attrs = true; |
263 | return false; |
264 | } |
265 | } |
266 | |
267 | /* We expect a string object type as the format arg. */ |
268 | if (is_char_ref) |
269 | { |
270 | error ("format argument should be a %qs reference but" |
271 | " a string was found" , format_name (format_num: expected_format_type)); |
272 | *no_add_attrs = true; |
273 | return false; |
274 | } |
275 | |
276 | /* We will assert that objective-c will support either its own string type |
277 | or the target-supplied variant. */ |
278 | if (!is_objc_sref) |
279 | is_target_sref = (*targetcm.string_object_ref_type_p) ((const_tree) ref); |
280 | |
281 | if (expected_format_type == (int) gcc_objc_string_format_type |
282 | && (is_objc_sref || is_target_sref)) |
283 | return true; |
284 | |
285 | /* We will allow a target string ref to match only itself. */ |
286 | if (first_target_format_type |
287 | && expected_format_type >= first_target_format_type |
288 | && is_target_sref) |
289 | return true; |
290 | else |
291 | { |
292 | error ("format argument should be a %qs reference" , |
293 | format_name (format_num: expected_format_type)); |
294 | *no_add_attrs = true; |
295 | return false; |
296 | } |
297 | } |
298 | |
299 | /* Under the control of FLAGS, verify EXPR is a valid constant that |
300 | refers to a positional argument ARGNO having a string type (char* |
301 | or, for targets like Darwin, a pointer to struct CFString) to |
302 | a function FN declared with attribute ATNAME. If valid, store the |
303 | constant's integer value in *VALUE and return true. If VALIDATED_P |
304 | is true assert the validation is successful. |
305 | |
306 | N.B. This function modifies EXPR. */ |
307 | |
308 | static bool |
309 | validate_constant (const_tree fn, const_tree atname, tree &expr, int argno, |
310 | unsigned HOST_WIDE_INT *value, int flags, bool validated_p) |
311 | { |
312 | /* Require the referenced argument to have a string type. For targets |
313 | like Darwin, also accept pointers to struct CFString. */ |
314 | if (tree val = positional_argument (fn, atname, expr, STRING_CST, |
315 | argno, flags)) |
316 | { |
317 | *value = TREE_INT_CST_LOW (val); |
318 | return true; |
319 | } |
320 | |
321 | gcc_assert (!validated_p); |
322 | return false; |
323 | } |
324 | |
325 | /* Decode the arguments to a "format" attribute into a |
326 | function_format_info structure. It is already known that the list |
327 | is of the right length. If VALIDATED_P is true, then these |
328 | attributes have already been validated and must not be erroneous; |
329 | if false, it will give an error message. FN is either a function |
330 | declaration or function type. Returns true if the attributes are |
331 | successfully decoded, false otherwise. */ |
332 | |
333 | static bool |
334 | decode_format_attr (const_tree fn, tree atname, tree args, |
335 | function_format_info *info, bool validated_p) |
336 | { |
337 | tree format_type_id = TREE_VALUE (args); |
338 | /* Note that TREE_VALUE (args) is changed in place below. Ditto |
339 | for the value of the next element on the list. */ |
340 | tree *format_num_expr = &TREE_VALUE (TREE_CHAIN (args)); |
341 | tree *first_arg_num_expr = &TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); |
342 | |
343 | if (TREE_CODE (format_type_id) != IDENTIFIER_NODE) |
344 | { |
345 | gcc_assert (!validated_p); |
346 | error ("unrecognized format specifier" ); |
347 | return false; |
348 | } |
349 | else |
350 | { |
351 | const char *p = IDENTIFIER_POINTER (format_type_id); |
352 | |
353 | info->format_type = decode_format_type (p, &info->is_raw); |
354 | |
355 | if (!c_dialect_objc () |
356 | && info->format_type == gcc_objc_string_format_type) |
357 | { |
358 | gcc_assert (!validated_p); |
359 | warning (OPT_Wformat_, "%qE is only allowed in Objective-C dialects" , |
360 | format_type_id); |
361 | info->format_type = format_type_error; |
362 | return false; |
363 | } |
364 | |
365 | if (info->format_type == format_type_error) |
366 | { |
367 | gcc_assert (!validated_p); |
368 | warning (OPT_Wformat_, "%qE is an unrecognized format function type" , |
369 | format_type_id); |
370 | return false; |
371 | } |
372 | } |
373 | |
374 | if (!validate_constant (fn, atname, expr&: *format_num_expr, argno: 2, value: &info->format_num, |
375 | flags: 0, validated_p)) |
376 | return false; |
377 | |
378 | if (!validate_constant (fn, atname, expr&: *first_arg_num_expr, argno: 3, |
379 | value: &info->first_arg_num, |
380 | flags: (POSARG_ZERO | POSARG_ELLIPSIS), validated_p)) |
381 | return false; |
382 | |
383 | if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num) |
384 | { |
385 | gcc_assert (!validated_p); |
386 | error ("format string argument follows the arguments to be formatted" ); |
387 | return false; |
388 | } |
389 | |
390 | return true; |
391 | } |
392 | |
393 | /* Check a call to a format function against a parameter list. */ |
394 | |
395 | /* The C standard version C++ is treated as equivalent to |
396 | or inheriting from, for the purpose of format features supported. */ |
397 | #define CPLUSPLUS_STD_VER (cxx_dialect < cxx11 ? STD_C94 : STD_C99) |
398 | /* The C standard version we are checking formats against when pedantic. */ |
399 | #define C_STD_VER ((int) (c_dialect_cxx () \ |
400 | ? CPLUSPLUS_STD_VER \ |
401 | : (flag_isoc23 \ |
402 | ? STD_C23 \ |
403 | : (flag_isoc99 \ |
404 | ? STD_C99 \ |
405 | : (flag_isoc94 ? STD_C94 : STD_C89))))) |
406 | /* The name to give to the standard version we are warning about when |
407 | pedantic. FEATURE_VER is the version in which the feature warned out |
408 | appeared, which is higher than C_STD_VER. */ |
409 | #define C_STD_NAME(FEATURE_VER) (c_dialect_cxx () \ |
410 | ? (cxx_dialect < cxx11 ? "ISO C++98" \ |
411 | : "ISO C++11") \ |
412 | : ((FEATURE_VER) == STD_EXT \ |
413 | ? "ISO C" \ |
414 | : ((FEATURE_VER) == STD_C23 \ |
415 | ? "ISO C17" \ |
416 | : "ISO C90"))) |
417 | /* Adjust a C standard version, which may be STD_C9L, to account for |
418 | -Wno-long-long. Returns other standard versions unchanged. */ |
419 | #define ADJ_STD(VER) ((int) ((VER) == STD_C9L \ |
420 | ? (warn_long_long ? STD_C99 : STD_C89) \ |
421 | : (VER))) |
422 | |
423 | /* Enum describing the kind of specifiers present in the format and |
424 | requiring an argument. */ |
425 | enum format_specifier_kind { |
426 | CF_KIND_FORMAT, |
427 | CF_KIND_FIELD_WIDTH, |
428 | CF_KIND_FIELD_PRECISION |
429 | }; |
430 | |
431 | static const char *kind_descriptions[] = { |
432 | N_("format" ), |
433 | N_("field width specifier" ), |
434 | N_("field precision specifier" ) |
435 | }; |
436 | |
437 | /* Structure describing details of a type expected in format checking, |
438 | and the type to check against it. */ |
439 | struct format_wanted_type |
440 | { |
441 | /* The type wanted. */ |
442 | tree wanted_type; |
443 | /* The name of this type to use in diagnostics. */ |
444 | const char *wanted_type_name; |
445 | /* Should be type checked just for scalar width identity. */ |
446 | int scalar_identity_flag; |
447 | /* The level of indirection through pointers at which this type occurs. */ |
448 | int pointer_count; |
449 | /* Whether, when pointer_count is 1, to allow any character type when |
450 | pedantic, rather than just the character or void type specified. */ |
451 | int char_lenient_flag; |
452 | /* Whether the argument, dereferenced once, is written into and so the |
453 | argument must not be a pointer to a const-qualified type. */ |
454 | int writing_in_flag; |
455 | /* Whether the argument, dereferenced once, is read from and so |
456 | must not be a NULL pointer. */ |
457 | int reading_from_flag; |
458 | /* The kind of specifier that this type is used for. */ |
459 | enum format_specifier_kind kind; |
460 | /* The starting character of the specifier. This never includes the |
461 | initial percent sign. */ |
462 | const char *format_start; |
463 | /* The length of the specifier. */ |
464 | int format_length; |
465 | /* The actual parameter to check against the wanted type. */ |
466 | tree param; |
467 | /* The argument number of that parameter. */ |
468 | int arg_num; |
469 | /* The offset location of this argument with respect to the format |
470 | string location. */ |
471 | unsigned int offset_loc; |
472 | /* The next type to check for this format conversion, or NULL if none. */ |
473 | struct format_wanted_type *next; |
474 | }; |
475 | |
476 | /* Convenience macro for format_length_info meaning unused. */ |
477 | #define NO_FMT NULL, FMT_LEN_none, STD_C89 |
478 | |
479 | static const format_length_info printf_length_specs[] = |
480 | { |
481 | { .name: "h" , .index: FMT_LEN_h, .std: STD_C89, .double_name: "hh" , .double_index: FMT_LEN_hh, .double_std: STD_C99, .scalar_identity_flag: 0 }, |
482 | { .name: "l" , .index: FMT_LEN_l, .std: STD_C89, .double_name: "ll" , .double_index: FMT_LEN_ll, .double_std: STD_C9L, .scalar_identity_flag: 0 }, |
483 | { .name: "q" , .index: FMT_LEN_ll, .std: STD_EXT, NO_FMT, .scalar_identity_flag: 0 }, |
484 | { .name: "L" , .index: FMT_LEN_L, .std: STD_C89, NO_FMT, .scalar_identity_flag: 0 }, |
485 | { .name: "z" , .index: FMT_LEN_z, .std: STD_C99, NO_FMT, .scalar_identity_flag: 0 }, |
486 | { .name: "Z" , .index: FMT_LEN_z, .std: STD_EXT, NO_FMT, .scalar_identity_flag: 0 }, |
487 | { .name: "t" , .index: FMT_LEN_t, .std: STD_C99, NO_FMT, .scalar_identity_flag: 0 }, |
488 | { .name: "j" , .index: FMT_LEN_j, .std: STD_C99, NO_FMT, .scalar_identity_flag: 0 }, |
489 | { .name: "H" , .index: FMT_LEN_H, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
490 | { .name: "D" , .index: FMT_LEN_D, .std: STD_C23, .double_name: "DD" , .double_index: FMT_LEN_DD, .double_std: STD_C23, .scalar_identity_flag: 0 }, |
491 | { .name: "w8" , .index: FMT_LEN_w8, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
492 | { .name: "w16" , .index: FMT_LEN_w16, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
493 | { .name: "w32" , .index: FMT_LEN_w32, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
494 | { .name: "w64" , .index: FMT_LEN_w64, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
495 | { .name: "wf8" , .index: FMT_LEN_wf8, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
496 | { .name: "wf16" , .index: FMT_LEN_wf16, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
497 | { .name: "wf32" , .index: FMT_LEN_wf32, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
498 | { .name: "wf64" , .index: FMT_LEN_wf64, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
499 | { NO_FMT, NO_FMT, .scalar_identity_flag: 0 } |
500 | }; |
501 | |
502 | /* Length specifiers valid for asm_fprintf. */ |
503 | static const format_length_info asm_fprintf_length_specs[] = |
504 | { |
505 | { .name: "l" , .index: FMT_LEN_l, .std: STD_C89, .double_name: "ll" , .double_index: FMT_LEN_ll, .double_std: STD_C89, .scalar_identity_flag: 0 }, |
506 | { .name: "w" , .index: FMT_LEN_w, .std: STD_C89, NO_FMT, .scalar_identity_flag: 0 }, |
507 | { NO_FMT, NO_FMT, .scalar_identity_flag: 0 } |
508 | }; |
509 | |
510 | /* Length specifiers valid for GCC diagnostics. */ |
511 | static const format_length_info gcc_diag_length_specs[] = |
512 | { |
513 | { .name: "l" , .index: FMT_LEN_l, .std: STD_C89, .double_name: "ll" , .double_index: FMT_LEN_ll, .double_std: STD_C89, .scalar_identity_flag: 0 }, |
514 | { .name: "w" , .index: FMT_LEN_w, .std: STD_C89, NO_FMT, .scalar_identity_flag: 0 }, |
515 | { NO_FMT, NO_FMT, .scalar_identity_flag: 0 } |
516 | }; |
517 | |
518 | /* The custom diagnostics all accept the same length specifiers. */ |
519 | #define gcc_tdiag_length_specs gcc_diag_length_specs |
520 | #define gcc_cdiag_length_specs gcc_diag_length_specs |
521 | #define gcc_cxxdiag_length_specs gcc_diag_length_specs |
522 | #define gcc_dump_printf_length_specs gcc_diag_length_specs |
523 | |
524 | /* This differs from printf_length_specs only in that "Z" is not accepted. */ |
525 | static const format_length_info scanf_length_specs[] = |
526 | { |
527 | { .name: "h" , .index: FMT_LEN_h, .std: STD_C89, .double_name: "hh" , .double_index: FMT_LEN_hh, .double_std: STD_C99, .scalar_identity_flag: 0 }, |
528 | { .name: "l" , .index: FMT_LEN_l, .std: STD_C89, .double_name: "ll" , .double_index: FMT_LEN_ll, .double_std: STD_C9L, .scalar_identity_flag: 0 }, |
529 | { .name: "q" , .index: FMT_LEN_ll, .std: STD_EXT, NO_FMT, .scalar_identity_flag: 0 }, |
530 | { .name: "L" , .index: FMT_LEN_L, .std: STD_C89, NO_FMT, .scalar_identity_flag: 0 }, |
531 | { .name: "z" , .index: FMT_LEN_z, .std: STD_C99, NO_FMT, .scalar_identity_flag: 0 }, |
532 | { .name: "t" , .index: FMT_LEN_t, .std: STD_C99, NO_FMT, .scalar_identity_flag: 0 }, |
533 | { .name: "j" , .index: FMT_LEN_j, .std: STD_C99, NO_FMT, .scalar_identity_flag: 0 }, |
534 | { .name: "H" , .index: FMT_LEN_H, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
535 | { .name: "D" , .index: FMT_LEN_D, .std: STD_C23, .double_name: "DD" , .double_index: FMT_LEN_DD, .double_std: STD_C23, .scalar_identity_flag: 0 }, |
536 | { .name: "w8" , .index: FMT_LEN_w8, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
537 | { .name: "w16" , .index: FMT_LEN_w16, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
538 | { .name: "w32" , .index: FMT_LEN_w32, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
539 | { .name: "w64" , .index: FMT_LEN_w64, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
540 | { .name: "wf8" , .index: FMT_LEN_wf8, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
541 | { .name: "wf16" , .index: FMT_LEN_wf16, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
542 | { .name: "wf32" , .index: FMT_LEN_wf32, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
543 | { .name: "wf64" , .index: FMT_LEN_wf64, .std: STD_C23, NO_FMT, .scalar_identity_flag: 0 }, |
544 | { NO_FMT, NO_FMT, .scalar_identity_flag: 0 } |
545 | }; |
546 | |
547 | |
548 | /* All tables for strfmon use STD_C89 everywhere, since -pedantic warnings |
549 | make no sense for a format type not part of any C standard version. */ |
550 | static const format_length_info strfmon_length_specs[] = |
551 | { |
552 | /* A GNU extension. */ |
553 | { .name: "L" , .index: FMT_LEN_L, .std: STD_C89, NO_FMT, .scalar_identity_flag: 0 }, |
554 | { NO_FMT, NO_FMT, .scalar_identity_flag: 0 } |
555 | }; |
556 | |
557 | |
558 | /* Length modifiers used by the fortran/error.cc routines. */ |
559 | static const format_length_info gcc_gfc_length_specs[] = |
560 | { |
561 | { .name: "l" , .index: FMT_LEN_l, .std: STD_C89, .double_name: "ll" , .double_index: FMT_LEN_ll, .double_std: STD_C89, .scalar_identity_flag: 0 }, |
562 | { .name: "w" , .index: FMT_LEN_w, .std: STD_C89, NO_FMT, .scalar_identity_flag: 0 }, |
563 | { NO_FMT, NO_FMT, .scalar_identity_flag: 0 } |
564 | }; |
565 | |
566 | |
567 | static const format_flag_spec printf_flag_specs[] = |
568 | { |
569 | { .flag_char: ' ', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("' ' flag" ), N_("the ' ' printf flag" ), .std: STD_C89 }, |
570 | { .flag_char: '+', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'+' flag" ), N_("the '+' printf flag" ), .std: STD_C89 }, |
571 | { .flag_char: '#', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'#' flag" ), N_("the '#' printf flag" ), .std: STD_C89 }, |
572 | { .flag_char: '0', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'0' flag" ), N_("the '0' printf flag" ), .std: STD_C89 }, |
573 | { .flag_char: '-', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'-' flag" ), N_("the '-' printf flag" ), .std: STD_C89 }, |
574 | { .flag_char: '\'', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("''' flag" ), N_("the ''' printf flag" ), .std: STD_EXT }, |
575 | { .flag_char: 'I', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'I' flag" ), N_("the 'I' printf flag" ), .std: STD_EXT }, |
576 | { .flag_char: 'w', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("field width" ), N_("field width in printf format" ), .std: STD_C89 }, |
577 | { .flag_char: 'p', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("precision" ), N_("precision in printf format" ), .std: STD_C89 }, |
578 | { .flag_char: 'L', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("length modifier" ), N_("length modifier in printf format" ), .std: STD_C89 }, |
579 | { .flag_char: 0, .predicate: 0, .skip_next_char: 0, .quoting: 0, NULL, NULL, .std: STD_C89 } |
580 | }; |
581 | |
582 | |
583 | static const format_flag_pair printf_flag_pairs[] = |
584 | { |
585 | { .flag_char1: ' ', .flag_char2: '+', .ignored: 1, .predicate: 0 }, |
586 | { .flag_char1: '0', .flag_char2: '-', .ignored: 1, .predicate: 0 }, |
587 | { .flag_char1: '0', .flag_char2: 'p', .ignored: 1, .predicate: 'i' }, |
588 | { .flag_char1: 0, .flag_char2: 0, .ignored: 0, .predicate: 0 } |
589 | }; |
590 | |
591 | static const format_flag_spec asm_fprintf_flag_specs[] = |
592 | { |
593 | { .flag_char: ' ', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("' ' flag" ), N_("the ' ' printf flag" ), .std: STD_C89 }, |
594 | { .flag_char: '+', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'+' flag" ), N_("the '+' printf flag" ), .std: STD_C89 }, |
595 | { .flag_char: '#', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'#' flag" ), N_("the '#' printf flag" ), .std: STD_C89 }, |
596 | { .flag_char: '0', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'0' flag" ), N_("the '0' printf flag" ), .std: STD_C89 }, |
597 | { .flag_char: '-', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'-' flag" ), N_("the '-' printf flag" ), .std: STD_C89 }, |
598 | { .flag_char: 'w', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("field width" ), N_("field width in printf format" ), .std: STD_C89 }, |
599 | { .flag_char: 'p', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("precision" ), N_("precision in printf format" ), .std: STD_C89 }, |
600 | { .flag_char: 'L', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("length modifier" ), N_("length modifier in printf format" ), .std: STD_C89 }, |
601 | { .flag_char: 0, .predicate: 0, .skip_next_char: 0, .quoting: 0, NULL, NULL, .std: STD_C89 } |
602 | }; |
603 | |
604 | static const format_flag_pair asm_fprintf_flag_pairs[] = |
605 | { |
606 | { .flag_char1: ' ', .flag_char2: '+', .ignored: 1, .predicate: 0 }, |
607 | { .flag_char1: '0', .flag_char2: '-', .ignored: 1, .predicate: 0 }, |
608 | { .flag_char1: '0', .flag_char2: 'p', .ignored: 1, .predicate: 'i' }, |
609 | { .flag_char1: 0, .flag_char2: 0, .ignored: 0, .predicate: 0 } |
610 | }; |
611 | |
612 | static const format_flag_pair gcc_diag_flag_pairs[] = |
613 | { |
614 | { .flag_char1: 0, .flag_char2: 0, .ignored: 0, .predicate: 0 } |
615 | }; |
616 | |
617 | #define gcc_tdiag_flag_pairs gcc_diag_flag_pairs |
618 | #define gcc_cdiag_flag_pairs gcc_diag_flag_pairs |
619 | #define gcc_cxxdiag_flag_pairs gcc_diag_flag_pairs |
620 | #define gcc_gfc_flag_pairs gcc_diag_flag_pairs |
621 | #define gcc_dump_printf_flag_pairs gcc_diag_flag_pairs |
622 | |
623 | static const format_flag_spec gcc_diag_flag_specs[] = |
624 | { |
625 | { .flag_char: '+', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'+' flag" ), N_("the '+' printf flag" ), .std: STD_C89 }, |
626 | { .flag_char: '#', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'#' flag" ), N_("the '#' printf flag" ), .std: STD_C89 }, |
627 | { .flag_char: 'q', .predicate: 0, .skip_next_char: 0, .quoting: 1, N_("'q' flag" ), N_("the 'q' diagnostic flag" ), .std: STD_C89 }, |
628 | { .flag_char: 'p', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("precision" ), N_("precision in printf format" ), .std: STD_C89 }, |
629 | { .flag_char: 'L', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("length modifier" ), N_("length modifier in printf format" ), .std: STD_C89 }, |
630 | { .flag_char: 0, .predicate: 0, .skip_next_char: 0, .quoting: 0, NULL, NULL, .std: STD_C89 } |
631 | }; |
632 | |
633 | #define gcc_tdiag_flag_specs gcc_diag_flag_specs |
634 | #define gcc_cdiag_flag_specs gcc_diag_flag_specs |
635 | #define gcc_cxxdiag_flag_specs gcc_diag_flag_specs |
636 | #define gcc_gfc_flag_specs gcc_diag_flag_specs |
637 | #define gcc_dump_printf_flag_specs gcc_diag_flag_specs |
638 | |
639 | static const format_flag_spec scanf_flag_specs[] = |
640 | { |
641 | { .flag_char: '*', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("assignment suppression" ), N_("the assignment suppression scanf feature" ), .std: STD_C89 }, |
642 | { .flag_char: 'a', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'a' flag" ), N_("the 'a' scanf flag" ), .std: STD_EXT }, |
643 | { .flag_char: 'm', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'m' flag" ), N_("the 'm' scanf flag" ), .std: STD_EXT }, |
644 | { .flag_char: 'w', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("field width" ), N_("field width in scanf format" ), .std: STD_C89 }, |
645 | { .flag_char: 'L', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("length modifier" ), N_("length modifier in scanf format" ), .std: STD_C89 }, |
646 | { .flag_char: '\'', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("''' flag" ), N_("the ''' scanf flag" ), .std: STD_EXT }, |
647 | { .flag_char: 'I', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'I' flag" ), N_("the 'I' scanf flag" ), .std: STD_EXT }, |
648 | { .flag_char: 0, .predicate: 0, .skip_next_char: 0, .quoting: 0, NULL, NULL, .std: STD_C89 } |
649 | }; |
650 | |
651 | |
652 | static const format_flag_pair scanf_flag_pairs[] = |
653 | { |
654 | { .flag_char1: '*', .flag_char2: 'L', .ignored: 0, .predicate: 0 }, |
655 | { .flag_char1: 'a', .flag_char2: 'm', .ignored: 0, .predicate: 0 }, |
656 | { .flag_char1: 0, .flag_char2: 0, .ignored: 0, .predicate: 0 } |
657 | }; |
658 | |
659 | |
660 | static const format_flag_spec strftime_flag_specs[] = |
661 | { |
662 | { .flag_char: '_', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'_' flag" ), N_("the '_' strftime flag" ), .std: STD_EXT }, |
663 | { .flag_char: '-', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'-' flag" ), N_("the '-' strftime flag" ), .std: STD_EXT }, |
664 | { .flag_char: '0', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'0' flag" ), N_("the '0' strftime flag" ), .std: STD_EXT }, |
665 | { .flag_char: '^', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'^' flag" ), N_("the '^' strftime flag" ), .std: STD_EXT }, |
666 | { .flag_char: '#', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'#' flag" ), N_("the '#' strftime flag" ), .std: STD_EXT }, |
667 | { .flag_char: 'w', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("field width" ), N_("field width in strftime format" ), .std: STD_EXT }, |
668 | { .flag_char: 'E', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'E' modifier" ), N_("the 'E' strftime modifier" ), .std: STD_C99 }, |
669 | { .flag_char: 'O', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'O' modifier" ), N_("the 'O' strftime modifier" ), .std: STD_C99 }, |
670 | { .flag_char: 'O', .predicate: 'o', .skip_next_char: 0, .quoting: 0, NULL, N_("the 'O' modifier" ), .std: STD_EXT }, |
671 | { .flag_char: 'O', .predicate: 'p', .skip_next_char: 0, .quoting: 0, NULL, N_("the 'O' modifier" ), .std: STD_C23 }, |
672 | { .flag_char: 0, .predicate: 0, .skip_next_char: 0, .quoting: 0, NULL, NULL, .std: STD_C89 } |
673 | }; |
674 | |
675 | |
676 | static const format_flag_pair strftime_flag_pairs[] = |
677 | { |
678 | { .flag_char1: 'E', .flag_char2: 'O', .ignored: 0, .predicate: 0 }, |
679 | { .flag_char1: '_', .flag_char2: '-', .ignored: 0, .predicate: 0 }, |
680 | { .flag_char1: '_', .flag_char2: '0', .ignored: 0, .predicate: 0 }, |
681 | { .flag_char1: '-', .flag_char2: '0', .ignored: 0, .predicate: 0 }, |
682 | { .flag_char1: '^', .flag_char2: '#', .ignored: 0, .predicate: 0 }, |
683 | { .flag_char1: 0, .flag_char2: 0, .ignored: 0, .predicate: 0 } |
684 | }; |
685 | |
686 | |
687 | static const format_flag_spec strfmon_flag_specs[] = |
688 | { |
689 | { .flag_char: '=', .predicate: 0, .skip_next_char: 1, .quoting: 0, N_("fill character" ), N_("fill character in strfmon format" ), .std: STD_C89 }, |
690 | { .flag_char: '^', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'^' flag" ), N_("the '^' strfmon flag" ), .std: STD_C89 }, |
691 | { .flag_char: '+', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'+' flag" ), N_("the '+' strfmon flag" ), .std: STD_C89 }, |
692 | { .flag_char: '(', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'(' flag" ), N_("the '(' strfmon flag" ), .std: STD_C89 }, |
693 | { .flag_char: '!', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'!' flag" ), N_("the '!' strfmon flag" ), .std: STD_C89 }, |
694 | { .flag_char: '-', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("'-' flag" ), N_("the '-' strfmon flag" ), .std: STD_C89 }, |
695 | { .flag_char: 'w', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("field width" ), N_("field width in strfmon format" ), .std: STD_C89 }, |
696 | { .flag_char: '#', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("left precision" ), N_("left precision in strfmon format" ), .std: STD_C89 }, |
697 | { .flag_char: 'p', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("right precision" ), N_("right precision in strfmon format" ), .std: STD_C89 }, |
698 | { .flag_char: 'L', .predicate: 0, .skip_next_char: 0, .quoting: 0, N_("length modifier" ), N_("length modifier in strfmon format" ), .std: STD_C89 }, |
699 | { .flag_char: 0, .predicate: 0, .skip_next_char: 0, .quoting: 0, NULL, NULL, .std: STD_C89 } |
700 | }; |
701 | |
702 | static const format_flag_pair strfmon_flag_pairs[] = |
703 | { |
704 | { .flag_char1: '+', .flag_char2: '(', .ignored: 0, .predicate: 0 }, |
705 | { .flag_char1: 0, .flag_char2: 0, .ignored: 0, .predicate: 0 } |
706 | }; |
707 | |
708 | |
709 | static const format_char_info print_char_table[] = |
710 | { |
711 | /* C89 conversion specifiers. */ |
712 | { .format_chars: "di" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_LL, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN, T23_I8, T23_I16, T23_I32, T23_I64, T23_IF8, T23_IF16, T23_IF32, T23_IF64 }, .flag_chars: "-wp0 +'I" , .flags2: "i" , NULL }, |
713 | { .format_chars: "oxX" , .pointer_count: 0, .std: STD_C89, .types: { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN, T23_U8, T23_U16, T23_U32, T23_U64, T23_UF8, T23_UF16, T23_UF32, T23_UF64 }, .flag_chars: "-wp0#" , .flags2: "i" , NULL }, |
714 | { .format_chars: "u" , .pointer_count: 0, .std: STD_C89, .types: { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN, T23_U8, T23_U16, T23_U32, T23_U64, T23_UF8, T23_UF16, T23_UF32, T23_UF64 }, .flag_chars: "-wp0'I" , .flags2: "i" , NULL }, |
715 | { .format_chars: "fgG" , .pointer_count: 0, .std: STD_C89, .types: { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0 +#'I" , .flags2: "" , NULL }, |
716 | { .format_chars: "eE" , .pointer_count: 0, .std: STD_C89, .types: { T89_D, BADLEN, BADLEN, T99_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0 +#I" , .flags2: "" , NULL }, |
717 | { .format_chars: "c" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, T94_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-w" , .flags2: "" , NULL }, |
718 | { .format_chars: "s" , .pointer_count: 1, .std: STD_C89, .types: { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp" , .flags2: "cR" , NULL }, |
719 | { .format_chars: "p" , .pointer_count: 1, .std: STD_C89, .types: { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-w" , .flags2: "c" , NULL }, |
720 | { .format_chars: "n" , .pointer_count: 1, .std: STD_C89, .types: { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN, T23_I8, T23_I16, T23_I32, T23_I64, T23_IF8, T23_IF16, T23_IF32, T23_IF64 }, .flag_chars: "" , .flags2: "W" , NULL }, |
721 | /* C99 conversion specifiers. */ |
722 | { .format_chars: "F" , .pointer_count: 0, .std: STD_C99, .types: { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0 +#'I" , .flags2: "" , NULL }, |
723 | { .format_chars: "aA" , .pointer_count: 0, .std: STD_C99, .types: { T99_D, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0 +#" , .flags2: "" , NULL }, |
724 | /* C23 conversion specifiers. */ |
725 | { .format_chars: "bB" , .pointer_count: 0, .std: STD_C23, .types: { T23_UI, T23_UC, T23_US, T23_UL, T23_ULL, TEX_ULL, T23_ST, T23_UPD, T23_UIM, BADLEN, BADLEN, BADLEN, T23_U8, T23_U16, T23_U32, T23_U64, T23_UF8, T23_UF16, T23_UF32, T23_UF64 }, .flag_chars: "-wp0#" , .flags2: "i" , NULL }, |
726 | /* X/Open conversion specifiers. */ |
727 | { .format_chars: "C" , .pointer_count: 0, .std: STD_EXT, .types: { TEX_WI, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-w" , .flags2: "" , NULL }, |
728 | { .format_chars: "S" , .pointer_count: 1, .std: STD_EXT, .types: { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp" , .flags2: "R" , NULL }, |
729 | /* GNU conversion specifiers. */ |
730 | { .format_chars: "m" , .pointer_count: 0, .std: STD_EXT, .types: { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp" , .flags2: "" , NULL }, |
731 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
732 | }; |
733 | |
734 | static const format_char_info asm_fprintf_char_table[] = |
735 | { |
736 | /* C89 conversion specifiers. */ |
737 | { .format_chars: "di" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0 +" , .flags2: "i" , NULL }, |
738 | { .format_chars: "oxX" , .pointer_count: 0, .std: STD_C89, .types: { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0#" , .flags2: "i" , NULL }, |
739 | { .format_chars: "u" , .pointer_count: 0, .std: STD_C89, .types: { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp0" , .flags2: "i" , NULL }, |
740 | { .format_chars: "c" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-w" , .flags2: "" , NULL }, |
741 | { .format_chars: "s" , .pointer_count: 1, .std: STD_C89, .types: { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "-wp" , .flags2: "cR" , NULL }, |
742 | |
743 | /* asm_fprintf conversion specifiers. */ |
744 | { .format_chars: "O" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
745 | { .format_chars: "R" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
746 | { .format_chars: "I" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
747 | { .format_chars: "L" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
748 | { .format_chars: "U" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
749 | { .format_chars: "r" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "" , .flags2: "" , NULL }, |
750 | { .format_chars: "z" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
751 | { .format_chars: "@" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
752 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
753 | }; |
754 | |
755 | /* GCC-specific format_char_info arrays. */ |
756 | |
757 | /* The conversion specifiers implemented within pp_format, and thus supported |
758 | by all pretty_printer instances within GCC. */ |
759 | |
760 | #define PP_FORMAT_CHAR_TABLE \ |
761 | { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, \ |
762 | { "ox", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, \ |
763 | { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, \ |
764 | { "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "", NULL }, \ |
765 | { "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "pq", "cR", NULL }, \ |
766 | { "p", 1, STD_C89, { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "q", "c", NULL }, \ |
767 | { "r", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "//cR", NULL }, \ |
768 | { "@", 1, STD_C89, { T_EVENT_PTR, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "\"", NULL }, \ |
769 | { "<", 0, STD_C89, NOARGUMENTS, "", "<", NULL }, \ |
770 | { ">", 0, STD_C89, NOARGUMENTS, "", ">", NULL }, \ |
771 | { "'" , 0, STD_C89, NOARGUMENTS, "", "", NULL }, \ |
772 | { "{", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL }, \ |
773 | { "}", 0, STD_C89, NOARGUMENTS, "", "", NULL }, \ |
774 | { "R", 0, STD_C89, NOARGUMENTS, "", "\\", NULL }, \ |
775 | { "m", 0, STD_C89, NOARGUMENTS, "q", "", NULL }, \ |
776 | { "Z", 1, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", &gcc_diag_char_table[0] } |
777 | |
778 | static const format_char_info gcc_diag_char_table[] = |
779 | { |
780 | /* The conversion specifiers implemented within pp_format. */ |
781 | PP_FORMAT_CHAR_TABLE, |
782 | |
783 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
784 | }; |
785 | |
786 | static const format_char_info gcc_tdiag_char_table[] = |
787 | { |
788 | /* The conversion specifiers implemented within pp_format. */ |
789 | PP_FORMAT_CHAR_TABLE, |
790 | |
791 | /* Custom conversion specifiers implemented by default_tree_printer. */ |
792 | |
793 | /* These will require a "tree" at runtime. */ |
794 | { .format_chars: "DFTV" , .pointer_count: 1, .std: STD_C89, .types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q+" , .flags2: "'" , NULL }, |
795 | { .format_chars: "E" , .pointer_count: 1, .std: STD_C89, .types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q+" , .flags2: "" , NULL }, |
796 | |
797 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
798 | }; |
799 | |
800 | static const format_char_info gcc_cdiag_char_table[] = |
801 | { |
802 | /* The conversion specifiers implemented within pp_format. */ |
803 | PP_FORMAT_CHAR_TABLE, |
804 | |
805 | /* Custom conversion specifiers implemented by c_tree_printer. */ |
806 | |
807 | /* These will require a "tree" at runtime. */ |
808 | { .format_chars: "DFTV" , .pointer_count: 1, .std: STD_C89, .types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q+" , .flags2: "'" , NULL }, |
809 | { .format_chars: "E" , .pointer_count: 1, .std: STD_C89, .types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q+" , .flags2: "" , NULL }, |
810 | |
811 | { .format_chars: "v" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q#" , .flags2: "" , NULL }, |
812 | |
813 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
814 | }; |
815 | |
816 | static const format_char_info gcc_cxxdiag_char_table[] = |
817 | { |
818 | /* The conversion specifiers implemented within pp_format. */ |
819 | PP_FORMAT_CHAR_TABLE, |
820 | |
821 | /* Custom conversion specifiers implemented by cp_printer. */ |
822 | |
823 | /* These will require a "tree" at runtime. */ |
824 | { .format_chars: "ADFHISTVX" ,.pointer_count: 1,.std: STD_C89,.types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q+#" , .flags2: "'" , NULL }, |
825 | { .format_chars: "E" , .pointer_count: 1,.std: STD_C89,.types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q+#" , .flags2: "" , NULL }, |
826 | |
827 | /* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.) */ |
828 | { .format_chars: "CLOPQ" ,.pointer_count: 0,.std: STD_C89, .types: { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q" , .flags2: "" , NULL }, |
829 | |
830 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
831 | }; |
832 | |
833 | static const format_char_info gcc_gfc_char_table[] = |
834 | { |
835 | /* C89 conversion specifiers. */ |
836 | { .format_chars: "di" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, T89_L, T9L_LL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q" , .flags2: "" , NULL }, |
837 | { .format_chars: "u" , .pointer_count: 0, .std: STD_C89, .types: { T89_UI, BADLEN, BADLEN, T89_UL, T9L_ULL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q" , .flags2: "" , NULL }, |
838 | { .format_chars: "c" , .pointer_count: 0, .std: STD_C89, .types: { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q" , .flags2: "" , NULL }, |
839 | { .format_chars: "s" , .pointer_count: 1, .std: STD_C89, .types: { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "q" , .flags2: "cR" , NULL }, |
840 | |
841 | /* gfc conversion specifiers. */ |
842 | |
843 | { .format_chars: "C" , .pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
844 | |
845 | /* This will require a "locus" at runtime. */ |
846 | { .format_chars: "L" , .pointer_count: 0, .std: STD_C89, .types: { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "" , .flags2: "R" , NULL }, |
847 | |
848 | /* These will require nothing. */ |
849 | { .format_chars: "<>" ,.pointer_count: 0, .std: STD_C89, NOARGUMENTS, .flag_chars: "" , .flags2: "" , NULL }, |
850 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
851 | }; |
852 | |
853 | static const format_char_info gcc_dump_printf_char_table[] = |
854 | { |
855 | /* The conversion specifiers implemented within pp_format. */ |
856 | PP_FORMAT_CHAR_TABLE, |
857 | |
858 | /* Custom conversion specifiers implemented by dump_pretty_printer. */ |
859 | |
860 | /* E and G require a "gimple *" argument at runtime. */ |
861 | { .format_chars: "EG" , .pointer_count: 1, .std: STD_C89, .types: { T89_G, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "" , .flags2: "\"" , NULL }, |
862 | |
863 | /* C requires a "cgraph_node *" argument at runtime. */ |
864 | { .format_chars: "C" , .pointer_count: 1, .std: STD_C89, .types: { T_CGRAPH_NODE, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "" , .flags2: "\"" , NULL }, |
865 | |
866 | /* T requires a "tree" at runtime. */ |
867 | { .format_chars: "T" , .pointer_count: 1, .std: STD_C89, .types: { T89_T, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "" , .flags2: "\"" , NULL }, |
868 | |
869 | /* %f requires a "double"; it doesn't support modifiers. */ |
870 | { .format_chars: "f" , .pointer_count: 0, .std: STD_C89, .types: { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "" , .flags2: "\"" , NULL }, |
871 | |
872 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
873 | }; |
874 | |
875 | static const format_char_info scan_char_table[] = |
876 | { |
877 | /* C89 conversion specifiers. */ |
878 | { .format_chars: "di" , .pointer_count: 1, .std: STD_C89, .types: { T89_I, T99_SC, T89_S, T89_L, T9L_LL, TEX_LL, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN, T23_I8, T23_I16, T23_I32, T23_I64, T23_IF8, T23_IF16, T23_IF32, T23_IF64 }, .flag_chars: "*w'I" , .flags2: "W" , NULL }, |
879 | { .format_chars: "u" , .pointer_count: 1, .std: STD_C89, .types: { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN, T23_U8, T23_U16, T23_U32, T23_U64, T23_UF8, T23_UF16, T23_UF32, T23_UF64 }, .flag_chars: "*w'I" , .flags2: "W" , NULL }, |
880 | { .format_chars: "oxX" , .pointer_count: 1, .std: STD_C89, .types: { T89_UI, T99_UC, T89_US, T89_UL, T9L_ULL, TEX_ULL, T99_ST, T99_UPD, T99_UIM, BADLEN, BADLEN, BADLEN, T23_U8, T23_U16, T23_U32, T23_U64, T23_UF8, T23_UF16, T23_UF32, T23_UF64 }, .flag_chars: "*w" , .flags2: "W" , NULL }, |
881 | { .format_chars: "efgEG" , .pointer_count: 1, .std: STD_C89, .types: { T89_F, BADLEN, BADLEN, T89_D, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*w'" , .flags2: "W" , NULL }, |
882 | { .format_chars: "c" , .pointer_count: 1, .std: STD_C89, .types: { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*mw" , .flags2: "cW" , NULL }, |
883 | { .format_chars: "s" , .pointer_count: 1, .std: STD_C89, .types: { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*amw" , .flags2: "cW" , NULL }, |
884 | { .format_chars: "[" , .pointer_count: 1, .std: STD_C89, .types: { T89_C, BADLEN, BADLEN, T94_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*amw" , .flags2: "cW[" , NULL }, |
885 | { .format_chars: "p" , .pointer_count: 2, .std: STD_C89, .types: { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*w" , .flags2: "W" , NULL }, |
886 | { .format_chars: "n" , .pointer_count: 1, .std: STD_C89, .types: { T89_I, T99_SC, T89_S, T89_L, T9L_LL, BADLEN, T99_SST, T99_PD, T99_IM, BADLEN, BADLEN, BADLEN, T23_I8, T23_I16, T23_I32, T23_I64, T23_IF8, T23_IF16, T23_IF32, T23_IF64 }, .flag_chars: "" , .flags2: "W" , NULL }, |
887 | /* C99 conversion specifiers. */ |
888 | { .format_chars: "F" , .pointer_count: 1, .std: STD_C99, .types: { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*w'" , .flags2: "W" , NULL }, |
889 | { .format_chars: "aA" , .pointer_count: 1, .std: STD_C99, .types: { T99_F, BADLEN, BADLEN, T99_D, BADLEN, T99_LD, BADLEN, BADLEN, BADLEN, T23_D32, T23_D64, T23_D128, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*w'" , .flags2: "W" , NULL }, |
890 | /* C23 conversion specifiers. */ |
891 | { .format_chars: "b" , .pointer_count: 1, .std: STD_C23, .types: { T23_UI, T23_UC, T23_US, T23_UL, T23_ULL, TEX_ULL, T23_ST, T23_UPD, T23_UIM, BADLEN, BADLEN, BADLEN, T23_U8, T23_U16, T23_U32, T23_U64, T23_UF8, T23_UF16, T23_UF32, T23_UF64 }, .flag_chars: "*w" , .flags2: "W" , NULL }, |
892 | /* X/Open conversion specifiers. */ |
893 | { .format_chars: "C" , .pointer_count: 1, .std: STD_EXT, .types: { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*mw" , .flags2: "W" , NULL }, |
894 | { .format_chars: "S" , .pointer_count: 1, .std: STD_EXT, .types: { TEX_W, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "*amw" , .flags2: "W" , NULL }, |
895 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
896 | }; |
897 | |
898 | static const format_char_info time_char_table[] = |
899 | { |
900 | /* C89 conversion specifiers. */ |
901 | { .format_chars: "AZa" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "^#" , .flags2: "" , NULL }, |
902 | { .format_chars: "Bb" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "O^#" , .flags2: "p" , NULL }, |
903 | { .format_chars: "cx" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "E" , .flags2: "3" , NULL }, |
904 | { .format_chars: "HIMSUWdmw" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "-_0Ow" , .flags2: "" , NULL }, |
905 | { .format_chars: "j" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "-_0Ow" , .flags2: "o" , NULL }, |
906 | { .format_chars: "p" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "#" , .flags2: "" , NULL }, |
907 | { .format_chars: "X" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "E" , .flags2: "" , NULL }, |
908 | { .format_chars: "y" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "EO-_0w" , .flags2: "4" , NULL }, |
909 | { .format_chars: "Y" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "-_0EOw" , .flags2: "o" , NULL }, |
910 | { .format_chars: "%" , .pointer_count: 0, .std: STD_C89, NOLENGTHS, .flag_chars: "" , .flags2: "" , NULL }, |
911 | /* C99 conversion specifiers. */ |
912 | { .format_chars: "C" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "-_0EOw" , .flags2: "o" , NULL }, |
913 | { .format_chars: "D" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "" , .flags2: "2" , NULL }, |
914 | { .format_chars: "eVu" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "-_0Ow" , .flags2: "" , NULL }, |
915 | { .format_chars: "FRTnrt" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "" , .flags2: "" , NULL }, |
916 | { .format_chars: "g" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "O-_0w" , .flags2: "2o" , NULL }, |
917 | { .format_chars: "G" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "-_0Ow" , .flags2: "o" , NULL }, |
918 | { .format_chars: "h" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "^#" , .flags2: "" , NULL }, |
919 | { .format_chars: "z" , .pointer_count: 0, .std: STD_C99, NOLENGTHS, .flag_chars: "O" , .flags2: "o" , NULL }, |
920 | /* GNU conversion specifiers. */ |
921 | { .format_chars: "kls" , .pointer_count: 0, .std: STD_EXT, NOLENGTHS, .flag_chars: "-_0Ow" , .flags2: "" , NULL }, |
922 | { .format_chars: "P" , .pointer_count: 0, .std: STD_EXT, NOLENGTHS, .flag_chars: "" , .flags2: "" , NULL }, |
923 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
924 | }; |
925 | |
926 | static const format_char_info monetary_char_table[] = |
927 | { |
928 | { .format_chars: "in" , .pointer_count: 0, .std: STD_C89, .types: { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, .flag_chars: "=^+(!-w#p" , .flags2: "" , NULL }, |
929 | { NULL, .pointer_count: 0, .std: STD_C89, NOLENGTHS, NULL, NULL, NULL } |
930 | }; |
931 | |
932 | /* This must be in the same order as enum format_type. */ |
933 | static const format_kind_info format_types_orig[] = |
934 | { |
935 | { .name: "gnu_printf" , .length_char_specs: printf_length_specs, .conversion_specs: print_char_table, .flag_chars: " +#0-'I" , NULL, |
936 | .flag_specs: printf_flag_specs, .bad_flag_pairs: printf_flag_pairs, |
937 | .flags: FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK, |
938 | .width_char: 'w', .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
939 | .width_type: &integer_type_node, .precision_type: &integer_type_node |
940 | }, |
941 | { .name: "asm_fprintf" , .length_char_specs: asm_fprintf_length_specs, .conversion_specs: asm_fprintf_char_table, .flag_chars: " +#0-" , NULL, |
942 | .flag_specs: asm_fprintf_flag_specs, .bad_flag_pairs: asm_fprintf_flag_pairs, |
943 | .flags: FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK, |
944 | .width_char: 'w', .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
945 | NULL, NULL |
946 | }, |
947 | { .name: "gcc_diag" , .length_char_specs: gcc_diag_length_specs, .conversion_specs: gcc_diag_char_table, .flag_chars: "q+#" , NULL, |
948 | .flag_specs: gcc_diag_flag_specs, .bad_flag_pairs: gcc_diag_flag_pairs, |
949 | .flags: FMT_FLAG_ARG_CONVERT, |
950 | .width_char: 0, .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
951 | NULL, .precision_type: &integer_type_node |
952 | }, |
953 | { .name: "gcc_tdiag" , gcc_tdiag_length_specs, .conversion_specs: gcc_tdiag_char_table, .flag_chars: "q+#" , NULL, |
954 | gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs, |
955 | .flags: FMT_FLAG_ARG_CONVERT, |
956 | .width_char: 0, .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
957 | NULL, .precision_type: &integer_type_node |
958 | }, |
959 | { .name: "gcc_cdiag" , gcc_cdiag_length_specs, .conversion_specs: gcc_cdiag_char_table, .flag_chars: "q+#" , NULL, |
960 | gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs, |
961 | .flags: FMT_FLAG_ARG_CONVERT, |
962 | .width_char: 0, .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
963 | NULL, .precision_type: &integer_type_node |
964 | }, |
965 | { .name: "gcc_cxxdiag" , gcc_cxxdiag_length_specs, .conversion_specs: gcc_cxxdiag_char_table, .flag_chars: "q+#" , NULL, |
966 | gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs, |
967 | .flags: FMT_FLAG_ARG_CONVERT, |
968 | .width_char: 0, .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
969 | NULL, .precision_type: &integer_type_node |
970 | }, |
971 | { .name: "gcc_gfc" , .length_char_specs: gcc_gfc_length_specs, .conversion_specs: gcc_gfc_char_table, .flag_chars: "q+#" , NULL, |
972 | gcc_gfc_flag_specs, gcc_gfc_flag_pairs, |
973 | .flags: FMT_FLAG_ARG_CONVERT, |
974 | .width_char: 0, .left_precision_char: 0, .precision_char: 0, .suppression_char: 0, .length_code_char: 0, .alloc_char: 0, |
975 | NULL, NULL |
976 | }, |
977 | { .name: "gcc_dump_printf" , gcc_dump_printf_length_specs, |
978 | .conversion_specs: gcc_dump_printf_char_table, .flag_chars: "q+#" , NULL, |
979 | gcc_dump_printf_flag_specs, gcc_dump_printf_flag_pairs, |
980 | .flags: FMT_FLAG_ARG_CONVERT, |
981 | .width_char: 0, .left_precision_char: 0, .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
982 | NULL, .precision_type: &integer_type_node |
983 | }, |
984 | { .name: "NSString" , NULL, NULL, NULL, NULL, |
985 | NULL, NULL, |
986 | .flags: FMT_FLAG_ARG_CONVERT|FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL, .width_char: 0, .left_precision_char: 0, .precision_char: 0, .suppression_char: 0, .length_code_char: 0, .alloc_char: 0, |
987 | NULL, NULL |
988 | }, |
989 | { .name: "gnu_scanf" , .length_char_specs: scanf_length_specs, .conversion_specs: scan_char_table, .flag_chars: "*'I" , NULL, |
990 | .flag_specs: scanf_flag_specs, .bad_flag_pairs: scanf_flag_pairs, |
991 | .flags: FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK, |
992 | .width_char: 'w', .left_precision_char: 0, .precision_char: 0, .suppression_char: '*', .length_code_char: 'L', .alloc_char: 'm', |
993 | NULL, NULL |
994 | }, |
995 | { .name: "gnu_strftime" , NULL, .conversion_specs: time_char_table, .flag_chars: "_-0^#" , .modifier_chars: "EO" , |
996 | .flag_specs: strftime_flag_specs, .bad_flag_pairs: strftime_flag_pairs, |
997 | .flags: FMT_FLAG_FANCY_PERCENT_OK, .width_char: 'w', .left_precision_char: 0, .precision_char: 0, .suppression_char: 0, .length_code_char: 0, .alloc_char: 0, |
998 | NULL, NULL |
999 | }, |
1000 | { .name: "gnu_strfmon" , .length_char_specs: strfmon_length_specs, .conversion_specs: monetary_char_table, .flag_chars: "=^+(!-" , NULL, |
1001 | .flag_specs: strfmon_flag_specs, .bad_flag_pairs: strfmon_flag_pairs, |
1002 | .flags: FMT_FLAG_ARG_CONVERT, .width_char: 'w', .left_precision_char: '#', .precision_char: 'p', .suppression_char: 0, .length_code_char: 'L', .alloc_char: 0, |
1003 | NULL, NULL |
1004 | } |
1005 | }; |
1006 | |
1007 | /* This layer of indirection allows GCC to reassign format_types with |
1008 | new data if necessary, while still allowing the original data to be |
1009 | const. */ |
1010 | static const format_kind_info *format_types = format_types_orig; |
1011 | /* We can modify this one. We also add target-specific format types |
1012 | to the end of the array. */ |
1013 | static format_kind_info *dynamic_format_types; |
1014 | |
1015 | static int n_format_types = ARRAY_SIZE (format_types_orig); |
1016 | |
1017 | /* Structure detailing the results of checking a format function call |
1018 | where the format expression may be a conditional expression with |
1019 | many leaves resulting from nested conditional expressions. */ |
1020 | struct format_check_results |
1021 | { |
1022 | /* Number of leaves of the format argument that could not be checked |
1023 | as they were not string literals. */ |
1024 | int number_non_literal; |
1025 | /* Number of leaves of the format argument that were null pointers or |
1026 | string literals, but had extra format arguments. */ |
1027 | int ; |
1028 | location_t ; |
1029 | /* Number of leaves of the format argument that were null pointers or |
1030 | string literals, but had extra format arguments and used $ operand |
1031 | numbers. */ |
1032 | int ; |
1033 | /* Number of leaves of the format argument that were wide string |
1034 | literals. */ |
1035 | int number_wide; |
1036 | /* Number of leaves of the format argument that are not array of "char". */ |
1037 | int number_non_char; |
1038 | /* Number of leaves of the format argument that were empty strings. */ |
1039 | int number_empty; |
1040 | /* Number of leaves of the format argument that were unterminated |
1041 | strings. */ |
1042 | int number_unterminated; |
1043 | /* Number of leaves of the format argument that were not counted above. */ |
1044 | int number_other; |
1045 | /* Location of the format string. */ |
1046 | location_t format_string_loc; |
1047 | }; |
1048 | |
1049 | struct format_check_context |
1050 | { |
1051 | format_check_results *res; |
1052 | function_format_info *info; |
1053 | tree params; |
1054 | vec<location_t> *arglocs; |
1055 | }; |
1056 | |
1057 | /* Return the format name (as specified in the original table) for the format |
1058 | type indicated by format_num. */ |
1059 | static const char * |
1060 | format_name (int format_num) |
1061 | { |
1062 | if (format_num >= 0 && format_num < n_format_types) |
1063 | return format_types[format_num].name; |
1064 | gcc_unreachable (); |
1065 | } |
1066 | |
1067 | /* Return the format flags (as specified in the original table) for the format |
1068 | type indicated by format_num. */ |
1069 | static int |
1070 | format_flags (int format_num) |
1071 | { |
1072 | if (format_num >= 0 && format_num < n_format_types) |
1073 | return format_types[format_num].flags; |
1074 | gcc_unreachable (); |
1075 | } |
1076 | |
1077 | static void check_format_info (function_format_info *, tree, |
1078 | vec<location_t> *); |
1079 | static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); |
1080 | static void check_format_info_main (format_check_results *, |
1081 | function_format_info *, const char *, |
1082 | location_t, tree, |
1083 | int, tree, |
1084 | unsigned HOST_WIDE_INT, |
1085 | object_allocator<format_wanted_type> &, |
1086 | vec<location_t> *); |
1087 | |
1088 | static void init_dollar_format_checking (int, tree); |
1089 | static int maybe_read_dollar_number (const char **, int, |
1090 | tree, tree *, const format_kind_info *); |
1091 | static bool avoid_dollar_number (const char *); |
1092 | static void finish_dollar_format_checking (format_check_results *, int); |
1093 | |
1094 | static const format_flag_spec *get_flag_spec (const format_flag_spec *, |
1095 | int, const char *); |
1096 | |
1097 | static void check_format_types (const substring_loc &fmt_loc, |
1098 | format_wanted_type *, |
1099 | const format_kind_info *fki, |
1100 | int offset_to_type_start, |
1101 | char conversion_char, |
1102 | vec<location_t> *arglocs); |
1103 | static void format_type_warning (const substring_loc &fmt_loc, |
1104 | location_t param_loc, |
1105 | format_wanted_type *, tree, |
1106 | tree, |
1107 | const format_kind_info *fki, |
1108 | int offset_to_type_start, |
1109 | char conversion_char); |
1110 | |
1111 | /* Decode a format type from a string, returning the type, or |
1112 | format_type_error if not valid, in which case the caller should |
1113 | print an error message. On success, when IS_RAW is non-null, set |
1114 | *IS_RAW when the format type corresponds to a GCC "raw" diagnostic |
1115 | formatting function and clear it otherwise. */ |
1116 | static format_type |
1117 | decode_format_type (const char *s, bool *is_raw /* = NULL */) |
1118 | { |
1119 | bool is_raw_buf; |
1120 | |
1121 | if (!is_raw) |
1122 | is_raw = &is_raw_buf; |
1123 | |
1124 | *is_raw = false; |
1125 | |
1126 | s = convert_format_name_to_system_name (attr_name: s); |
1127 | |
1128 | size_t slen = strlen (s: s); |
1129 | for (int i = 0; i < n_format_types; i++) |
1130 | { |
1131 | /* Check for a match with no underscores. */ |
1132 | if (!strcmp (s1: s, s2: format_types[i].name)) |
1133 | return static_cast<format_type> (i); |
1134 | |
1135 | /* Check for leading and trailing underscores. */ |
1136 | size_t alen = strlen (s: format_types[i].name); |
1137 | if (slen == alen + 4 && s[0] == '_' && s[1] == '_' |
1138 | && s[slen - 1] == '_' && s[slen - 2] == '_' |
1139 | && !strncmp (s1: s + 2, s2: format_types[i].name, n: alen)) |
1140 | return static_cast<format_type>(i); |
1141 | |
1142 | /* Check for the "_raw" suffix and no leading underscores. */ |
1143 | if (slen == alen + 4 |
1144 | && !strncmp (s1: s, s2: format_types[i].name, n: alen) |
1145 | && !strcmp (s1: s + alen, s2: "_raw" )) |
1146 | { |
1147 | *is_raw = true; |
1148 | return static_cast<format_type>(i); |
1149 | } |
1150 | |
1151 | /* Check for the "_raw__" suffix and leading underscores. */ |
1152 | if (slen == alen + 8 && s[0] == '_' && s[1] == '_' |
1153 | && !strncmp (s1: s + 2, s2: format_types[i].name, n: alen) |
1154 | && !strcmp (s1: s + 2 + alen, s2: "_raw__" )) |
1155 | { |
1156 | *is_raw = true; |
1157 | return static_cast<format_type>(i); |
1158 | } |
1159 | } |
1160 | |
1161 | return format_type_error; |
1162 | } |
1163 | |
1164 | |
1165 | /* Check the argument list of a call to printf, scanf, etc. |
1166 | ATTRS are the attributes on the function type. There are NARGS argument |
1167 | values in the array ARGARRAY. FN is either a function declaration or |
1168 | function type. Also, if -Wsuggest-attribute=format, warn for calls to |
1169 | vprintf or vscanf in functions with no such format attribute themselves. */ |
1170 | |
1171 | void |
1172 | check_function_format (const_tree fn, tree attrs, int nargs, |
1173 | tree *argarray, vec<location_t> *arglocs) |
1174 | { |
1175 | tree a; |
1176 | |
1177 | tree atname = get_identifier ("format" ); |
1178 | bool skipped_default_format = false; |
1179 | |
1180 | /* See if this function has any format attributes. */ |
1181 | for (a = attrs; a; a = TREE_CHAIN (a)) |
1182 | { |
1183 | if (is_attribute_p (attr_name: "format" , ident: get_attribute_name (a))) |
1184 | { |
1185 | /* Yup; check it. */ |
1186 | function_format_info info; |
1187 | decode_format_attr (fn, atname, TREE_VALUE (a), info: &info, |
1188 | /*validated=*/validated_p: true); |
1189 | |
1190 | /* Mingw32 targets have traditionally used ms_printf format for the |
1191 | printf function, and this format is built in GCC. But nowadays, |
1192 | if mingw-w64 is configured to target UCRT, the printf function |
1193 | uses the gnu_printf format (specified in the stdio.h header). This |
1194 | causes GCC to check both formats, which means that GCC would |
1195 | warn twice about the same issue when both formats are violated, |
1196 | e.g. for %lu used to print long long unsigned. |
1197 | |
1198 | Hence, if there is a built-in attribute specifier and at least |
1199 | one another, we skip the built-in one. See PR 95130 (but note that |
1200 | GCC ms_printf already supports %llu) and PR 92292. */ |
1201 | |
1202 | if (!skipped_default_format |
1203 | && fn |
1204 | && TREE_CODE (fn) == FUNCTION_DECL |
1205 | && fndecl_built_in_p (node: fn, klass: BUILT_IN_NORMAL) |
1206 | && (tree_to_uhwi (TREE_PURPOSE (TREE_VALUE (a))) |
1207 | & (int) ATTR_FLAG_BUILT_IN)) |
1208 | { |
1209 | tree aa; |
1210 | for (aa = attrs; aa; aa = TREE_CHAIN (aa)) |
1211 | if (a != aa |
1212 | && is_attribute_p (attr_name: "format" , ident: get_attribute_name (aa))) |
1213 | { |
1214 | skipped_default_format = true; |
1215 | break; |
1216 | } |
1217 | if (skipped_default_format) |
1218 | continue; |
1219 | } |
1220 | |
1221 | if (warn_format) |
1222 | { |
1223 | /* FIXME: Rewrite all the internal functions in this file |
1224 | to use the ARGARRAY directly instead of constructing this |
1225 | temporary list. */ |
1226 | tree params = NULL_TREE; |
1227 | int i; |
1228 | for (i = nargs - 1; i >= 0; i--) |
1229 | params = tree_cons (NULL_TREE, argarray[i], params); |
1230 | check_format_info (&info, params, arglocs); |
1231 | } |
1232 | |
1233 | /* Attempt to detect whether the current function might benefit |
1234 | from the format attribute if the called function is decorated |
1235 | with it. Avoid using calls with string literal formats for |
1236 | guidance since those are unlikely to be viable candidates. */ |
1237 | if (warn_suggest_attribute_format |
1238 | && current_function_decl != NULL_TREE |
1239 | && info.first_arg_num == 0 |
1240 | && (format_types[info.format_type].flags |
1241 | & (int) FMT_FLAG_ARG_CONVERT) |
1242 | /* c_strlen will fail for a function parameter but succeed |
1243 | for a literal or constant array. */ |
1244 | && !c_strlen (argarray[info.format_num - 1], 1)) |
1245 | { |
1246 | tree c; |
1247 | for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); |
1248 | c; |
1249 | c = TREE_CHAIN (c)) |
1250 | if (is_attribute_p (attr_name: "format" , ident: get_attribute_name (c)) |
1251 | && (decode_format_type (IDENTIFIER_POINTER |
1252 | (TREE_VALUE (TREE_VALUE (c)))) |
1253 | == info.format_type)) |
1254 | break; |
1255 | if (c == NULL_TREE) |
1256 | { |
1257 | /* Check if the current function has a parameter to which |
1258 | the format attribute could be attached; if not, it |
1259 | can't be a candidate for a format attribute, despite |
1260 | the vprintf-like or vscanf-like call. */ |
1261 | tree args; |
1262 | for (args = DECL_ARGUMENTS (current_function_decl); |
1263 | args != 0; |
1264 | args = DECL_CHAIN (args)) |
1265 | { |
1266 | if (TREE_CODE (TREE_TYPE (args)) == POINTER_TYPE |
1267 | && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (args))) |
1268 | == char_type_node)) |
1269 | break; |
1270 | } |
1271 | if (args != 0) |
1272 | warning (OPT_Wsuggest_attribute_format, "function %qD " |
1273 | "might be a candidate for %qs format attribute" , |
1274 | current_function_decl, |
1275 | format_types[info.format_type].name); |
1276 | } |
1277 | } |
1278 | } |
1279 | } |
1280 | } |
1281 | |
1282 | |
1283 | /* Variables used by the checking of $ operand number formats. */ |
1284 | static char *dollar_arguments_used = NULL; |
1285 | static char *dollar_arguments_pointer_p = NULL; |
1286 | static int dollar_arguments_alloc = 0; |
1287 | static int dollar_arguments_count; |
1288 | static int dollar_first_arg_num; |
1289 | static int dollar_max_arg_used; |
1290 | static int dollar_format_warned; |
1291 | |
1292 | /* Initialize the checking for a format string that may contain $ |
1293 | parameter number specifications; we will need to keep track of whether |
1294 | each parameter has been used. FIRST_ARG_NUM is the number of the first |
1295 | argument that is a parameter to the format, or 0 for a vprintf-style |
1296 | function; PARAMS is the list of arguments starting at this argument. */ |
1297 | |
1298 | static void |
1299 | init_dollar_format_checking (int first_arg_num, tree params) |
1300 | { |
1301 | tree oparams = params; |
1302 | |
1303 | dollar_first_arg_num = first_arg_num; |
1304 | dollar_arguments_count = 0; |
1305 | dollar_max_arg_used = 0; |
1306 | dollar_format_warned = 0; |
1307 | if (first_arg_num > 0) |
1308 | { |
1309 | while (params) |
1310 | { |
1311 | dollar_arguments_count++; |
1312 | params = TREE_CHAIN (params); |
1313 | } |
1314 | } |
1315 | if (dollar_arguments_alloc < dollar_arguments_count) |
1316 | { |
1317 | free (ptr: dollar_arguments_used); |
1318 | free (ptr: dollar_arguments_pointer_p); |
1319 | dollar_arguments_alloc = dollar_arguments_count; |
1320 | dollar_arguments_used = XNEWVEC (char, dollar_arguments_alloc); |
1321 | dollar_arguments_pointer_p = XNEWVEC (char, dollar_arguments_alloc); |
1322 | } |
1323 | if (dollar_arguments_alloc) |
1324 | { |
1325 | memset (s: dollar_arguments_used, c: 0, n: dollar_arguments_alloc); |
1326 | if (first_arg_num > 0) |
1327 | { |
1328 | int i = 0; |
1329 | params = oparams; |
1330 | while (params) |
1331 | { |
1332 | dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params))) |
1333 | == POINTER_TYPE); |
1334 | params = TREE_CHAIN (params); |
1335 | i++; |
1336 | } |
1337 | } |
1338 | } |
1339 | } |
1340 | |
1341 | |
1342 | /* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED |
1343 | is set, it is an error if one is not found; otherwise, it is OK. If |
1344 | such a number is found, check whether it is within range and mark that |
1345 | numbered operand as being used for later checking. Returns the operand |
1346 | number if found and within range, zero if no such number was found and |
1347 | this is OK, or -1 on error. PARAMS points to the first operand of the |
1348 | format; PARAM_PTR is made to point to the parameter referred to. If |
1349 | a $ format is found, *FORMAT is updated to point just after it. */ |
1350 | |
1351 | static int |
1352 | maybe_read_dollar_number (const char **format, |
1353 | int dollar_needed, tree params, tree *param_ptr, |
1354 | const format_kind_info *fki) |
1355 | { |
1356 | int argnum; |
1357 | int overflow_flag; |
1358 | const char *fcp = *format; |
1359 | if (!ISDIGIT (*fcp)) |
1360 | { |
1361 | if (dollar_needed) |
1362 | { |
1363 | warning (OPT_Wformat_, "missing $ operand number in format" ); |
1364 | return -1; |
1365 | } |
1366 | else |
1367 | return 0; |
1368 | } |
1369 | argnum = 0; |
1370 | overflow_flag = 0; |
1371 | while (ISDIGIT (*fcp)) |
1372 | { |
1373 | HOST_WIDE_INT nargnum |
1374 | = HOST_WIDE_INT_UC (10) * argnum + (*fcp - '0'); |
1375 | if ((int) nargnum != nargnum) |
1376 | overflow_flag = 1; |
1377 | argnum = nargnum; |
1378 | fcp++; |
1379 | } |
1380 | if (*fcp != '$') |
1381 | { |
1382 | if (dollar_needed) |
1383 | { |
1384 | warning (OPT_Wformat_, "missing $ operand number in format" ); |
1385 | return -1; |
1386 | } |
1387 | else |
1388 | return 0; |
1389 | } |
1390 | *format = fcp + 1; |
1391 | if (pedantic && !dollar_format_warned) |
1392 | { |
1393 | warning (OPT_Wformat_, "%s does not support %%n$ operand number formats" , |
1394 | C_STD_NAME (STD_EXT)); |
1395 | dollar_format_warned = 1; |
1396 | } |
1397 | if (overflow_flag || argnum == 0 |
1398 | || (dollar_first_arg_num && argnum > dollar_arguments_count)) |
1399 | { |
1400 | warning (OPT_Wformat_, "operand number out of range in format" ); |
1401 | return -1; |
1402 | } |
1403 | if (argnum > dollar_max_arg_used) |
1404 | dollar_max_arg_used = argnum; |
1405 | /* For vprintf-style functions we may need to allocate more memory to |
1406 | track which arguments are used. */ |
1407 | while (dollar_arguments_alloc < dollar_max_arg_used) |
1408 | { |
1409 | int nalloc; |
1410 | nalloc = 2 * dollar_arguments_alloc + 16; |
1411 | dollar_arguments_used = XRESIZEVEC (char, dollar_arguments_used, |
1412 | nalloc); |
1413 | dollar_arguments_pointer_p = XRESIZEVEC (char, dollar_arguments_pointer_p, |
1414 | nalloc); |
1415 | memset (s: dollar_arguments_used + dollar_arguments_alloc, c: 0, |
1416 | n: nalloc - dollar_arguments_alloc); |
1417 | dollar_arguments_alloc = nalloc; |
1418 | } |
1419 | if (!(fki->flags & (int) FMT_FLAG_DOLLAR_MULTIPLE) |
1420 | && dollar_arguments_used[argnum - 1] == 1) |
1421 | { |
1422 | dollar_arguments_used[argnum - 1] = 2; |
1423 | warning (OPT_Wformat_, "format argument %d used more than once in %s format" , |
1424 | argnum, fki->name); |
1425 | } |
1426 | else |
1427 | dollar_arguments_used[argnum - 1] = 1; |
1428 | if (dollar_first_arg_num) |
1429 | { |
1430 | int i; |
1431 | *param_ptr = params; |
1432 | for (i = 1; i < argnum && *param_ptr != 0; i++) |
1433 | *param_ptr = TREE_CHAIN (*param_ptr); |
1434 | |
1435 | /* This case shouldn't be caught here. */ |
1436 | gcc_assert (*param_ptr); |
1437 | } |
1438 | else |
1439 | *param_ptr = 0; |
1440 | return argnum; |
1441 | } |
1442 | |
1443 | /* Ensure that FORMAT does not start with a decimal number followed by |
1444 | a $; give a diagnostic and return true if it does, false otherwise. */ |
1445 | |
1446 | static bool |
1447 | avoid_dollar_number (const char *format) |
1448 | { |
1449 | if (!ISDIGIT (*format)) |
1450 | return false; |
1451 | while (ISDIGIT (*format)) |
1452 | format++; |
1453 | if (*format == '$') |
1454 | { |
1455 | warning (OPT_Wformat_, |
1456 | "%<$%>operand number used after format without operand number" ); |
1457 | return true; |
1458 | } |
1459 | return false; |
1460 | } |
1461 | |
1462 | |
1463 | /* Finish the checking for a format string that used $ operand number formats |
1464 | instead of non-$ formats. We check for unused operands before used ones |
1465 | (a serious error, since the implementation of the format function |
1466 | can't know what types to pass to va_arg to find the later arguments). |
1467 | and for unused operands at the end of the format (if we know how many |
1468 | arguments the format had, so not for vprintf). If there were operand |
1469 | numbers out of range on a non-vprintf-style format, we won't have reached |
1470 | here. If POINTER_GAP_OK, unused arguments are OK if all arguments are |
1471 | pointers. */ |
1472 | |
1473 | static void |
1474 | finish_dollar_format_checking (format_check_results *res, int pointer_gap_ok) |
1475 | { |
1476 | int i; |
1477 | bool found_pointer_gap = false; |
1478 | for (i = 0; i < dollar_max_arg_used; i++) |
1479 | { |
1480 | if (!dollar_arguments_used[i]) |
1481 | { |
1482 | if (pointer_gap_ok && (dollar_first_arg_num == 0 |
1483 | || dollar_arguments_pointer_p[i])) |
1484 | found_pointer_gap = true; |
1485 | else |
1486 | warning_at (res->format_string_loc, OPT_Wformat_, |
1487 | "format argument %d unused before used argument %d " |
1488 | "in %<$%>-style format" , |
1489 | i + 1, dollar_max_arg_used); |
1490 | } |
1491 | } |
1492 | if (found_pointer_gap |
1493 | || (dollar_first_arg_num |
1494 | && dollar_max_arg_used < dollar_arguments_count)) |
1495 | { |
1496 | res->number_other--; |
1497 | res->number_dollar_extra_args++; |
1498 | } |
1499 | } |
1500 | |
1501 | |
1502 | /* Retrieve the specification for a format flag. SPEC contains the |
1503 | specifications for format flags for the applicable kind of format. |
1504 | FLAG is the flag in question. If PREDICATES is NULL, the basic |
1505 | spec for that flag must be retrieved and must exist. If |
1506 | PREDICATES is not NULL, it is a string listing possible predicates |
1507 | for the spec entry; if an entry predicated on any of these is |
1508 | found, it is returned, otherwise NULL is returned. */ |
1509 | |
1510 | static const format_flag_spec * |
1511 | get_flag_spec (const format_flag_spec *spec, int flag, const char *predicates) |
1512 | { |
1513 | int i; |
1514 | for (i = 0; spec[i].flag_char != 0; i++) |
1515 | { |
1516 | if (spec[i].flag_char != flag) |
1517 | continue; |
1518 | if (predicates != NULL) |
1519 | { |
1520 | if (spec[i].predicate != 0 |
1521 | && strchr (s: predicates, c: spec[i].predicate) != 0) |
1522 | return &spec[i]; |
1523 | } |
1524 | else if (spec[i].predicate == 0) |
1525 | return &spec[i]; |
1526 | } |
1527 | gcc_assert (predicates); |
1528 | return NULL; |
1529 | } |
1530 | |
1531 | |
1532 | /* Check the argument list of a call to printf, scanf, etc. |
1533 | INFO points to the function_format_info structure. |
1534 | PARAMS is the list of argument values. */ |
1535 | |
1536 | static void |
1537 | check_format_info (function_format_info *info, tree params, |
1538 | vec<location_t> *arglocs) |
1539 | { |
1540 | format_check_context format_ctx; |
1541 | unsigned HOST_WIDE_INT arg_num; |
1542 | tree format_tree; |
1543 | format_check_results res; |
1544 | /* Skip to format argument. If the argument isn't available, there's |
1545 | no work for us to do; prototype checking will catch the problem. */ |
1546 | for (arg_num = 1; ; ++arg_num) |
1547 | { |
1548 | if (params == 0) |
1549 | return; |
1550 | if (arg_num == info->format_num) |
1551 | break; |
1552 | params = TREE_CHAIN (params); |
1553 | } |
1554 | format_tree = TREE_VALUE (params); |
1555 | params = TREE_CHAIN (params); |
1556 | if (format_tree == 0) |
1557 | return; |
1558 | |
1559 | res.number_non_literal = 0; |
1560 | res.number_extra_args = 0; |
1561 | res.extra_arg_loc = UNKNOWN_LOCATION; |
1562 | res.number_dollar_extra_args = 0; |
1563 | res.number_wide = 0; |
1564 | res.number_non_char = 0; |
1565 | res.number_empty = 0; |
1566 | res.number_unterminated = 0; |
1567 | res.number_other = 0; |
1568 | res.format_string_loc = input_location; |
1569 | |
1570 | format_ctx.res = &res; |
1571 | format_ctx.info = info; |
1572 | format_ctx.params = params; |
1573 | format_ctx.arglocs = arglocs; |
1574 | |
1575 | check_function_arguments_recurse (check_format_arg, &format_ctx, |
1576 | format_tree, arg_num, OPT_Wformat_); |
1577 | |
1578 | location_t loc = format_ctx.res->format_string_loc; |
1579 | |
1580 | if (res.number_non_literal > 0) |
1581 | { |
1582 | /* Functions taking a va_list normally pass a non-literal format |
1583 | string. These functions typically are declared with |
1584 | first_arg_num == 0, so avoid warning in those cases. */ |
1585 | if (!(format_types[info->format_type].flags & (int) FMT_FLAG_ARG_CONVERT)) |
1586 | { |
1587 | /* For strftime-like formats, warn for not checking the format |
1588 | string; but there are no arguments to check. */ |
1589 | warning_at (loc, OPT_Wformat_nonliteral, |
1590 | "format not a string literal, format string not checked" ); |
1591 | } |
1592 | else if (info->first_arg_num != 0) |
1593 | { |
1594 | /* If there are no arguments for the format at all, we may have |
1595 | printf (foo) which is likely to be a security hole. */ |
1596 | while (arg_num + 1 < info->first_arg_num) |
1597 | { |
1598 | if (params == 0) |
1599 | break; |
1600 | params = TREE_CHAIN (params); |
1601 | ++arg_num; |
1602 | } |
1603 | if (params == 0 && warn_format_security) |
1604 | warning_at (loc, OPT_Wformat_security, |
1605 | "format not a string literal and no format arguments" ); |
1606 | else if (params == 0 && warn_format_nonliteral) |
1607 | warning_at (loc, OPT_Wformat_nonliteral, |
1608 | "format not a string literal and no format arguments" ); |
1609 | else |
1610 | warning_at (loc, OPT_Wformat_nonliteral, |
1611 | "format not a string literal, argument types not checked" ); |
1612 | } |
1613 | } |
1614 | |
1615 | /* If there were extra arguments to the format, normally warn. However, |
1616 | the standard does say extra arguments are ignored, so in the specific |
1617 | case where we have multiple leaves (conditional expressions or |
1618 | ngettext) allow extra arguments if at least one leaf didn't have extra |
1619 | arguments, but was otherwise OK (either non-literal or checked OK). |
1620 | If the format is an empty string, this should be counted similarly to the |
1621 | case of extra format arguments. */ |
1622 | if (res.number_extra_args > 0 && res.number_non_literal == 0 |
1623 | && res.number_other == 0) |
1624 | { |
1625 | if (res.extra_arg_loc == UNKNOWN_LOCATION) |
1626 | res.extra_arg_loc = loc; |
1627 | warning_at (res.extra_arg_loc, OPT_Wformat_extra_args, |
1628 | "too many arguments for format" ); |
1629 | } |
1630 | if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0 |
1631 | && res.number_other == 0) |
1632 | warning_at (loc, OPT_Wformat_extra_args, |
1633 | "unused arguments in %<$%>-style format" ); |
1634 | if (res.number_empty > 0 && res.number_non_literal == 0 |
1635 | && res.number_other == 0) |
1636 | warning_at (loc, OPT_Wformat_zero_length, "zero-length %s format string" , |
1637 | format_types[info->format_type].name); |
1638 | |
1639 | if (res.number_wide > 0) |
1640 | warning_at (loc, OPT_Wformat_, "format is a wide character string" ); |
1641 | |
1642 | if (res.number_non_char > 0) |
1643 | warning_at (loc, OPT_Wformat_, |
1644 | "format string is not an array of type %qs" , "char" ); |
1645 | |
1646 | if (res.number_unterminated > 0) |
1647 | warning_at (loc, OPT_Wformat_, "unterminated format string" ); |
1648 | } |
1649 | |
1650 | /* Callback from check_function_arguments_recurse to check a |
1651 | format string. FORMAT_TREE is the format parameter. ARG_NUM |
1652 | is the number of the format argument. CTX points to a |
1653 | format_check_context. */ |
1654 | |
1655 | static void |
1656 | check_format_arg (void *ctx, tree format_tree, |
1657 | unsigned HOST_WIDE_INT arg_num) |
1658 | { |
1659 | format_check_context *format_ctx = (format_check_context *) ctx; |
1660 | format_check_results *res = format_ctx->res; |
1661 | function_format_info *info = format_ctx->info; |
1662 | tree params = format_ctx->params; |
1663 | vec<location_t> *arglocs = format_ctx->arglocs; |
1664 | |
1665 | int format_length; |
1666 | HOST_WIDE_INT offset; |
1667 | const char *format_chars; |
1668 | tree array_size = 0; |
1669 | tree array_init; |
1670 | |
1671 | location_t fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location); |
1672 | |
1673 | /* Pull out a constant value if the front end didn't, and handle location |
1674 | wrappers. */ |
1675 | format_tree = fold_for_warn (format_tree); |
1676 | STRIP_NOPS (format_tree); |
1677 | |
1678 | if (integer_zerop (format_tree)) |
1679 | { |
1680 | /* Skip to first argument to check, so we can see if this format |
1681 | has any arguments (it shouldn't). */ |
1682 | while (arg_num + 1 < info->first_arg_num) |
1683 | { |
1684 | if (params == 0) |
1685 | return; |
1686 | params = TREE_CHAIN (params); |
1687 | ++arg_num; |
1688 | } |
1689 | |
1690 | if (params == 0) |
1691 | res->number_other++; |
1692 | else |
1693 | { |
1694 | if (res->number_extra_args == 0) |
1695 | res->extra_arg_loc = EXPR_LOC_OR_LOC (TREE_VALUE (params), |
1696 | input_location); |
1697 | res->number_extra_args++; |
1698 | } |
1699 | return; |
1700 | } |
1701 | |
1702 | offset = 0; |
1703 | if (TREE_CODE (format_tree) == POINTER_PLUS_EXPR) |
1704 | { |
1705 | tree arg0, arg1; |
1706 | |
1707 | arg0 = TREE_OPERAND (format_tree, 0); |
1708 | arg1 = TREE_OPERAND (format_tree, 1); |
1709 | STRIP_NOPS (arg0); |
1710 | STRIP_NOPS (arg1); |
1711 | if (TREE_CODE (arg1) == INTEGER_CST) |
1712 | format_tree = arg0; |
1713 | else |
1714 | { |
1715 | res->number_non_literal++; |
1716 | return; |
1717 | } |
1718 | /* POINTER_PLUS_EXPR offsets are to be interpreted signed. */ |
1719 | if (!cst_and_fits_in_hwi (arg1)) |
1720 | { |
1721 | res->number_non_literal++; |
1722 | return; |
1723 | } |
1724 | offset = int_cst_value (arg1); |
1725 | } |
1726 | if (TREE_CODE (format_tree) != ADDR_EXPR) |
1727 | { |
1728 | res->number_non_literal++; |
1729 | return; |
1730 | } |
1731 | res->format_string_loc = EXPR_LOC_OR_LOC (format_tree, input_location); |
1732 | format_tree = TREE_OPERAND (format_tree, 0); |
1733 | if (format_types[info->format_type].flags |
1734 | & (int) FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL) |
1735 | { |
1736 | bool objc_str = (info->format_type == gcc_objc_string_format_type); |
1737 | /* We cannot examine this string here - but we can check that it is |
1738 | a valid type. */ |
1739 | if (TREE_CODE (format_tree) != CONST_DECL |
1740 | || !((objc_str && objc_string_ref_type_p (TREE_TYPE (format_tree))) |
1741 | || (*targetcm.string_object_ref_type_p) |
1742 | ((const_tree) TREE_TYPE (format_tree)))) |
1743 | { |
1744 | res->number_non_literal++; |
1745 | return; |
1746 | } |
1747 | /* Skip to first argument to check. */ |
1748 | while (arg_num + 1 < info->first_arg_num) |
1749 | { |
1750 | if (params == 0) |
1751 | return; |
1752 | params = TREE_CHAIN (params); |
1753 | ++arg_num; |
1754 | } |
1755 | /* So, we have a valid literal string object and one or more params. |
1756 | We need to use an external helper to parse the string into format |
1757 | info. For Objective-C variants we provide the resource within the |
1758 | objc tree, for target variants, via a hook. */ |
1759 | if (objc_str) |
1760 | objc_check_format_arg (format_tree, params); |
1761 | else if (targetcm.check_string_object_format_arg) |
1762 | (*targetcm.check_string_object_format_arg) (format_tree, params); |
1763 | /* Else we can't handle it and retire quietly. */ |
1764 | return; |
1765 | } |
1766 | if (TREE_CODE (format_tree) == ARRAY_REF |
1767 | && tree_fits_shwi_p (TREE_OPERAND (format_tree, 1)) |
1768 | && (offset += tree_to_shwi (TREE_OPERAND (format_tree, 1))) >= 0) |
1769 | format_tree = TREE_OPERAND (format_tree, 0); |
1770 | if (offset < 0) |
1771 | { |
1772 | res->number_non_literal++; |
1773 | return; |
1774 | } |
1775 | if (VAR_P (format_tree) |
1776 | && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE |
1777 | && (array_init = decl_constant_value (format_tree)) != format_tree |
1778 | && TREE_CODE (array_init) == STRING_CST) |
1779 | { |
1780 | /* Extract the string constant initializer. Note that this may include |
1781 | a trailing NUL character that is not in the array (e.g. |
1782 | const char a[3] = "foo";). */ |
1783 | array_size = DECL_SIZE_UNIT (format_tree); |
1784 | format_tree = array_init; |
1785 | } |
1786 | if (TREE_CODE (format_tree) != STRING_CST) |
1787 | { |
1788 | res->number_non_literal++; |
1789 | return; |
1790 | } |
1791 | tree underlying_type |
1792 | = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))); |
1793 | if (underlying_type != char_type_node |
1794 | && !(flag_char8_t && underlying_type == char8_type_node)) |
1795 | { |
1796 | if (underlying_type == char16_type_node |
1797 | || underlying_type == char32_type_node |
1798 | || underlying_type == wchar_type_node) |
1799 | res->number_wide++; |
1800 | else |
1801 | res->number_non_char++; |
1802 | return; |
1803 | } |
1804 | format_chars = TREE_STRING_POINTER (format_tree); |
1805 | format_length = TREE_STRING_LENGTH (format_tree); |
1806 | if (array_size != 0) |
1807 | { |
1808 | /* Variable length arrays can't be initialized. */ |
1809 | gcc_assert (TREE_CODE (array_size) == INTEGER_CST); |
1810 | |
1811 | if (tree_fits_shwi_p (array_size)) |
1812 | { |
1813 | HOST_WIDE_INT array_size_value = tree_to_shwi (array_size); |
1814 | if (array_size_value > 0 |
1815 | && array_size_value == (int) array_size_value |
1816 | && format_length > array_size_value) |
1817 | format_length = array_size_value; |
1818 | } |
1819 | } |
1820 | if (offset) |
1821 | { |
1822 | if (offset >= format_length) |
1823 | { |
1824 | res->number_non_literal++; |
1825 | return; |
1826 | } |
1827 | format_chars += offset; |
1828 | format_length -= offset; |
1829 | } |
1830 | if (format_length < 1 || format_chars[--format_length] != 0) |
1831 | { |
1832 | res->number_unterminated++; |
1833 | return; |
1834 | } |
1835 | if (format_length == 0) |
1836 | { |
1837 | res->number_empty++; |
1838 | return; |
1839 | } |
1840 | |
1841 | /* Skip to first argument to check. */ |
1842 | while (arg_num + 1 < info->first_arg_num) |
1843 | { |
1844 | if (params == 0) |
1845 | return; |
1846 | params = TREE_CHAIN (params); |
1847 | ++arg_num; |
1848 | } |
1849 | /* Provisionally increment res->number_other; check_format_info_main |
1850 | will decrement it if it finds there are extra arguments, but this way |
1851 | need not adjust it for every return. */ |
1852 | res->number_other++; |
1853 | object_allocator <format_wanted_type> fwt_pool ("format_wanted_type pool" ); |
1854 | check_format_info_main (res, info, format_chars, fmt_param_loc, format_tree, |
1855 | format_length, params, arg_num, fwt_pool, arglocs); |
1856 | } |
1857 | |
1858 | /* Support class for argument_parser and check_format_info_main. |
1859 | Tracks any flag characters that have been applied to the |
1860 | current argument. */ |
1861 | |
1862 | class flag_chars_t |
1863 | { |
1864 | public: |
1865 | flag_chars_t (); |
1866 | bool has_char_p (char ch) const; |
1867 | void add_char (char ch); |
1868 | void validate (const format_kind_info *fki, |
1869 | const format_char_info *fci, |
1870 | const format_flag_spec *flag_specs, |
1871 | const char * const format_chars, |
1872 | tree format_string_cst, |
1873 | location_t format_string_loc, |
1874 | const char * const orig_format_chars, |
1875 | char format_char, |
1876 | bool quoted); |
1877 | int get_alloc_flag (const format_kind_info *fki); |
1878 | int assignment_suppression_p (const format_kind_info *fki); |
1879 | |
1880 | private: |
1881 | char m_flag_chars[256]; |
1882 | }; |
1883 | |
1884 | /* Support struct for argument_parser and check_format_info_main. |
1885 | Encapsulates any length modifier applied to the current argument. */ |
1886 | |
1887 | class length_modifier |
1888 | { |
1889 | public: |
1890 | length_modifier () |
1891 | : chars (NULL), val (FMT_LEN_none), std (STD_C89), |
1892 | scalar_identity_flag (0) |
1893 | { |
1894 | } |
1895 | |
1896 | length_modifier (const char *chars_, |
1897 | enum format_lengths val_, |
1898 | enum format_std_version std_, |
1899 | int scalar_identity_flag_) |
1900 | : chars (chars_), val (val_), std (std_), |
1901 | scalar_identity_flag (scalar_identity_flag_) |
1902 | { |
1903 | } |
1904 | |
1905 | const char *chars; |
1906 | enum format_lengths val; |
1907 | enum format_std_version std; |
1908 | int scalar_identity_flag; |
1909 | }; |
1910 | |
1911 | /* Parsing one argument within a format string. */ |
1912 | |
1913 | class argument_parser |
1914 | { |
1915 | public: |
1916 | argument_parser (function_format_info *info, const char *&format_chars, |
1917 | tree format_string_cst, |
1918 | const char * const orig_format_chars, |
1919 | location_t format_string_loc, flag_chars_t &flag_chars, |
1920 | int &has_operand_number, tree first_fillin_param, |
1921 | object_allocator <format_wanted_type> &fwt_pool_, |
1922 | vec<location_t> *arglocs); |
1923 | |
1924 | bool read_any_dollar (); |
1925 | |
1926 | bool read_format_flags (); |
1927 | |
1928 | bool |
1929 | read_any_format_width (tree ¶ms, |
1930 | unsigned HOST_WIDE_INT &arg_num); |
1931 | |
1932 | void |
1933 | read_any_format_left_precision (); |
1934 | |
1935 | bool |
1936 | read_any_format_precision (tree ¶ms, |
1937 | unsigned HOST_WIDE_INT &arg_num); |
1938 | |
1939 | void handle_alloc_chars (); |
1940 | |
1941 | length_modifier read_any_length_modifier (); |
1942 | |
1943 | void read_any_other_modifier (); |
1944 | |
1945 | const format_char_info *find_format_char_info (char format_char); |
1946 | |
1947 | void |
1948 | validate_flag_pairs (const format_char_info *fci, |
1949 | char format_char); |
1950 | |
1951 | void |
1952 | give_y2k_warnings (const format_char_info *fci, |
1953 | char format_char); |
1954 | |
1955 | void parse_any_scan_set (const format_char_info *fci); |
1956 | |
1957 | bool handle_conversions (const format_char_info *fci, |
1958 | const length_modifier &len_modifier, |
1959 | tree &wanted_type, |
1960 | const char *&wanted_type_name, |
1961 | unsigned HOST_WIDE_INT &arg_num, |
1962 | tree ¶ms, |
1963 | char format_char); |
1964 | |
1965 | bool |
1966 | check_argument_type (const format_char_info *fci, |
1967 | const length_modifier &len_modifier, |
1968 | tree &wanted_type, |
1969 | const char *&wanted_type_name, |
1970 | const bool suppressed, |
1971 | unsigned HOST_WIDE_INT &arg_num, |
1972 | tree ¶ms, |
1973 | const int alloc_flag, |
1974 | const char * const format_start, |
1975 | const char * const type_start, |
1976 | location_t fmt_param_loc, |
1977 | char conversion_char); |
1978 | |
1979 | private: |
1980 | const function_format_info *const info; |
1981 | const format_kind_info * const fki; |
1982 | const format_flag_spec * const flag_specs; |
1983 | const char *start_of_this_format; |
1984 | const char *&format_chars; |
1985 | const tree format_string_cst; |
1986 | const char * const orig_format_chars; |
1987 | const location_t format_string_loc; |
1988 | object_allocator <format_wanted_type> &fwt_pool; |
1989 | flag_chars_t &flag_chars; |
1990 | int main_arg_num; |
1991 | tree main_arg_params; |
1992 | int &has_operand_number; |
1993 | const tree first_fillin_param; |
1994 | format_wanted_type width_wanted_type; |
1995 | format_wanted_type precision_wanted_type; |
1996 | public: |
1997 | format_wanted_type main_wanted_type; |
1998 | private: |
1999 | format_wanted_type *first_wanted_type; |
2000 | format_wanted_type *last_wanted_type; |
2001 | vec<location_t> *arglocs; |
2002 | }; |
2003 | |
2004 | /* flag_chars_t's constructor. */ |
2005 | |
2006 | flag_chars_t::flag_chars_t () |
2007 | { |
2008 | m_flag_chars[0] = 0; |
2009 | } |
2010 | |
2011 | /* Has CH been seen as a flag within the current argument? */ |
2012 | |
2013 | bool |
2014 | flag_chars_t::has_char_p (char ch) const |
2015 | { |
2016 | return strchr (s: m_flag_chars, c: ch) != 0; |
2017 | } |
2018 | |
2019 | /* Add CH to the flags seen within the current argument. */ |
2020 | |
2021 | void |
2022 | flag_chars_t::add_char (char ch) |
2023 | { |
2024 | int i = strlen (s: m_flag_chars); |
2025 | m_flag_chars[i++] = ch; |
2026 | m_flag_chars[i] = 0; |
2027 | } |
2028 | |
2029 | /* Validate the individual flags used, removing any that are invalid. */ |
2030 | |
2031 | void |
2032 | flag_chars_t::validate (const format_kind_info *fki, |
2033 | const format_char_info *fci, |
2034 | const format_flag_spec *flag_specs, |
2035 | const char * const format_chars, |
2036 | tree format_string_cst, |
2037 | location_t format_string_loc, |
2038 | const char * const orig_format_chars, |
2039 | char format_char, |
2040 | bool quoted) |
2041 | { |
2042 | int i; |
2043 | int d = 0; |
2044 | bool quotflag = false; |
2045 | |
2046 | for (i = 0; m_flag_chars[i] != 0; i++) |
2047 | { |
2048 | const format_flag_spec *s = get_flag_spec (spec: flag_specs, |
2049 | flag: m_flag_chars[i], NULL); |
2050 | m_flag_chars[i - d] = m_flag_chars[i]; |
2051 | if (m_flag_chars[i] == fki->length_code_char) |
2052 | continue; |
2053 | |
2054 | /* Remember if a quoting flag is seen. */ |
2055 | quotflag |= s->quoting; |
2056 | |
2057 | if (strchr (s: fci->flag_chars, c: m_flag_chars[i]) == 0) |
2058 | { |
2059 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2060 | char_idx: format_chars - orig_format_chars, |
2061 | opt: OPT_Wformat_, |
2062 | gmsgid: "%s used with %<%%%c%> %s format" , |
2063 | _(s->name), format_char, fki->name); |
2064 | d++; |
2065 | continue; |
2066 | } |
2067 | if (pedantic) |
2068 | { |
2069 | const format_flag_spec *t; |
2070 | if (ADJ_STD (s->std) > C_STD_VER) |
2071 | warning_at (format_string_loc, OPT_Wformat_, |
2072 | "%s does not support %s" , |
2073 | C_STD_NAME (s->std), _(s->long_name)); |
2074 | t = get_flag_spec (spec: flag_specs, flag: m_flag_chars[i], predicates: fci->flags2); |
2075 | if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std)) |
2076 | { |
2077 | const char *long_name = (t->long_name != NULL |
2078 | ? t->long_name |
2079 | : s->long_name); |
2080 | if (ADJ_STD (t->std) > C_STD_VER) |
2081 | warning_at (format_string_loc, OPT_Wformat_, |
2082 | "%s does not support %s with" |
2083 | " the %<%%%c%> %s format" , |
2084 | C_STD_NAME (t->std), _(long_name), |
2085 | format_char, fki->name); |
2086 | } |
2087 | } |
2088 | |
2089 | /* Detect quoting directives used within a quoted sequence, such |
2090 | as GCC's "%<...%qE". */ |
2091 | if (quoted && s->quoting) |
2092 | { |
2093 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2094 | char_idx: format_chars - orig_format_chars - 1, |
2095 | opt: OPT_Wformat_, |
2096 | gmsgid: "%s used within a quoted sequence" , |
2097 | _(s->name)); |
2098 | } |
2099 | } |
2100 | m_flag_chars[i - d] = 0; |
2101 | |
2102 | if (!quoted |
2103 | && !quotflag |
2104 | && strchr (s: fci->flags2, c: '\'')) |
2105 | { |
2106 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2107 | char_idx: format_chars - orig_format_chars, |
2108 | opt: OPT_Wformat_, |
2109 | gmsgid: "%qc conversion used unquoted" , |
2110 | format_char); |
2111 | } |
2112 | } |
2113 | |
2114 | /* Determine if an assignment-allocation has been set, requiring |
2115 | an extra char ** for writing back a dynamically-allocated char *. |
2116 | This is for handling the optional 'm' character in scanf. */ |
2117 | |
2118 | int |
2119 | flag_chars_t::get_alloc_flag (const format_kind_info *fki) |
2120 | { |
2121 | if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) |
2122 | && has_char_p (ch: 'a')) |
2123 | return 1; |
2124 | if (fki->alloc_char && has_char_p (ch: fki->alloc_char)) |
2125 | return 1; |
2126 | return 0; |
2127 | } |
2128 | |
2129 | /* Determine if an assignment-suppression character was seen. |
2130 | ('*' in scanf, for discarding the converted input). */ |
2131 | |
2132 | int |
2133 | flag_chars_t::assignment_suppression_p (const format_kind_info *fki) |
2134 | { |
2135 | if (fki->suppression_char |
2136 | && has_char_p (ch: fki->suppression_char)) |
2137 | return 1; |
2138 | return 0; |
2139 | } |
2140 | |
2141 | /* Constructor for argument_parser. Initialize for parsing one |
2142 | argument within a format string. */ |
2143 | |
2144 | argument_parser:: |
2145 | argument_parser (function_format_info *info_, const char *&format_chars_, |
2146 | tree format_string_cst_, |
2147 | const char * const orig_format_chars_, |
2148 | location_t format_string_loc_, |
2149 | flag_chars_t &flag_chars_, |
2150 | int &has_operand_number_, |
2151 | tree first_fillin_param_, |
2152 | object_allocator <format_wanted_type> &fwt_pool_, |
2153 | vec<location_t> *arglocs_) |
2154 | : info (info_), |
2155 | fki (&format_types[info->format_type]), |
2156 | flag_specs (fki->flag_specs), |
2157 | start_of_this_format (format_chars_), |
2158 | format_chars (format_chars_), |
2159 | format_string_cst (format_string_cst_), |
2160 | orig_format_chars (orig_format_chars_), |
2161 | format_string_loc (format_string_loc_), |
2162 | fwt_pool (fwt_pool_), |
2163 | flag_chars (flag_chars_), |
2164 | main_arg_num (0), |
2165 | main_arg_params (NULL), |
2166 | has_operand_number (has_operand_number_), |
2167 | first_fillin_param (first_fillin_param_), |
2168 | first_wanted_type (NULL), |
2169 | last_wanted_type (NULL), |
2170 | arglocs (arglocs_) |
2171 | { |
2172 | } |
2173 | |
2174 | /* Handle dollars at the start of format arguments, setting up main_arg_params |
2175 | and main_arg_num. |
2176 | |
2177 | Return true if format parsing is to continue, false otherwise. */ |
2178 | |
2179 | bool |
2180 | argument_parser::read_any_dollar () |
2181 | { |
2182 | if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0) |
2183 | { |
2184 | /* Possibly read a $ operand number at the start of the format. |
2185 | If one was previously used, one is required here. If one |
2186 | is not used here, we can't immediately conclude this is a |
2187 | format without them, since it could be printf %m or scanf %*. */ |
2188 | int opnum; |
2189 | opnum = maybe_read_dollar_number (format: &format_chars, dollar_needed: 0, |
2190 | params: first_fillin_param, |
2191 | param_ptr: &main_arg_params, fki); |
2192 | if (opnum == -1) |
2193 | return false; |
2194 | else if (opnum > 0) |
2195 | { |
2196 | has_operand_number = 1; |
2197 | main_arg_num = opnum + info->first_arg_num - 1; |
2198 | } |
2199 | } |
2200 | else if (fki->flags & FMT_FLAG_USE_DOLLAR) |
2201 | { |
2202 | if (avoid_dollar_number (format: format_chars)) |
2203 | return false; |
2204 | } |
2205 | return true; |
2206 | } |
2207 | |
2208 | /* Read any format flags, but do not yet validate them beyond removing |
2209 | duplicates, since in general validation depends on the rest of |
2210 | the format. |
2211 | |
2212 | Return true if format parsing is to continue, false otherwise. */ |
2213 | |
2214 | bool |
2215 | argument_parser::read_format_flags () |
2216 | { |
2217 | while (*format_chars != 0 |
2218 | && strchr (s: fki->flag_chars, c: *format_chars) != 0) |
2219 | { |
2220 | const format_flag_spec *s = get_flag_spec (spec: flag_specs, |
2221 | flag: *format_chars, NULL); |
2222 | if (flag_chars.has_char_p (ch: *format_chars)) |
2223 | { |
2224 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2225 | char_idx: format_chars + 1 - orig_format_chars, |
2226 | opt: OPT_Wformat_, |
2227 | gmsgid: "repeated %s in format" , _(s->name)); |
2228 | } |
2229 | else |
2230 | flag_chars.add_char (ch: *format_chars); |
2231 | |
2232 | if (s->skip_next_char) |
2233 | { |
2234 | ++format_chars; |
2235 | if (*format_chars == 0) |
2236 | { |
2237 | warning_at (format_string_loc, OPT_Wformat_, |
2238 | "missing fill character at end of strfmon format" ); |
2239 | return false; |
2240 | } |
2241 | } |
2242 | ++format_chars; |
2243 | } |
2244 | |
2245 | return true; |
2246 | } |
2247 | |
2248 | /* Read any format width, possibly * or *m$. |
2249 | |
2250 | Return true if format parsing is to continue, false otherwise. */ |
2251 | |
2252 | bool |
2253 | argument_parser:: |
2254 | read_any_format_width (tree ¶ms, |
2255 | unsigned HOST_WIDE_INT &arg_num) |
2256 | { |
2257 | if (!fki->width_char) |
2258 | return true; |
2259 | |
2260 | if (fki->width_type != NULL && *format_chars == '*') |
2261 | { |
2262 | flag_chars.add_char (ch: fki->width_char); |
2263 | /* "...a field width...may be indicated by an asterisk. |
2264 | In this case, an int argument supplies the field width..." */ |
2265 | ++format_chars; |
2266 | if (has_operand_number != 0) |
2267 | { |
2268 | int opnum; |
2269 | opnum = maybe_read_dollar_number (format: &format_chars, |
2270 | dollar_needed: has_operand_number == 1, |
2271 | params: first_fillin_param, |
2272 | param_ptr: ¶ms, fki); |
2273 | if (opnum == -1) |
2274 | return false; |
2275 | else if (opnum > 0) |
2276 | { |
2277 | has_operand_number = 1; |
2278 | arg_num = opnum + info->first_arg_num - 1; |
2279 | } |
2280 | else |
2281 | has_operand_number = 0; |
2282 | } |
2283 | else |
2284 | { |
2285 | if (avoid_dollar_number (format: format_chars)) |
2286 | return false; |
2287 | } |
2288 | if (info->first_arg_num != 0) |
2289 | { |
2290 | tree cur_param; |
2291 | if (params == 0) |
2292 | cur_param = NULL; |
2293 | else |
2294 | { |
2295 | cur_param = TREE_VALUE (params); |
2296 | if (has_operand_number <= 0) |
2297 | { |
2298 | params = TREE_CHAIN (params); |
2299 | ++arg_num; |
2300 | } |
2301 | } |
2302 | width_wanted_type.wanted_type = *fki->width_type; |
2303 | width_wanted_type.wanted_type_name = NULL; |
2304 | width_wanted_type.pointer_count = 0; |
2305 | width_wanted_type.char_lenient_flag = 0; |
2306 | width_wanted_type.scalar_identity_flag = 0; |
2307 | width_wanted_type.writing_in_flag = 0; |
2308 | width_wanted_type.reading_from_flag = 0; |
2309 | width_wanted_type.kind = CF_KIND_FIELD_WIDTH; |
2310 | width_wanted_type.format_start = format_chars - 1; |
2311 | width_wanted_type.format_length = 1; |
2312 | width_wanted_type.param = cur_param; |
2313 | width_wanted_type.arg_num = arg_num; |
2314 | width_wanted_type.offset_loc = |
2315 | format_chars - orig_format_chars; |
2316 | width_wanted_type.next = NULL; |
2317 | if (last_wanted_type != 0) |
2318 | last_wanted_type->next = &width_wanted_type; |
2319 | if (first_wanted_type == 0) |
2320 | first_wanted_type = &width_wanted_type; |
2321 | last_wanted_type = &width_wanted_type; |
2322 | } |
2323 | } |
2324 | else |
2325 | { |
2326 | /* Possibly read a numeric width. If the width is zero, |
2327 | we complain if appropriate. */ |
2328 | int non_zero_width_char = false; |
2329 | int found_width = false; |
2330 | while (ISDIGIT (*format_chars)) |
2331 | { |
2332 | found_width = true; |
2333 | if (*format_chars != '0') |
2334 | non_zero_width_char = true; |
2335 | ++format_chars; |
2336 | } |
2337 | if (found_width && !non_zero_width_char && |
2338 | (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD)) |
2339 | warning_at (format_string_loc, OPT_Wformat_, |
2340 | "zero width in %s format" , fki->name); |
2341 | if (found_width) |
2342 | flag_chars.add_char (ch: fki->width_char); |
2343 | } |
2344 | |
2345 | return true; |
2346 | } |
2347 | |
2348 | /* Read any format left precision (must be a number, not *). */ |
2349 | void |
2350 | argument_parser::read_any_format_left_precision () |
2351 | { |
2352 | if (fki->left_precision_char == 0) |
2353 | return; |
2354 | if (*format_chars != '#') |
2355 | return; |
2356 | |
2357 | ++format_chars; |
2358 | flag_chars.add_char (ch: fki->left_precision_char); |
2359 | if (!ISDIGIT (*format_chars)) |
2360 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2361 | char_idx: format_chars - orig_format_chars, |
2362 | opt: OPT_Wformat_, |
2363 | gmsgid: "empty left precision in %s format" , fki->name); |
2364 | while (ISDIGIT (*format_chars)) |
2365 | ++format_chars; |
2366 | } |
2367 | |
2368 | /* Read any format precision, possibly * or *m$. |
2369 | |
2370 | Return true if format parsing is to continue, false otherwise. */ |
2371 | |
2372 | bool |
2373 | argument_parser:: |
2374 | read_any_format_precision (tree ¶ms, |
2375 | unsigned HOST_WIDE_INT &arg_num) |
2376 | { |
2377 | if (fki->precision_char == 0) |
2378 | return true; |
2379 | if (*format_chars != '.') |
2380 | return true; |
2381 | |
2382 | ++format_chars; |
2383 | flag_chars.add_char (ch: fki->precision_char); |
2384 | if (fki->precision_type != NULL && *format_chars == '*') |
2385 | { |
2386 | /* "...a...precision...may be indicated by an asterisk. |
2387 | In this case, an int argument supplies the...precision." */ |
2388 | ++format_chars; |
2389 | if (has_operand_number != 0) |
2390 | { |
2391 | int opnum; |
2392 | opnum = maybe_read_dollar_number (format: &format_chars, |
2393 | dollar_needed: has_operand_number == 1, |
2394 | params: first_fillin_param, |
2395 | param_ptr: ¶ms, fki); |
2396 | if (opnum == -1) |
2397 | return false; |
2398 | else if (opnum > 0) |
2399 | { |
2400 | has_operand_number = 1; |
2401 | arg_num = opnum + info->first_arg_num - 1; |
2402 | } |
2403 | else |
2404 | has_operand_number = 0; |
2405 | } |
2406 | else |
2407 | { |
2408 | if (avoid_dollar_number (format: format_chars)) |
2409 | return false; |
2410 | } |
2411 | if (info->first_arg_num != 0) |
2412 | { |
2413 | tree cur_param; |
2414 | if (params == 0) |
2415 | cur_param = NULL; |
2416 | else |
2417 | { |
2418 | cur_param = TREE_VALUE (params); |
2419 | if (has_operand_number <= 0) |
2420 | { |
2421 | params = TREE_CHAIN (params); |
2422 | ++arg_num; |
2423 | } |
2424 | } |
2425 | precision_wanted_type.wanted_type = *fki->precision_type; |
2426 | precision_wanted_type.wanted_type_name = NULL; |
2427 | precision_wanted_type.pointer_count = 0; |
2428 | precision_wanted_type.char_lenient_flag = 0; |
2429 | precision_wanted_type.scalar_identity_flag = 0; |
2430 | precision_wanted_type.writing_in_flag = 0; |
2431 | precision_wanted_type.reading_from_flag = 0; |
2432 | precision_wanted_type.kind = CF_KIND_FIELD_PRECISION; |
2433 | precision_wanted_type.param = cur_param; |
2434 | precision_wanted_type.format_start = format_chars - 2; |
2435 | precision_wanted_type.format_length = 2; |
2436 | precision_wanted_type.arg_num = arg_num; |
2437 | precision_wanted_type.offset_loc = |
2438 | format_chars - orig_format_chars; |
2439 | precision_wanted_type.next = NULL; |
2440 | if (last_wanted_type != 0) |
2441 | last_wanted_type->next = &precision_wanted_type; |
2442 | if (first_wanted_type == 0) |
2443 | first_wanted_type = &precision_wanted_type; |
2444 | last_wanted_type = &precision_wanted_type; |
2445 | } |
2446 | } |
2447 | else |
2448 | { |
2449 | if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) |
2450 | && !ISDIGIT (*format_chars)) |
2451 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2452 | char_idx: format_chars - orig_format_chars, |
2453 | opt: OPT_Wformat_, |
2454 | gmsgid: "empty precision in %s format" , fki->name); |
2455 | while (ISDIGIT (*format_chars)) |
2456 | ++format_chars; |
2457 | } |
2458 | |
2459 | return true; |
2460 | } |
2461 | |
2462 | /* Parse any assignment-allocation flags, which request an extra |
2463 | char ** for writing back a dynamically-allocated char *. |
2464 | This is for handling the optional 'm' character in scanf, |
2465 | and, before C99, 'a' (for compatibility with a non-standard |
2466 | GNU libc extension). */ |
2467 | |
2468 | void |
2469 | argument_parser::handle_alloc_chars () |
2470 | { |
2471 | if (fki->alloc_char && fki->alloc_char == *format_chars) |
2472 | { |
2473 | flag_chars.add_char (ch: fki->alloc_char); |
2474 | format_chars++; |
2475 | } |
2476 | |
2477 | /* Handle the scanf allocation kludge. */ |
2478 | if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) |
2479 | { |
2480 | if (*format_chars == 'a' && !flag_isoc99) |
2481 | { |
2482 | if (format_chars[1] == 's' || format_chars[1] == 'S' |
2483 | || format_chars[1] == '[') |
2484 | { |
2485 | /* 'a' is used as a flag. */ |
2486 | flag_chars.add_char (ch: 'a'); |
2487 | format_chars++; |
2488 | } |
2489 | } |
2490 | } |
2491 | } |
2492 | |
2493 | /* Look for length modifiers within the current format argument, |
2494 | returning a length_modifier instance describing it (or the |
2495 | default if one is not found). |
2496 | |
2497 | Issue warnings about non-standard modifiers. */ |
2498 | |
2499 | length_modifier |
2500 | argument_parser::read_any_length_modifier () |
2501 | { |
2502 | length_modifier result; |
2503 | |
2504 | const format_length_info *fli = fki->length_char_specs; |
2505 | if (!fli) |
2506 | return result; |
2507 | |
2508 | while (fli->name != 0 |
2509 | && strncmp (s1: fli->name, s2: format_chars, n: strlen (s: fli->name))) |
2510 | fli++; |
2511 | if (fli->name != 0) |
2512 | { |
2513 | format_chars += strlen (s: fli->name); |
2514 | if (fli->double_name != 0 && fli->name[0] == *format_chars) |
2515 | { |
2516 | format_chars++; |
2517 | result = length_modifier (fli->double_name, fli->double_index, |
2518 | fli->double_std, 0); |
2519 | } |
2520 | else |
2521 | { |
2522 | result = length_modifier (fli->name, fli->index, fli->std, |
2523 | fli->scalar_identity_flag); |
2524 | } |
2525 | flag_chars.add_char (ch: fki->length_code_char); |
2526 | } |
2527 | if (pedantic) |
2528 | { |
2529 | /* Warn if the length modifier is non-standard. */ |
2530 | if (ADJ_STD (result.std) > C_STD_VER) |
2531 | warning_at (format_string_loc, OPT_Wformat_, |
2532 | "%s does not support the %qs %s length modifier" , |
2533 | C_STD_NAME (result.std), result.chars, |
2534 | fki->name); |
2535 | } |
2536 | |
2537 | return result; |
2538 | } |
2539 | |
2540 | /* Read any other modifier (strftime E/O). */ |
2541 | |
2542 | void |
2543 | argument_parser::read_any_other_modifier () |
2544 | { |
2545 | if (fki->modifier_chars == NULL) |
2546 | return; |
2547 | |
2548 | while (*format_chars != 0 |
2549 | && strchr (s: fki->modifier_chars, c: *format_chars) != 0) |
2550 | { |
2551 | if (flag_chars.has_char_p (ch: *format_chars)) |
2552 | { |
2553 | const format_flag_spec *s = get_flag_spec (spec: flag_specs, |
2554 | flag: *format_chars, NULL); |
2555 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2556 | char_idx: format_chars - orig_format_chars, |
2557 | opt: OPT_Wformat_, |
2558 | gmsgid: "repeated %s in format" , _(s->name)); |
2559 | } |
2560 | else |
2561 | flag_chars.add_char (ch: *format_chars); |
2562 | ++format_chars; |
2563 | } |
2564 | } |
2565 | |
2566 | /* Return the format_char_info corresponding to FORMAT_CHAR, |
2567 | potentially issuing a warning if the format char is |
2568 | not supported in the C standard version we are checking |
2569 | against. |
2570 | |
2571 | Issue a warning and return NULL if it is not found. |
2572 | |
2573 | Issue warnings about non-standard modifiers. */ |
2574 | |
2575 | const format_char_info * |
2576 | argument_parser::find_format_char_info (char format_char) |
2577 | { |
2578 | const format_char_info *fci = fki->conversion_specs; |
2579 | |
2580 | while (fci->format_chars != 0 |
2581 | && strchr (s: fci->format_chars, c: format_char) == 0) |
2582 | ++fci; |
2583 | if (fci->format_chars == 0) |
2584 | { |
2585 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2586 | char_idx: format_chars - orig_format_chars, |
2587 | opt: OPT_Wformat_, |
2588 | gmsgid: "unknown conversion type character" |
2589 | " %qc in format" , |
2590 | format_char); |
2591 | return NULL; |
2592 | } |
2593 | |
2594 | if (pedantic) |
2595 | { |
2596 | if (ADJ_STD (fci->std) > C_STD_VER) |
2597 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2598 | char_idx: format_chars - orig_format_chars, |
2599 | opt: OPT_Wformat_, |
2600 | gmsgid: "%s does not support the %<%%%c%> %s format" , |
2601 | C_STD_NAME (fci->std), format_char, fki->name); |
2602 | } |
2603 | |
2604 | return fci; |
2605 | } |
2606 | |
2607 | /* Validate the pairs of flags used. |
2608 | Issue warnings about incompatible combinations of flags. */ |
2609 | |
2610 | void |
2611 | argument_parser::validate_flag_pairs (const format_char_info *fci, |
2612 | char format_char) |
2613 | { |
2614 | const format_flag_pair * const bad_flag_pairs = fki->bad_flag_pairs; |
2615 | |
2616 | for (int i = 0; bad_flag_pairs[i].flag_char1 != 0; i++) |
2617 | { |
2618 | const format_flag_spec *s, *t; |
2619 | if (!flag_chars.has_char_p (ch: bad_flag_pairs[i].flag_char1)) |
2620 | continue; |
2621 | if (!flag_chars.has_char_p (ch: bad_flag_pairs[i].flag_char2)) |
2622 | continue; |
2623 | if (bad_flag_pairs[i].predicate != 0 |
2624 | && strchr (s: fci->flags2, c: bad_flag_pairs[i].predicate) == 0) |
2625 | continue; |
2626 | s = get_flag_spec (spec: flag_specs, flag: bad_flag_pairs[i].flag_char1, NULL); |
2627 | t = get_flag_spec (spec: flag_specs, flag: bad_flag_pairs[i].flag_char2, NULL); |
2628 | if (bad_flag_pairs[i].ignored) |
2629 | { |
2630 | if (bad_flag_pairs[i].predicate != 0) |
2631 | warning_at (format_string_loc, OPT_Wformat_, |
2632 | "%s ignored with %s and %<%%%c%> %s format" , |
2633 | _(s->name), _(t->name), format_char, |
2634 | fki->name); |
2635 | else |
2636 | warning_at (format_string_loc, OPT_Wformat_, |
2637 | "%s ignored with %s in %s format" , |
2638 | _(s->name), _(t->name), fki->name); |
2639 | } |
2640 | else |
2641 | { |
2642 | if (bad_flag_pairs[i].predicate != 0) |
2643 | warning_at (format_string_loc, OPT_Wformat_, |
2644 | "use of %s and %s together with %<%%%c%> %s format" , |
2645 | _(s->name), _(t->name), format_char, |
2646 | fki->name); |
2647 | else |
2648 | warning_at (format_string_loc, OPT_Wformat_, |
2649 | "use of %s and %s together in %s format" , |
2650 | _(s->name), _(t->name), fki->name); |
2651 | } |
2652 | } |
2653 | } |
2654 | |
2655 | /* Give Y2K warnings. */ |
2656 | |
2657 | void |
2658 | argument_parser::give_y2k_warnings (const format_char_info *fci, |
2659 | char format_char) |
2660 | { |
2661 | if (!warn_format_y2k) |
2662 | return; |
2663 | |
2664 | int y2k_level = 0; |
2665 | if (strchr (s: fci->flags2, c: '4') != 0) |
2666 | if (flag_chars.has_char_p (ch: 'E')) |
2667 | y2k_level = 3; |
2668 | else |
2669 | y2k_level = 2; |
2670 | else if (strchr (s: fci->flags2, c: '3') != 0) |
2671 | y2k_level = 3; |
2672 | else if (strchr (s: fci->flags2, c: '2') != 0) |
2673 | y2k_level = 2; |
2674 | if (y2k_level == 3) |
2675 | warning_at (format_string_loc, OPT_Wformat_y2k, |
2676 | "%<%%%c%> yields only last 2 digits of " |
2677 | "year in some locales" , format_char); |
2678 | else if (y2k_level == 2) |
2679 | warning_at (format_string_loc, OPT_Wformat_y2k, |
2680 | "%<%%%c%> yields only last 2 digits of year" , |
2681 | format_char); |
2682 | } |
2683 | |
2684 | /* Parse any "scan sets" enclosed in square brackets, e.g. |
2685 | for scanf-style calls. */ |
2686 | |
2687 | void |
2688 | argument_parser::parse_any_scan_set (const format_char_info *fci) |
2689 | { |
2690 | if (strchr (s: fci->flags2, c: '[') == NULL) |
2691 | return; |
2692 | |
2693 | /* Skip over scan set, in case it happens to have '%' in it. */ |
2694 | if (*format_chars == '^') |
2695 | ++format_chars; |
2696 | /* Find closing bracket; if one is hit immediately, then |
2697 | it's part of the scan set rather than a terminator. */ |
2698 | if (*format_chars == ']') |
2699 | ++format_chars; |
2700 | while (*format_chars && *format_chars != ']') |
2701 | ++format_chars; |
2702 | if (*format_chars != ']') |
2703 | /* The end of the format string was reached. */ |
2704 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2705 | char_idx: format_chars - orig_format_chars, |
2706 | opt: OPT_Wformat_, |
2707 | gmsgid: "no closing %<]%> for %<%%[%> format" ); |
2708 | } |
2709 | |
2710 | /* Return true if this argument is to be continued to be parsed, |
2711 | false to skip to next argument. */ |
2712 | |
2713 | bool |
2714 | argument_parser::handle_conversions (const format_char_info *fci, |
2715 | const length_modifier &len_modifier, |
2716 | tree &wanted_type, |
2717 | const char *&wanted_type_name, |
2718 | unsigned HOST_WIDE_INT &arg_num, |
2719 | tree ¶ms, |
2720 | char format_char) |
2721 | { |
2722 | enum format_std_version wanted_type_std; |
2723 | |
2724 | if (!(fki->flags & (int) FMT_FLAG_ARG_CONVERT)) |
2725 | return true; |
2726 | |
2727 | wanted_type = (fci->types[len_modifier.val].type |
2728 | ? *fci->types[len_modifier.val].type : 0); |
2729 | wanted_type_name = fci->types[len_modifier.val].name; |
2730 | wanted_type_std = fci->types[len_modifier.val].std; |
2731 | if (wanted_type == 0) |
2732 | { |
2733 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2734 | char_idx: format_chars - orig_format_chars, |
2735 | opt: OPT_Wformat_, |
2736 | gmsgid: "use of %qs length modifier with %qc type" |
2737 | " character has either no effect" |
2738 | " or undefined behavior" , |
2739 | len_modifier.chars, format_char); |
2740 | /* Heuristic: skip one argument when an invalid length/type |
2741 | combination is encountered. */ |
2742 | arg_num++; |
2743 | if (params != 0) |
2744 | params = TREE_CHAIN (params); |
2745 | return false; |
2746 | } |
2747 | else if (pedantic |
2748 | /* Warn if non-standard, provided it is more non-standard |
2749 | than the length and type characters that may already |
2750 | have been warned for. */ |
2751 | && ADJ_STD (wanted_type_std) > ADJ_STD (len_modifier.std) |
2752 | && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) |
2753 | { |
2754 | if (ADJ_STD (wanted_type_std) > C_STD_VER) |
2755 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
2756 | char_idx: format_chars - orig_format_chars, |
2757 | opt: OPT_Wformat_, |
2758 | gmsgid: "%s does not support the %<%%%s%c%> %s format" , |
2759 | C_STD_NAME (wanted_type_std), |
2760 | len_modifier.chars, |
2761 | format_char, fki->name); |
2762 | } |
2763 | |
2764 | return true; |
2765 | } |
2766 | |
2767 | /* Check type of argument against desired type. |
2768 | |
2769 | Return true if format parsing is to continue, false otherwise. */ |
2770 | |
2771 | bool |
2772 | argument_parser:: |
2773 | check_argument_type (const format_char_info *fci, |
2774 | const length_modifier &len_modifier, |
2775 | tree &wanted_type, |
2776 | const char *&wanted_type_name, |
2777 | const bool suppressed, |
2778 | unsigned HOST_WIDE_INT &arg_num, |
2779 | tree ¶ms, |
2780 | const int alloc_flag, |
2781 | const char * const format_start, |
2782 | const char * const type_start, |
2783 | location_t fmt_param_loc, |
2784 | char conversion_char) |
2785 | { |
2786 | if (info->first_arg_num == 0) |
2787 | return true; |
2788 | |
2789 | if ((fci->pointer_count == 0 && wanted_type == void_type_node) |
2790 | || suppressed) |
2791 | { |
2792 | if (main_arg_num != 0) |
2793 | { |
2794 | if (suppressed) |
2795 | warning_at (format_string_loc, OPT_Wformat_, |
2796 | "operand number specified with " |
2797 | "suppressed assignment" ); |
2798 | else |
2799 | warning_at (format_string_loc, OPT_Wformat_, |
2800 | "operand number specified for format " |
2801 | "taking no argument" ); |
2802 | } |
2803 | } |
2804 | else |
2805 | { |
2806 | format_wanted_type *wanted_type_ptr; |
2807 | |
2808 | if (main_arg_num != 0) |
2809 | { |
2810 | arg_num = main_arg_num; |
2811 | params = main_arg_params; |
2812 | } |
2813 | else |
2814 | { |
2815 | ++arg_num; |
2816 | if (has_operand_number > 0) |
2817 | { |
2818 | warning_at (format_string_loc, OPT_Wformat_, |
2819 | "missing $ operand number in format" ); |
2820 | return false; |
2821 | } |
2822 | else |
2823 | has_operand_number = 0; |
2824 | } |
2825 | |
2826 | wanted_type_ptr = &main_wanted_type; |
2827 | while (fci) |
2828 | { |
2829 | tree cur_param; |
2830 | if (params == 0) |
2831 | cur_param = NULL; |
2832 | else |
2833 | { |
2834 | cur_param = TREE_VALUE (params); |
2835 | params = TREE_CHAIN (params); |
2836 | } |
2837 | |
2838 | wanted_type_ptr->wanted_type = wanted_type; |
2839 | wanted_type_ptr->wanted_type_name = wanted_type_name; |
2840 | wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag; |
2841 | wanted_type_ptr->char_lenient_flag = 0; |
2842 | if (strchr (s: fci->flags2, c: 'c') != 0) |
2843 | wanted_type_ptr->char_lenient_flag = 1; |
2844 | wanted_type_ptr->scalar_identity_flag = 0; |
2845 | if (len_modifier.scalar_identity_flag) |
2846 | wanted_type_ptr->scalar_identity_flag = 1; |
2847 | wanted_type_ptr->writing_in_flag = 0; |
2848 | wanted_type_ptr->reading_from_flag = 0; |
2849 | if (alloc_flag) |
2850 | wanted_type_ptr->writing_in_flag = 1; |
2851 | else |
2852 | { |
2853 | if (strchr (s: fci->flags2, c: 'W') != 0) |
2854 | wanted_type_ptr->writing_in_flag = 1; |
2855 | if (strchr (s: fci->flags2, c: 'R') != 0) |
2856 | wanted_type_ptr->reading_from_flag = 1; |
2857 | } |
2858 | wanted_type_ptr->kind = CF_KIND_FORMAT; |
2859 | wanted_type_ptr->param = cur_param; |
2860 | wanted_type_ptr->arg_num = arg_num; |
2861 | wanted_type_ptr->format_start = format_start; |
2862 | wanted_type_ptr->format_length = format_chars - format_start; |
2863 | wanted_type_ptr->offset_loc = format_chars - orig_format_chars; |
2864 | wanted_type_ptr->next = NULL; |
2865 | if (last_wanted_type != 0) |
2866 | last_wanted_type->next = wanted_type_ptr; |
2867 | if (first_wanted_type == 0) |
2868 | first_wanted_type = wanted_type_ptr; |
2869 | last_wanted_type = wanted_type_ptr; |
2870 | |
2871 | fci = fci->chain; |
2872 | if (fci) |
2873 | { |
2874 | wanted_type_ptr = fwt_pool.allocate (); |
2875 | arg_num++; |
2876 | wanted_type = *fci->types[len_modifier.val].type; |
2877 | wanted_type_name = fci->types[len_modifier.val].name; |
2878 | } |
2879 | } |
2880 | } |
2881 | |
2882 | if (first_wanted_type != 0) |
2883 | { |
2884 | ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars; |
2885 | ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars; |
2886 | /* By default, use the end of the range for the caret location. */ |
2887 | substring_loc fmt_loc (fmt_param_loc, TREE_TYPE (format_string_cst), |
2888 | offset_to_format_end, |
2889 | offset_to_format_start, offset_to_format_end); |
2890 | ptrdiff_t offset_to_type_start = type_start - orig_format_chars; |
2891 | check_format_types (fmt_loc, first_wanted_type, fki, |
2892 | offset_to_type_start, |
2893 | conversion_char, arglocs); |
2894 | } |
2895 | |
2896 | return true; |
2897 | } |
2898 | |
2899 | /* Describes "paired tokens" within the format string that are |
2900 | expected to be balanced. */ |
2901 | |
2902 | class baltoks_t |
2903 | { |
2904 | public: |
2905 | baltoks_t (): singlequote (), doublequote () { } |
2906 | |
2907 | typedef auto_vec<const char *> balanced_tokens_t; |
2908 | /* Vectors of pointers to opening brackets ('['), curly brackets ('{'), |
2909 | quoting directives (like GCC "%<"), parentheses, and angle brackets |
2910 | ('<'). Used to detect unbalanced tokens. */ |
2911 | balanced_tokens_t brackets; |
2912 | balanced_tokens_t curly; |
2913 | balanced_tokens_t quotdirs; |
2914 | balanced_tokens_t parens; |
2915 | balanced_tokens_t pointy; |
2916 | /* Pointer to the last opening quote. */ |
2917 | const char *singlequote; |
2918 | const char *doublequote; |
2919 | }; |
2920 | |
2921 | /* Describes a keyword, operator, or other name. */ |
2922 | |
2923 | struct token_t |
2924 | { |
2925 | const char *name; /* Keyword/operator name. */ |
2926 | unsigned char len; /* Its length. */ |
2927 | const char *alt; /* Alternate spelling. */ |
2928 | }; |
2929 | |
2930 | /* Helper for initializing global token_t arrays below. */ |
2931 | #define NAME(name) { name, sizeof name - 1, NULL } |
2932 | |
2933 | /* C/C++ operators that are expected to be quoted within the format |
2934 | string. */ |
2935 | |
2936 | static const token_t c_opers[] = |
2937 | { |
2938 | NAME ("!=" ), NAME ("%=" ), NAME ("&&" ), NAME ("&=" ), NAME ("*=" ), |
2939 | NAME ("++" ), NAME ("+=" ), NAME ("--" ), NAME ("-=" ), NAME ("->" ), |
2940 | NAME ("/=" ), NAME ("<<" ), NAME ("<<=" ), NAME ("<=" ), NAME ("==" ), |
2941 | NAME (">=" ), NAME (">>=" ), NAME (">>" ), NAME ("?:" ), NAME ("^=" ), |
2942 | NAME ("|=" ), NAME ("||" ) |
2943 | }; |
2944 | |
2945 | static const token_t cxx_opers[] = |
2946 | { |
2947 | NAME ("->*" ), NAME (".*" ), NAME ("::" ), NAME ("<=>" ) |
2948 | }; |
2949 | |
2950 | /* Common C/C++ keywords that are expected to be quoted within the format |
2951 | string. Keywords like auto, inline, or volatile are excluded because |
2952 | they are sometimes used in common terms like /auto variables/, /inline |
2953 | function/, or /volatile access/ where they should not be quoted. */ |
2954 | |
2955 | static const token_t c_keywords[] = |
2956 | { |
2957 | #undef NAME |
2958 | #define NAME(name, alt) { name, sizeof name - 1, alt } |
2959 | |
2960 | NAME ("alignas" , NULL), |
2961 | NAME ("alignof" , NULL), |
2962 | NAME ("asm" , NULL), |
2963 | NAME ("bool" , NULL), |
2964 | NAME ("char" , NULL), |
2965 | NAME ("const %" , NULL), |
2966 | NAME ("const-qualified" , "%<const%>-qualified" ), |
2967 | NAME ("float" , NULL), |
2968 | NAME ("ifunc" , NULL), |
2969 | NAME ("int" , NULL), |
2970 | NAME ("long double" , NULL), |
2971 | NAME ("long int" , NULL), |
2972 | NAME ("long long" , NULL), |
2973 | NAME ("malloc" , NULL), |
2974 | NAME ("noclone" , NULL), |
2975 | NAME ("noinline" , NULL), |
2976 | NAME ("nonnull" , NULL), |
2977 | NAME ("noreturn" , NULL), |
2978 | NAME ("offsetof" , NULL), |
2979 | NAME ("readonly" , "read-only" ), |
2980 | NAME ("readwrite" , "read-write" ), |
2981 | NAME ("restrict %" , NULL), |
2982 | NAME ("restrict-qualified" , "%<restrict%>-qualified" ), |
2983 | NAME ("short int" , NULL), |
2984 | NAME ("signed char" , NULL), |
2985 | NAME ("signed int" , NULL), |
2986 | NAME ("signed long" , NULL), |
2987 | NAME ("signed short" , NULL), |
2988 | NAME ("sizeof" , NULL), |
2989 | NAME ("typeof" , NULL), |
2990 | NAME ("unsigned char" , NULL), |
2991 | NAME ("unsigned int" , NULL), |
2992 | NAME ("unsigned long" , NULL), |
2993 | NAME ("unsigned short" , NULL), |
2994 | NAME ("volatile %" , NULL), |
2995 | NAME ("volatile-qualified" , "%<volatile%>-qualified" ), |
2996 | NAME ("weakref" , NULL), |
2997 | }; |
2998 | |
2999 | static const token_t cxx_keywords[] = |
3000 | { |
3001 | /* C++ only keywords and operators. */ |
3002 | NAME ("catch" , NULL), |
3003 | NAME ("constexpr if" , NULL), |
3004 | NAME ("constexpr" , NULL), |
3005 | NAME ("constinit" , NULL), |
3006 | NAME ("consteval" , NULL), |
3007 | NAME ("decltype" , NULL), |
3008 | NAME ("nullptr" , NULL), |
3009 | NAME ("operator delete" , NULL), |
3010 | NAME ("operator new" , NULL), |
3011 | NAME ("typeid" , NULL), |
3012 | NAME ("typeinfo" , NULL) |
3013 | }; |
3014 | |
3015 | /* Blacklisted words such as misspellings that should be avoided in favor |
3016 | of the specified alternatives. */ |
3017 | static const struct |
3018 | { |
3019 | const char *name; /* Bad word. */ |
3020 | unsigned char len; /* Its length. */ |
3021 | const char *alt; /* Preferred alternative. */ |
3022 | } badwords[] = |
3023 | { |
3024 | NAME ("arg" , "argument" ), |
3025 | NAME ("bitfield" , "bit-field" ), |
3026 | NAME ("builtin function" , "built-in function" ), |
3027 | NAME ("can not" , "cannot" ), |
3028 | NAME ("commandline option" , "command-line option" ), |
3029 | NAME ("commandline" , "command line" ), |
3030 | NAME ("command line option" , "command-line option" ), |
3031 | NAME ("decl" , "declaration" ), |
3032 | NAME ("enumeral" , "enumerated" ), |
3033 | NAME ("floating point" , "floating-point" ), |
3034 | NAME ("nonstatic" , "non-static" ), |
3035 | NAME ("non-zero" , "nonzero" ), |
3036 | NAME ("reg" , "register" ), |
3037 | NAME ("stmt" , "statement" ), |
3038 | }; |
3039 | |
3040 | /* Common contractions that should be avoided in favor of the specified |
3041 | alternatives. */ |
3042 | |
3043 | static const struct |
3044 | { |
3045 | const char *name; /* Contraction. */ |
3046 | unsigned char len; /* Its length. */ |
3047 | const char *alt; /* Preferred alternative. */ |
3048 | } contrs[] = |
3049 | { |
3050 | NAME ("can't" , "cannot" ), |
3051 | NAME ("didn't" , "did not" ), |
3052 | /* These are commonly abused. Avoid diagnosing them for now. |
3053 | NAME ("isn't", "is not"), |
3054 | NAME ("don't", "is not"), |
3055 | */ |
3056 | NAME ("mustn't" , "must not" ), |
3057 | NAME ("needn't" , "need not" ), |
3058 | NAME ("should't" , "should not" ), |
3059 | NAME ("that's" , "that is" ), |
3060 | NAME ("there's" , "there is" ), |
3061 | NAME ("they're" , "they are" ), |
3062 | NAME ("what's" , "what is" ), |
3063 | NAME ("won't" , "will not" ) |
3064 | }; |
3065 | |
3066 | /* Check for unquoted TOKENS. FORMAT_STRING_LOC is the location of |
3067 | the format string, FORMAT_STRING_CST the format string itself (as |
3068 | a tree), ORIG_FORMAT_CHARS and FORMAT_CHARS are pointers to |
3069 | the beginning of the format string and the character currently |
3070 | being processed, and BALTOKS describes paired "tokens" within |
3071 | the format string that are expected to be balanced. |
3072 | Returns a pointer to the last processed character or null when |
3073 | nothing was done. */ |
3074 | |
3075 | static const char* |
3076 | check_tokens (const token_t *tokens, unsigned ntoks, |
3077 | location_t format_string_loc, tree format_string_cst, |
3078 | const char *orig_format_chars, const char *format_chars, |
3079 | baltoks_t &baltoks) |
3080 | { |
3081 | /* For brevity. */ |
3082 | const int opt = OPT_Wformat_diag; |
3083 | /* Zero-based starting position of a problem sequence. */ |
3084 | int fmtchrpos = format_chars - orig_format_chars; |
3085 | |
3086 | /* For identifier-like "words," set to the word length. */ |
3087 | unsigned wlen = 0; |
3088 | /* Set for an operator, clear for an identifier/word. */ |
3089 | bool is_oper = false; |
3090 | bool underscore = false; |
3091 | |
3092 | if (format_chars[0] == '_' || ISALPHA (format_chars[0])) |
3093 | { |
3094 | while (format_chars[wlen] == '_' || ISALNUM (format_chars[wlen])) |
3095 | { |
3096 | underscore |= format_chars[wlen] == '_'; |
3097 | ++wlen; |
3098 | } |
3099 | } |
3100 | else |
3101 | is_oper = true; |
3102 | |
3103 | for (unsigned i = 0; i != ntoks; ++i) |
3104 | { |
3105 | unsigned toklen = tokens[i].len; |
3106 | |
3107 | if (toklen < wlen |
3108 | || strncmp (s1: format_chars, s2: tokens[i].name, n: toklen)) |
3109 | continue; |
3110 | |
3111 | if (toklen == 2 |
3112 | && format_chars - orig_format_chars > 0 |
3113 | && (TOUPPER (format_chars[-1]) == 'C' |
3114 | || TOUPPER (format_chars[-1]) == 'G')) |
3115 | return format_chars + toklen - 1; /* Reference to C++ or G++. */ |
3116 | |
3117 | if (ISPUNCT (format_chars[toklen - 1])) |
3118 | { |
3119 | if (format_chars[toklen - 1] == format_chars[toklen]) |
3120 | return NULL; /* Operator followed by another punctuator. */ |
3121 | } |
3122 | else if (ISALNUM (format_chars[toklen])) |
3123 | return NULL; /* Keyword prefix for a longer word. */ |
3124 | |
3125 | if (toklen == 2 |
3126 | && format_chars[0] == '-' |
3127 | && format_chars[1] == '-' |
3128 | && ISALNUM (format_chars[2])) |
3129 | return NULL; /* Probably option like --help. */ |
3130 | |
3131 | /* Allow this ugly warning for the time being. */ |
3132 | if (toklen == 2 |
3133 | && format_chars - orig_format_chars > 6 |
3134 | && startswith (str: format_chars - 7, prefix: " count >= width of " )) |
3135 | return format_chars + 10; |
3136 | |
3137 | /* The token is a type if it ends in an alphabetic character. */ |
3138 | bool is_type = (ISALPHA (tokens[i].name[toklen - 1]) |
3139 | && strchr (s: tokens[i].name, c: ' ')); |
3140 | |
3141 | /* Backtrack to the last alphabetic character (for tokens whose |
3142 | names end in '%'). */ |
3143 | if (!is_oper) |
3144 | while (!ISALPHA (tokens[i].name[toklen - 1])) |
3145 | --toklen; |
3146 | |
3147 | if (format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3148 | pos1: fmtchrpos, pos2: fmtchrpos + toklen, opt, |
3149 | gmsgid: (is_type |
3150 | ? G_("unquoted type name %<%.*s%> in format" ) |
3151 | : (is_oper |
3152 | ? G_("unquoted operator %<%.*s%> in format" ) |
3153 | : G_("unquoted keyword %<%.*s%> in format" ))), |
3154 | toklen, format_chars) |
3155 | && tokens[i].alt) |
3156 | inform (format_string_loc, "use %qs instead" , tokens[i].alt); |
3157 | |
3158 | return format_chars + toklen - 1; |
3159 | } |
3160 | |
3161 | /* Diagnose unquoted __attribute__. Consider any parenthesized |
3162 | argument to the attribute to avoid redundant warnings for |
3163 | the double parentheses that might follow. */ |
3164 | if (startswith (str: format_chars, prefix: "__attribute" )) |
3165 | { |
3166 | unsigned nchars = sizeof "__attribute" - 1; |
3167 | while ('_' == format_chars[nchars]) |
3168 | ++nchars; |
3169 | |
3170 | for (int i = nchars; format_chars[i]; ++i) |
3171 | if (' ' != format_chars[i]) |
3172 | { |
3173 | nchars = i; |
3174 | break; |
3175 | } |
3176 | |
3177 | if (format_chars[nchars] == '(') |
3178 | { |
3179 | baltoks.parens.safe_push (obj: format_chars + nchars); |
3180 | |
3181 | ++nchars; |
3182 | bool close = false; |
3183 | if (format_chars[nchars] == '(') |
3184 | { |
3185 | baltoks.parens.safe_push (obj: format_chars + nchars); |
3186 | close = true; |
3187 | ++nchars; |
3188 | } |
3189 | for (int i = nchars; format_chars[i]; ++i) |
3190 | if (')' == format_chars[i]) |
3191 | { |
3192 | if (baltoks.parens.length () > 0) |
3193 | baltoks.parens.pop (); |
3194 | nchars = i + 1; |
3195 | break; |
3196 | } |
3197 | |
3198 | if (close && format_chars[nchars] == ')') |
3199 | { |
3200 | if (baltoks.parens.length () > 0) |
3201 | baltoks.parens.pop (); |
3202 | ++nchars; |
3203 | } |
3204 | } |
3205 | |
3206 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3207 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3208 | gmsgid: "unquoted attribute in format" ); |
3209 | return format_chars + nchars - 1; |
3210 | } |
3211 | |
3212 | /* Diagnose unquoted built-ins. */ |
3213 | if (format_chars[0] == '_' |
3214 | && format_chars[1] == '_' |
3215 | && (startswith (str: format_chars + 2, prefix: "atomic" ) |
3216 | || startswith (str: format_chars + 2, prefix: "builtin" ) |
3217 | || startswith (str: format_chars + 2, prefix: "sync" ))) |
3218 | { |
3219 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3220 | pos1: fmtchrpos, pos2: fmtchrpos + wlen, opt, |
3221 | gmsgid: "unquoted name of built-in function %<%.*s%> " |
3222 | "in format" , |
3223 | wlen, format_chars); |
3224 | return format_chars + wlen - 1; |
3225 | } |
3226 | |
3227 | /* Diagnose unquoted substrings of alphanumeric characters containing |
3228 | underscores. They most likely refer to identifiers and should be |
3229 | quoted. */ |
3230 | if (underscore) |
3231 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3232 | pos1: format_chars - orig_format_chars, |
3233 | pos2: format_chars + wlen - orig_format_chars, |
3234 | opt, |
3235 | gmsgid: "unquoted identifier or keyword %<%.*s%> in format" , |
3236 | wlen, format_chars); |
3237 | else |
3238 | { |
3239 | /* Diagnose some common misspellings. */ |
3240 | for (unsigned i = 0; i != ARRAY_SIZE (badwords); ++i) |
3241 | { |
3242 | unsigned badwlen = strspn (s: badwords[i].name, accept: " -" ); |
3243 | if (wlen >= badwlen |
3244 | && (wlen <= badwords[i].len |
3245 | || (wlen == badwords[i].len + 1U |
3246 | && TOUPPER (format_chars[wlen - 1]) == 'S')) |
3247 | && !strncasecmp (s1: format_chars, s2: badwords[i].name, n: badwords[i].len)) |
3248 | { |
3249 | /* Handle singular as well as plural forms of all bad words |
3250 | even though the latter don't necessarily make sense for |
3251 | all of the former (like "can nots"). */ |
3252 | badwlen = badwords[i].len; |
3253 | const char *plural = "" ; |
3254 | if (TOUPPER (format_chars[badwlen]) == 'S') |
3255 | { |
3256 | ++badwlen; |
3257 | plural = "s" ; |
3258 | } |
3259 | |
3260 | /* As an exception, don't warn about "decl-specifier*" since |
3261 | it's a C++ grammar production. */ |
3262 | if (badwords[i].name[0] == 'd' |
3263 | && startswith (str: format_chars, prefix: "decl-specifier" )) |
3264 | continue; |
3265 | |
3266 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3267 | pos1: fmtchrpos, pos2: fmtchrpos + badwords[i].len, |
3268 | opt, |
3269 | gmsgid: "misspelled term %<%.*s%> in format; " |
3270 | "use %<%s%s%> instead" , |
3271 | badwlen, format_chars, |
3272 | badwords[i].alt, plural); |
3273 | |
3274 | return format_chars + badwords[i].len - 1; |
3275 | } |
3276 | } |
3277 | |
3278 | /* Skip C++/G++. */ |
3279 | if (!strncasecmp (s1: format_chars, s2: "c++" , n: 3) |
3280 | || !strncasecmp (s1: format_chars, s2: "g++" , n: 3)) |
3281 | return format_chars + 2; |
3282 | } |
3283 | |
3284 | return wlen ? format_chars + wlen - 1 : NULL; |
3285 | } |
3286 | |
3287 | /* Check plain text in a format string of a GCC diagnostic function |
3288 | for common quoting, punctuation, and spelling mistakes, and issue |
3289 | -Wformat-diag warnings if they are found. FORMAT_STRING_LOC is |
3290 | the location of the format string, FORMAT_STRING_CST the format |
3291 | string itself (as a tree), ORIG_FORMAT_CHARS and FORMAT_CHARS are |
3292 | pointers to the beginning of the format string and the character |
3293 | currently being processed, and BALTOKS describes paired "tokens" |
3294 | within the format string that are expected to be balanced. |
3295 | Returns a pointer to the last processed character. */ |
3296 | |
3297 | static const char* |
3298 | check_plain (location_t format_string_loc, tree format_string_cst, |
3299 | const char *orig_format_chars, const char *format_chars, |
3300 | baltoks_t &baltoks) |
3301 | { |
3302 | /* For brevity. */ |
3303 | const int opt = OPT_Wformat_diag; |
3304 | /* Zero-based starting position of a problem sequence. */ |
3305 | int fmtchrpos = format_chars - orig_format_chars; |
3306 | |
3307 | if (*format_chars == '%') |
3308 | { |
3309 | /* Diagnose %<%s%> and suggest using %qs instead. */ |
3310 | if (startswith (str: format_chars, prefix: "%<%s%>" )) |
3311 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3312 | pos1: fmtchrpos, pos2: fmtchrpos + 6, opt, |
3313 | gmsgid: "quoted %qs directive in format; " |
3314 | "use %qs instead" , "%s" , "%qs" ); |
3315 | else if (format_chars - orig_format_chars > 2 |
3316 | && !strncasecmp (s1: format_chars - 3, s2: "can%'t" , n: 6)) |
3317 | format_warning_substr (fmt_string_loc: format_string_loc, |
3318 | format_string_cst, |
3319 | pos1: fmtchrpos - 3, pos2: fmtchrpos + 3, opt, |
3320 | gmsgid: "contraction %<%.*s%> in format; " |
3321 | "use %qs instead" , |
3322 | 6, format_chars - 3, "cannot" ); |
3323 | |
3324 | return format_chars; |
3325 | } |
3326 | |
3327 | if (baltoks.quotdirs.length ()) |
3328 | { |
3329 | /* Skip over all plain text within a quoting directive until |
3330 | the next directive. */ |
3331 | while (*format_chars && '%' != *format_chars) |
3332 | ++format_chars; |
3333 | |
3334 | return format_chars; |
3335 | } |
3336 | |
3337 | /* The length of the problem sequence. */ |
3338 | int nchars = 0; |
3339 | |
3340 | /* Diagnose any whitespace characters other than <space> but only |
3341 | leading, trailing, and two or more consecutive <space>s. Do |
3342 | this before diagnosing control characters because whitespace |
3343 | is a subset of controls. */ |
3344 | const char *other_than_space = NULL; |
3345 | while (ISSPACE (format_chars[nchars])) |
3346 | { |
3347 | if (format_chars[nchars] != ' ' && !other_than_space) |
3348 | other_than_space = format_chars + nchars; |
3349 | ++nchars; |
3350 | } |
3351 | |
3352 | if (nchars) |
3353 | { |
3354 | /* This is the most common problem: go the extra mile to describe |
3355 | the problem in as much helpful detail as possible. */ |
3356 | if (other_than_space) |
3357 | { |
3358 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3359 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3360 | gmsgid: "unquoted whitespace character %qc in format" , |
3361 | *other_than_space); |
3362 | return format_chars + nchars - 1; |
3363 | } |
3364 | |
3365 | if (fmtchrpos == 0) |
3366 | /* Accept strings of leading spaces with no warning. */ |
3367 | return format_chars + nchars - 1; |
3368 | |
3369 | if (!format_chars[nchars]) |
3370 | { |
3371 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3372 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3373 | gmsgid: "spurious trailing space in format" ); |
3374 | return format_chars + nchars - 1; |
3375 | } |
3376 | |
3377 | if (nchars > 1) |
3378 | { |
3379 | if (nchars == 2 |
3380 | && orig_format_chars < format_chars |
3381 | && format_chars[-1] == '.' |
3382 | && format_chars[0] == ' ' |
3383 | && format_chars[1] == ' ') |
3384 | { |
3385 | /* A period followed by two spaces. */ |
3386 | if (ISUPPER (*orig_format_chars)) |
3387 | { |
3388 | /* If the part before the period is a capitalized |
3389 | sentence check to make sure that what follows |
3390 | is also capitalized. */ |
3391 | if (ISLOWER (format_chars[2])) |
3392 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3393 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3394 | gmsgid: "inconsistent capitalization in " |
3395 | "format" ); |
3396 | } |
3397 | } |
3398 | else |
3399 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3400 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3401 | gmsgid: "unquoted sequence of %i consecutive " |
3402 | "space characters in format" , nchars); |
3403 | return format_chars + nchars - 1; |
3404 | } |
3405 | |
3406 | format_chars += nchars; |
3407 | nchars = 0; |
3408 | } |
3409 | |
3410 | fmtchrpos = format_chars - orig_format_chars; |
3411 | |
3412 | /* Diagnose any unquoted control characters other than the terminating |
3413 | NUL. */ |
3414 | while (format_chars[nchars] && ISCNTRL (format_chars[nchars])) |
3415 | ++nchars; |
3416 | |
3417 | if (nchars > 1) |
3418 | { |
3419 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3420 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3421 | gmsgid: "unquoted control characters in format" ); |
3422 | return format_chars + nchars - 1; |
3423 | } |
3424 | if (nchars) |
3425 | { |
3426 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3427 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3428 | gmsgid: "unquoted control character %qc in format" , |
3429 | *format_chars); |
3430 | return format_chars + nchars - 1; |
3431 | } |
3432 | |
3433 | if (ISPUNCT (format_chars[0])) |
3434 | { |
3435 | size_t nelts = ARRAY_SIZE (c_opers); |
3436 | if (const char *ret = check_tokens (tokens: c_opers, ntoks: nelts, |
3437 | format_string_loc, format_string_cst, |
3438 | orig_format_chars, format_chars, |
3439 | baltoks)) |
3440 | return ret; |
3441 | |
3442 | nelts = c_dialect_cxx () ? ARRAY_SIZE (cxx_opers) : 0; |
3443 | if (const char *ret = check_tokens (tokens: cxx_opers, ntoks: nelts, |
3444 | format_string_loc, format_string_cst, |
3445 | orig_format_chars, format_chars, |
3446 | baltoks)) |
3447 | return ret; |
3448 | } |
3449 | |
3450 | if (ISALPHA (format_chars[0])) |
3451 | { |
3452 | size_t nelts = ARRAY_SIZE (c_keywords); |
3453 | if (const char *ret = check_tokens (tokens: c_keywords, ntoks: nelts, |
3454 | format_string_loc, format_string_cst, |
3455 | orig_format_chars, format_chars, |
3456 | baltoks)) |
3457 | return ret; |
3458 | |
3459 | nelts = c_dialect_cxx () ? ARRAY_SIZE (cxx_keywords) : 0; |
3460 | if (const char *ret = check_tokens (tokens: cxx_keywords, ntoks: nelts, |
3461 | format_string_loc, format_string_cst, |
3462 | orig_format_chars, format_chars, |
3463 | baltoks)) |
3464 | return ret; |
3465 | } |
3466 | |
3467 | nchars = 0; |
3468 | |
3469 | /* Diagnose unquoted options. */ |
3470 | if ((format_chars == orig_format_chars |
3471 | || format_chars[-1] == ' ') |
3472 | && format_chars[0] == '-' |
3473 | && ((format_chars[1] == '-' |
3474 | && ISALPHA (format_chars[2])) |
3475 | || ISALPHA (format_chars[1]))) |
3476 | { |
3477 | nchars = 1; |
3478 | while (ISALNUM (format_chars[nchars]) |
3479 | || '_' == format_chars[nchars] |
3480 | || '-' == format_chars[nchars] |
3481 | || '+' == format_chars[nchars]) |
3482 | ++nchars; |
3483 | |
3484 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3485 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3486 | gmsgid: "unquoted option name %<%.*s%> in format" , |
3487 | nchars, format_chars); |
3488 | return format_chars + nchars - 1; |
3489 | } |
3490 | |
3491 | /* Diagnose leading, trailing, and two or more consecutive punctuation |
3492 | characters. */ |
3493 | const char *unbalanced = NULL; |
3494 | while ('%' != format_chars[nchars] |
3495 | && ISPUNCT (format_chars[nchars]) |
3496 | && !unbalanced) |
3497 | { |
3498 | switch (format_chars[nchars]) |
3499 | { |
3500 | case '[': |
3501 | baltoks.brackets.safe_push (obj: format_chars + nchars); |
3502 | break; |
3503 | case '{': |
3504 | baltoks.curly.safe_push (obj: format_chars + nchars); |
3505 | break; |
3506 | case '(': |
3507 | baltoks.parens.safe_push (obj: format_chars + nchars); |
3508 | break; |
3509 | case '<': |
3510 | baltoks.pointy.safe_push (obj: format_chars + nchars); |
3511 | break; |
3512 | |
3513 | case ']': |
3514 | if (baltoks.brackets.length () > 0) |
3515 | baltoks.brackets.pop (); |
3516 | else |
3517 | unbalanced = format_chars + nchars; |
3518 | break; |
3519 | case '}': |
3520 | if (baltoks.curly.length () > 0) |
3521 | baltoks.curly.pop (); |
3522 | else |
3523 | unbalanced = format_chars + nchars; |
3524 | break; |
3525 | case ')': |
3526 | if (baltoks.parens.length () > 0) |
3527 | baltoks.parens.pop (); |
3528 | else |
3529 | unbalanced = format_chars + nchars; |
3530 | break; |
3531 | case '>': |
3532 | if (baltoks.pointy.length () > 0) |
3533 | baltoks.pointy.pop (); |
3534 | else |
3535 | unbalanced = format_chars + nchars; |
3536 | break; |
3537 | } |
3538 | |
3539 | ++nchars; |
3540 | } |
3541 | |
3542 | if (unbalanced) |
3543 | { |
3544 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3545 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3546 | gmsgid: "unbalanced punctuation character %qc in format" , |
3547 | *unbalanced); |
3548 | return format_chars + nchars - 1; |
3549 | } |
3550 | |
3551 | if (nchars) |
3552 | { |
3553 | /* Consider any identifier that follows the pound ('#') sign |
3554 | a preprocessing directive. */ |
3555 | if (nchars == 1 |
3556 | && format_chars[0] == '#' |
3557 | && ISALPHA (format_chars[1])) |
3558 | { |
3559 | while (ISALNUM (format_chars[nchars]) |
3560 | || format_chars[nchars] == '_') |
3561 | ++nchars; |
3562 | |
3563 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3564 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3565 | gmsgid: "unquoted preprocessing directive %<%.*s%> " |
3566 | "in format" , nchars, format_chars); |
3567 | return format_chars + nchars - 1; |
3568 | } |
3569 | |
3570 | /* Diagnose a bare single quote. */ |
3571 | if (nchars == 1 |
3572 | && format_chars[0] == '\'' |
3573 | && format_chars - orig_format_chars |
3574 | && ISALPHA (format_chars[-1]) |
3575 | && ISALPHA (format_chars[1])) |
3576 | { |
3577 | /* Diagnose a subset of contractions that are best avoided. */ |
3578 | for (unsigned i = 0; i != ARRAY_SIZE (contrs); ++i) |
3579 | { |
3580 | const char *apos = strchr (s: contrs[i].name, c: '\''); |
3581 | gcc_assert (apos != NULL); |
3582 | int off = apos - contrs[i].name; |
3583 | |
3584 | if (format_chars - orig_format_chars >= off |
3585 | && !strncmp (s1: format_chars - off, |
3586 | s2: contrs[i].name, n: contrs[i].len)) |
3587 | { |
3588 | format_warning_substr (fmt_string_loc: format_string_loc, |
3589 | format_string_cst, |
3590 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3591 | gmsgid: "contraction %<%.*s%> in format; " |
3592 | "use %qs instead" , |
3593 | contrs[i].len, contrs[i].name, |
3594 | contrs[i].alt); |
3595 | return format_chars + nchars - 1; |
3596 | } |
3597 | } |
3598 | |
3599 | if (format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3600 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3601 | gmsgid: "bare apostrophe %<'%> in format" )) |
3602 | inform (format_string_loc, |
3603 | "if avoiding the apostrophe is not feasible, enclose " |
3604 | "it in a pair of %qs and %qs directives instead" , |
3605 | "%<" , "%>" ); |
3606 | return format_chars + nchars - 1; |
3607 | } |
3608 | |
3609 | /* Diagnose a backtick (grave accent). */ |
3610 | if (nchars == 1 |
3611 | && format_chars[0] == '`') |
3612 | { |
3613 | if (format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3614 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3615 | gmsgid: "grave accent %<`%> in format" )) |
3616 | inform (format_string_loc, |
3617 | "use the apostrophe directive %qs instead" , "%'" ); |
3618 | return format_chars + nchars - 1; |
3619 | } |
3620 | |
3621 | /* Diagnose a punctuation character after a space. */ |
3622 | if (nchars == 1 |
3623 | && format_chars - orig_format_chars |
3624 | && format_chars[-1] == ' ' |
3625 | && strspn (s: format_chars, accept: "!?:;.," ) == 1) |
3626 | { |
3627 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3628 | pos1: fmtchrpos - 1, pos2: fmtchrpos, opt, |
3629 | gmsgid: "space followed by punctuation character " |
3630 | "%<%c%>" , format_chars[0]); |
3631 | return format_chars; |
3632 | } |
3633 | |
3634 | if (nchars == 1) |
3635 | { |
3636 | if (startswith (str: format_chars, prefix: "\"%s\"" )) |
3637 | { |
3638 | if (format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3639 | pos1: fmtchrpos, pos2: fmtchrpos + 4, opt, |
3640 | gmsgid: "quoted %qs directive in format" , |
3641 | "%s" )) |
3642 | inform (format_string_loc, "if using %qs is not feasible, " |
3643 | "use %qs instead" , "%qs" , "\"%-s\"" ); |
3644 | } |
3645 | |
3646 | if (format_chars[0] == '"') |
3647 | { |
3648 | baltoks.doublequote = baltoks.doublequote ? NULL : format_chars; |
3649 | return format_chars + nchars - 1; |
3650 | } |
3651 | if (format_chars[0] == '\'') |
3652 | { |
3653 | baltoks.singlequote = baltoks.singlequote ? NULL : format_chars; |
3654 | return format_chars + nchars - 1; |
3655 | } |
3656 | } |
3657 | |
3658 | if (fmtchrpos == 0) |
3659 | { |
3660 | if (nchars == 1 |
3661 | && format_chars[0] == '(') |
3662 | ; /* Text beginning in an open parenthesis. */ |
3663 | else if (nchars == 3 |
3664 | && startswith (str: format_chars, prefix: "..." ) |
3665 | && format_chars[3]) |
3666 | ; /* Text beginning in an ellipsis. */ |
3667 | else |
3668 | { |
3669 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3670 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3671 | gmsgid: "spurious leading punctuation sequence " |
3672 | "%<%.*s%> in format" , |
3673 | nchars, format_chars); |
3674 | return format_chars + nchars - 1; |
3675 | } |
3676 | } |
3677 | else if (!format_chars[nchars]) |
3678 | { |
3679 | if (nchars == 1 |
3680 | && (format_chars[nchars - 1] == ':' |
3681 | || format_chars[nchars - 1] == ')')) |
3682 | ; /* Text ending in a colon or a closing parenthesis. */ |
3683 | else if (nchars == 1 |
3684 | && ((ISUPPER (*orig_format_chars) |
3685 | && format_chars[nchars - 1] == '.') |
3686 | || strspn (s: format_chars + nchars - 1, accept: "?])" ) == 1)) |
3687 | ; /* Capitalized sentence terminated by a single period, |
3688 | or text ending in a question mark, closing bracket, |
3689 | or parenthesis. */ |
3690 | else if (nchars == 2 |
3691 | && format_chars[0] == '?' |
3692 | && format_chars[1] == ')') |
3693 | ; /* A question mark after a closing parenthetical note. */ |
3694 | else if (nchars == 2 |
3695 | && format_chars[0] == ')' |
3696 | && (format_chars[1] == '?' |
3697 | || format_chars[1] == ';' |
3698 | || format_chars[1] == ':' |
3699 | || (ISUPPER (*orig_format_chars) |
3700 | && format_chars[1] == '.'))) |
3701 | ; /* Closing parenthetical note followed by a question mark, |
3702 | semicolon, or colon at the end of the string, or by |
3703 | a period at the end of a capitalized sentence. */ |
3704 | else if (nchars == 3 |
3705 | && format_chars - orig_format_chars > 0 |
3706 | && startswith (str: format_chars, prefix: "..." )) |
3707 | ; /* Text ending in the ellipsis. */ |
3708 | else |
3709 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3710 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3711 | gmsgid: "spurious trailing punctuation sequence " |
3712 | "%<%.*s%> in format" , |
3713 | nchars, format_chars); |
3714 | |
3715 | return format_chars + nchars - 1; |
3716 | } |
3717 | else if (nchars == 2 |
3718 | && format_chars[0] == ')' |
3719 | && (format_chars[1] == ':' |
3720 | || format_chars[1] == ';' |
3721 | || format_chars[1] == ',') |
3722 | && format_chars[2] == ' ') |
3723 | ; /* Closing parenthetical note followed by a colon, semicolon |
3724 | or a comma followed by a space in the middle of the string. */ |
3725 | else if (nchars > 1) |
3726 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3727 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3728 | gmsgid: "unquoted sequence of %i consecutive " |
3729 | "punctuation characters %q.*s in format" , |
3730 | nchars, nchars, format_chars); |
3731 | return format_chars + nchars - 1; |
3732 | } |
3733 | |
3734 | nchars = 0; |
3735 | |
3736 | /* Finally, diagnose any unquoted non-graph, non-punctuation characters |
3737 | other than the terminating NUL. */ |
3738 | while (format_chars[nchars] |
3739 | && '%' != format_chars[nchars] |
3740 | && !ISPUNCT (format_chars[nchars]) |
3741 | && !ISGRAPH (format_chars[nchars])) |
3742 | ++nchars; |
3743 | |
3744 | if (nchars > 1) |
3745 | { |
3746 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3747 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3748 | gmsgid: "unquoted non-graph characters in format" ); |
3749 | return format_chars + nchars - 1; |
3750 | } |
3751 | if (nchars) |
3752 | { |
3753 | format_warning_substr (fmt_string_loc: format_string_loc, format_string_cst, |
3754 | pos1: fmtchrpos, pos2: fmtchrpos + nchars, opt, |
3755 | gmsgid: "unquoted non-graph character %qc in format" , |
3756 | *format_chars); |
3757 | return format_chars + nchars - 1; |
3758 | } |
3759 | |
3760 | return format_chars; |
3761 | } |
3762 | |
3763 | /* Diagnose unbalanced tokens described by BALTOKS in format string |
3764 | ORIG_FORMAT_CHARS and the corresponding FORMAT_STRING_CST. */ |
3765 | |
3766 | static void |
3767 | maybe_diag_unbalanced_tokens (location_t format_string_loc, |
3768 | const char *orig_format_chars, |
3769 | tree format_string_cst, |
3770 | baltoks_t &baltoks) |
3771 | { |
3772 | const char *unbalanced = NULL; |
3773 | |
3774 | if (baltoks.brackets.length ()) |
3775 | unbalanced = baltoks.brackets.pop (); |
3776 | else if (baltoks.curly.length ()) |
3777 | unbalanced = baltoks.curly.pop (); |
3778 | else if (baltoks.parens.length ()) |
3779 | unbalanced = baltoks.parens.pop (); |
3780 | else if (baltoks.pointy.length ()) |
3781 | unbalanced = baltoks.pointy.pop (); |
3782 | |
3783 | if (unbalanced) |
3784 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3785 | char_idx: unbalanced - orig_format_chars + 1, |
3786 | opt: OPT_Wformat_diag, |
3787 | gmsgid: "unbalanced punctuation character %<%c%> in format" , |
3788 | *unbalanced); |
3789 | |
3790 | if (baltoks.quotdirs.length ()) |
3791 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3792 | char_idx: baltoks.quotdirs.pop () - orig_format_chars, |
3793 | opt: OPT_Wformat_, |
3794 | gmsgid: "unterminated quoting directive" ); |
3795 | |
3796 | const char *quote |
3797 | = baltoks.singlequote ? baltoks.singlequote : baltoks.doublequote; |
3798 | |
3799 | if (quote) |
3800 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3801 | char_idx: quote - orig_format_chars + 1, |
3802 | opt: OPT_Wformat_diag, |
3803 | gmsgid: "unterminated quote character %<%c%> in format" , |
3804 | *quote); |
3805 | } |
3806 | |
3807 | /* Do the main part of checking a call to a format function. FORMAT_CHARS |
3808 | is the NUL-terminated format string (which at this point may contain |
3809 | internal NUL characters); FORMAT_LENGTH is its length (excluding the |
3810 | terminating NUL character). ARG_NUM is one less than the number of |
3811 | the first format argument to check; PARAMS points to that format |
3812 | argument in the list of arguments. */ |
3813 | |
3814 | static void |
3815 | check_format_info_main (format_check_results *res, |
3816 | function_format_info *info, const char *format_chars, |
3817 | location_t fmt_param_loc, tree format_string_cst, |
3818 | int format_length, tree params, |
3819 | unsigned HOST_WIDE_INT arg_num, |
3820 | object_allocator <format_wanted_type> &fwt_pool, |
3821 | vec<location_t> *arglocs) |
3822 | { |
3823 | const char * const orig_format_chars = format_chars; |
3824 | const tree first_fillin_param = params; |
3825 | |
3826 | const format_kind_info * const fki = &format_types[info->format_type]; |
3827 | const format_flag_spec * const flag_specs = fki->flag_specs; |
3828 | const location_t format_string_loc = res->format_string_loc; |
3829 | |
3830 | /* -1 if no conversions taking an operand have been found; 0 if one has |
3831 | and it didn't use $; 1 if $ formats are in use. */ |
3832 | int has_operand_number = -1; |
3833 | |
3834 | /* Vectors of pointers to opening quoting directives (like GCC "%<"), |
3835 | opening braces, brackets, and parentheses. Used to detect unbalanced |
3836 | tokens. */ |
3837 | baltoks_t baltoks; |
3838 | |
3839 | /* Pointers to the most recent color directives (like GCC's "%r or %R"). |
3840 | A starting color directive much be terminated before the end of |
3841 | the format string. A terminating directive makes no sense without |
3842 | a prior starting directive. */ |
3843 | const char *color_begin = NULL; |
3844 | const char *color_end = NULL; |
3845 | |
3846 | init_dollar_format_checking (first_arg_num: info->first_arg_num, params: first_fillin_param); |
3847 | |
3848 | /* In GCC diagnostic functions check plain directives (substrings within |
3849 | the format string that don't start with %) for quoting and punctuations |
3850 | problems. */ |
3851 | bool ck_plain = (!info->is_raw |
3852 | && (info->format_type == gcc_diag_format_type |
3853 | || info->format_type == gcc_tdiag_format_type |
3854 | || info->format_type == gcc_cdiag_format_type |
3855 | || info->format_type == gcc_cxxdiag_format_type)); |
3856 | |
3857 | while (*format_chars != 0) |
3858 | { |
3859 | if (ck_plain) |
3860 | format_chars = check_plain (format_string_loc, |
3861 | format_string_cst, |
3862 | orig_format_chars, format_chars, |
3863 | baltoks); |
3864 | |
3865 | if (*format_chars == 0 || *format_chars++ != '%') |
3866 | continue; |
3867 | |
3868 | if (*format_chars == 0) |
3869 | { |
3870 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3871 | char_idx: format_chars - orig_format_chars, |
3872 | opt: OPT_Wformat_, |
3873 | gmsgid: "spurious trailing %<%%%> in format" ); |
3874 | continue; |
3875 | } |
3876 | if (*format_chars == '%') |
3877 | { |
3878 | ++format_chars; |
3879 | continue; |
3880 | } |
3881 | |
3882 | /* ARGUMENT_PARSER ctor takes FORMAT_CHARS by reference and calls |
3883 | to ARG_PARSER members may modify the variable. */ |
3884 | flag_chars_t flag_chars; |
3885 | argument_parser arg_parser (info, format_chars, format_string_cst, |
3886 | orig_format_chars, format_string_loc, |
3887 | flag_chars, has_operand_number, |
3888 | first_fillin_param, fwt_pool, arglocs); |
3889 | |
3890 | if (!arg_parser.read_any_dollar ()) |
3891 | return; |
3892 | |
3893 | if (!arg_parser.read_format_flags ()) |
3894 | return; |
3895 | |
3896 | /* Read any format width, possibly * or *m$. */ |
3897 | if (!arg_parser.read_any_format_width (params, arg_num)) |
3898 | return; |
3899 | |
3900 | /* Read any format left precision (must be a number, not *). */ |
3901 | arg_parser.read_any_format_left_precision (); |
3902 | |
3903 | /* Read any format precision, possibly * or *m$. */ |
3904 | if (!arg_parser.read_any_format_precision (params, arg_num)) |
3905 | return; |
3906 | |
3907 | const char *format_start = format_chars; |
3908 | |
3909 | arg_parser.handle_alloc_chars (); |
3910 | |
3911 | /* The rest of the conversion specification is the length modifier |
3912 | (if any), and the conversion specifier, so this is where the |
3913 | type information starts. If we need to issue a suggestion |
3914 | about a type mismatch, then we should preserve everything up |
3915 | to here. */ |
3916 | const char *type_start = format_chars; |
3917 | |
3918 | /* Read any length modifier, if this kind of format has them. */ |
3919 | const length_modifier len_modifier |
3920 | = arg_parser.read_any_length_modifier (); |
3921 | |
3922 | /* Read any modifier (strftime E/O). */ |
3923 | arg_parser.read_any_other_modifier (); |
3924 | |
3925 | char format_char = *format_chars; |
3926 | if (format_char == 0 |
3927 | || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK) |
3928 | && format_char == '%')) |
3929 | { |
3930 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3931 | char_idx: format_chars - orig_format_chars, |
3932 | opt: OPT_Wformat_, |
3933 | gmsgid: "conversion lacks type at end of format" ); |
3934 | continue; |
3935 | } |
3936 | format_chars++; |
3937 | |
3938 | const format_char_info * const fci |
3939 | = arg_parser.find_format_char_info (format_char); |
3940 | if (!fci) |
3941 | continue; |
3942 | |
3943 | flag_chars.validate (fki, fci, flag_specs, format_chars, |
3944 | format_string_cst, |
3945 | format_string_loc, orig_format_chars, format_char, |
3946 | quoted: baltoks.quotdirs.length () > 0); |
3947 | |
3948 | const int alloc_flag = flag_chars.get_alloc_flag (fki); |
3949 | const bool suppressed = flag_chars.assignment_suppression_p (fki); |
3950 | |
3951 | /* Diagnose nested or unmatched quoting directives such as GCC's |
3952 | "%<...%<" and "%>...%>". */ |
3953 | bool quot_begin_p = strchr (s: fci->flags2, c: '<'); |
3954 | bool quot_end_p = strchr (s: fci->flags2, c: '>'); |
3955 | |
3956 | if (quot_begin_p && !quot_end_p) |
3957 | { |
3958 | if (baltoks.quotdirs.length ()) |
3959 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3960 | char_idx: format_chars - orig_format_chars, |
3961 | opt: OPT_Wformat_, |
3962 | gmsgid: "nested quoting directive" ); |
3963 | baltoks.quotdirs.safe_push (obj: format_chars); |
3964 | } |
3965 | else if (!quot_begin_p && quot_end_p) |
3966 | { |
3967 | if (baltoks.quotdirs.length ()) |
3968 | baltoks.quotdirs.pop (); |
3969 | else |
3970 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3971 | char_idx: format_chars - orig_format_chars, |
3972 | opt: OPT_Wformat_, |
3973 | gmsgid: "unmatched quoting directive" ); |
3974 | } |
3975 | |
3976 | bool color_begin_p = strchr (s: fci->flags2, c: '/'); |
3977 | if (color_begin_p) |
3978 | { |
3979 | color_begin = format_chars; |
3980 | color_end = NULL; |
3981 | } |
3982 | else if (strchr (s: fci->flags2, c: '\\')) |
3983 | { |
3984 | if (color_end) |
3985 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3986 | char_idx: format_chars - orig_format_chars, |
3987 | opt: OPT_Wformat_, |
3988 | gmsgid: "%qc directive redundant after prior " |
3989 | "occurence of the same" , format_char); |
3990 | else if (!color_begin) |
3991 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
3992 | char_idx: format_chars - orig_format_chars, |
3993 | opt: OPT_Wformat_, |
3994 | gmsgid: "unmatched color reset directive" ); |
3995 | color_end = format_chars; |
3996 | } |
3997 | |
3998 | /* Diagnose directives that shouldn't appear in a quoted sequence. |
3999 | (They are denoted by a double quote in FLAGS2.) */ |
4000 | if (baltoks.quotdirs.length ()) |
4001 | { |
4002 | if (strchr (s: fci->flags2, c: '"')) |
4003 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
4004 | char_idx: format_chars - orig_format_chars, |
4005 | opt: OPT_Wformat_, |
4006 | gmsgid: "%qc conversion used within a quoted " |
4007 | "sequence" , |
4008 | format_char); |
4009 | } |
4010 | |
4011 | /* Validate the pairs of flags used. */ |
4012 | arg_parser.validate_flag_pairs (fci, format_char); |
4013 | |
4014 | arg_parser.give_y2k_warnings (fci, format_char); |
4015 | |
4016 | arg_parser.parse_any_scan_set (fci); |
4017 | |
4018 | tree wanted_type = NULL; |
4019 | const char *wanted_type_name = NULL; |
4020 | |
4021 | if (!arg_parser.handle_conversions (fci, len_modifier, |
4022 | wanted_type, wanted_type_name, |
4023 | arg_num, |
4024 | params, |
4025 | format_char)) |
4026 | continue; |
4027 | |
4028 | arg_parser.main_wanted_type.next = NULL; |
4029 | |
4030 | /* Finally. . .check type of argument against desired type! */ |
4031 | if (!arg_parser.check_argument_type (fci, len_modifier, |
4032 | wanted_type, wanted_type_name, |
4033 | suppressed, |
4034 | arg_num, params, |
4035 | alloc_flag, |
4036 | format_start, type_start, |
4037 | fmt_param_loc, |
4038 | conversion_char: format_char)) |
4039 | return; |
4040 | } |
4041 | |
4042 | if (format_chars - orig_format_chars != format_length) |
4043 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
4044 | char_idx: format_chars + 1 - orig_format_chars, |
4045 | opt: OPT_Wformat_contains_nul, |
4046 | gmsgid: "embedded %<\\0%> in format" ); |
4047 | if (info->first_arg_num != 0 && params != 0 |
4048 | && has_operand_number <= 0) |
4049 | { |
4050 | res->number_other--; |
4051 | res->number_extra_args++; |
4052 | } |
4053 | if (has_operand_number > 0) |
4054 | finish_dollar_format_checking (res, pointer_gap_ok: fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); |
4055 | |
4056 | maybe_diag_unbalanced_tokens (format_string_loc, orig_format_chars, |
4057 | format_string_cst, baltoks); |
4058 | |
4059 | if (color_begin && !color_end) |
4060 | format_warning_at_char (fmt_string_loc: format_string_loc, format_string_cst, |
4061 | char_idx: color_begin - orig_format_chars, |
4062 | opt: OPT_Wformat_, gmsgid: "unterminated color directive" ); |
4063 | } |
4064 | |
4065 | /* Check the argument types from a single format conversion (possibly |
4066 | including width and precision arguments). |
4067 | |
4068 | FMT_LOC is the location of the format conversion. |
4069 | |
4070 | TYPES is a singly-linked list expressing the parts of the format |
4071 | conversion that expect argument types, and the arguments they |
4072 | correspond to. |
4073 | |
4074 | OFFSET_TO_TYPE_START is the offset within the execution-charset encoded |
4075 | format string to where type information begins for the conversion |
4076 | (the length modifier and conversion specifier). |
4077 | |
4078 | CONVERSION_CHAR is the user-provided conversion specifier. |
4079 | |
4080 | For example, given: |
4081 | |
4082 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4083 | |
4084 | then FMT_LOC covers this range: |
4085 | |
4086 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4087 | ^^^^^^^^^ |
4088 | |
4089 | and TYPES in this case is a three-entry singly-linked list consisting of: |
4090 | (1) the check for the field width here: |
4091 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4092 | ^ ^^^^ |
4093 | against arg3, and |
4094 | (2) the check for the field precision here: |
4095 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4096 | ^^ ^^^^ |
4097 | against arg4, and |
4098 | (3) the check for the length modifier and conversion char here: |
4099 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4100 | ^^^ ^^^^ |
4101 | against arg5. |
4102 | |
4103 | OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the |
4104 | STRING_CST: |
4105 | |
4106 | 0000000000111111111122 |
4107 | 0123456789012345678901 |
4108 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4109 | ^ ^ |
4110 | | ` CONVERSION_CHAR: 'd' |
4111 | type starts here. */ |
4112 | |
4113 | static void |
4114 | check_format_types (const substring_loc &fmt_loc, |
4115 | format_wanted_type *types, const format_kind_info *fki, |
4116 | int offset_to_type_start, |
4117 | char conversion_char, |
4118 | vec<location_t> *arglocs) |
4119 | { |
4120 | for (; types != 0; types = types->next) |
4121 | { |
4122 | tree cur_param; |
4123 | tree cur_type; |
4124 | tree orig_cur_type; |
4125 | tree wanted_type; |
4126 | int arg_num; |
4127 | int i; |
4128 | int char_type_flag; |
4129 | |
4130 | wanted_type = types->wanted_type; |
4131 | arg_num = types->arg_num; |
4132 | |
4133 | /* The following should not occur here. */ |
4134 | gcc_assert (wanted_type); |
4135 | gcc_assert (wanted_type != void_type_node || types->pointer_count); |
4136 | |
4137 | if (types->pointer_count == 0) |
4138 | wanted_type = lang_hooks.types.type_promotes_to (wanted_type); |
4139 | |
4140 | wanted_type = TYPE_MAIN_VARIANT (wanted_type); |
4141 | |
4142 | cur_param = types->param; |
4143 | if (!cur_param) |
4144 | { |
4145 | format_type_warning (fmt_loc, UNKNOWN_LOCATION, types, wanted_type, |
4146 | NULL, fki, offset_to_type_start, |
4147 | conversion_char); |
4148 | continue; |
4149 | } |
4150 | |
4151 | cur_type = TREE_TYPE (cur_param); |
4152 | if (cur_type == error_mark_node) |
4153 | continue; |
4154 | orig_cur_type = cur_type; |
4155 | char_type_flag = 0; |
4156 | |
4157 | location_t param_loc = UNKNOWN_LOCATION; |
4158 | if (EXPR_HAS_LOCATION (cur_param)) |
4159 | param_loc = EXPR_LOCATION (cur_param); |
4160 | else if (arglocs) |
4161 | { |
4162 | /* arg_num is 1-based. */ |
4163 | gcc_assert (types->arg_num > 0); |
4164 | param_loc = (*arglocs)[types->arg_num - 1]; |
4165 | } |
4166 | |
4167 | STRIP_NOPS (cur_param); |
4168 | |
4169 | /* Check the types of any additional pointer arguments |
4170 | that precede the "real" argument. */ |
4171 | for (i = 0; i < types->pointer_count; ++i) |
4172 | { |
4173 | if (TREE_CODE (cur_type) == POINTER_TYPE) |
4174 | { |
4175 | cur_type = TREE_TYPE (cur_type); |
4176 | if (cur_type == error_mark_node) |
4177 | break; |
4178 | |
4179 | /* Check for writing through a NULL pointer. */ |
4180 | if (types->writing_in_flag |
4181 | && i == 0 |
4182 | && cur_param != 0 |
4183 | && integer_zerop (cur_param)) |
4184 | warning (OPT_Wformat_, "writing through null pointer " |
4185 | "(argument %d)" , arg_num); |
4186 | |
4187 | /* Check for reading through a NULL pointer. Ignore |
4188 | printf-family of functions as they are checked for |
4189 | null arguments by the middle-end. */ |
4190 | if (fki->conversion_specs != print_char_table |
4191 | && types->reading_from_flag |
4192 | && i == 0 |
4193 | && cur_param != 0 |
4194 | && integer_zerop (cur_param)) |
4195 | warning (OPT_Wformat_, "reading through null pointer " |
4196 | "(argument %d)" , arg_num); |
4197 | |
4198 | if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR) |
4199 | cur_param = TREE_OPERAND (cur_param, 0); |
4200 | else |
4201 | cur_param = 0; |
4202 | |
4203 | /* See if this is an attempt to write into a const type with |
4204 | scanf or with printf "%n". Note: the writing in happens |
4205 | at the first indirection only, if for example |
4206 | void * const * is passed to scanf %p; passing |
4207 | const void ** is simply passing an incompatible type. */ |
4208 | if (types->writing_in_flag |
4209 | && i == 0 |
4210 | && (TYPE_READONLY (cur_type) |
4211 | || (cur_param != 0 |
4212 | && (CONSTANT_CLASS_P (cur_param) |
4213 | || (DECL_P (cur_param) |
4214 | && TREE_READONLY (cur_param)))))) |
4215 | warning (OPT_Wformat_, "writing into constant object " |
4216 | "(argument %d)" , arg_num); |
4217 | |
4218 | /* If there are extra type qualifiers beyond the first |
4219 | indirection, then this makes the types technically |
4220 | incompatible. */ |
4221 | if (i > 0 |
4222 | && pedantic |
4223 | && (TYPE_READONLY (cur_type) |
4224 | || TYPE_VOLATILE (cur_type) |
4225 | || TYPE_ATOMIC (cur_type) |
4226 | || TYPE_RESTRICT (cur_type))) |
4227 | warning (OPT_Wformat_, "extra type qualifiers in format " |
4228 | "argument (argument %d)" , |
4229 | arg_num); |
4230 | |
4231 | } |
4232 | else |
4233 | { |
4234 | format_type_warning (fmt_loc, param_loc, |
4235 | types, wanted_type, orig_cur_type, fki, |
4236 | offset_to_type_start, conversion_char); |
4237 | break; |
4238 | } |
4239 | } |
4240 | |
4241 | if (i < types->pointer_count) |
4242 | continue; |
4243 | |
4244 | cur_type = TYPE_MAIN_VARIANT (cur_type); |
4245 | |
4246 | /* Check whether the argument type is a character type. This leniency |
4247 | only applies to certain formats, flagged with 'c'. */ |
4248 | if (types->char_lenient_flag) |
4249 | char_type_flag = (cur_type == char_type_node |
4250 | || cur_type == signed_char_type_node |
4251 | || cur_type == unsigned_char_type_node); |
4252 | |
4253 | /* Check the type of the "real" argument, if there's a type we want. */ |
4254 | if (lang_hooks.types_compatible_p (wanted_type, cur_type)) |
4255 | continue; |
4256 | /* If we want 'void *', allow any pointer type. |
4257 | (Anything else would already have got a warning.) |
4258 | With -Wpedantic, only allow pointers to void and to character |
4259 | types. */ |
4260 | if (wanted_type == void_type_node |
4261 | && (!pedantic || (i == 1 && char_type_flag))) |
4262 | continue; |
4263 | /* Don't warn about differences merely in signedness, unless |
4264 | -Wpedantic. With -Wpedantic, warn if the type is a pointer |
4265 | target and not a character type, and for character types at |
4266 | a second level of indirection. */ |
4267 | if (TREE_CODE (wanted_type) == INTEGER_TYPE |
4268 | && TREE_CODE (cur_type) == INTEGER_TYPE |
4269 | && ((!pedantic && !warn_format_signedness) |
4270 | || (i == 0 && !warn_format_signedness) |
4271 | || (i == 1 && char_type_flag)) |
4272 | && (TYPE_UNSIGNED (wanted_type) |
4273 | ? wanted_type == c_common_unsigned_type (cur_type) |
4274 | : wanted_type == c_common_signed_type (cur_type))) |
4275 | continue; |
4276 | /* Don't warn about differences merely in signedness if we know |
4277 | that the current type is integer-promoted and its original type |
4278 | was unsigned such as that it is in the range of WANTED_TYPE. */ |
4279 | if (TREE_CODE (wanted_type) == INTEGER_TYPE |
4280 | && TREE_CODE (cur_type) == INTEGER_TYPE |
4281 | && warn_format_signedness |
4282 | && TYPE_UNSIGNED (wanted_type) |
4283 | && cur_param != NULL_TREE |
4284 | && TREE_CODE (cur_param) == NOP_EXPR) |
4285 | { |
4286 | tree t = TREE_TYPE (TREE_OPERAND (cur_param, 0)); |
4287 | if (TYPE_UNSIGNED (t) |
4288 | && cur_type == lang_hooks.types.type_promotes_to (t)) |
4289 | continue; |
4290 | } |
4291 | /* Likewise, "signed char", "unsigned char" and "char" are |
4292 | equivalent but the above test won't consider them equivalent. */ |
4293 | if (wanted_type == char_type_node |
4294 | && (!pedantic || i < 2) |
4295 | && char_type_flag) |
4296 | continue; |
4297 | if (types->scalar_identity_flag |
4298 | && (TREE_CODE (cur_type) == TREE_CODE (wanted_type) |
4299 | || (INTEGRAL_TYPE_P (cur_type) |
4300 | && INTEGRAL_TYPE_P (wanted_type))) |
4301 | && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)) |
4302 | continue; |
4303 | /* Now we have a type mismatch. */ |
4304 | format_type_warning (fmt_loc, param_loc, types, |
4305 | wanted_type, orig_cur_type, fki, |
4306 | offset_to_type_start, conversion_char); |
4307 | } |
4308 | } |
4309 | |
4310 | /* Given type TYPE, attempt to dereference the type N times |
4311 | (e.g. from ("int ***", 2) to "int *") |
4312 | |
4313 | Return the derefenced type, with any qualifiers |
4314 | such as "const" stripped from the result, or |
4315 | NULL if unsuccessful (e.g. TYPE is not a pointer type). */ |
4316 | |
4317 | static tree |
4318 | deref_n_times (tree type, int n) |
4319 | { |
4320 | gcc_assert (type); |
4321 | |
4322 | for (int i = n; i > 0; i--) |
4323 | { |
4324 | if (TREE_CODE (type) != POINTER_TYPE) |
4325 | return NULL_TREE; |
4326 | type = TREE_TYPE (type); |
4327 | } |
4328 | /* Strip off any "const" etc. */ |
4329 | return build_qualified_type (type, 0); |
4330 | } |
4331 | |
4332 | /* Lookup the format code for FORMAT_LEN within FLI, |
4333 | returning the string code for expressing it, or NULL |
4334 | if it is not found. */ |
4335 | |
4336 | static const char * |
4337 | get_modifier_for_format_len (const format_length_info *fli, |
4338 | enum format_lengths format_len) |
4339 | { |
4340 | for (; fli->name; fli++) |
4341 | { |
4342 | if (fli->index == format_len) |
4343 | return fli->name; |
4344 | if (fli->double_index == format_len) |
4345 | return fli->double_name; |
4346 | } |
4347 | return NULL; |
4348 | } |
4349 | |
4350 | #if CHECKING_P |
4351 | |
4352 | namespace selftest { |
4353 | |
4354 | static void |
4355 | test_get_modifier_for_format_len () |
4356 | { |
4357 | ASSERT_STREQ ("h" , |
4358 | get_modifier_for_format_len (printf_length_specs, FMT_LEN_h)); |
4359 | ASSERT_STREQ ("hh" , |
4360 | get_modifier_for_format_len (printf_length_specs, FMT_LEN_hh)); |
4361 | ASSERT_STREQ ("L" , |
4362 | get_modifier_for_format_len (printf_length_specs, FMT_LEN_L)); |
4363 | ASSERT_EQ (NULL, |
4364 | get_modifier_for_format_len (printf_length_specs, FMT_LEN_none)); |
4365 | } |
4366 | |
4367 | } // namespace selftest |
4368 | |
4369 | #endif /* CHECKING_P */ |
4370 | |
4371 | /* Determine if SPEC_TYPE and ARG_TYPE are sufficiently similar for a |
4372 | format_type_detail using SPEC_TYPE to be offered as a suggestion for |
4373 | Wformat type errors where the argument has type ARG_TYPE. */ |
4374 | |
4375 | static bool |
4376 | matching_type_p (tree spec_type, tree arg_type) |
4377 | { |
4378 | gcc_assert (spec_type); |
4379 | gcc_assert (arg_type); |
4380 | |
4381 | /* If any of the types requires structural equality, we can't compare |
4382 | their canonical types. */ |
4383 | if (TYPE_STRUCTURAL_EQUALITY_P (spec_type) |
4384 | || TYPE_STRUCTURAL_EQUALITY_P (arg_type)) |
4385 | return false; |
4386 | |
4387 | spec_type = TYPE_CANONICAL (spec_type); |
4388 | arg_type = TYPE_CANONICAL (arg_type); |
4389 | |
4390 | if (TREE_CODE (spec_type) == INTEGER_TYPE |
4391 | && TREE_CODE (arg_type) == INTEGER_TYPE |
4392 | && (TYPE_UNSIGNED (spec_type) |
4393 | ? spec_type == c_common_unsigned_type (arg_type) |
4394 | : spec_type == c_common_signed_type (arg_type))) |
4395 | return true; |
4396 | |
4397 | return spec_type == arg_type; |
4398 | } |
4399 | |
4400 | /* Subroutine of get_format_for_type. |
4401 | |
4402 | Generate a string containing the length modifier and conversion specifier |
4403 | that should be used to format arguments of type ARG_TYPE within FKI |
4404 | (effectively the inverse of the checking code). |
4405 | |
4406 | If CONVERSION_CHAR is not zero (the first pass), the resulting suggestion |
4407 | is required to use it, for correcting bogus length modifiers. |
4408 | If CONVERSION_CHAR is zero (the second pass), then allow any suggestion |
4409 | that matches ARG_TYPE. |
4410 | |
4411 | If successful, returns a non-NULL string which should be freed |
4412 | by the caller. |
4413 | Otherwise, returns NULL. */ |
4414 | |
4415 | static char * |
4416 | get_format_for_type_1 (const format_kind_info *fki, tree arg_type, |
4417 | char conversion_char) |
4418 | { |
4419 | gcc_assert (arg_type); |
4420 | |
4421 | const format_char_info *spec; |
4422 | for (spec = &fki->conversion_specs[0]; |
4423 | spec->format_chars; |
4424 | spec++) |
4425 | { |
4426 | if (conversion_char) |
4427 | if (!strchr (s: spec->format_chars, c: conversion_char)) |
4428 | continue; |
4429 | |
4430 | tree effective_arg_type = deref_n_times (type: arg_type, |
4431 | n: spec->pointer_count); |
4432 | if (!effective_arg_type) |
4433 | continue; |
4434 | for (int i = 0; i < FMT_LEN_MAX; i++) |
4435 | { |
4436 | const format_type_detail *ftd = &spec->types[i]; |
4437 | if (!ftd->type || *ftd->type == NULL_TREE) |
4438 | continue; |
4439 | if (matching_type_p (spec_type: *ftd->type, arg_type: effective_arg_type)) |
4440 | { |
4441 | const char *len_modifier |
4442 | = get_modifier_for_format_len (fli: fki->length_char_specs, |
4443 | format_len: (enum format_lengths)i); |
4444 | if (!len_modifier) |
4445 | len_modifier = "" ; |
4446 | |
4447 | if (conversion_char) |
4448 | /* We found a match, using the given conversion char - the |
4449 | length modifier was incorrect (or absent). |
4450 | Provide a suggestion using the conversion char with the |
4451 | correct length modifier for the type. */ |
4452 | return xasprintf ("%s%c" , len_modifier, conversion_char); |
4453 | else |
4454 | /* 2nd pass: no match was possible using the user-provided |
4455 | conversion char, but we do have a match without using it. |
4456 | Provide a suggestion using the first conversion char |
4457 | listed for the given type. */ |
4458 | return xasprintf ("%s%c" , len_modifier, spec->format_chars[0]); |
4459 | } |
4460 | } |
4461 | } |
4462 | |
4463 | return NULL; |
4464 | } |
4465 | |
4466 | /* Generate a string containing the length modifier and conversion specifier |
4467 | that should be used to format arguments of type ARG_TYPE within FKI |
4468 | (effectively the inverse of the checking code). |
4469 | |
4470 | If successful, returns a non-NULL string which should be freed |
4471 | by the caller. |
4472 | Otherwise, returns NULL. */ |
4473 | |
4474 | static char * |
4475 | get_format_for_type (const format_kind_info *fki, tree arg_type, |
4476 | char conversion_char) |
4477 | { |
4478 | gcc_assert (arg_type); |
4479 | gcc_assert (conversion_char); |
4480 | |
4481 | /* First pass: look for a format_char_info containing CONVERSION_CHAR |
4482 | If we find one, then presumably the length modifier was incorrect |
4483 | (or absent). */ |
4484 | char *result = get_format_for_type_1 (fki, arg_type, conversion_char); |
4485 | if (result) |
4486 | return result; |
4487 | |
4488 | /* Second pass: we didn't find a match for CONVERSION_CHAR, so try |
4489 | matching just on the type. */ |
4490 | return get_format_for_type_1 (fki, arg_type, conversion_char: '\0'); |
4491 | } |
4492 | |
4493 | /* Attempt to get a string for use as a replacement fix-it hint for the |
4494 | source range in FMT_LOC. |
4495 | |
4496 | Preserve all of the text within the range of FMT_LOC up to |
4497 | OFFSET_TO_TYPE_START, replacing the rest with an appropriate |
4498 | length modifier and conversion specifier for ARG_TYPE, attempting |
4499 | to keep the user-provided CONVERSION_CHAR if possible. |
4500 | |
4501 | For example, given a long vs long long mismatch for arg5 here: |
4502 | |
4503 | 000000000111111111122222222223333333333| |
4504 | 123456789012345678901234567890123456789` column numbers |
4505 | 0000000000111111111122| |
4506 | 0123456789012345678901` string offsets |
4507 | V~~~~~~~~ : range of FMT_LOC, from cols 23-31 |
4508 | sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5); |
4509 | ^ ^ |
4510 | | ` CONVERSION_CHAR: 'd' |
4511 | type starts here |
4512 | |
4513 | where OFFSET_TO_TYPE_START is 13 (the offset to the "lld" within the |
4514 | STRING_CST), where the user provided: |
4515 | %-+*.*lld |
4516 | the result (assuming "long" argument 5) should be: |
4517 | %-+*.*ld |
4518 | |
4519 | If successful, returns a non-NULL string which should be freed |
4520 | by the caller. |
4521 | Otherwise, returns NULL. */ |
4522 | |
4523 | static char * |
4524 | get_corrected_substring (const substring_loc &fmt_loc, |
4525 | format_wanted_type *type, tree arg_type, |
4526 | const format_kind_info *fki, |
4527 | int offset_to_type_start, char conversion_char) |
4528 | { |
4529 | /* Attempt to provide hints for argument types, but not for field widths |
4530 | and precisions. */ |
4531 | if (!arg_type) |
4532 | return NULL; |
4533 | if (type->kind != CF_KIND_FORMAT) |
4534 | return NULL; |
4535 | |
4536 | /* Locate the current code within the source range, rejecting |
4537 | any awkward cases where the format string occupies more than |
4538 | one line. |
4539 | Lookup the place where the type starts (including any length |
4540 | modifiers), getting it as the caret location. */ |
4541 | substring_loc type_loc (fmt_loc); |
4542 | type_loc.set_caret_index (offset_to_type_start); |
4543 | |
4544 | location_t fmt_substring_loc; |
4545 | const char *err = type_loc.get_location (out_loc: &fmt_substring_loc); |
4546 | if (err) |
4547 | return NULL; |
4548 | |
4549 | source_range fmt_substring_range |
4550 | = get_range_from_loc (set: line_table, loc: fmt_substring_loc); |
4551 | |
4552 | expanded_location caret |
4553 | = expand_location_to_spelling_point (fmt_substring_loc); |
4554 | expanded_location start |
4555 | = expand_location_to_spelling_point (fmt_substring_range.m_start); |
4556 | expanded_location finish |
4557 | = expand_location_to_spelling_point (fmt_substring_range.m_finish); |
4558 | if (caret.file != start.file) |
4559 | return NULL; |
4560 | if (start.file != finish.file) |
4561 | return NULL; |
4562 | if (caret.line != start.line) |
4563 | return NULL; |
4564 | if (start.line != finish.line) |
4565 | return NULL; |
4566 | if (start.column > caret.column) |
4567 | return NULL; |
4568 | if (start.column > finish.column) |
4569 | return NULL; |
4570 | if (caret.column > finish.column) |
4571 | return NULL; |
4572 | |
4573 | char_span line = location_get_source_line (file_path: start.file, line: start.line); |
4574 | if (!line) |
4575 | return NULL; |
4576 | |
4577 | /* If we got this far, then we have the line containing the |
4578 | existing conversion specification. |
4579 | |
4580 | Generate a trimmed copy, containing the prefix part of the conversion |
4581 | specification, up to the (but not including) the length modifier. |
4582 | In the above example, this would be "%-+*.*". */ |
4583 | int length_up_to_type = caret.column - start.column; |
4584 | char_span prefix_span = line.subspan (offset: start.column - 1, n_elts: length_up_to_type); |
4585 | char *prefix = prefix_span.xstrdup (); |
4586 | |
4587 | /* Now attempt to generate a suggestion for the rest of the specification |
4588 | (length modifier and conversion char), based on ARG_TYPE and |
4589 | CONVERSION_CHAR. |
4590 | In the above example, this would be "ld". */ |
4591 | char *format_for_type = get_format_for_type (fki, arg_type, conversion_char); |
4592 | if (!format_for_type) |
4593 | { |
4594 | free (ptr: prefix); |
4595 | return NULL; |
4596 | } |
4597 | |
4598 | /* Success. Generate the resulting suggestion for the whole range of |
4599 | FMT_LOC by concatenating the two strings. |
4600 | In the above example, this would be "%-+*.*ld". */ |
4601 | char *result = concat (prefix, format_for_type, NULL); |
4602 | free (ptr: format_for_type); |
4603 | free (ptr: prefix); |
4604 | return result; |
4605 | } |
4606 | |
4607 | /* Helper class for adding zero or more trailing '*' to types. |
4608 | |
4609 | The format type and name exclude any '*' for pointers, so those |
4610 | must be formatted manually. For all the types we currently have, |
4611 | this is adequate, but formats taking pointers to functions or |
4612 | arrays would require the full type to be built up in order to |
4613 | print it with %T. */ |
4614 | |
4615 | class indirection_suffix |
4616 | { |
4617 | public: |
4618 | indirection_suffix (int pointer_count) : m_pointer_count (pointer_count) {} |
4619 | |
4620 | /* Determine the size of the buffer (including NUL-terminator). */ |
4621 | |
4622 | size_t get_buffer_size () const |
4623 | { |
4624 | return m_pointer_count + 2; |
4625 | } |
4626 | |
4627 | /* Write the '*' to DST and add a NUL-terminator. */ |
4628 | |
4629 | void fill_buffer (char *dst) const |
4630 | { |
4631 | if (m_pointer_count == 0) |
4632 | dst[0] = 0; |
4633 | else if (c_dialect_cxx ()) |
4634 | { |
4635 | memset (s: dst, c: '*', n: m_pointer_count); |
4636 | dst[m_pointer_count] = 0; |
4637 | } |
4638 | else |
4639 | { |
4640 | dst[0] = ' '; |
4641 | memset (s: dst + 1, c: '*', n: m_pointer_count); |
4642 | dst[m_pointer_count + 1] = 0; |
4643 | } |
4644 | } |
4645 | |
4646 | private: |
4647 | int m_pointer_count; |
4648 | }; |
4649 | |
4650 | /* Subclass of range_label for labelling the range in the format string |
4651 | with the type in question, adding trailing '*' for pointer_count. */ |
4652 | |
4653 | class range_label_for_format_type_mismatch |
4654 | : public range_label_for_type_mismatch |
4655 | { |
4656 | public: |
4657 | range_label_for_format_type_mismatch (tree labelled_type, tree other_type, |
4658 | int pointer_count) |
4659 | : range_label_for_type_mismatch (labelled_type, other_type), |
4660 | m_pointer_count (pointer_count) |
4661 | { |
4662 | } |
4663 | |
4664 | label_text get_text (unsigned range_idx) const final override |
4665 | { |
4666 | label_text text = range_label_for_type_mismatch::get_text (range_idx); |
4667 | if (text.get () == NULL) |
4668 | return text; |
4669 | |
4670 | indirection_suffix suffix (m_pointer_count); |
4671 | char *p = (char *) alloca (suffix.get_buffer_size ()); |
4672 | suffix.fill_buffer (dst: p); |
4673 | |
4674 | char *result = concat (text.get (), p, NULL); |
4675 | return label_text::take (buffer: result); |
4676 | } |
4677 | |
4678 | private: |
4679 | int m_pointer_count; |
4680 | }; |
4681 | |
4682 | /* Give a warning about a format argument of different type from that expected. |
4683 | The range of the diagnostic is taken from WHOLE_FMT_LOC; the caret location |
4684 | is based on the location of the char at TYPE->offset_loc. |
4685 | PARAM_LOC is the location of the relevant argument, or UNKNOWN_LOCATION |
4686 | if this is unavailable. |
4687 | WANTED_TYPE is the type the argument should have, |
4688 | possibly stripped of pointer dereferences. The description (such as "field |
4689 | precision"), the placement in the format string, a possibly more |
4690 | friendly name of WANTED_TYPE, and the number of pointer dereferences |
4691 | are taken from TYPE. ARG_TYPE is the type of the actual argument, |
4692 | or NULL if it is missing. |
4693 | |
4694 | OFFSET_TO_TYPE_START is the offset within the execution-charset encoded |
4695 | format string to where type information begins for the conversion |
4696 | (the length modifier and conversion specifier). |
4697 | CONVERSION_CHAR is the user-provided conversion specifier. |
4698 | |
4699 | For example, given a type mismatch for argument 5 here: |
4700 | |
4701 | 00000000011111111112222222222333333333344444444445555555555| |
4702 | 12345678901234567890123456789012345678901234567890123456789` column numbers |
4703 | 0000000000111111111122| |
4704 | 0123456789012345678901` offsets within STRING_CST |
4705 | V~~~~~~~~ : range of WHOLE_FMT_LOC, from cols 23-31 |
4706 | sprintf (d, "before %-+*.*lld after", int_expr, int_expr, long_expr); |
4707 | ^ ^ ^~~~~~~~~ |
4708 | | ` CONVERSION_CHAR: 'd' PARAM_LOC |
4709 | type starts here |
4710 | |
4711 | OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the |
4712 | STRING_CST. */ |
4713 | |
4714 | static void |
4715 | format_type_warning (const substring_loc &whole_fmt_loc, |
4716 | location_t param_loc, |
4717 | format_wanted_type *type, |
4718 | tree wanted_type, tree arg_type, |
4719 | const format_kind_info *fki, |
4720 | int offset_to_type_start, |
4721 | char conversion_char) |
4722 | { |
4723 | enum format_specifier_kind kind = type->kind; |
4724 | const char *wanted_type_name = type->wanted_type_name; |
4725 | const char *format_start = type->format_start; |
4726 | int format_length = type->format_length; |
4727 | int pointer_count = type->pointer_count; |
4728 | int arg_num = type->arg_num; |
4729 | |
4730 | /* If ARG_TYPE is a typedef with a misleading name (for example, |
4731 | size_t but not the standard size_t expected by printf %zu), avoid |
4732 | printing the typedef name. */ |
4733 | if (wanted_type_name |
4734 | && arg_type |
4735 | && TYPE_NAME (arg_type) |
4736 | && TREE_CODE (TYPE_NAME (arg_type)) == TYPE_DECL |
4737 | && DECL_NAME (TYPE_NAME (arg_type)) |
4738 | && !strcmp (s1: wanted_type_name, |
4739 | s2: lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2))) |
4740 | arg_type = TYPE_MAIN_VARIANT (arg_type); |
4741 | |
4742 | indirection_suffix suffix (pointer_count); |
4743 | char *p = (char *) alloca (suffix.get_buffer_size ()); |
4744 | suffix.fill_buffer (dst: p); |
4745 | |
4746 | /* WHOLE_FMT_LOC has the caret at the end of the range. |
4747 | Set the caret to be at the offset from TYPE. Subtract one |
4748 | from the offset for the same reason as in format_warning_at_char. */ |
4749 | substring_loc fmt_loc (whole_fmt_loc); |
4750 | fmt_loc.set_caret_index (type->offset_loc - 1); |
4751 | |
4752 | range_label_for_format_type_mismatch fmt_label (wanted_type, arg_type, |
4753 | pointer_count); |
4754 | range_label_for_type_mismatch param_label (arg_type, wanted_type); |
4755 | |
4756 | /* Get a string for use as a replacement fix-it hint for the range in |
4757 | fmt_loc, or NULL. */ |
4758 | char *corrected_substring |
4759 | = get_corrected_substring (fmt_loc, type, arg_type, fki, |
4760 | offset_to_type_start, conversion_char); |
4761 | format_string_diagnostic_t diag (fmt_loc, &fmt_label, param_loc, ¶m_label, |
4762 | corrected_substring); |
4763 | if (wanted_type_name) |
4764 | { |
4765 | if (arg_type) |
4766 | diag.emit_warning |
4767 | (opt: OPT_Wformat_, |
4768 | gmsgid: "%s %<%s%.*s%> expects argument of type %<%s%s%>, " |
4769 | "but argument %d has type %qT" , |
4770 | gettext (msgid: kind_descriptions[kind]), |
4771 | (kind == CF_KIND_FORMAT ? "%" : "" ), |
4772 | format_length, format_start, |
4773 | wanted_type_name, p, arg_num, arg_type); |
4774 | else |
4775 | diag.emit_warning |
4776 | (opt: OPT_Wformat_, |
4777 | gmsgid: "%s %<%s%.*s%> expects a matching %<%s%s%> argument" , |
4778 | gettext (msgid: kind_descriptions[kind]), |
4779 | (kind == CF_KIND_FORMAT ? "%" : "" ), |
4780 | format_length, format_start, wanted_type_name, p); |
4781 | } |
4782 | else |
4783 | { |
4784 | if (arg_type) |
4785 | diag.emit_warning |
4786 | (opt: OPT_Wformat_, |
4787 | gmsgid: "%s %<%s%.*s%> expects argument of type %<%T%s%>, " |
4788 | "but argument %d has type %qT" , |
4789 | gettext (msgid: kind_descriptions[kind]), |
4790 | (kind == CF_KIND_FORMAT ? "%" : "" ), |
4791 | format_length, format_start, |
4792 | wanted_type, p, arg_num, arg_type); |
4793 | else |
4794 | diag.emit_warning |
4795 | (opt: OPT_Wformat_, |
4796 | gmsgid: "%s %<%s%.*s%> expects a matching %<%T%s%> argument" , |
4797 | gettext (msgid: kind_descriptions[kind]), |
4798 | (kind == CF_KIND_FORMAT ? "%" : "" ), |
4799 | format_length, format_start, wanted_type, p); |
4800 | } |
4801 | |
4802 | free (ptr: corrected_substring); |
4803 | } |
4804 | |
4805 | |
4806 | /* Given a format_char_info array FCI, and a character C, this function |
4807 | returns the index into the conversion_specs where that specifier's |
4808 | data is located. The character must exist. */ |
4809 | static unsigned int |
4810 | find_char_info_specifier_index (const format_char_info *fci, int c) |
4811 | { |
4812 | unsigned i; |
4813 | |
4814 | for (i = 0; fci->format_chars; i++, fci++) |
4815 | if (strchr (s: fci->format_chars, c: c)) |
4816 | return i; |
4817 | |
4818 | /* We shouldn't be looking for a non-existent specifier. */ |
4819 | gcc_unreachable (); |
4820 | } |
4821 | |
4822 | /* Given a format_length_info array FLI, and a character C, this |
4823 | function returns the index into the conversion_specs where that |
4824 | modifier's data is located. The character must exist. */ |
4825 | static unsigned int |
4826 | find_length_info_modifier_index (const format_length_info *fli, int c) |
4827 | { |
4828 | unsigned i; |
4829 | |
4830 | for (i = 0; fli->name; i++, fli++) |
4831 | if (strchr (s: fli->name, c: c)) |
4832 | return i; |
4833 | |
4834 | /* We shouldn't be looking for a non-existent modifier. */ |
4835 | gcc_unreachable (); |
4836 | } |
4837 | |
4838 | /* Determine the type of HOST_WIDE_INT in the code being compiled for |
4839 | use in GCC's __asm_fprintf__ custom format attribute. You must |
4840 | have set dynamic_format_types before calling this function. */ |
4841 | static void |
4842 | init_dynamic_asm_fprintf_info (void) |
4843 | { |
4844 | static tree hwi; |
4845 | |
4846 | if (!hwi) |
4847 | { |
4848 | format_length_info *new_asm_fprintf_length_specs; |
4849 | unsigned int i; |
4850 | |
4851 | /* Find the underlying type for HOST_WIDE_INT. For the %w |
4852 | length modifier to work, one must have issued: "typedef |
4853 | HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code |
4854 | prior to using that modifier. */ |
4855 | hwi = maybe_get_identifier ("__gcc_host_wide_int__" ); |
4856 | if (!hwi) |
4857 | { |
4858 | error ("%<__gcc_host_wide_int__%> is not defined as a type" ); |
4859 | return; |
4860 | } |
4861 | hwi = identifier_global_value (hwi); |
4862 | if (!hwi || TREE_CODE (hwi) != TYPE_DECL) |
4863 | { |
4864 | error ("%<__gcc_host_wide_int__%> is not defined as a type" ); |
4865 | return; |
4866 | } |
4867 | hwi = DECL_ORIGINAL_TYPE (hwi); |
4868 | gcc_assert (hwi); |
4869 | if (hwi != long_integer_type_node && hwi != long_long_integer_type_node) |
4870 | { |
4871 | error ("%<__gcc_host_wide_int__%> is not defined as %<long%>" |
4872 | " or %<long long%>" ); |
4873 | return; |
4874 | } |
4875 | |
4876 | /* Create a new (writable) copy of asm_fprintf_length_specs. */ |
4877 | new_asm_fprintf_length_specs = (format_length_info *) |
4878 | xmemdup (asm_fprintf_length_specs, |
4879 | sizeof (asm_fprintf_length_specs), |
4880 | sizeof (asm_fprintf_length_specs)); |
4881 | |
4882 | /* HOST_WIDE_INT must be one of 'long' or 'long long'. */ |
4883 | i = find_length_info_modifier_index (fli: new_asm_fprintf_length_specs, c: 'w'); |
4884 | if (hwi == long_integer_type_node) |
4885 | new_asm_fprintf_length_specs[i].index = FMT_LEN_l; |
4886 | else if (hwi == long_long_integer_type_node) |
4887 | new_asm_fprintf_length_specs[i].index = FMT_LEN_ll; |
4888 | else |
4889 | gcc_unreachable (); |
4890 | |
4891 | /* Assign the new data for use. */ |
4892 | dynamic_format_types[asm_fprintf_format_type].length_char_specs = |
4893 | new_asm_fprintf_length_specs; |
4894 | } |
4895 | } |
4896 | |
4897 | static const format_length_info* |
4898 | get_init_dynamic_hwi (void) |
4899 | { |
4900 | static tree hwi; |
4901 | static format_length_info *diag_ls; |
4902 | |
4903 | if (!hwi) |
4904 | { |
4905 | unsigned int i; |
4906 | |
4907 | /* Find the underlying type for HOST_WIDE_INT. For the 'w' |
4908 | length modifier to work, one must have issued: "typedef |
4909 | HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code |
4910 | prior to using that modifier. */ |
4911 | if ((hwi = maybe_get_identifier ("__gcc_host_wide_int__" ))) |
4912 | { |
4913 | hwi = identifier_global_value (hwi); |
4914 | if (hwi) |
4915 | { |
4916 | if (TREE_CODE (hwi) != TYPE_DECL) |
4917 | { |
4918 | error ("%<__gcc_host_wide_int__%> is not defined as a type" ); |
4919 | hwi = 0; |
4920 | } |
4921 | else |
4922 | { |
4923 | hwi = DECL_ORIGINAL_TYPE (hwi); |
4924 | gcc_assert (hwi); |
4925 | if (hwi != long_integer_type_node |
4926 | && hwi != long_long_integer_type_node) |
4927 | { |
4928 | error ("%<__gcc_host_wide_int__%> is not defined" |
4929 | " as %<long%> or %<long long%>" ); |
4930 | hwi = 0; |
4931 | } |
4932 | } |
4933 | } |
4934 | } |
4935 | if (!diag_ls) |
4936 | diag_ls = (format_length_info *) |
4937 | xmemdup (gcc_diag_length_specs, |
4938 | sizeof (gcc_diag_length_specs), |
4939 | sizeof (gcc_diag_length_specs)); |
4940 | if (hwi) |
4941 | { |
4942 | /* HOST_WIDE_INT must be one of 'long' or 'long long'. */ |
4943 | i = find_length_info_modifier_index (fli: diag_ls, c: 'w'); |
4944 | if (hwi == long_integer_type_node) |
4945 | diag_ls[i].index = FMT_LEN_l; |
4946 | else if (hwi == long_long_integer_type_node) |
4947 | diag_ls[i].index = FMT_LEN_ll; |
4948 | else |
4949 | gcc_unreachable (); |
4950 | } |
4951 | } |
4952 | return diag_ls; |
4953 | } |
4954 | |
4955 | /* Determine the type of a "locus" in the code being compiled for use |
4956 | in GCC's __gcc_gfc__ custom format attribute. You must have set |
4957 | dynamic_format_types before calling this function. */ |
4958 | static void |
4959 | init_dynamic_gfc_info (void) |
4960 | { |
4961 | dynamic_format_types[gcc_gfc_format_type].length_char_specs |
4962 | = get_init_dynamic_hwi (); |
4963 | |
4964 | if (!locus) |
4965 | { |
4966 | static format_char_info *gfc_fci; |
4967 | |
4968 | /* For the GCC __gcc_gfc__ custom format specifier to work, one |
4969 | must have declared 'locus' prior to using this attribute. If |
4970 | we haven't seen this declarations then you shouldn't use the |
4971 | specifier requiring that type. */ |
4972 | if ((locus = maybe_get_identifier ("locus" ))) |
4973 | { |
4974 | locus = identifier_global_value (locus); |
4975 | if (locus) |
4976 | { |
4977 | if (TREE_CODE (locus) != TYPE_DECL |
4978 | || TREE_TYPE (locus) == error_mark_node) |
4979 | { |
4980 | error ("%<locus%> is not defined as a type" ); |
4981 | locus = 0; |
4982 | } |
4983 | else |
4984 | locus = TREE_TYPE (locus); |
4985 | } |
4986 | } |
4987 | |
4988 | /* Assign the new data for use. */ |
4989 | |
4990 | /* Handle the __gcc_gfc__ format specifics. */ |
4991 | if (!gfc_fci) |
4992 | dynamic_format_types[gcc_gfc_format_type].conversion_specs = |
4993 | gfc_fci = (format_char_info *) |
4994 | xmemdup (gcc_gfc_char_table, |
4995 | sizeof (gcc_gfc_char_table), |
4996 | sizeof (gcc_gfc_char_table)); |
4997 | if (locus) |
4998 | { |
4999 | const unsigned i = find_char_info_specifier_index (fci: gfc_fci, c: 'L'); |
5000 | gfc_fci[i].types[0].type = &locus; |
5001 | gfc_fci[i].pointer_count = 1; |
5002 | } |
5003 | } |
5004 | } |
5005 | |
5006 | /* Lookup the type named NAME and return a NAME type if found. |
5007 | Otherwise, return void_type_node if NAME has not been used yet, |
5008 | or NULL_TREE if NAME is not a type (issuing an error). */ |
5009 | |
5010 | static tree |
5011 | get_named_type (const char *name) |
5012 | { |
5013 | if (tree result = maybe_get_identifier (name)) |
5014 | { |
5015 | result = identifier_global_tag (result); |
5016 | if (result) |
5017 | { |
5018 | if (TYPE_P (result)) |
5019 | ; |
5020 | else if (TREE_CODE (result) == TYPE_DECL) |
5021 | result = TREE_TYPE (result); |
5022 | else |
5023 | { |
5024 | error ("%qs is not defined as a type" , name); |
5025 | result = NULL_TREE; |
5026 | } |
5027 | } |
5028 | return result; |
5029 | } |
5030 | else |
5031 | return void_type_node; |
5032 | } |
5033 | |
5034 | /* Determine the types of "tree" and "location_t" in the code being |
5035 | compiled for use in GCC's diagnostic custom format attributes. You |
5036 | must have set dynamic_format_types before calling this function. */ |
5037 | static void |
5038 | init_dynamic_diag_info (void) |
5039 | { |
5040 | /* For the GCC-diagnostics custom format specifiers to work, one |
5041 | must have declared 'tree' and 'location_t' prior to using those |
5042 | attributes. If we haven't seen these declarations then |
5043 | the specifiers requiring these types shouldn't be used. |
5044 | However we don't force a hard ICE because we may see only one |
5045 | or the other type. */ |
5046 | if (tree loc = maybe_get_identifier ("location_t" )) |
5047 | { |
5048 | loc = identifier_global_value (loc); |
5049 | if (loc && TREE_CODE (loc) != TYPE_DECL) |
5050 | error ("%<location_t%> is not defined as a type" ); |
5051 | } |
5052 | |
5053 | /* Initialize the global tree node type local to this file. */ |
5054 | if (!local_tree_type_node |
5055 | || local_tree_type_node == void_type_node) |
5056 | { |
5057 | /* We need to grab the underlying 'union tree_node' so peek into |
5058 | an extra type level. */ |
5059 | if ((local_tree_type_node = maybe_get_identifier ("tree" ))) |
5060 | { |
5061 | local_tree_type_node |
5062 | = identifier_global_value (local_tree_type_node); |
5063 | if (local_tree_type_node) |
5064 | { |
5065 | if (TREE_CODE (local_tree_type_node) != TYPE_DECL) |
5066 | { |
5067 | error ("%<tree%> is not defined as a type" ); |
5068 | local_tree_type_node = NULL_TREE; |
5069 | } |
5070 | else if (TREE_CODE (TREE_TYPE (local_tree_type_node)) |
5071 | != POINTER_TYPE) |
5072 | { |
5073 | error ("%<tree%> is not defined as a pointer type" ); |
5074 | local_tree_type_node = NULL_TREE; |
5075 | } |
5076 | else |
5077 | local_tree_type_node |
5078 | = TREE_TYPE (TREE_TYPE (local_tree_type_node)); |
5079 | } |
5080 | } |
5081 | else |
5082 | local_tree_type_node = void_type_node; |
5083 | } |
5084 | |
5085 | /* Similar to the above but for gimple*. */ |
5086 | if (!local_gimple_ptr_node |
5087 | || local_gimple_ptr_node == void_type_node) |
5088 | local_gimple_ptr_node = get_named_type (name: "gimple" ); |
5089 | |
5090 | /* Similar to the above but for cgraph_node*. */ |
5091 | if (!local_cgraph_node_ptr_node |
5092 | || local_cgraph_node_ptr_node == void_type_node) |
5093 | local_cgraph_node_ptr_node = get_named_type (name: "cgraph_node" ); |
5094 | |
5095 | /* Similar to the above but for diagnostic_event_id_t*. */ |
5096 | if (!local_event_ptr_node |
5097 | || local_event_ptr_node == void_type_node) |
5098 | local_event_ptr_node = get_named_type (name: "diagnostic_event_id_t" ); |
5099 | |
5100 | /* All the GCC diag formats use the same length specs. */ |
5101 | dynamic_format_types[gcc_diag_format_type].length_char_specs = |
5102 | dynamic_format_types[gcc_tdiag_format_type].length_char_specs = |
5103 | dynamic_format_types[gcc_cdiag_format_type].length_char_specs = |
5104 | dynamic_format_types[gcc_cxxdiag_format_type].length_char_specs = |
5105 | dynamic_format_types[gcc_dump_printf_format_type].length_char_specs |
5106 | = get_init_dynamic_hwi (); |
5107 | |
5108 | /* It's safe to "re-initialize these to the same values. */ |
5109 | dynamic_format_types[gcc_diag_format_type].conversion_specs = |
5110 | gcc_diag_char_table; |
5111 | dynamic_format_types[gcc_tdiag_format_type].conversion_specs = |
5112 | gcc_tdiag_char_table; |
5113 | dynamic_format_types[gcc_cdiag_format_type].conversion_specs = |
5114 | gcc_cdiag_char_table; |
5115 | dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs = |
5116 | gcc_cxxdiag_char_table; |
5117 | dynamic_format_types[gcc_dump_printf_format_type].conversion_specs = |
5118 | gcc_dump_printf_char_table; |
5119 | } |
5120 | |
5121 | #ifdef TARGET_FORMAT_TYPES |
5122 | extern const format_kind_info TARGET_FORMAT_TYPES[]; |
5123 | #endif |
5124 | |
5125 | #ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES |
5126 | extern const target_ovr_attr TARGET_OVERRIDES_FORMAT_ATTRIBUTES[]; |
5127 | #endif |
5128 | #ifdef TARGET_OVERRIDES_FORMAT_INIT |
5129 | extern void TARGET_OVERRIDES_FORMAT_INIT (void); |
5130 | #endif |
5131 | |
5132 | /* Attributes such as "printf" are equivalent to those such as |
5133 | "gnu_printf" unless this is overridden by a target. */ |
5134 | static const target_ovr_attr gnu_target_overrides_format_attributes[] = |
5135 | { |
5136 | { .named_attr_src: "gnu_printf" , .named_attr_dst: "printf" }, |
5137 | { .named_attr_src: "gnu_scanf" , .named_attr_dst: "scanf" }, |
5138 | { .named_attr_src: "gnu_strftime" , .named_attr_dst: "strftime" }, |
5139 | { .named_attr_src: "gnu_strfmon" , .named_attr_dst: "strfmon" }, |
5140 | { NULL, NULL } |
5141 | }; |
5142 | |
5143 | /* Translate to unified attribute name. This is used in decode_format_type and |
5144 | decode_format_attr. In attr_name the user specified argument is passed. It |
5145 | returns the unified format name from TARGET_OVERRIDES_FORMAT_ATTRIBUTES |
5146 | or the attr_name passed to this function, if there is no matching entry. */ |
5147 | static const char * |
5148 | convert_format_name_to_system_name (const char *attr_name) |
5149 | { |
5150 | int i; |
5151 | |
5152 | if (attr_name == NULL || *attr_name == 0 |
5153 | || startswith (str: attr_name, prefix: "gcc_" )) |
5154 | return attr_name; |
5155 | #ifdef TARGET_OVERRIDES_FORMAT_INIT |
5156 | TARGET_OVERRIDES_FORMAT_INIT (); |
5157 | #endif |
5158 | |
5159 | #ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES |
5160 | /* Check if format attribute is overridden by target. */ |
5161 | if (TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT > 0) |
5162 | { |
5163 | for (i = 0; i < TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT; ++i) |
5164 | { |
5165 | if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src, |
5166 | attr_name)) |
5167 | return attr_name; |
5168 | if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_dst, |
5169 | attr_name)) |
5170 | return TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src; |
5171 | } |
5172 | } |
5173 | #endif |
5174 | /* Otherwise default to gnu format. */ |
5175 | for (i = 0; |
5176 | gnu_target_overrides_format_attributes[i].named_attr_src != NULL; |
5177 | ++i) |
5178 | { |
5179 | if (cmp_attribs (attr1: gnu_target_overrides_format_attributes[i].named_attr_src, |
5180 | attr2: attr_name)) |
5181 | return attr_name; |
5182 | if (cmp_attribs (attr1: gnu_target_overrides_format_attributes[i].named_attr_dst, |
5183 | attr2: attr_name)) |
5184 | return gnu_target_overrides_format_attributes[i].named_attr_src; |
5185 | } |
5186 | |
5187 | return attr_name; |
5188 | } |
5189 | |
5190 | /* Handle a "format" attribute; arguments as in |
5191 | struct attribute_spec.handler. */ |
5192 | tree |
5193 | handle_format_attribute (tree node[3], tree atname, tree args, |
5194 | int flags, bool *no_add_attrs) |
5195 | { |
5196 | const_tree type = *node; |
5197 | /* NODE[2] may be NULL, and it also may be a PARM_DECL for function |
5198 | pointers. */ |
5199 | const_tree fndecl = ((node[2] && TREE_CODE (node[2]) == FUNCTION_DECL) |
5200 | ? node[2] : NULL_TREE); |
5201 | function_format_info info; |
5202 | |
5203 | #ifdef TARGET_FORMAT_TYPES |
5204 | /* If the target provides additional format types, we need to |
5205 | add them to FORMAT_TYPES at first use. */ |
5206 | if (!dynamic_format_types) |
5207 | { |
5208 | dynamic_format_types = XNEWVEC (format_kind_info, |
5209 | n_format_types + TARGET_N_FORMAT_TYPES); |
5210 | memcpy (dynamic_format_types, format_types_orig, |
5211 | sizeof (format_types_orig)); |
5212 | memcpy (&dynamic_format_types[n_format_types], TARGET_FORMAT_TYPES, |
5213 | TARGET_N_FORMAT_TYPES * sizeof (dynamic_format_types[0])); |
5214 | |
5215 | format_types = dynamic_format_types; |
5216 | /* Provide a reference for the first potential external type. */ |
5217 | first_target_format_type = n_format_types; |
5218 | n_format_types += TARGET_N_FORMAT_TYPES; |
5219 | } |
5220 | #endif |
5221 | |
5222 | /* Canonicalize name of format function. */ |
5223 | if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE) |
5224 | TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args)); |
5225 | |
5226 | /* record the flags for check_function_format */ |
5227 | TREE_PURPOSE (args) = build_int_cst (unsigned_type_node, flags); |
5228 | |
5229 | if (!decode_format_attr (fn: fndecl ? fndecl : type, atname, args, info: &info, |
5230 | /* validated_p = */false)) |
5231 | { |
5232 | *no_add_attrs = true; |
5233 | return NULL_TREE; |
5234 | } |
5235 | |
5236 | if (prototype_p (type)) |
5237 | { |
5238 | if (!check_format_string (fntype: type, format_num: info.format_num, flags, |
5239 | no_add_attrs, expected_format_type: info.format_type)) |
5240 | return NULL_TREE; |
5241 | |
5242 | if (info.first_arg_num != 0) |
5243 | { |
5244 | unsigned HOST_WIDE_INT arg_num = 1; |
5245 | function_args_iterator iter; |
5246 | tree arg_type; |
5247 | |
5248 | /* Verify that first_arg_num points to the last arg, |
5249 | the ... */ |
5250 | FOREACH_FUNCTION_ARGS (type, arg_type, iter) |
5251 | arg_num++; |
5252 | |
5253 | if (arg_num != info.first_arg_num) |
5254 | { |
5255 | if (!(flags & (int) ATTR_FLAG_BUILT_IN)) |
5256 | error ("argument to be formatted is not %<...%>" ); |
5257 | *no_add_attrs = true; |
5258 | return NULL_TREE; |
5259 | } |
5260 | } |
5261 | } |
5262 | |
5263 | /* Check if this is a strftime variant. Just for this variant |
5264 | FMT_FLAG_ARG_CONVERT is not set. */ |
5265 | if ((format_types[info.format_type].flags & (int) FMT_FLAG_ARG_CONVERT) == 0 |
5266 | && info.first_arg_num != 0) |
5267 | { |
5268 | error ("strftime formats cannot format arguments" ); |
5269 | *no_add_attrs = true; |
5270 | return NULL_TREE; |
5271 | } |
5272 | |
5273 | /* If this is a custom GCC-internal format type, we have to |
5274 | initialize certain bits at runtime. */ |
5275 | if (info.format_type == asm_fprintf_format_type |
5276 | || info.format_type == gcc_gfc_format_type |
5277 | || info.format_type == gcc_diag_format_type |
5278 | || info.format_type == gcc_tdiag_format_type |
5279 | || info.format_type == gcc_cdiag_format_type |
5280 | || info.format_type == gcc_cxxdiag_format_type |
5281 | || info.format_type == gcc_dump_printf_format_type) |
5282 | { |
5283 | /* Our first time through, we have to make sure that our |
5284 | format_type data is allocated dynamically and is modifiable. */ |
5285 | if (!dynamic_format_types) |
5286 | format_types = dynamic_format_types = (format_kind_info *) |
5287 | xmemdup (format_types_orig, sizeof (format_types_orig), |
5288 | sizeof (format_types_orig)); |
5289 | |
5290 | /* If this is format __asm_fprintf__, we have to initialize |
5291 | GCC's notion of HOST_WIDE_INT for checking %wd. */ |
5292 | if (info.format_type == asm_fprintf_format_type) |
5293 | init_dynamic_asm_fprintf_info (); |
5294 | /* If this is format __gcc_gfc__, we have to initialize GCC's |
5295 | notion of 'locus' at runtime for %L. */ |
5296 | else if (info.format_type == gcc_gfc_format_type) |
5297 | init_dynamic_gfc_info (); |
5298 | /* If this is one of the diagnostic attributes, then we have to |
5299 | initialize 'location_t' and 'tree' at runtime. */ |
5300 | else if (info.format_type == gcc_diag_format_type |
5301 | || info.format_type == gcc_tdiag_format_type |
5302 | || info.format_type == gcc_cdiag_format_type |
5303 | || info.format_type == gcc_cxxdiag_format_type |
5304 | || info.format_type == gcc_dump_printf_format_type) |
5305 | init_dynamic_diag_info (); |
5306 | else |
5307 | gcc_unreachable (); |
5308 | } |
5309 | |
5310 | return NULL_TREE; |
5311 | } |
5312 | |
5313 | #if CHECKING_P |
5314 | |
5315 | namespace selftest { |
5316 | |
5317 | /* Selftests of location handling. */ |
5318 | |
5319 | /* Get the format_kind_info with the given name. */ |
5320 | |
5321 | static const format_kind_info * |
5322 | get_info (const char *name) |
5323 | { |
5324 | int idx = decode_format_type (s: name); |
5325 | const format_kind_info *fki = &format_types[idx]; |
5326 | ASSERT_STREQ (fki->name, name); |
5327 | return fki; |
5328 | } |
5329 | |
5330 | /* Verify that get_format_for_type (FKI, TYPE, CONVERSION_CHAR) |
5331 | is EXPECTED_FORMAT. */ |
5332 | |
5333 | static void |
5334 | assert_format_for_type_streq (const location &loc, const format_kind_info *fki, |
5335 | const char *expected_format, tree type, |
5336 | char conversion_char) |
5337 | { |
5338 | gcc_assert (fki); |
5339 | gcc_assert (expected_format); |
5340 | gcc_assert (type); |
5341 | |
5342 | char *actual_format = get_format_for_type (fki, arg_type: type, conversion_char); |
5343 | ASSERT_STREQ_AT (loc, expected_format, actual_format); |
5344 | free (ptr: actual_format); |
5345 | } |
5346 | |
5347 | /* Selftests for get_format_for_type. */ |
5348 | |
5349 | #define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE, CONVERSION_CHAR) \ |
5350 | assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), \ |
5351 | (TYPE), (CONVERSION_CHAR)) |
5352 | |
5353 | /* Selftest for get_format_for_type for "printf"-style functions. */ |
5354 | |
5355 | static void |
5356 | test_get_format_for_type_printf () |
5357 | { |
5358 | const format_kind_info *fki = get_info (name: "gnu_printf" ); |
5359 | ASSERT_NE (fki, NULL); |
5360 | |
5361 | ASSERT_FORMAT_FOR_TYPE_STREQ ("f" , double_type_node, 'i'); |
5362 | ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf" , long_double_type_node, 'i'); |
5363 | ASSERT_FORMAT_FOR_TYPE_STREQ ("f" , double_type_node, 'o'); |
5364 | ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf" , long_double_type_node, 'o'); |
5365 | ASSERT_FORMAT_FOR_TYPE_STREQ ("f" , double_type_node, 'x'); |
5366 | ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf" , long_double_type_node, 'x'); |
5367 | ASSERT_FORMAT_FOR_TYPE_STREQ ("f" , double_type_node, 'X'); |
5368 | ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf" , long_double_type_node, 'X'); |
5369 | ASSERT_FORMAT_FOR_TYPE_STREQ ("d" , integer_type_node, 'd'); |
5370 | ASSERT_FORMAT_FOR_TYPE_STREQ ("i" , integer_type_node, 'i'); |
5371 | ASSERT_FORMAT_FOR_TYPE_STREQ ("o" , integer_type_node, 'o'); |
5372 | ASSERT_FORMAT_FOR_TYPE_STREQ ("x" , integer_type_node, 'x'); |
5373 | ASSERT_FORMAT_FOR_TYPE_STREQ ("X" , integer_type_node, 'X'); |
5374 | ASSERT_FORMAT_FOR_TYPE_STREQ ("d" , unsigned_type_node, 'd'); |
5375 | ASSERT_FORMAT_FOR_TYPE_STREQ ("i" , unsigned_type_node, 'i'); |
5376 | ASSERT_FORMAT_FOR_TYPE_STREQ ("o" , unsigned_type_node, 'o'); |
5377 | ASSERT_FORMAT_FOR_TYPE_STREQ ("x" , unsigned_type_node, 'x'); |
5378 | ASSERT_FORMAT_FOR_TYPE_STREQ ("X" , unsigned_type_node, 'X'); |
5379 | ASSERT_FORMAT_FOR_TYPE_STREQ ("ld" , long_integer_type_node, 'd'); |
5380 | ASSERT_FORMAT_FOR_TYPE_STREQ ("li" , long_integer_type_node, 'i'); |
5381 | ASSERT_FORMAT_FOR_TYPE_STREQ ("lx" , long_integer_type_node, 'x'); |
5382 | ASSERT_FORMAT_FOR_TYPE_STREQ ("lo" , long_unsigned_type_node, 'o'); |
5383 | ASSERT_FORMAT_FOR_TYPE_STREQ ("lx" , long_unsigned_type_node, 'x'); |
5384 | ASSERT_FORMAT_FOR_TYPE_STREQ ("lld" , long_long_integer_type_node, 'd'); |
5385 | ASSERT_FORMAT_FOR_TYPE_STREQ ("lli" , long_long_integer_type_node, 'i'); |
5386 | ASSERT_FORMAT_FOR_TYPE_STREQ ("llo" , long_long_unsigned_type_node, 'o'); |
5387 | ASSERT_FORMAT_FOR_TYPE_STREQ ("llx" , long_long_unsigned_type_node, 'x'); |
5388 | ASSERT_FORMAT_FOR_TYPE_STREQ ("s" , build_pointer_type (char_type_node), 'i'); |
5389 | } |
5390 | |
5391 | /* Selftest for get_format_for_type for "scanf"-style functions. */ |
5392 | |
5393 | static void |
5394 | test_get_format_for_type_scanf () |
5395 | { |
5396 | const format_kind_info *fki = get_info (name: "gnu_scanf" ); |
5397 | ASSERT_NE (fki, NULL); |
5398 | ASSERT_FORMAT_FOR_TYPE_STREQ ("d" , build_pointer_type (integer_type_node), 'd'); |
5399 | ASSERT_FORMAT_FOR_TYPE_STREQ ("u" , build_pointer_type (unsigned_type_node), 'u'); |
5400 | ASSERT_FORMAT_FOR_TYPE_STREQ ("ld" , |
5401 | build_pointer_type (long_integer_type_node), 'd'); |
5402 | ASSERT_FORMAT_FOR_TYPE_STREQ ("lu" , |
5403 | build_pointer_type (long_unsigned_type_node), 'u'); |
5404 | ASSERT_FORMAT_FOR_TYPE_STREQ |
5405 | ("lld" , build_pointer_type (long_long_integer_type_node), 'd'); |
5406 | ASSERT_FORMAT_FOR_TYPE_STREQ |
5407 | ("llu" , build_pointer_type (long_long_unsigned_type_node), 'u'); |
5408 | ASSERT_FORMAT_FOR_TYPE_STREQ ("e" , build_pointer_type (float_type_node), 'e'); |
5409 | ASSERT_FORMAT_FOR_TYPE_STREQ ("le" , build_pointer_type (double_type_node), 'e'); |
5410 | } |
5411 | |
5412 | #undef ASSERT_FORMAT_FOR_TYPE_STREQ |
5413 | |
5414 | /* Exercise the type-printing label code, to give some coverage |
5415 | under "make selftest-valgrind" (in particular, to ensure that |
5416 | the label-printing machinery doesn't leak). */ |
5417 | |
5418 | static void |
5419 | test_type_mismatch_range_labels () |
5420 | { |
5421 | /* Create a tempfile and write some text to it. |
5422 | ....................0000000001 11111111 12 22222222 |
5423 | ....................1234567890 12345678 90 12345678. */ |
5424 | const char *content = " printf (\"msg: %i\\n\", msg);\n" ; |
5425 | temp_source_file tmp (SELFTEST_LOCATION, ".c" , content); |
5426 | line_table_test ltt; |
5427 | |
5428 | linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1); |
5429 | |
5430 | location_t c17 = linemap_position_for_column (line_table, 17); |
5431 | ASSERT_EQ (LOCATION_COLUMN (c17), 17); |
5432 | location_t c18 = linemap_position_for_column (line_table, 18); |
5433 | location_t c24 = linemap_position_for_column (line_table, 24); |
5434 | location_t c26 = linemap_position_for_column (line_table, 26); |
5435 | |
5436 | /* Don't attempt to run the tests if column data might be unavailable. */ |
5437 | if (c26 > LINE_MAP_MAX_LOCATION_WITH_COLS) |
5438 | return; |
5439 | |
5440 | location_t fmt = make_location (caret: c18, start: c17, finish: c18); |
5441 | ASSERT_EQ (LOCATION_COLUMN (fmt), 18); |
5442 | |
5443 | location_t param = make_location (caret: c24, start: c24, finish: c26); |
5444 | ASSERT_EQ (LOCATION_COLUMN (param), 24); |
5445 | |
5446 | range_label_for_format_type_mismatch fmt_label (char_type_node, |
5447 | integer_type_node, 1); |
5448 | range_label_for_type_mismatch param_label (integer_type_node, |
5449 | char_type_node); |
5450 | gcc_rich_location richloc (fmt, &fmt_label); |
5451 | richloc.add_range (loc: param, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: ¶m_label); |
5452 | |
5453 | test_diagnostic_context dc; |
5454 | diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR); |
5455 | if (c_dialect_cxx ()) |
5456 | /* "char*", without a space. */ |
5457 | ASSERT_STREQ (" printf (\"msg: %i\\n\", msg);\n" |
5458 | " ~^ ~~~\n" |
5459 | " | |\n" |
5460 | " char* int\n" , |
5461 | pp_formatted_text (dc.printer)); |
5462 | else |
5463 | /* "char *", with a space. */ |
5464 | ASSERT_STREQ (" printf (\"msg: %i\\n\", msg);\n" |
5465 | " ~^ ~~~\n" |
5466 | " | |\n" |
5467 | " | int\n" |
5468 | " char *\n" , |
5469 | pp_formatted_text (dc.printer)); |
5470 | } |
5471 | |
5472 | /* Run all of the selftests within this file. */ |
5473 | |
5474 | void |
5475 | c_format_cc_tests () |
5476 | { |
5477 | test_get_modifier_for_format_len (); |
5478 | test_get_format_for_type_printf (); |
5479 | test_get_format_for_type_scanf (); |
5480 | test_type_mismatch_range_labels (); |
5481 | } |
5482 | |
5483 | } // namespace selftest |
5484 | |
5485 | #endif /* CHECKING_P */ |
5486 | |
5487 | #include "gt-c-family-c-format.h" |
5488 | |