simon_tatham created this revision. simon_tatham added reviewers: aaron.ballman, lebedev.ri. Herald added a project: clang. Herald added a subscriber: cfe-commits.
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 something specific which is earlier than 2015, it's useful to warn about 'z' modifiers in printf format strings we check. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D73457 Files: clang/include/clang/Basic/LangOptions.h clang/lib/AST/FormatString.cpp clang/test/Sema/format-strings-ms.c Index: clang/test/Sema/format-strings-ms.c =================================================================== --- clang/test/Sema/format-strings-ms.c +++ 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 Index: clang/lib/AST/FormatString.cpp =================================================================== --- clang/lib/AST/FormatString.cpp +++ 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: Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ 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; }
Index: clang/test/Sema/format-strings-ms.c =================================================================== --- clang/test/Sema/format-strings-ms.c +++ 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 Index: clang/lib/AST/FormatString.cpp =================================================================== --- clang/lib/AST/FormatString.cpp +++ 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: Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ 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; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits