https://github.com/drodriguez updated 
https://github.com/llvm/llvm-project/pull/126694

From 6dd960c6cf0550763b986f1909373a5b7e3a069d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?=
 <[email protected]>
Date: Mon, 10 Feb 2025 23:23:58 -0800
Subject: [PATCH 1/2] [Sema] -Wformat: warn about C23 %b %B when not in C23
 language mode

The validation of %b and %B was done without taking into account the
current language mode, which means that %b and %B could have been used
in format strings before they were supported.

Change the code to check for the language mode being C23 or after,
modify some tests that use %b and %B to specify C23 language mode and
add a new test that checks the different behaviour depending on the
current language mode.
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 ++
 clang/lib/AST/PrintfFormatString.cpp          |  4 ++-
 clang/lib/Sema/SemaChecking.cpp               | 12 +++++++
 clang/test/Sema/format-strings-c23-binary.c   | 32 +++++++++++++++++++
 clang/test/Sema/format-strings-fixit.c        |  6 ++--
 clang/test/Sema/format-strings.c              |  8 ++---
 6 files changed, 57 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/Sema/format-strings-c23-binary.c

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ef0e29af0f224..d3e42c4e5596b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10631,6 +10631,9 @@ def warn_printf_data_arg_not_used : Warning<
   "data argument not used by format string">, InGroup<FormatExtraArgs>;
 def warn_format_invalid_conversion : Warning<
   "invalid conversion specifier '%0'">, InGroup<FormatInvalidSpecifier>;
+def warn_format_conversion_specifier_requires_c23 : Warning<
+  "conversion specifier '%0' requires a C standard library compatible with "
+  "C23; data argument may not be used by format">, InGroup<Format>;
 def warn_printf_incomplete_specifier : Warning<
   "incomplete format specifier">, InGroup<Format>;
 def warn_missing_format_string : Warning<
