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
