From 510260c2bffb9212cdaeace66020ed8dd6f421ca Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 13 Sep 2012 02:11:03 +0000 Subject: [PATCH] Format strings: %D, %U, and %O are valid on Darwin (same as %d, %u, %o). These will warn under -Wformat-non-iso, and will still be rejected outright on other platforms. llvm-svn: 163771 --- .../clang/Analysis/Analyses/FormatString.h | 9 ++- clang/lib/Analysis/FormatString.cpp | 12 ++++ clang/lib/Analysis/PrintfFormatString.cpp | 33 +++++++++- clang/lib/Analysis/ScanfFormatString.cpp | 26 +++++++- clang/lib/Sema/SemaChecking.cpp | 6 +- clang/test/FixIt/format-darwin.m | 10 +++ clang/test/Sema/format-strings-darwin.c | 64 +++++++++++++++++++ 7 files changed, 150 insertions(+), 10 deletions(-) create mode 100644 clang/test/Sema/format-strings-darwin.c diff --git a/clang/include/clang/Analysis/Analyses/FormatString.h b/clang/include/clang/Analysis/Analyses/FormatString.h index 0579265db40b..3af506e174b9 100644 --- a/clang/include/clang/Analysis/Analyses/FormatString.h +++ b/clang/include/clang/Analysis/Analyses/FormatString.h @@ -117,11 +117,14 @@ public: // C99 conversion specifiers. cArg, dArg, + DArg, // Apple extension iArg, IntArgBeg = dArg, IntArgEnd = iArg, oArg, + OArg, // Apple extension uArg, + UArg, // Apple extension xArg, XArg, UIntArgBeg = oArg, UIntArgEnd = XArg, @@ -628,10 +631,12 @@ public: }; bool ParsePrintfString(FormatStringHandler &H, - const char *beg, const char *end, const LangOptions &LO); + const char *beg, const char *end, const LangOptions &LO, + const TargetInfo &Target); bool ParseScanfString(FormatStringHandler &H, - const char *beg, const char *end, const LangOptions &LO); + const char *beg, const char *end, const LangOptions &LO, + const TargetInfo &Target); } // end analyze_format_string namespace } // end clang namespace diff --git a/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index c6ba6fab0708..f70086b8830c 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/lib/Analysis/FormatString.cpp @@ -490,9 +490,12 @@ analyze_format_string::LengthModifier::toString() const { const char *ConversionSpecifier::toString() const { switch (kind) { case dArg: return "d"; + case DArg: return "D"; case iArg: return "i"; case oArg: return "o"; + case OArg: return "O"; case uArg: return "u"; + case UArg: return "U"; case xArg: return "x"; case XArg: return "X"; case fArg: return "f"; @@ -564,9 +567,12 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const { case LengthModifier::AsPtrDiff: switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: case ConversionSpecifier::nArg: @@ -579,9 +585,12 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const { case LengthModifier::AsLong: switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: case ConversionSpecifier::aArg: @@ -699,6 +708,9 @@ bool FormatSpecifier::hasStandardConversionSpecifier(const LangOptions &LangOpt) return LangOpt.ObjC1 || LangOpt.ObjC2; case ConversionSpecifier::InvalidSpecifier: case ConversionSpecifier::PrintErrno: + case ConversionSpecifier::DArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::UArg: return false; } llvm_unreachable("Invalid ConversionSpecifier Kind!"); diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index b85c1c425b2b..2fa5a88f2c71 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/lib/Analysis/PrintfFormatString.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/FormatString.h" +#include "clang/Basic/TargetInfo.h" #include "FormatStringParsing.h" using clang::analyze_format_string::ArgType; @@ -52,7 +53,8 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, unsigned &argIndex, - const LangOptions &LO) { + const LangOptions &LO, + const TargetInfo &Target) { using namespace clang::analyze_format_string; using namespace clang::analyze_printf; @@ -196,6 +198,19 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, case '@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. case 'm': k = ConversionSpecifier::PrintErrno; break; + // Apple-specific + case 'D': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::DArg; + break; + case 'O': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::OArg; + break; + case 'U': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::UArg; + break; } PrintfConversionSpecifier CS(conversionPosition, k); FS.setConversionSpecifier(CS); @@ -212,14 +227,15 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, const char *I, const char *E, - const LangOptions &LO) { + const LangOptions &LO, + const TargetInfo &Target) { unsigned argIndex = 0; // Keep looking for a format specifier until we have exhausted the string. while (I != E) { const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex, - LO); + LO, Target); // Did a fail-stop error of any kind occur when parsing the specifier? // If so, don't do any more processing. if (FSR.shouldStop()) @@ -551,6 +567,7 @@ bool PrintfSpecifier::hasValidPlusPrefix() const { // The plus prefix only makes sense for signed conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::fArg: case ConversionSpecifier::FArg: @@ -574,6 +591,7 @@ bool PrintfSpecifier::hasValidAlternativeForm() const { // Alternate form flag only valid with the oxXaAeEfFgG conversions switch (CS.getKind()) { case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: case ConversionSpecifier::aArg: @@ -598,9 +616,12 @@ bool PrintfSpecifier::hasValidLeadingZeros() const { // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: case ConversionSpecifier::aArg: @@ -625,6 +646,7 @@ bool PrintfSpecifier::hasValidSpacePrefix() const { // The space prefix only makes sense for signed conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::fArg: case ConversionSpecifier::FArg: @@ -661,8 +683,10 @@ bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const { switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: case ConversionSpecifier::fArg: case ConversionSpecifier::FArg: case ConversionSpecifier::gArg: @@ -680,9 +704,12 @@ bool PrintfSpecifier::hasValidPrecision() const { // Precision is only valid with the diouxXaAeEfFgGs conversions switch (CS.getKind()) { case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: case ConversionSpecifier::aArg: diff --git a/clang/lib/Analysis/ScanfFormatString.cpp b/clang/lib/Analysis/ScanfFormatString.cpp index 92a7d9c1dd4e..574e56a5e068 100644 --- a/clang/lib/Analysis/ScanfFormatString.cpp +++ b/clang/lib/Analysis/ScanfFormatString.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/FormatString.h" +#include "clang/Basic/TargetInfo.h" #include "FormatStringParsing.h" using clang::analyze_format_string::ArgType; @@ -67,7 +68,8 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, unsigned &argIndex, - const LangOptions &LO) { + const LangOptions &LO, + const TargetInfo &Target) { using namespace clang::analyze_scanf; const char *I = Beg; @@ -172,6 +174,20 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, case 'o': k = ConversionSpecifier::oArg; break; case 's': k = ConversionSpecifier::sArg; break; case 'p': k = ConversionSpecifier::pArg; break; + // Apple extensions + // Apple-specific + case 'D': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::DArg; + break; + case 'O': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::OArg; + break; + case 'U': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::UArg; + break; } ScanfConversionSpecifier CS(conversionPosition, k); if (k == ScanfConversionSpecifier::ScanListArg) { @@ -202,6 +218,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { switch(CS.getKind()) { // Signed int. case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: case ConversionSpecifier::iArg: switch (LM.getKind()) { case LengthModifier::None: @@ -233,7 +250,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { // Unsigned int. case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: case ConversionSpecifier::xArg: case ConversionSpecifier::XArg: switch (LM.getKind()) { @@ -465,14 +484,15 @@ void ScanfSpecifier::toString(raw_ostream &os) const { bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, const char *I, const char *E, - const LangOptions &LO) { + const LangOptions &LO, + const TargetInfo &Target) { unsigned argIndex = 0; // Keep looking for a format specifier until we have exhausted the string. while (I != E) { const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex, - LO); + LO, Target); // Did a fail-stop error of any kind occur when parsing the specifier? // If so, don't do any more processing. if (FSR.shouldStop()) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index dd1f9e109baf..2ae1d6ee918d 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3020,7 +3020,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, inFunctionCall, CallType); if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen, - getLangOpts())) + getLangOpts(), + Context.getTargetInfo())) H.DoneProcessing(); } else if (Type == FST_Scanf) { CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs, @@ -3028,7 +3029,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, inFunctionCall, CallType); if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen, - getLangOpts())) + getLangOpts(), + Context.getTargetInfo())) H.DoneProcessing(); } // TODO: handle other formats } diff --git a/clang/test/FixIt/format-darwin.m b/clang/test/FixIt/format-darwin.m index a006d4a8c7e4..7f6f4155f578 100644 --- a/clang/test/FixIt/format-darwin.m +++ b/clang/test/FixIt/format-darwin.m @@ -179,3 +179,13 @@ void testCasts() { // CHECK: fix-it:"{{.*}}":{168:11-168:13}:"%u" // CHECK: fix-it:"{{.*}}":{168:16-168:24}:"(unsigned int)" } + +void testCapitals() { + printf("%D", 1); // no-warning + printf("%U", 1); // no-warning + printf("%O", 1); // no-warning + + printf("%lD", 1); // expected-warning{{format specifies type 'long' but the argument has type 'int'}} + + // CHECK: fix-it:"{{.*}}":{188:11-188:14}:"%D" +} diff --git a/clang/test/Sema/format-strings-darwin.c b/clang/test/Sema/format-strings-darwin.c new file mode 100644 index 000000000000..284cd2656134 --- /dev/null +++ b/clang/test/Sema/format-strings-darwin.c @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple i386-apple-darwin9 -pedantic -DALLOWED %s +// RUN: %clang_cc1 -fsyntax-only -verify -triple thumbv6-apple-ios4.0 -pedantic -DALLOWED %s + +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-mingw32 -pedantic %s +// RUN: %clang_cc1 -fsyntax-only -verify -triple i686-pc-win32 -pedantic %s + +// RUN: %clang_cc1 -fsyntax-only -verify -triple i686-linux-gnu -pedantic %s +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-freebsd -pedantic %s + +int printf(const char *restrict, ...); +int scanf(const char * restrict, ...) ; + +void test() { + int justRight = 1; + long tooLong = 2; + + printf("%D", justRight); + printf("%D", tooLong); + printf("%U", justRight); + printf("%U", tooLong); + printf("%O", justRight); + printf("%O", tooLong); + +#ifdef ALLOWED + // expected-warning@-8 {{'D' conversion specifier is not supported by ISO C}} + // expected-warning@-8 {{'D' conversion specifier is not supported by ISO C}} expected-warning@-8 {{format specifies type 'int' but the argument has type 'long'}} + // expected-warning@-8 {{'U' conversion specifier is not supported by ISO C}} + // expected-warning@-8 {{'U' conversion specifier is not supported by ISO C}} expected-warning@-8 {{format specifies type 'unsigned int' but the argument has type 'long'}} + // expected-warning@-8 {{'O' conversion specifier is not supported by ISO C}} + // expected-warning@-8 {{'O' conversion specifier is not supported by ISO C}} expected-warning@-8 {{format specifies type 'unsigned int' but the argument has type 'long'}} +#else + // expected-warning@-15 {{invalid conversion specifier 'D'}} + // expected-warning@-15 {{invalid conversion specifier 'D'}} + // expected-warning@-15 {{invalid conversion specifier 'U'}} + // expected-warning@-15 {{invalid conversion specifier 'U'}} + // expected-warning@-15 {{invalid conversion specifier 'O'}} + // expected-warning@-15 {{invalid conversion specifier 'O'}} +#endif +} + +#ifdef ALLOWED +void testPrintf(short x, long y) { + printf("%hD", x); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%lD", y); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%hU", x); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%lU", y); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%hO", x); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%lO", y); // expected-warning{{conversion specifier is not supported by ISO C}} + + printf("%+'0.5lD", y); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("% '0.5lD", y); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%#0.5lO", y); // expected-warning{{conversion specifier is not supported by ISO C}} + printf("%'0.5lU", y); // expected-warning{{conversion specifier is not supported by ISO C}} +} + +void testScanf(short *x, long *y) { + scanf("%hD", x); // expected-warning{{conversion specifier is not supported by ISO C}} + scanf("%lD", y); // expected-warning{{conversion specifier is not supported by ISO C}} + scanf("%hU", x); // expected-warning{{conversion specifier is not supported by ISO C}} + scanf("%lU", y); // expected-warning{{conversion specifier is not supported by ISO C}} + scanf("%hO", x); // expected-warning{{conversion specifier is not supported by ISO C}} + scanf("%lO", y); // expected-warning{{conversion specifier is not supported by ISO C}} +} +#endif