diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -328,6 +328,10 @@ !ObjCSubscriptingLegacyRuntime; } + bool isMSCompatibilityVersionSpecified() const { + return MSCompatibilityVersion != 0; + } + bool isCompatibleWithMSVC(MSVCMajorVersion MajorVersion) const { return MSCompatibilityVersion >= MajorVersion * 100000U; } diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -748,6 +748,16 @@ case LengthModifier::AsIntMax: case LengthModifier::AsSizeT: case LengthModifier::AsPtrDiff: + if (LM.getKind() == LengthModifier::AsSizeT && + Target.getTriple().isOSMSVCRT() && + LO.isMSCompatibilityVersionSpecified() && + !LO.isCompatibleWithMSVC(LangOptions::MSVC2015)) { + // The standard libraries before MSVC2015 didn't support the 'z' length + // modifier for size_t. So if the MS compatibility version is specified + // and less than that, reject. + return false; + } + switch (CS.getKind()) { case ConversionSpecifier::dArg: case ConversionSpecifier::DArg: diff --git a/clang/test/Sema/format-strings-ms.c b/clang/test/Sema/format-strings-ms.c --- a/clang/test/Sema/format-strings-ms.c +++ b/clang/test/Sema/format-strings-ms.c @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 %s +// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 -fms-compatibility-version=19 %s +// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 -fms-compatibility-version=18 -DNO_z_MODIFIER %s // RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 -Wformat-non-iso -DNON_ISO_WARNING %s int printf(const char *format, ...) __attribute__((format(printf, 1, 2))); @@ -85,4 +87,11 @@ scanf("%Z", p); // expected-warning{{invalid conversion specifier 'Z'}} } +void size_t_test(size_t s) { + printf("%zu", s); +#ifdef NO_z_MODIFIER + // expected-warning@-2 {{length modifier 'z' results in undefined behavior or no effect with 'u' conversion specifier}} +#endif +} + #endif