From 399adce235ea85530dabde38340b8adb5e5af865 Mon Sep 17 00:00:00 2001
From: Hans Wennborg <hans@hanshq.net>
Date: Sun, 7 Sep 2014 03:03:51 +0000
Subject: [PATCH] MS format strings: parse the 'Z' printf conversion specifier
 (PR20808)

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@217326 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/Analysis/Analyses/FormatString.h |  2 ++
 lib/Analysis/FormatString.cpp                  |  9 ++++++++-
 lib/Analysis/PrintfFormatString.cpp            |  6 +++++-
 test/Sema/format-strings-ms.c                  | 12 +++++++++++-
 4 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h
index 52dcd03b0b9..d75194679c0 100644
--- a/include/clang/Analysis/Analyses/FormatString.h
+++ b/include/clang/Analysis/Analyses/FormatString.h
@@ -155,6 +155,8 @@ public:
 
     // ** Printf-specific **
 
+    ZArg, // MS extension
+
     // Objective-C specific specifiers.
     ObjCObjArg,  // '@'
     ObjCBeg = ObjCObjArg, ObjCEnd = ObjCObjArg,
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index 4959854a078..8c663d856f6 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/lib/Analysis/FormatString.cpp
@@ -554,6 +554,9 @@ const char *ConversionSpecifier::toString() const {
 
   // GlibC specific specifiers.
   case PrintErrno: return "m";
+
+  // MS specific specifiers.
+  case ZArg: return "Z";
   }
   return nullptr;
 }
@@ -619,6 +622,7 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
           case ConversionSpecifier::CArg:
           case ConversionSpecifier::sArg:
           case ConversionSpecifier::SArg:
+          case ConversionSpecifier::ZArg:
             return true;
           default:
             break;
@@ -671,6 +675,7 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
         case ConversionSpecifier::cArg:
         case ConversionSpecifier::sArg:
         case ConversionSpecifier::ScanListArg:
+        case ConversionSpecifier::ZArg:
           return true;
         default:
           return false;
@@ -740,7 +745,8 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
         case ConversionSpecifier::cArg:
         case ConversionSpecifier::CArg:
         case ConversionSpecifier::sArg:
-        case ConversionSpecifier::SArg: // FIXME: Or Z.
+        case ConversionSpecifier::SArg:
+        case ConversionSpecifier::ZArg:
           return Target.getTriple().isOSMSVCRT();
         default:
           return false;
@@ -805,6 +811,7 @@ bool FormatSpecifier::hasStandardConversionSpecifier(const LangOptions &LangOpt)
     case ConversionSpecifier::DArg:
     case ConversionSpecifier::OArg:
     case ConversionSpecifier::UArg:
+    case ConversionSpecifier::ZArg:
       return false;
   }
   llvm_unreachable("Invalid ConversionSpecifier Kind!");
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index 1bb3aac8874..c6453b66549 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -198,7 +198,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
     case '@': k = ConversionSpecifier::ObjCObjArg; break;
     // Glibc specific.
     case 'm': k = ConversionSpecifier::PrintErrno; break;
-    // Apple-specific
+    // Apple-specific.
     case 'D':
       if (Target.getTriple().isOSDarwin())
         k = ConversionSpecifier::DArg;
@@ -211,6 +211,10 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
       if (Target.getTriple().isOSDarwin())
         k = ConversionSpecifier::UArg;
       break;
+    // MS specific.
+    case 'Z':
+      if (Target.getTriple().isOSMSVCRT())
+        k = ConversionSpecifier::ZArg;
   }
   PrintfConversionSpecifier CS(conversionPosition, k);
   FS.setConversionSpecifier(CS);
diff --git a/test/Sema/format-strings-ms.c b/test/Sema/format-strings-ms.c
index 4a6f91b5598..42676e7a4e0 100644
--- a/test/Sema/format-strings-ms.c
+++ b/test/Sema/format-strings-ms.c
@@ -8,11 +8,12 @@ typedef unsigned short wchar_t;
 #ifdef NON_ISO_WARNING
 
 // Split off this test to reduce the warning noise in the rest of the file.
-void non_iso_warning_test(__int32 i32, __int64 i64, wchar_t c) {
+void non_iso_warning_test(__int32 i32, __int64 i64, wchar_t c, void *p) {
   printf("%Id", i32); // expected-warning{{'I' length modifier is not supported by ISO C}}
   printf("%I32d", i32); // expected-warning{{'I32' length modifier is not supported by ISO C}}
   printf("%I64d", i64); // expected-warning{{'I64' length modifier is not supported by ISO C}}
   printf("%wc", c); // expected-warning{{'w' length modifier is not supported by ISO C}}
+  printf("%Z", p); // expected-warning{{'Z' conversion specifier is not supported by ISO C}}
 }
 
 #else
@@ -75,4 +76,13 @@ void h_test(char c, char* s) {
   scanf("%hS", &bad); // expected-warning{{format specifies type 'char *' but the argument has type 'double *'}}
 }
 
+void z_test(void *p) {
+  printf("%Z", p);
+  printf("%hZ", p);
+  printf("%lZ", p);
+  printf("%wZ", p);
+  printf("%hhZ", p); // expected-warning{{length modifier 'hh' results in undefined behavior or no effect with 'Z' conversion specifier}}
+  scanf("%Z", p); // expected-warning{{invalid conversion specifier 'Z'}}
+}
+
 #endif
-- 
GitLab