`Issue 50055: <https://github.com/llvm/llvm-project/issues/50055>`_.
- Clang will now check compile-time determinable string literals as format strings.
This fixes `Issue 55805: <https://github.com/llvm/llvm-project/issues/55805>`_.
+- ``-Wformat`` now recognizes ``%b`` for the ``printf``/``scanf`` family of
+ functions and ``%B`` for the ``printf`` family of functions. Fixes
+ `Issue 56885: <https://github.com/llvm/llvm-project/issues/56885>`_.
Non-comprehensive list of changes in this release
-------------------------------------------------
dArg,
DArg, // Apple extension
iArg,
+ // C23 conversion specifiers.
+ bArg,
+ BArg,
+
IntArgBeg = dArg,
- IntArgEnd = iArg,
+ IntArgEnd = BArg,
oArg,
OArg, // Apple extension
const char *ConversionSpecifier::toString() const {
switch (kind) {
+ case bArg: return "b";
+ case BArg: return "B";
case dArg: return "d";
case DArg: return "D";
case iArg: return "i";
case LengthModifier::AsSizeT:
case LengthModifier::AsPtrDiff:
switch (CS.getKind()) {
+ case ConversionSpecifier::bArg:
+ case ConversionSpecifier::BArg:
case ConversionSpecifier::dArg:
case ConversionSpecifier::DArg:
case ConversionSpecifier::iArg:
bool FormatSpecifier::hasStandardConversionSpecifier(
const LangOptions &LangOpt) const {
switch (CS.getKind()) {
+ case ConversionSpecifier::bArg:
+ case ConversionSpecifier::BArg:
case ConversionSpecifier::cArg:
case ConversionSpecifier::dArg:
case ConversionSpecifier::iArg:
case 's': k = ConversionSpecifier::sArg; break;
case 'u': k = ConversionSpecifier::uArg; break;
case 'x': k = ConversionSpecifier::xArg; break;
+ // C23.
+ case 'b':
+ if (isFreeBSDKPrintf)
+ k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
+ else
+ k = ConversionSpecifier::bArg;
+ break;
+ case 'B': k = ConversionSpecifier::BArg; break;
// POSIX specific.
case 'C': k = ConversionSpecifier::CArg; break;
case 'S': k = ConversionSpecifier::SArg; break;
case '@': k = ConversionSpecifier::ObjCObjArg; break;
// Glibc specific.
case 'm': k = ConversionSpecifier::PrintErrno; break;
- // FreeBSD kernel specific.
- case 'b':
- if (isFreeBSDKPrintf)
- k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
- break;
case 'r':
if (isFreeBSDKPrintf)
k = ConversionSpecifier::FreeBSDrArg; // int
if (!HasAlternativeForm)
return true;
- // Alternate form flag only valid with the oxXaAeEfFgG conversions
+ // Alternate form flag only valid with the bBoxXaAeEfFgG conversions
switch (CS.getKind()) {
+ case ConversionSpecifier::bArg:
+ case ConversionSpecifier::BArg:
case ConversionSpecifier::oArg:
case ConversionSpecifier::OArg:
case ConversionSpecifier::xArg:
if (!HasLeadingZeroes)
return true;
- // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
+ // Leading zeroes flag only valid with the bBdiouxXaAeEfFgG conversions
switch (CS.getKind()) {
+ case ConversionSpecifier::bArg:
+ case ConversionSpecifier::BArg:
case ConversionSpecifier::dArg:
case ConversionSpecifier::DArg:
case ConversionSpecifier::iArg:
if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
return true;
- // Precision is only valid with the diouxXaAeEfFgGsP conversions
+ // Precision is only valid with the bBdiouxXaAeEfFgGsP conversions
switch (CS.getKind()) {
+ case ConversionSpecifier::bArg:
+ case ConversionSpecifier::BArg:
case ConversionSpecifier::dArg:
case ConversionSpecifier::DArg:
case ConversionSpecifier::iArg:
default:
break;
case '%': k = ConversionSpecifier::PercentArg; break;
+ case 'b': k = ConversionSpecifier::bArg; break;
case 'A': k = ConversionSpecifier::AArg; break;
case 'E': k = ConversionSpecifier::EArg; break;
case 'F': k = ConversionSpecifier::FArg; break;
llvm_unreachable("Unsupported LengthModifier Type");
// Unsigned int.
+ case ConversionSpecifier::bArg:
case ConversionSpecifier::oArg:
case ConversionSpecifier::OArg:
case ConversionSpecifier::uArg:
printf("%s", (int) 123);
printf("abc%0f", "testing testing 123");
printf("%u", (long) -12);
+ printf("%b", (long) -13);
printf("%p", 123);
printf("%c\n", "x");
printf("%c\n", 1.23);
scanf("%f", (my_int_type*)&intVar);
// Preserve the original formatting.
+ scanf("%b", &longVar);
scanf("%o", &longVar);
scanf("%u", &longVar);
scanf("%x", &longVar);
// CHECK: printf("%d", (int) 123);
// CHECK: printf("abc%s", "testing testing 123");
// CHECK: printf("%ld", (long) -12);
+// CHECK: printf("%ld", (long) -13);
// CHECK: printf("%d", 123);
// CHECK: printf("%s\n", "x");
// CHECK: printf("%f\n", 1.23);
// CHECK: scanf("%ju", (my_uintmax_type*)&uIntmaxVar);
// CHECK: scanf("%td", (my_ptrdiff_type*)&ptrdiffVar);
// CHECK: scanf("%d", (my_int_type*)&intVar);
+// CHECK: scanf("%ld", &longVar);
// CHECK: scanf("%lo", &longVar);
// CHECK: scanf("%lu", &longVar);
// CHECK: scanf("%lx", &longVar);
scanf("%0d", i); // expected-warning{{zero field width in scanf format string is unused}}
scanf("%00d", i); // expected-warning{{zero field width in scanf format string is unused}}
scanf("%d%[asdfasdfd", i, s); // expected-warning{{no closing ']' for '%[' in scanf format string}}
+ scanf("%B", i); // expected-warning{{invalid conversion specifier 'B'}}
unsigned short s_x;
scanf ("%" "hu" "\n", &s_x); // no-warning
+ scanf("%hb", &s_x);
scanf("%y", i); // expected-warning{{invalid conversion specifier 'y'}}
scanf("%%"); // no-warning
scanf("%%%1$d", i); // no-warning
scanf("%s", (signed char*)0); // no-warning
scanf("%s", (unsigned char*)0); // no-warning
scanf("%hhu", (signed char*)0); // no-warning
+ scanf("%hhb", (signed char*)0); // no-warning
}
void bad_length_modifiers(char *s, void *p, wchar_t *ws, long double *ld) {
void test_size_types(void) {
size_t s = 0;
scanf("%zu", &s); // No warning.
+ scanf("%zb", &s);
double d1 = 0.;
scanf("%zu", &d1); // expected-warning-re{{format specifies type 'size_t *' (aka '{{.+}}') but the argument has type 'double *'}}
void test_ptrdiff_t_types(void) {
__UNSIGNED_PTRDIFF_TYPE__ p1 = 0;
scanf("%tu", &p1); // No warning.
+ scanf("%tb", &p1);
double d1 = 0.;
scanf("%tu", &d1); // expected-warning-re{{format specifies type 'unsigned ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}
void check_invalid_specifier(FILE* fp, char *buf)
{
- printf("%s%lb%d","unix",10,20); // expected-warning {{invalid conversion specifier 'b'}} expected-warning {{data argument not used by format string}}
+ printf("%s%lv%d","unix",10,20); // expected-warning {{invalid conversion specifier 'v'}} expected-warning {{data argument not used by format string}}
fprintf(fp,"%%%l"); // expected-warning {{incomplete format specifier}}
sprintf(buf,"%%%%%ld%d%d", 1, 2, 3); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
snprintf(buf, 2, "%%%%%ld%;%d", 1, 2, 3); // expected-warning{{format specifies type 'long' but the argument has type 'int'}} expected-warning {{invalid conversion specifier ';'}} expected-warning {{data argument not used by format string}}
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("%llb %llB", (long long) 10, (long long) 10); // no-warning
// 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'}}
// 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("%-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}}
void pr8641(void) {
printf("%#x\n", 10);
printf("%#X\n", 10);
+ printf("%#b %#15.8B\n", 10, 10u);
}
void posix_extensions(void) {
printf("%'i\n", 123456789); // no-warning
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}}
+ printf("%'B\n", 123456789); // expected-warning{{results in undefined behavior with 'B' conversion specifier}}
}
// PR8486
printf("%hhi", x); // no-warning
printf("%c", x); // no-warning
printf("%hhu", y); // no-warning
+ printf("%hhb %hhB", x, x); // no-warning
}
// Test suppression of individual warnings.
void check_nslog(unsigned k) {
NSLog(@"%d%%", k); // no-warning
- NSLog(@"%s%lb%d", "unix", 10, 20); // expected-warning {{invalid conversion specifier 'b'}} expected-warning {{data argument not used by format string}}
+ NSLog(@"%s%lv%d", "unix", 10, 20); // expected-warning {{invalid conversion specifier 'v'}} expected-warning {{data argument not used by format string}}
}
// Check type validation