diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -127,8 +127,12 @@ dArg, DArg, // Apple extension iArg, + // C23 conversion specifiers. + bArg, + BArg, + IntArgBeg = dArg, - IntArgEnd = iArg, + IntArgEnd = BArg, oArg, OArg, // Apple extension diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -624,6 +624,8 @@ 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"; @@ -753,6 +755,8 @@ case LengthModifier::AsSizeT: case LengthModifier::AsPtrDiff: switch (CS.getKind()) { + case ConversionSpecifier::bArg: + case ConversionSpecifier::BArg: case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: @@ -908,6 +912,8 @@ 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: diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -326,6 +326,14 @@ 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; @@ -337,11 +345,6 @@ 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 @@ -961,8 +964,10 @@ 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: @@ -988,8 +993,10 @@ 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: @@ -1080,8 +1087,10 @@ 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: diff --git a/clang/test/Sema/format-strings-fixit.c b/clang/test/Sema/format-strings-fixit.c --- a/clang/test/Sema/format-strings-fixit.c +++ b/clang/test/Sema/format-strings-fixit.c @@ -21,6 +21,7 @@ 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); @@ -179,6 +180,7 @@ // 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); diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -199,7 +199,7 @@ 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}} @@ -304,6 +304,7 @@ 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'}} @@ -429,6 +430,7 @@ // 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}} @@ -499,6 +501,7 @@ void pr8641(void) { printf("%#x\n", 10); printf("%#X\n", 10); + printf("%#b %#B\n", 10, 10u); } void posix_extensions(void) { @@ -508,6 +511,8 @@ 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 @@ -540,6 +545,7 @@ 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. diff --git a/clang/test/SemaObjC/format-strings-objc.m b/clang/test/SemaObjC/format-strings-objc.m --- a/clang/test/SemaObjC/format-strings-objc.m +++ b/clang/test/SemaObjC/format-strings-objc.m @@ -56,7 +56,7 @@ 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