Collin Funk wrote:
> If that sounds okay, I'll figure out what version of GCC and Clang added
> them and submit a patch.

I've now fixed the warnings through these patches. Thanks for your help,
Collin!


2026-08-26  Bruno Haible  <[email protected]>

        gettext-h: Get rid of gcc and clang warnings also in C++ mode.
        * lib/gettext.h (gettext, dgettext, dcgettext, ngettext, dngettext,
        dcngettext, textdomain): In C++ mode, define them as aliases to inline
        functions.

        gettext-h: Get rid of gcc -Wunused-value warnings for textdomain.
        * lib/gettext.h (textdomain): Define as inline function.

        gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext.
        * lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline
        functions, like gettext, dgettext, dcgettext.

        gettext-h: Get rid of -Wformat-security warnings with clang.
        Idea by Collin Funk in
        <https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>.
        * lib/gettext.h (_LIBGETTEXT_INLINE): New macro.
        (gettext, dgettext, dcgettext): Use it. Declare as inline functions also
        with clang. Declare with __format_arg__ attribute.

>From 12806e284d93ef4a02a46d6fca22481a3865b1a4 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Thu, 27 Aug 2026 01:18:39 +0200
Subject: [PATCH 1/4] gettext-h: Get rid of -Wformat-security warnings with
 clang.

Idea by Collin Funk in
<https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>.

* lib/gettext.h (_LIBGETTEXT_INLINE): New macro.
(gettext, dgettext, dcgettext): Use it. Declare as inline functions also
with clang. Declare with __format_arg__ attribute.
---
 ChangeLog     |  9 +++++++++
 lib/gettext.h | 48 ++++++++++++++++++++----------------------------
 2 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e22523135b..5a1bcbb7d7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2026-08-26  Bruno Haible  <[email protected]>
+
+	gettext-h: Get rid of -Wformat-security warnings with clang.
+	Idea by Collin Funk in
+	<https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>.
+	* lib/gettext.h (_LIBGETTEXT_INLINE): New macro.
+	(gettext, dgettext, dcgettext): Use it. Declare as inline functions also
+	with clang. Declare with __format_arg__ attribute.
+
 2026-08-26  Bruno Haible  <[email protected]>
 
 	gettext-h: Fix compilation error (regression 2026-08-24).
diff --git a/lib/gettext.h b/lib/gettext.h
index 68826302c3..6c1a64006b 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -67,16 +67,13 @@
 # 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]"
+/* When gcc or clang is used with option -Wformat=2, we need to silence
+   gcc: "warning: format not a string literal, argument types not checked [-Wformat-nonliteral]"
+   clang: "warning: format string is not a string literal [-Wformat-nonliteral]",
+          "warning: format string is not a string literal (potentially insecure) [-Wformat-security]"
    warnings that would occur at every invocation of a *gettext function
    in a *printf format string position.
-   Do this with inline functions when possible, namely for gettext, dgettext,
-   dcgettext, which are known to gcc as "external built-ins".  */
-/* 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.  */
+   Do this with inline functions when possible.  */
 /* These warnings would not occur with enabled NLS.  */
 /* A test case:
    ================================ foo.c ================================
@@ -106,21 +103,22 @@
      printf (dcgettext ("toto", some_computed_string (), LC_MESSAGES));
    }
    =======================================================================
-   $CC -Wformat=2 -S foo.c
-   $CC -Wformat=2 -S -x c++ foo.c
+   $CC -Wall -Wformat=2 -S foo.c
+   $CC -Wall -Wformat=2 -S -x c++ foo.c
  */
-# if defined __GNUC__ && !defined __clang__ && !defined __cplusplus
+# if (defined __GNUC__ || defined __clang__) && !defined __cplusplus
+#  if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4 || defined __clang__
+#   define _LIBGETTEXT_INLINE __always_inline__, __gnu_inline__
+#  else
+#   define _LIBGETTEXT_INLINE __always_inline__
+#  endif
 /* The return type 'const char *' serves the purpose of producing warnings
    for invalid uses of the value returned from these functions.  */
-#  if __GNUC__ >= 9
+#  if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__
 #   pragma GCC diagnostic push
 #   pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch"
 #  endif
-#  if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4
-__attribute__ ((__always_inline__, __gnu_inline__))
-#  else
-__attribute__ ((__always_inline__))
-#  endif
+__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (1)))
 extern inline
 #  if !defined(__sun)
 const
@@ -134,11 +132,7 @@ gettext (const char *msgid)
 #  endif
     msgid;
 }
-#  if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4
-__attribute__ ((__always_inline__, __gnu_inline__))
-#  else
-__attribute__ ((__always_inline__))
-#  endif
+__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2)))
 extern inline
 #  if !defined(__sun)
 const
