Index: include/clang/Analysis/Analyses/FormatString.h =================================================================== --- include/clang/Analysis/Analyses/FormatString.h +++ include/clang/Analysis/Analyses/FormatString.h @@ -257,17 +257,21 @@ const Kind K; QualType T; const char *Name; - bool Ptr; + bool Ptr, IsSizeT; + public: ArgType(Kind k = UnknownTy, const char *n = nullptr) - : K(k), Name(n), Ptr(false) {} + : K(k), Name(n), Ptr(false), IsSizeT(false) {} ArgType(QualType t, const char *n = nullptr) - : K(SpecificTy), T(t), Name(n), Ptr(false) {} - ArgType(CanQualType t) : K(SpecificTy), T(t), Name(nullptr), Ptr(false) {} + : K(SpecificTy), T(t), Name(n), Ptr(false), IsSizeT(false) {} + ArgType(CanQualType t) + : K(SpecificTy), T(t), Name(nullptr), Ptr(false), IsSizeT(false) {} static ArgType Invalid() { return ArgType(InvalidTy); } bool isValid() const { return K != InvalidTy; } + bool isSizeT() const { return IsSizeT; } + /// Create an ArgType which corresponds to the type pointer to A. static ArgType PtrTo(const ArgType& A) { assert(A.K >= InvalidTy && "ArgType cannot be pointer to invalid/unknown"); @@ -276,6 +280,13 @@ return Res; } + /// Create an ArgType which corresponds to the size_t/ssize_t type. + static ArgType makeSizeT(const ArgType &A) { + ArgType Res = A; + Res.IsSizeT = true; + return Res; + } + MatchKind matchesType(ASTContext &C, QualType argTy) const; QualType getRepresentativeType(ASTContext &C) const; Index: lib/Analysis/PrintfFormatString.cpp =================================================================== --- lib/Analysis/PrintfFormatString.cpp +++ lib/Analysis/PrintfFormatString.cpp @@ -466,7 +466,7 @@ case LengthModifier::AsIntMax: return ArgType(Ctx.getIntMaxType(), "intmax_t"); case LengthModifier::AsSizeT: - return ArgType(Ctx.getSignedSizeType(), "ssize_t"); + return ArgType::makeSizeT(ArgType(Ctx.getSignedSizeType(), "ssize_t")); case LengthModifier::AsInt3264: return Ctx.getTargetInfo().getTriple().isArch64Bit() ? ArgType(Ctx.LongLongTy, "__int64") @@ -499,7 +499,7 @@ case LengthModifier::AsIntMax: return ArgType(Ctx.getUIntMaxType(), "uintmax_t"); case LengthModifier::AsSizeT: - return ArgType(Ctx.getSizeType(), "size_t"); + return ArgType::makeSizeT(ArgType(Ctx.getSizeType(), "size_t")); case LengthModifier::AsInt3264: return Ctx.getTargetInfo().getTriple().isArch64Bit() ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64") Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -6374,6 +6374,11 @@ QualType CastTy; std::tie(CastTy, CastTyName) = shouldNotPrintDirectly(S.Context, IntendedTy, E); if (!CastTy.isNull()) { + // %zi/%zu are ok to use for NSInteger/NSUInteger of type int + // (long in ASTContext). + if ((CastTyName == "NSInteger" || CastTyName == "NSUInteger") && + AT.isSizeT() && AT.matchesType(S.Context, CastTy)) + return true; IntendedTy = CastTy; ShouldNotPrintDirectly = true; } Index: test/SemaObjC/format-size-spec-nsinteger.m =================================================================== --- /dev/null +++ test/SemaObjC/format-size-spec-nsinteger.m @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple thumbv7-apple-ios -Wformat -fsyntax-only -fblocks -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -triple arm64-apple-ios -Wformat -fsyntax-only -fblocks -verify -Wno-objc-root-class %s +// expected-no-diagnostics + +#if __LP64__ +typedef unsigned long NSUInteger; +typedef long NSInteger; +#else +typedef unsigned int NSUInteger; +typedef int NSInteger; +#endif + +@class NSString; + +extern void NSLog(NSString *format, ...); + +void testSizeSpecifier() { + NSInteger i = 0; + NSUInteger j = 0; + NSLog(@"max NSInteger = %zi", i); + NSLog(@"max NSUinteger = %zu", j); +}