diff --git a/clang/lib/AST/PrintfFormatString.cpp 
b/clang/lib/AST/PrintfFormatString.cpp
index 855550475721a..2e0adddb70369 100644
--- a/clang/lib/AST/PrintfFormatString.cpp
+++ b/clang/lib/AST/PrintfFormatString.cpp
@@ -333,7 +333,9 @@ static PrintfSpecifierResult 
ParsePrintfSpecifier(FormatStringHandler &H,
       else
         k = ConversionSpecifier::bArg;
       break;
-    case 'B': k = ConversionSpecifier::BArg; break;
+    case 'B':
+      k = ConversionSpecifier::BArg;
+      break;
     // POSIX specific.
     case 'C': k = ConversionSpecifier::CArg; break;
     case 'S': k = ConversionSpecifier::SArg; break;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index a49e3883a35a5..a09bf79cfb8ed 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -8863,6 +8863,18 @@ bool CheckPrintfHandler::HandlePrintfSpecifier(
   if (!FS.hasStandardConversionSpecifier(S.getLangOpts()))
     HandleNonStandardConversionSpecifier(CS, startSpecifier, specifierLen);
 
+  // Warn about C23 %b/%B format specifiers when not in C23 mode.
+  if ((CS.getKind() == ConversionSpecifier::bArg ||
+       CS.getKind() == ConversionSpecifier::BArg) &&
+      !LangStandard::getLangStandardForKind(S.getLangOpts().LangStd).isC23()) {
+    EmitFormatDiagnostic(
+        S.PDiag(diag::warn_format_conversion_specifier_requires_c23)
+            << CS.toString(),
+        getLocationOfByte(CS.getStart()), /*IsStringLocation*/ true,
+        getSpecifierRange(startSpecifier, specifierLen));
+    return true;
+  }
+
   // The remaining checks depend on the data arguments.
   if (!HasFormatArguments())
     return true;
diff --git a/clang/test/Sema/format-strings-c23-binary.c 
b/clang/test/Sema/format-strings-c23-binary.c
new file mode 100644
index 0000000000000..a023df82c9a05
--- /dev/null
+++ b/clang/test/Sema/format-strings-c23-binary.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=c17 -isystem %S/Inputs %s
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify=c23 -isystem %S/Inputs %s
+
+#include <stdarg.h>
+#include <stddef.h>
+
+int printf(const char *restrict, ...);
+
+void test(unsigned char x) {
+  printf("%lb %lB", (long) 10, (long) 10); // c17-warning{{conversion 
specifier 'b' requires a C standard library compatible with C23; data argument 
may not be used by format}}
+                                           // c17-warning@-1{{conversion 
specifier 'B' requires a C standard library compatible with C23; data argument 
may not be used by format}}
+
+  printf("%llb %llB", (long long) 10, (long long) 10); // 
c17-warning{{conversion specifier 'b' requires a C standard library compatible 
with C23; data argument may not be used by format}}
+                                                       // 
c17-warning@-1{{conversion specifier 'B' requires a C standard library 
compatible with C23; data argument may not be used by format}}
+
+  printf("%0b%0B", -1u, -1u); // c17-warning{{conversion specifier 'b' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                              // c17-warning@-1{{conversion specifier 'B' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+
+  printf("%#b %#15.8B\n", 10, 10u); // c17-warning{{conversion specifier 'b' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                                    // c17-warning@-1{{conversion specifier 
'B' requires a C standard library compatible with C23; data argument may not be 
used by format}}
+
+  printf("%'b\n", 123456789); // c17-warning{{conversion specifier 'b' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                              // c17-warning@-1{{results in undefined behavior 
with 'b' conversion specifier}}
+                              // c23-warning@-2{{results in undefined behavior 
with 'b' conversion specifier}}
+
+  printf("%'B\n", 123456789); // c17-warning{{conversion specifier 'B' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                              // c17-warning@-1{{results in undefined behavior 
with 'B' conversion specifier}}
+                              // c23-warning@-2{{results in undefined behavior 
with 'B' conversion specifier}}
+
+  printf("%hhb %hhB", x, x); // c17-warning{{conversion specifier 'b' requires 
a C standard library compatible with C23; data argument may not be used by 
format}}
+                             // c17-warning@-1{{conversion specifier 'B' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+}
diff --git a/clang/test/Sema/format-strings-fixit.c 
b/clang/test/Sema/format-strings-fixit.c
index 5e37ec76fed21..6fb191e57bcf2 100644
--- a/clang/test/Sema/format-strings-fixit.c
+++ b/clang/test/Sema/format-strings-fixit.c
@@ -1,7 +1,7 @@
 // RUN: cp %s %t
