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,15 @@ case LengthModifier::AsIntMax: case LengthModifier::AsSizeT: case LengthModifier::AsPtrDiff: + if (LM.getKind() == LengthModifier::AsSizeT && + Target.getTriple().isOSMSVCRT() && + !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=18 %s +// RUN: %clang_cc1 -fsyntax-only -verify -fms-compatibility -triple=i386-pc-win32 -fms-compatibility-version=19 -DSIZE_T_OK %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); +#ifndef SIZE_T_OK + // expected-warning@-2 {{length modifier 'z' results in undefined behavior or no effect with 'u' conversion specifier}} +#endif +} + #endif