Commit fe0d1b6a authored by Simon Tatham's avatar Simon Tatham
Browse files

[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
parent 422dfea5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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:
+9 −0
Original line number Diff line number Diff line
// 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