@@ -153,11 +147,7 @@ dgettext (const char *domain, const char *msgid)
 #  endif
     msgid;
 }
-#  if __GNUC__ + (__GNUC_MINOR__ >= 2) > 4
-__attribute__ ((__always_inline__, __gnu_inline__))
-#  else
-__attribute__ ((__always_inline__))
-#  endif
+__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2)))
 extern inline
 #  if !defined(__sun)
 const
@@ -173,7 +163,7 @@ dcgettext (const char *domain, const char *msgid, int category)
 #  endif
     msgid;
 }
-#  if __GNUC__ >= 9
+#  if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__
 #   pragma GCC diagnostic pop
 #  endif
 /* Silence -Wuseless-cast warnings.  */
@@ -219,6 +209,7 @@ dcgettext (const char *domain, const char *msgid, int category)
 #  define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
      ((void) _LIBGETTEXT_FUNCAST (int, Category), \
       dngettext (Domainname, Msgid1, Msgid2, N))
+
 # endif
 
 # undef textdomain
@@ -231,6 +222,7 @@ dcgettext (const char *domain, const char *msgid, int category)
 # define bind_textdomain_codeset(Domainname, Codeset) \
     ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), \
      _LIBGETTEXT_FUNCAST (const char *, Codeset))
+
 #endif
 
 
-- 
2.53.0

>From c42e9f5d63ed6c6c7a5093dcb253319ae43bee5f Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Thu, 27 Aug 2026 01:21:03 +0200
Subject: [PATCH 2/4] gettext-h: Get rid of -Wformat warnings also for
 ngettext, d[c]ngettext.

* lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline
functions, like gettext, dgettext, dcgettext.
---
 ChangeLog     |  4 ++++
 lib/gettext.h | 61 +++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 49 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5a1bcbb7d7..dbc64feedb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2026-08-26  Bruno Haible  <[email protected]>
 
+	gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext.
+	* lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline
+	functions, like gettext, dgettext, dcgettext.
+
 	gettext-h: Get rid of -Wformat-security warnings with clang.
 	Idea by Collin Funk in
 	<https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00292.html>.
diff --git a/lib/gettext.h b/lib/gettext.h
index 6c1a64006b..e12a1e4ec5 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -163,25 +163,54 @@ dcgettext (const char *domain, const char *msgid, int category)
 #  endif
     msgid;
 }
+__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (1), __format_arg__ (2)))
+extern inline
+#  if !defined(__sun)
+const
+#  endif
+char *
+ngettext (const char *msgid1, const char *msgid2, unsigned long n)
+{
+  return
+#  ifdef __sun
+    (char *)
+#  endif
+    (n == 1 ? msgid1 : msgid2);
+}
+__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2), __format_arg__ (3)))
+extern inline
+#  if !defined(__sun)
+const
+#  endif
+char *
+dngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n)
+{
+  (void) domain;
+  return
+#  ifdef __sun
+    (char *)
+#  endif
+    (n == 1 ? msgid1 : msgid2);
+}
+__attribute__ ((_LIBGETTEXT_INLINE, __format_arg__ (2), __format_arg__ (3)))
+extern inline
+#  if !defined(__sun)
+const
+#  endif
+char *
+dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n, int category)
+{
+  (void) domain;
+  (void) category;
+  return
+#  ifdef __sun
+    (char *)
+#  endif
+    (n == 1 ? msgid1 : msgid2);
+}
 #  if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__
 #   pragma GCC diagnostic pop
 #  endif
-/* Silence -Wuseless-cast warnings.  */
-#  if __GNUC__ >= 14
-#   pragma GCC diagnostic ignored "-Wuseless-cast"
-#  endif
-/* Use plain casts to pacify -Wformat-nonliteral.
-   This ngettext implementation does not evaluate all arguments,
-   as doing so would trigger -Wformat-nonliteral false positives.  */
-#  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
 
-- 
2.53.0

>From 3513989c286d3204dbe3d575fcff08b5c2d62f55 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Thu, 27 Aug 2026 01:24:47 +0200
Subject: [PATCH 3/4] gettext-h: Get rid of gcc -Wunused-value warnings for
 textdomain.

* lib/gettext.h (textdomain): Define as inline function.
---
 ChangeLog     |  3 +++
 lib/gettext.h | 10 ++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index dbc64feedb..7422f6c058 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 2026-08-26  Bruno Haible  <[email protected]>
 
+	gettext-h: Get rid of gcc -Wunused-value warnings for textdomain.
+	* lib/gettext.h (textdomain): Define as inline function.
+
 	gettext-h: Get rid of -Wformat warnings also for ngettext, d[c]ngettext.
 	* lib/gettext.h (ngettext, dngettext, dcngettext): Define as inline
 	functions, like gettext, dgettext, dcgettext.
