]> granicus.if.org Git - clang/commitdiff
Format strings: %D, %U, and %O are valid on Darwin (same as %d, %u, %o).
authorJordan Rose <jordan_rose@apple.com>
Thu, 13 Sep 2012 02:11:03 +0000 (02:11 +0000)
committerJordan Rose <jordan_rose@apple.com>
Thu, 13 Sep 2012 02:11:03 +0000 (02:11 +0000)
These will warn under -Wformat-non-iso, and will still be rejected
outright on other platforms.

<rdar://problem/12061922>

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163771 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/Analyses/FormatString.h
lib/Analysis/FormatString.cpp
lib/Analysis/PrintfFormatString.cpp
lib/Analysis/ScanfFormatString.cpp
lib/Sema/SemaChecking.cpp
test/FixIt/format-darwin.m
test/Sema/format-strings-darwin.c [new file with mode: 0644]

index 0579265db40b98491c51741091efa2abdc71430d..3af506e174b9e2324d9ce43987809be6a68b4541 100644 (file)
@@ -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
index c6ba6fab070878bb877be4c4d9d9498f15a0ac45..f70086b8830c3633e3b2b7ae9d14dae42ea78dc6 100644 (file)
@@ -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!");
index b85c1c425b2b69ee6ca5655f843f9a329540d641..2fa5a88f2c718e7715fadff47ce096fcd5a789e2 100644 (file)
@@ -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:
index 92a7d9c1dd4ecdfab8d9b90ee67acbd324f32088..574e56a5e068f15b4051280823fb92513472caf1 100644 (file)
@@ -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())
index dd1f9e109baf575d75bf8a6ae2eab7a83415c5bc..2ae1d6ee918d9f2e59c3edd60fe261de2513b6d8 100644 (file)
@@ -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
 }
index a006d4a8c7e47ef666958f04a8b62213f820ce3c..7f6f4155f578bfb77c36dc4eb212ae7e4b1da420 100644 (file)
@@ -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/test/Sema/format-strings-darwin.c b/test/Sema/format-strings-darwin.c
new file mode 100644 (file)
index 0000000..284cd26
--- /dev/null
@@ -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