The 2026-05-08 change of lib/gettext.h triggers new compiler warnings
- in packages configured with --disable-nls,
- when compiled with "clang -Wformat=2",
- at each occurrence of
printf (gettext ("some string"), ...)
But similar warnings are also seen, after and before that change,
- in packages configured with --disable-nls,
- when compiled with "gcc -Wformat=2",
- at each occurrence of
printf (ngettext ("singular ...", "plural ...", n), ...)
This cannot stay like this, because
* Many distros enable -Wformat=2 (or -Wformat-security) by default.
* Developers would think that gettext() has a security problem, which it
hasn't.
* Some package builds would now fail because they enable both -Wformat=2
and -Werror.
So, I'm applying this patch to fix the warnings both with clang and gcc.
Unfortunately, with gcc, this reintroduces the -Wuseless-cast warnings
that the 2026-05-08 change removed. So, I'm applying the #pragma hammer to
silence them.
Security-aware people would be angry if I disable some -Wformat
warnings through #pragma, but they will certainly tolerate disabling
of -Wuseless-cast warnings.
2026-05-11 Bruno Haible <[email protected]>
gettext-h: Ensure no warnings with --disable-nls and -Wformat=2.
* lib/gettext.h (gettext, dgettext, dcgettext): Define differently for
clang.
(ngettext, dngettext, dcngettext): Define differently for gcc and clang.
With gcc in C mode, silence -Wuseless-cast warnings.
diff --git a/lib/gettext.h b/lib/gettext.h
index 1ea5c9be78..07f2dfed53 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -60,10 +60,39 @@
# endif
/* Disabled NLS. */
+/* When gcc is used with option -Wformat=2, we need to silence
+ "warning: format not a string literal, argument types not checked
[-Wformat-nonliteral]"
+ warnings that would occur at every invocation of a *ngettext function
+ in a *printf format string position.
+ Do this with inline functions when possible.
+ It is not ideal to ignore the possible side effects done in the
+ Domainname and Category arguments, but it's better than to have a
+ warning at every invocation in a format string position. */
+/* When clang is used with option -Wformat=2, we need to silence
+ "warning: format string is not a string literal [-Wformat-nonliteral]"
+ warnings that would occur at every invocation of a *gettext function
+ in a *printf format string position.
+ It is not ideal to ignore the possible side effects done in the
+ Domainname and Category arguments, but it's better than to have a
+ warning at every invocation in a format string position. */
+/* These warnings would not occur with enabled NLS. */
+/* A test case:
+ ================================ foo.c ================================
+ #include <stdio.h>
+ #include "gettext.h"
+ void foo (int n)
+ {
+ printf (gettext ("foo %d"), n);
+ printf (dgettext ("toto", "foo %d"), n);
+ printf (dcgettext ("toto", "foo %d", LC_MESSAGES), n);
+ printf (ngettext ("foo %d", "bar %d", n), n);
+ printf (dngettext ("toto", "foo %d", "bar %d", n), n);
+ printf (dcngettext ("toto", "foo %d", "bar %d", n, LC_MESSAGES), n);
+ }
+ =======================================================================
+ $CC -Wformat=2 -S foo.c
+ */
# if defined __GNUC__ && !defined __clang__ && !defined __cplusplus
-/* Use inline functions, to avoid warnings
- warning: format not a string literal and no format arguments
- that don't occur with enabled NLS. */
/* The return type 'const char *' serves the purpose of producing warnings
for invalid uses of the value returned from these functions. */
# if __GNUC__ >= 9
@@ -92,7 +121,7 @@ __attribute__ ((__always_inline__))
extern inline
# if !defined(__sun)
const
-# endif
+# endif
char *
dgettext (const char *domain, const char *msgid)
{
@@ -118,11 +147,21 @@ dcgettext (const char *domain, const char *msgid, int
category)
# if __GNUC__ >= 9
# pragma GCC diagnostic pop
# endif
+# elif defined __clang__
+# undef gettext
+# define gettext(Msgid) ((const char *) (Msgid))
+# undef dgettext
+# define dgettext(Domainname, Msgid) gettext (Msgid)
+# undef dcgettext
+# define dcgettext(Domainname, Msgid, Category) dgettext (Domainname, Msgid)
# else
-/* The 'const char *' compound literals produce warnings
- for invalid uses of the value returned from these functions. */
+/* The conversions to 'const char *' via compound literals serve the purpose
+ of producing warnings for invalid uses of the value returned from these
+ functions and for invalid-typed Msgid arguments. */
# undef gettext
# define gettext(Msgid) ((const char *) {(Msgid)})
+/* The conversions via compound literals serve the purpose of producing
warnings
+ for invalid-typed arguments. */
# undef dgettext
# define dgettext(Domainname, Msgid) \
((void) (const char *) {(Domainname)}, gettext (Msgid))
@@ -130,17 +169,50 @@ dcgettext (const char *domain, const char *msgid, int
category)
# define dcgettext(Domainname, Msgid, Category) \
((void) (int) {(Category)}, dgettext (Domainname, Msgid))
# endif
-# undef ngettext
-# define ngettext(Msgid1, Msgid2, N) \
- ((N) == 1 \
- ? ((void) (Msgid2), (const char *) {(Msgid1)}) \
- : ((void) (Msgid1), (const char *) {(Msgid2)}))
-# undef dngettext
-# define dngettext(Domainname, Msgid1, Msgid2, N) \
- ((void) (const char *) {(Domainname)}, ngettext (Msgid1, Msgid2, N))
-# undef dcngettext
-# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
- ((void) (int) {(Category)}, dngettext (Domainname, Msgid1, Msgid2, N))
+
+# if (defined __GNUC__ && defined __cplusplus) || defined __clang__
+# undef ngettext
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# undef dngettext
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ngettext (Msgid1, Msgid2, N)
+# undef dcngettext
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ dngettext (Domainname, Msgid1, Msgid2, N)
+# elif defined __GNUC__ && !defined __cplusplus
+/* Silence -Wuseless-cast warnings. */
+# if __GNUC__ >= 14
+# pragma GCC diagnostic ignored "-Wuseless-cast"
+# endif
+# undef ngettext
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# undef dngettext
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((void) (const char *) (Domainname), ngettext (Msgid1, Msgid2, N))
+# undef dcngettext
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((void) (int) (Category), dngettext (Domainname, Msgid1, Msgid2, N))
+# else
+/* The conversions to 'const char *' via compound literals serve the purpose
+ of producing warnings for invalid uses of the value returned from these
+ functions and for invalid-typed Msgid1 and Msgid2 arguments. */
+# undef ngettext
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 \
+ ? ((void) (Msgid2), (const char *) {(Msgid1)}) \
+ : ((void) (Msgid1), (const char *) {(Msgid2)}))
+/* The conversions via compound literals serve the purpose of producing
warnings
+ for invalid-typed arguments. */
+# undef dngettext
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((void) (const char *) {(Domainname)}, ngettext (Msgid1, Msgid2, N))
+# undef dcngettext
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((void) (int) {(Category)}, dngettext (Domainname, Msgid1, Msgid2, N))
+# endif
+
# undef textdomain
# define textdomain(Domainname) ((const char *) {(Domainname)})
# undef bindtextdomain