[Clang] Warn about 'z' printf modifier in old MSVC.

Summary:
The 'z' length modifier, signalling that an integer format specifier
takes a `size_t` sized integer, is only supported by the C library of
MSVC 2015 and later. Earlier versions don't recognize the 'z' at all,
and respond to `printf("%zu", x)` by just printing "zu".

So, if the MS compatibility version is set to a value earlier than
MSVC2015, it's useful to warn about 'z' modifiers in printf format
strings we check.

Reviewers: aaron.ballman, lebedev.ri, rnk, majnemer, zturner

Reviewed By: aaron.ballman

Subscribers: amccarth, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D73457
This commit is contained in:
Simon Tatham 2020-01-28 09:03:31 +00:00
parent 422dfea577
commit fe0d1b6a8a
2 changed files with 18 additions and 0 deletions

View File

@ -748,6 +748,15 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target,
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 less than
// that, reject.
return false;
}
switch (CS.getKind()) {
case ConversionSpecifier::dArg:
case ConversionSpecifier::DArg:

View File

@ -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 @@ void z_test(void *p) {
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