Index: include/clang/Analysis/Analyses/FormatString.h =================================================================== --- include/clang/Analysis/Analyses/FormatString.h +++ include/clang/Analysis/Analyses/FormatString.h @@ -257,7 +257,12 @@ const Kind K; QualType T; const char *Name = nullptr; - bool Ptr = false, IsSizeT = false; + bool Ptr = false; + + /// The TypeKind identifies certain well-known types like size_t and + /// ptrdiff_t. + enum class TypeKind { DontCare, SizeT, PtrdiffT }; + TypeKind TK = TypeKind::DontCare; public: ArgType(Kind K = UnknownTy, const char *N = nullptr) : K(K), Name(N) {} @@ -267,7 +272,9 @@ static ArgType Invalid() { return ArgType(InvalidTy); } bool isValid() const { return K != InvalidTy; } - bool isSizeT() const { return IsSizeT; } + bool isSizeT() const { return TK == TypeKind::SizeT; } + + bool isPtrdiffT() const { return TK == TypeKind::PtrdiffT; } /// Create an ArgType which corresponds to the type pointer to A. static ArgType PtrTo(const ArgType& A) { @@ -280,7 +287,15 @@ /// Create an ArgType which corresponds to the size_t/ssize_t type. static ArgType makeSizeT(const ArgType &A) { ArgType Res = A; - Res.IsSizeT = true; + Res.TK = TypeKind::SizeT; + return Res; + } + + /// Create an ArgType which corresponds to the ptrdiff_t/unsigned ptrdiff_t + /// type. + static ArgType makePtrdiffT(const ArgType &A) { + ArgType Res = A; + Res.TK = TypeKind::PtrdiffT; return Res; } Index: lib/Analysis/PrintfFormatString.cpp =================================================================== --- lib/Analysis/PrintfFormatString.cpp +++ lib/Analysis/PrintfFormatString.cpp @@ -472,7 +472,8 @@ ? ArgType(Ctx.LongLongTy, "__int64") : ArgType(Ctx.IntTy, "__int32"); case LengthModifier::AsPtrDiff: - return ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"); + return ArgType::makePtrdiffT( + ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: case LengthModifier::AsWide: @@ -505,7 +506,8 @@ ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64") : ArgType(Ctx.UnsignedIntTy, "unsigned __int32"); case LengthModifier::AsPtrDiff: - return ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t"); + return ArgType::makePtrdiffT( + ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t")); case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: case LengthModifier::AsWide: Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -6894,10 +6894,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 + // %zi/%zu and %td/%tu are OK to use for NSInteger/NSUInteger of type int // (long in ASTContext). Only complain to pedants. if ((CastTyName == "NSInteger" || CastTyName == "NSUInteger") && - AT.isSizeT() && AT.matchesType(S.Context, CastTy)) + (AT.isSizeT() || AT.isPtrdiffT()) && + AT.matchesType(S.Context, CastTy)) Pedantic = true; IntendedTy = CastTy; ShouldNotPrintDirectly = true; Index: test/SemaObjC/format-size-spec-nsinteger.m =================================================================== --- test/SemaObjC/format-size-spec-nsinteger.m +++ test/SemaObjC/format-size-spec-nsinteger.m @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -triple thumbv7-apple-ios -Wno-objc-root-class -fsyntax-only -verify -Wformat %s // RUN: %clang_cc1 -triple thumbv7-apple-ios -Wno-objc-root-class -fsyntax-only -verify -Wformat-pedantic -DPEDANTIC %s +// RUN: %clang_cc1 -triple thumbv7k-apple-watchos2.0.0 -fsyntax-only -fblocks -verify %s +// RUN: %clang_cc1 -triple thumbv7k-apple-watchos2.0.0 -fsyntax-only -fblocks -verify -Wformat-pedantic -DPEDANTIC %s #if !defined(PEDANTIC) // expected-no-diagnostics @@ -8,9 +10,16 @@ #if __LP64__ typedef unsigned long NSUInteger; typedef long NSInteger; +typedef long ptrdiff_t; #else typedef unsigned int NSUInteger; typedef int NSInteger; +#if __is_target_os(watchos) + // Watch ABI uses long for ptrdiff_t. + typedef long ptrdiff_t; +#else + typedef int ptrdiff_t; +#endif #endif @class NSString; @@ -28,3 +37,16 @@ // expected-warning@-4 {{values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead}} #endif } + +void testPtrdiffSpecifier(ptrdiff_t x) { + NSInteger i = 0; + NSUInteger j = 0; + + NSLog(@"ptrdiff_t NSUinteger: %tu", j); + NSLog(@"ptrdiff_t NSInteger: %td", i); + NSLog(@"ptrdiff_t %tu, %td", x, x); +#if __is_target_os(watchos) && defined(PEDANTIC) + // expected-warning@-4 {{values of type 'NSUInteger' should not be used as format arguments; add an explicit cast to 'unsigned long' instead}} + // expected-warning@-4 {{values of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead}} +#endif +}