diff --git a/lib/gettext.h b/lib/gettext.h
index e12a1e4ec5..24150dfd44 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -208,6 +208,12 @@ dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned
 #  endif
     (n == 1 ? msgid1 : msgid2);
 }
+__attribute__ ((_LIBGETTEXT_INLINE))
+extern inline const char *
+textdomain (const char *domainname)
+{
+  return domainname;
+}
 #  if defined __GNUC__ && __GNUC__ >= 9 && !defined __clang__
 #   pragma GCC diagnostic pop
 #  endif
@@ -238,11 +244,11 @@ dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned
 #  define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
      ((void) _LIBGETTEXT_FUNCAST (int, Category), \
       dngettext (Domainname, Msgid1, Msgid2, N))
+#  undef textdomain
+#  define textdomain(Domainname) _LIBGETTEXT_FUNCAST (const char *, Domainname)
 
 # endif
 
-# undef textdomain
-# define textdomain(Domainname) _LIBGETTEXT_FUNCAST (const char *, Domainname)
 # undef bindtextdomain
 # define bindtextdomain(Domainname, Dirname) \
     ((void) _LIBGETTEXT_FUNCAST (const char *, Domainname), \
-- 
2.53.0

>From ec224ee81bbdcebfd136186e777f0dae85308006 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Thu, 27 Aug 2026 01:29:08 +0200
Subject: [PATCH 4/4] gettext-h: Get rid of gcc and clang warnings also in C++
 mode.

* lib/gettext.h (gettext, dgettext, dcgettext, ngettext, dngettext,
dcngettext, textdomain): In C++ mode, define them as aliases to inline
functions.
---
 ChangeLog     |  5 ++++
 lib/gettext.h | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 7422f6c058..c56a9fc010 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2026-08-26  Bruno Haible  <[email protected]>
 
+	gettext-h: Get rid of gcc and clang warnings also in C++ mode.
+	* lib/gettext.h (gettext, dgettext, dcgettext, ngettext, dngettext,
+	dcngettext, textdomain): In C++ mode, define them as aliases to inline
+	functions.
+
 	gettext-h: Get rid of gcc -Wunused-value warnings for textdomain.
 	* lib/gettext.h (textdomain): Define as inline function.
 
diff --git a/lib/gettext.h b/lib/gettext.h
index 24150dfd44..f201b98227 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -218,6 +218,70 @@ textdomain (const char *domainname)
 #   pragma GCC diagnostic pop
 #  endif
 
+# elif (defined __GNUC__ || defined __clang__) && defined __cplusplus
+
+__attribute__ ((__format_arg__ (1)))
+static inline const char *
+_libgettext_gettext (const char *msgid)
+{
+  return msgid;
+}
+#  undef gettext
+#  define gettext _libgettext_gettext
+__attribute__ ((__format_arg__ (2)))
+static inline const char *
+_libgettext_dgettext (const char *domain, const char *msgid)
+{
+  (void) domain;
+  return msgid;
+}
+#  undef dgettext
+#  define dgettext _libgettext_dgettext
+__attribute__ ((__format_arg__ (2)))
+static inline const char *
+_libgettext_dcgettext (const char *domain, const char *msgid, int category)
+{
+  (void) domain;
+  (void) category;
+  return msgid;
+}
+#  undef dcgettext
+#  define dcgettext _libgettext_dcgettext
+__attribute__ ((__format_arg__ (1), __format_arg__ (2)))
+static inline const char *
+_libgettext_ngettext (const char *msgid1, const char *msgid2, unsigned long n)
+{
+  return (n == 1 ? msgid1 : msgid2);
+}
+#  undef ngettext
+#  define ngettext _libgettext_ngettext
+__attribute__ ((__format_arg__ (2), __format_arg__ (3)))
+static inline const char *
+_libgettext_dngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n)
+{
+  (void) domain;
+  return (n == 1 ? msgid1 : msgid2);
+}
+#  undef dngettext
+#  define dngettext _libgettext_dngettext
+__attribute__ ((__format_arg__ (2), __format_arg__ (3)))
+static inline const char *
+_libgettext_dcngettext (const char *domain, const char *msgid1, const char *msgid2, unsigned long n, int category)
+{
+  (void) domain;
+  (void) category;
+  return (n == 1 ? msgid1 : msgid2);
+}
+#  undef dcngettext
+#  define dcngettext _libgettext_dcngettext
+static inline const char *
+_libgettext_textdomain (const char *domainname)
+{
+  return domainname;
+}
+#  undef textdomain
+#  define textdomain _libgettext_textdomain
+
 # else
 
 #  undef gettext
-- 
2.53.0

Reply via email to