diff --git a/clang/include/clang/Analysis/Analyses/FormatString.h b/clang/include/clang/Analysis/Analyses/FormatString.h index a2bd19c22833..ac15cfc64dee 100644 --- a/clang/include/clang/Analysis/Analyses/FormatString.h +++ b/clang/include/clang/Analysis/Analyses/FormatString.h @@ -199,7 +199,7 @@ protected: class ArgTypeResult { public: enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, - CStrTy, WCStrTy, WIntTy }; + AnyCharTy, CStrTy, WCStrTy, WIntTy }; private: const Kind K; QualType T; diff --git a/clang/lib/Analysis/FormatString.cpp b/clang/lib/Analysis/FormatString.cpp index 0f807e21e7fc..6498ded4e374 100644 --- a/clang/lib/Analysis/FormatString.cpp +++ b/clang/lib/Analysis/FormatString.cpp @@ -183,13 +183,13 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, return false; case 'h': ++I; - lmKind = (I != E && *I == 'h') ? - ++I, LengthModifier::AsChar : LengthModifier::AsShort; + lmKind = (I != E && *I == 'h') ? (++I, LengthModifier::AsChar) + : LengthModifier::AsShort; break; case 'l': ++I; - lmKind = (I != E && *I == 'l') ? - ++I, LengthModifier::AsLongLong : LengthModifier::AsLong; + lmKind = (I != E && *I == 'l') ? (++I, LengthModifier::AsLongLong) + : LengthModifier::AsLong; break; case 'j': lmKind = LengthModifier::AsIntMax; ++I; break; case 'z': lmKind = LengthModifier::AsSizeT; ++I; break; @@ -213,7 +213,21 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { case UnknownTy: return true; - + + case AnyCharTy: { + if (const BuiltinType *BT = argTy->getAs()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::UChar: + case BuiltinType::Char_U: + return true; + } + return false; + } + case SpecificTy: { argTy = C.getCanonicalType(argTy).getUnqualifiedType(); if (T == argTy) @@ -314,6 +328,8 @@ QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { llvm_unreachable("No representative type for Invalid ArgTypeResult"); case UnknownTy: return QualType(); + case AnyCharTy: + return C.CharTy; case SpecificTy: return T; case CStrTy: diff --git a/clang/lib/Analysis/PrintfFormatString.cpp b/clang/lib/Analysis/PrintfFormatString.cpp index affc83e7efb8..7347dca3c21e 100644 --- a/clang/lib/Analysis/PrintfFormatString.cpp +++ b/clang/lib/Analysis/PrintfFormatString.cpp @@ -297,7 +297,7 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsLongDouble: return ArgTypeResult::Invalid(); case LengthModifier::None: return Ctx.IntTy; - case LengthModifier::AsChar: return Ctx.SignedCharTy; + case LengthModifier::AsChar: return ArgTypeResult::AnyCharTy; case LengthModifier::AsShort: return Ctx.ShortTy; case LengthModifier::AsLong: return Ctx.LongTy; case LengthModifier::AsLongLong: return Ctx.LongLongTy; diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index 6b5f7e2c2662..4f2ad99a5727 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -212,6 +212,12 @@ void test12(char *b) { asprintf(&b, "%d", "asprintf"); // expected-warning{{conversion specifies type 'int' but the argument has type 'char *'}} } +void test13(short x) { + char bel = 007; + printf("bel: '0%hhd'\n", bel); // no-warning + printf("x: '0%hhd'\n", x); // expected-warning {{conversion specifies type 'char' but the argument has type 'short'}} +} + typedef struct __aslclient *aslclient; typedef struct __aslmsg *aslmsg; int asl_log(aslclient asl, aslmsg msg, int level, const char *format, ...) __attribute__((__format__ (__printf__, 4, 5)));