From f3802d8ef93f754b899a57a33afe720db21e4013 Mon Sep 17 00:00:00 2001 From: Tomas Kalibera Date: Mon, 16 May 2022 05:55:29 -0400 Subject: [PATCH] c-family: Let stdio.h override built in printf format [PR95130,PR92292] Mingw32 targets use ms_printf format for printf, but mingw-w64 when configured for UCRT uses gnu_format (via stdio.h). GCC checks both formats, which means that one gets a warning twice if the format string violates both formats: printf("Hello %lu\n", (long long unsigned) x); Fixed by disabling the built in format in case there are additional ones. This fixes also prevents issues if the print formats disagree. In the past it was the case when printing 64-bit integers, but GCC ms_printf format since c51f1e7427e6a5ae2a6d82b5a790df77a3adc99 supports %llu. gcc/c-family/ChangeLog: PR c/95130 PR c/92292 * c-common.c (check_function_arguments): Pass also function declaration to check_function_format. * c-common.h (check_function_format): Extra argument - function declaration. * c-format.c (check_function_format): For builtin functions with a built in format and at least one more, do not check the first one. --- gcc/c-family/c-common.c | 2 +- gcc/c-family/c-common.h | 2 +- gcc/c-family/c-format.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 16fc52302e5..5a8bcb6774f 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -5849,7 +5849,7 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype, /* Check for errors in format strings. */ if (warn_format || warn_suggest_attribute_format) - check_function_format (fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray, + check_function_format (fndecl, fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray, arglocs); if (warn_format) diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index f30b6c6ac33..34d936e7b48 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -854,7 +854,7 @@ extern void check_function_arguments_recurse (void (*) unsigned HOST_WIDE_INT); extern bool check_builtin_function_arguments (location_t, vec, tree, tree, int, tree *); -extern void check_function_format (const_tree, tree, int, tree *, +extern void check_function_format (const_tree, const_tree, tree, int, tree *, vec *); extern bool attribute_fallthrough_p (tree); extern tree handle_format_attribute (tree *, tree, tree, int, bool *); diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index f4359657fc1..46a85bae712 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -1168,12 +1168,13 @@ decode_format_type (const char *s, bool *is_raw /* = NULL */) attribute themselves. */ void -check_function_format (const_tree fntype, tree attrs, int nargs, +check_function_format (const_tree fndecl, const_tree fntype, tree attrs, int nargs, tree *argarray, vec *arglocs) { - tree a; + tree a, aa; tree atname = get_identifier ("format"); + bool skipped_default_format = false; /* See if this function has any format attributes. */ for (a = attrs; a; a = TREE_CHAIN (a)) @@ -1184,6 +1185,33 @@ check_function_format (const_tree fntype, tree attrs, int nargs, function_format_info info; decode_format_attr (fntype, atname, TREE_VALUE (a), &info, /*validated=*/true); + + /* Mingw32 targets have traditionally used ms_printf format for the + printf function, and this format is built in GCC. But nowadays, + if mingw-w64 is configured to target UCRT, the printf function + uses the gnu_printf format (specified in the stdio.h header). This + causes GCC to check both formats, which means that GCC would warn + twice about the same issue when both formats are violated, e.g. + for %lu used to print long long unsigned. Also, it would be + impossible to use features permitted by only one format. + + Hence, if there are multiple format specifiers, we skip the first + one. See PR 95130 (but note that GCC ms_printf already supports + %llu) and PR 92292. */ + + if (!skipped_default_format && fndecl) + { + for(aa = TREE_CHAIN (a); aa; aa = TREE_CHAIN (aa)) + if (is_attribute_p ("format", get_attribute_name (aa)) + && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) + { + skipped_default_format = true; + break; + } + if (skipped_default_format) + continue; + } + if (warn_format) { /* FIXME: Rewrite all the internal functions in this file -- 2.25.1