-// RUN: %clang_cc1 -pedantic -Wall -fixit %t
-// RUN: %clang_cc1 -fsyntax-only -pedantic -Wall -Werror %t
-// RUN: %clang_cc1 -E -o - %t | FileCheck %s
+// RUN: %clang_cc1 -std=c23 -pedantic -Wall -fixit %t
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -pedantic -Wall -Werror %t
+// RUN: %clang_cc1 -std=c23 -E -o - %t | FileCheck %s
 
 /* This is a test of the various code modification hints that are
    provided as part of warning or extension diagnostics. All of the
diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c
index bdb4466dc6ae8..956a4dd27255b 100644
--- a/clang/test/Sema/format-strings.c
+++ b/clang/test/Sema/format-strings.c
@@ -1,7 +1,7 @@
-// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem 
%S/Inputs %s
-// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem 
%S/Inputs -fno-signed-char %s
-// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem 
%S/Inputs -triple=x86_64-unknown-fuchsia %s
-// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem 
%S/Inputs -triple=x86_64-linux-android %s
+// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs %s
+// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs -fno-signed-char %s
+// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs -triple=x86_64-unknown-fuchsia %s
+// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs -triple=x86_64-linux-android %s
 
 #include <stdarg.h>
 #include <stddef.h>

From 840e05b5c661039766a4307d0d139866afa2ee24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez?= <[email protected]>
Date: Mon, 2 Mar 2026 10:54:09 -0800
Subject: [PATCH 2/2] Adapt format-strings.c to handle both pre-C23 and
 post-C23 warnings

---
 clang/test/Sema/format-strings.c | 74 +++++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 25 deletions(-)

diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c
index 956a4dd27255b..e38b3e0ba7b44 100644
--- a/clang/test/Sema/format-strings.c
+++ b/clang/test/Sema/format-strings.c
@@ -1,7 +1,8 @@
-// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs %s
-// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs -fno-signed-char %s
-// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs -triple=x86_64-unknown-fuchsia %s
-// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify -Wformat-nonliteral 
-isystem %S/Inputs -triple=x86_64-linux-android %s
+// RUN: %clang_cc1 -fblocks -fsyntax-only -verify=expected,prec23 
-Wformat-nonliteral -isystem %S/Inputs %s
+// RUN: %clang_cc1 -fblocks -fsyntax-only -verify=expected,prec23 
-Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s
+// RUN: %clang_cc1 -fblocks -fsyntax-only -verify=expected,prec23 
-Wformat-nonliteral -isystem %S/Inputs -triple=x86_64-unknown-fuchsia %s
+// RUN: %clang_cc1 -fblocks -fsyntax-only -verify=expected,prec23 
-Wformat-nonliteral -isystem %S/Inputs -triple=x86_64-linux-android %s
+// RUN: %clang_cc1 -std=c23 -fblocks -fsyntax-only -verify=expected,c23 
-Wformat-nonliteral -isystem %S/Inputs %s
 
 #include <stdarg.h>
 #include <stddef.h>
@@ -382,8 +383,10 @@ void test10(int x, float f, int i, long long lli) {
   printf("%qp", (void *)0); // expected-warning{{length modifier 'q' results 
in undefined behavior or no effect with 'p' conversion specifier}}
   printf("hhX %hhX", (unsigned char)10); // no-warning
   printf("llX %llX", (long long) 10); // no-warning
-  printf("%lb %lB", (long) 10, (long) 10); // no-warning
-  printf("%llb %llB", (long long) 10, (long long) 10); // no-warning
+  printf("%lb %lB", (long) 10, (long) 10); // prec23-warning{{conversion 
specifier 'b' requires a C standard library compatible with C23; data argument 
may not be used by format}}
+                                           // prec23-warning@-1{{conversion 
specifier 'B' requires a C standard library compatible with C23; data argument 
may not be used by format}}
+  printf("%llb %llB", (long long) 10, (long long) 10); // 
prec23-warning{{conversion specifier 'b' requires a C standard library 
compatible with C23; data argument may not be used by format}}
+                                                       // 
prec23-warning@-1{{conversion specifier 'B' requires a C standard library 
compatible with C23; data argument may not be used by format}}
   // This is fine, because there is an implicit conversion to an int.
   printf("%d", (unsigned char) 10); // no-warning
   printf("%d", (long long) 10); // expected-warning{{format specifies type 
'int' but the argument has type 'long long'}}
@@ -507,7 +510,8 @@ void bug7377_bad_length_mod_usage(void) {
   // Bad flag usage
   printf("%#p", (void *) 0); // expected-warning{{flag '#' results in 
undefined behavior with 'p' conversion specifier}}
   printf("%0d", -1); // no-warning
-  printf("%0b%0B", -1u, -1u); // no-warning
+  printf("%0b%0B", -1u, -1u); // prec23-warning{{conversion specifier 'b' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                              // prec23-warning@-1{{conversion specifier 'B' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
   printf("%-p", (void *) 0); // no-warning
 #if !defined(__ANDROID__) && !defined(__Fuchsia__)
   printf("%#n", (int *) 0); // expected-warning{{flag '#' results in undefined 
behavior with 'n' conversion specifier}}
@@ -596,7 +600,8 @@ void vprintf_scanf_bad(const char *p, va_list ap, const 
char *s, ...) {
 void pr8641(void) {
   printf("%#x\n", 10);
   printf("%#X\n", 10);
-  printf("%#b %#15.8B\n", 10, 10u);
+  printf("%#b %#15.8B\n", 10, 10u); // prec23-warning{{conversion specifier 
'b' requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                                    // prec23-warning@-1{{conversion specifier 
'B' requires a C standard library compatible with C23; data argument may not be 
used by format}}
 }
 
 void posix_extensions(void) {
@@ -606,7 +611,9 @@ void posix_extensions(void) {
   printf("%'f\n", (float) 1.0); // no-warning
   printf("%'p\n", (void*) 0); // expected-warning{{results in undefined 
behavior with 'p' conversion specifier}}
   printf("%'b\n", 123456789); // expected-warning{{results in undefined 
behavior with 'b' conversion specifier}}
+                              // prec23-warning@-1{{conversion specifier 'b' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
   printf("%'B\n", 123456789); // expected-warning{{results in undefined 
behavior with 'B' conversion specifier}}
+                              // prec23-warning@-1{{conversion specifier 'B' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
 }
 
 // PR8486
@@ -639,7 +646,8 @@ void check_char(unsigned char x, signed char y) {
   printf("%hhi", x); // no-warning
   printf("%c", x); // no-warning
   printf("%hhu", y); // no-warning
-  printf("%hhb %hhB", x, x); // no-warning
+  printf("%hhb %hhB", x, x); // prec23-warning{{conversion specifier 'b' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
+                             // prec23-warning@-1{{conversion specifier 'B' 
requires a C standard library compatible with C23; data argument may not be 
used by format}}
 }
 
 // Test suppression of individual warnings.
@@ -987,33 +995,49 @@ void test_promotion(void) {
 void test_bool(_Bool b, _Bool* bp)
 {
 #if __SIZEOF_INT__ != __SIZEOF_SIZE_T__
-  printf("%zu", b); // expected-warning-re{{format specifies type 'size_t' 
(aka '{{.+}}') but the argument has type '_Bool'}}
-  printf("%td", b); // expected-warning-re{{format specifies type 'ptrdiff_t' 
(aka '{{.+}}') but the argument has type '_Bool'}}
+  printf("%zu", b); // prec23-warning-re{{format specifies type 'size_t' (aka 
'{{.+}}') but the argument has type '_Bool'}}
+                    // c23-warning-re@-1{{format specifies type 'size_t' (aka 
'{{.+}}') but the argument has type 'bool'}}
+  printf("%td", b); // prec23-warning-re{{format specifies type 'ptrdiff_t' 
(aka '{{.+}}') but the argument has type '_Bool'}}
+                    // c23-warning-re@-1{{format specifies type 'ptrdiff_t' 
(aka '{{.+}}') but the argument has type 'bool'}}
 #endif
-  printf("%jd", b); // expected-warning-re{{format specifies type 'intmax_t' 
(aka '{{.+}}') but the argument has type '_Bool'}}
-  printf("%lld", b); // expected-warning{{format specifies type 'long long' 
but the argument has type '_Bool'}}
-  printf("%ld", b); // expected-warning{{format specifies type 'long' but the 
argument has type '_Bool'}}
+  printf("%jd", b); // prec23-warning-re{{format specifies type 'intmax_t' 
(aka '{{.+}}') but the argument has type '_Bool'}}
+                    // c23-warning-re@-1{{format specifies type 'intmax_t' 
(aka '{{.+}}') but the argument has type 'bool'}}
+  printf("%lld", b); // prec23-warning{{format specifies type 'long long' but 
the argument has type '_Bool'}}
+                     // c23-warning@-1{{format specifies type 'long long' but 
the argument has type 'bool'}}
+  printf("%ld", b); // prec23-warning{{format specifies type 'long' but the 
argument has type '_Bool'}}
+                    // c23-warning@-1{{format specifies type 'long' but the 
argument has type 'bool'}}
   printf("%d", b); // promoted from _Bool to int
   printf("%hhd", b); // promoted from _Bool to int
   printf("%hd", b); // promoted from _Bool to int
 #if !defined(__Fuchsia__) && !defined(__ANDROID__) //'%n' specifier not 
supported on this platform
   // The n conversion specifier only supports signed types
-  printf("%zn", bp); // expected-warning-re{{format specifies type 'signed 
size_t *' (aka '{{.+}}') but the argument has type '_Bool *'}}
-  printf("%jn", bp); // expected-warning-re{{format specifies type 'intmax_t 
*' (aka '{{.+}}') but the argument has type '_Bool *'}}
-  printf("%lln", bp); // expected-warning{{format specifies type 'long long *' 
but the argument has type '_Bool *'}}
-  printf("%ln", bp); // expected-warning{{format specifies type 'long *' but 
the argument has type '_Bool *'}}
-  printf("%n", bp); // expected-warning{{format specifies type 'int *' but the 
argument has type '_Bool *'}}
-  printf("%hhn", bp); // expected-warning{{format specifies type 'signed char 
*' but the argument has type '_Bool *'}}
+  printf("%zn", bp); // prec23-warning-re{{format specifies type 'signed 
size_t *' (aka '{{.+}}') but the argument has type '_Bool *'}}
+                     // c23-warning-re@-1{{format specifies type 'signed 
size_t *' (aka '{{.+}}') but the argument has type 'bool *'}}
+  printf("%jn", bp); // prec23-warning-re{{format specifies type 'intmax_t *' 
(aka '{{.+}}') but the argument has type '_Bool *'}}
+                     // c23-warning-re@-1{{format specifies type 'intmax_t *' 
(aka '{{.+}}') but the argument has type 'bool *'}}
+  printf("%lln", bp); // prec23-warning{{format specifies type 'long long *' 
but the argument has type '_Bool *'}}
+                      // c23-warning@-1{{format specifies type 'long long *' 
but the argument has type 'bool *'}}
+  printf("%ln", bp); // prec23-warning{{format specifies type 'long *' but the 
argument has type '_Bool *'}}
+                     // c23-warning@-1{{format specifies type 'long *' but the 
argument has type 'bool *'}}
+  printf("%n", bp); // prec23-warning{{format specifies type 'int *' but the 
argument has type '_Bool *'}}
+                    // c23-warning@-1{{format specifies type 'int *' but the 
argument has type 'bool *'}}
+  printf("%hhn", bp); // prec23-warning{{format specifies type 'signed char *' 
but the argument has type '_Bool *'}}
+                      // c23-warning@-1{{format specifies type 'signed char *' 
but the argument has type 'bool *'}}
   printf("%hn", bp); // belong to -Wformat-type-confusion
 #endif
   printf("%c", b); // expected-warning{{using '%c' format specifier, but 
argument has boolean value}}
-  printf("%s", b); // expected-warning{{format specifies type 'char *' but the 
argument has type '_Bool'}}
+  printf("%s", b); // prec23-warning{{format specifies type 'char *' but the 
argument has type '_Bool'}}
+                   // c23-warning@-1{{format specifies type 'char *' but the 
argument has type 'bool'}}
   printf("%d", b); // promoted from _Bool to int
   printf("%o", b); // promoted from _Bool to int
   printf("%x", b); // promoted from _Bool to int
   printf("%u", b); // promoted from _Bool to int
-  printf("%f", b); // expected-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
-  printf("%e", b); // expected-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
-  printf("%a", b); // expected-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
-  printf("%g", b); // expected-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
+  printf("%f", b); // prec23-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
+                   // c23-warning@-1{{format specifies type 'double' but the 
argument has type 'bool'}}
+  printf("%e", b); // prec23-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
+                   // c23-warning@-1{{format specifies type 'double' but the 
argument has type 'bool'}}
+  printf("%a", b); // prec23-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
+                   // c23-warning@-1{{format specifies type 'double' but the 
argument has type 'bool'}}
+  printf("%g", b); // prec23-warning{{format specifies type 'double' but the 
argument has type '_Bool'}}
+                   // c23-warning@-1{{format specifies type 'double' but the 
argument has type 'bool'}}
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to