diff --git a/clang/include/clang/Analysis/Analyses/FormatString.h b/clang/include/clang/Analysis/Analyses/FormatString.h index 52dcd03b0b9d..d75194679c0b 100644 --- a/clang/include/clang/Analysis/Analyses/FormatString.h +++ b/clang/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/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index 4959854a078b..8c663d856f6a 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/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/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index 1bb3aac88749..c6453b66549d 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/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/clang/test/Sema/format-strings-ms.c b/clang/test/Sema/format-strings-ms.c index 4a6f91b5598c..42676e7a4e06 100644 --- a/clang/test/Sema/format-strings-ms.c +++ b/clang/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