commit 67d8a6b3c7037d809b76bce9b1286e849a292451
Author: Jonathan Schleifer <js@webkeks.org>
Date:   Sat Nov 23 11:19:14 2013 +0100

    Accept %k and %K format specifier in format strings.

diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h
index c9516b5..61560ff 100644
--- a/include/clang/Analysis/Analyses/FormatString.h
+++ b/include/clang/Analysis/Analyses/FormatString.h
@@ -161,7 +161,11 @@ public:
     // GlibC specific specifiers.
     PrintErrno,   // 'm'
 
-    PrintfConvBeg = ObjCObjArg, PrintfConvEnd = PrintErrno,
+    // ObjFW specific specifiers.
+    kArg,         // 'k'
+    KArg,         // 'K'
+
+    PrintfConvBeg = ObjCObjArg, PrintfConvEnd = KArg,
 
     // ** Scanf-specific **
     ScanListArg, // '['
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index 43ecc66..58ca802 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/lib/Analysis/FormatString.cpp
@@ -550,6 +550,10 @@ const char *ConversionSpecifier::toString() const {
 
   // GlibC specific specifiers.
   case PrintErrno: return "m";
+
+  // ObjFW specific specifiers.
+  case kArg: return "k";
+  case KArg: return "K";
   }
   return NULL;
 }
@@ -775,6 +779,8 @@ bool FormatSpecifier::hasStandardConversionSpecifier(const LangOptions &LangOpt)
       return LangOpt.ObjC1 || LangOpt.ObjC2;
     case ConversionSpecifier::InvalidSpecifier:
     case ConversionSpecifier::PrintErrno:
+    case ConversionSpecifier::kArg:
+    case ConversionSpecifier::KArg:
     case ConversionSpecifier::DArg:
     case ConversionSpecifier::OArg:
     case ConversionSpecifier::UArg:
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index f21b407..abc010c 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -211,6 +211,9 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
       if (Target.getTriple().isOSDarwin())
         k = ConversionSpecifier::UArg;
       break;
+    // ObjFW specific.
+    case 'k': k = ConversionSpecifier::kArg; break;
+    case 'K': k = ConversionSpecifier::KArg; break;
   }
   PrintfConversionSpecifier CS(conversionPosition, k);
   FS.setConversionSpecifier(CS);
@@ -398,6 +401,11 @@ ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
       return ArgType::CPointerTy;
     case ConversionSpecifier::ObjCObjArg:
       return ArgType::ObjCPointerTy;
+    case ConversionSpecifier::kArg:
+      return ArgType(Ctx.Char32Ty, "of_unichar_t");
+    case ConversionSpecifier::KArg:
+      return ArgType(Ctx.getPointerType(Ctx.Char32Ty.withConst()),
+                     "const of_unichar_t *");
     default:
       break;
   }
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index f5ce84f..1430a89 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -188,6 +188,9 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
       if (Target.getTriple().isOSDarwin())
         k = ConversionSpecifier::UArg;
       break;
+    // ObjFW extensions
+    case 'k': k = ConversionSpecifier::kArg; break;
+    case 'K': k = ConversionSpecifier::KArg; break;
   }
   ScanfConversionSpecifier CS(conversionPosition, k);
   if (k == ScanfConversionSpecifier::ScanListArg) {
@@ -336,6 +339,10 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
         default:
           return ArgType::Invalid();
       }
+    case ConversionSpecifier::kArg:
+      return ArgType(Ctx.Char32Ty, "of_unichar_t");
+    case ConversionSpecifier::KArg:
+      return ArgType::PtrTo(ArgType(Ctx.Char32Ty, "of_unichar_t"));
 
     // Pointer.
     case ConversionSpecifier::pArg:
