diff options
author | Ted Kremenek <kremenek@apple.com> | 2010-02-16 01:46:59 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2010-02-16 01:46:59 +0000 |
commit | 4e4b30ec62d78b24e6556fea2624855c193d0e3e (patch) | |
tree | c4d277ecc23e3313dd7145b30d068e3fb16c4b44 | |
parent | 252bee91e9961d9182323c4017092f90d6a09b1d (diff) |
Refactor the logic for printf argument type-checking into analyze_printf::ArgTypeResult.
Implement printf argument type checking for '%s'.
Fixes <rdar://problem/3065808>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96310 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Analysis/Analyses/PrintfFormatString.h | 97 | ||||
-rw-r--r-- | lib/Analysis/PrintfFormatString.cpp | 175 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 159 | ||||
-rw-r--r-- | test/Sema/block-printf-attribute-1.c | 5 | ||||
-rw-r--r-- | test/Sema/format-strings.c | 6 |
5 files changed, 263 insertions, 179 deletions
diff --git a/include/clang/Analysis/Analyses/PrintfFormatString.h b/include/clang/Analysis/Analyses/PrintfFormatString.h index 4a02831a6d..a4ad0b7037 100644 --- a/include/clang/Analysis/Analyses/PrintfFormatString.h +++ b/include/clang/Analysis/Analyses/PrintfFormatString.h @@ -18,11 +18,39 @@ #include "clang/AST/CanonicalType.h" namespace clang { - + class ASTContext; - + namespace analyze_printf { +class ArgTypeResult { +public: + enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CStrTy, + WCStrTy }; +private: + const Kind K; + QualType T; + ArgTypeResult(bool) : K(InvalidTy) {} +public: + ArgTypeResult(Kind k = UnknownTy) : K(k) {} + ArgTypeResult(QualType t) : K(SpecificTy), T(t) {} + ArgTypeResult(CanQualType t) : K(SpecificTy), T(t) {} + + static ArgTypeResult Invalid() { return ArgTypeResult(true); } + + bool isValid() const { return K != InvalidTy; } + + const QualType *getSpecificType() const { + return K == SpecificTy ? &T : 0; + } + + bool matchesType(ASTContext &C, QualType argTy) const; + + bool matchesAnyObjCObjectRef() const { return K == ObjCPointerTy; } + + QualType getRepresentativeType(ASTContext &C) const; +}; + class ConversionSpecifier { public: enum Kind { @@ -73,11 +101,11 @@ public: const char *getStart() const { return Position; } - + llvm::StringRef getCharacters() const { return llvm::StringRef(getStart(), getLength()); } - + bool consumesDataArgument() const { switch (kind) { case PercentArg: @@ -87,7 +115,7 @@ public: return true; } } - + bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; } bool isIntArg() const { return kind >= dArg && kind <= iArg; } bool isUIntArg() const { return kind >= oArg && kind <= XArg; } @@ -98,7 +126,7 @@ public: // single characters, but we be flexible. return 1; } - + private: const char *Position; Kind kind; @@ -121,19 +149,19 @@ class OptionalAmount { public: enum HowSpecified { NotSpecified, Constant, Arg }; - OptionalAmount(HowSpecified h, const char *st) + OptionalAmount(HowSpecified h, const char *st) : start(st), hs(h), amt(0) {} OptionalAmount() : start(0), hs(NotSpecified), amt(0) {} - - OptionalAmount(unsigned i, const char *st) + + OptionalAmount(unsigned i, const char *st) : start(st), hs(Constant), amt(i) {} HowSpecified getHowSpecified() const { return hs; } bool hasDataArgument() const { return hs == Arg; } - unsigned getConstantAmount() const { + unsigned getConstantAmount() const { assert(hs == Constant); return amt; } @@ -141,33 +169,14 @@ public: const char *getStart() const { return start; } - + + ArgTypeResult getArgType(ASTContext &Ctx) const; + private: const char *start; HowSpecified hs; unsigned amt; }; - -class ArgTypeResult { - enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy }; - const Kind K; - QualType T; - ArgTypeResult(bool) : K(InvalidTy) {} -public: - ArgTypeResult() : K(UnknownTy) {} - ArgTypeResult(QualType t) : K(SpecificTy), T(t) {} - ArgTypeResult(CanQualType t) : K(SpecificTy), T(t) {} - - static ArgTypeResult Invalid() { return ArgTypeResult(true); } - - bool isValid() const { return K != InvalidTy; } - - const QualType *getSpecificType() const { - return K == SpecificTy ? &T : 0; - } - - bool matchesAnyObjCObjectRef() const { return K == ObjCPointerTy; } -}; class FormatSpecifier { LengthModifier LM; @@ -184,7 +193,7 @@ public: FormatSpecifier() : LM(None), IsLeftJustified(0), HasPlusPrefix(0), HasSpacePrefix(0), HasAlternativeForm(0), HasLeadingZeroes(0) {} - + static FormatSpecifier Parse(const char *beg, const char *end); // Methods for incrementally constructing the FormatSpecifier. @@ -209,23 +218,23 @@ public: LengthModifier getLengthModifier() const { return LM; } - + const OptionalAmount &getFieldWidth() const { return FieldWidth; } - + void setFieldWidth(const OptionalAmount &Amt) { FieldWidth = Amt; } - + void setPrecision(const OptionalAmount &Amt) { Precision = Amt; } - + const OptionalAmount &getPrecision() const { return Precision; } - + /// \brief Returns the builtin type that a data argument /// paired with this format specifier should have. This method /// will return null if the format specifier does not have @@ -236,7 +245,7 @@ public: bool isLeftJustified() const { return (bool) IsLeftJustified; } bool hasPlusPrefix() const { return (bool) HasPlusPrefix; } bool hasAlternativeForm() const { return (bool) HasAlternativeForm; } - bool hasLeadingZeros() const { return (bool) HasLeadingZeroes; } + bool hasLeadingZeros() const { return (bool) HasLeadingZeroes; } bool hasSpacePrefix() const { return (bool) HasSpacePrefix; } }; @@ -244,24 +253,24 @@ class FormatStringHandler { public: FormatStringHandler() {} virtual ~FormatStringHandler(); - + virtual void HandleIncompleteFormatSpecifier(const char *startSpecifier, unsigned specifierLen) {} virtual void HandleNullChar(const char *nullCharacter) {} - - virtual void + + virtual void HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, const char *startSpecifier, unsigned specifierLen) {} - + virtual bool HandleFormatSpecifier(const analyze_printf::FormatSpecifier &FS, const char *startSpecifier, unsigned specifierLen) { return true; } }; - + bool ParseFormatString(FormatStringHandler &H, const char *beg, const char *end); diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp index 28d6b4f0e7..55abd10771 100644 --- a/lib/Analysis/PrintfFormatString.cpp +++ b/lib/Analysis/PrintfFormatString.cpp @@ -33,7 +33,7 @@ public: const FormatSpecifier &fs) : FS(fs), Start(start), Stop(false) {} - + const char *getStart() const { return Start; } bool shouldStop() const { return Stop; } bool hasValue() const { return Start != 0; } @@ -52,16 +52,20 @@ class UpdateOnReturn { public: UpdateOnReturn(T &valueToUpdate, const T &valueToCopy) : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {} - + ~UpdateOnReturn() { ValueToUpdate = ValueToCopy; } -}; +}; + +//===----------------------------------------------------------------------===// +// Methods for parsing format strings. +//===----------------------------------------------------------------------===// static OptionalAmount ParseAmount(const char *&Beg, const char *E) { const char *I = Beg; UpdateOnReturn <const char*> UpdateBeg(Beg, I); - + bool foundDigits = false; unsigned accumulator = 0; @@ -75,24 +79,24 @@ static OptionalAmount ParseAmount(const char *&Beg, const char *E) { if (foundDigits) return OptionalAmount(accumulator, Beg); - + if (c == '*') { ++I; return OptionalAmount(OptionalAmount::Arg, Beg); } - + break; } - - return OptionalAmount(); + + return OptionalAmount(); } static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, const char *&Beg, const char *E) { - + using namespace clang::analyze_printf; - + const char *I = Beg; const char *Start = 0; UpdateOnReturn <const char*> UpdateBeg(Beg, I); @@ -110,19 +114,19 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, break; } } - + // No format specifier found? if (!Start) return false; - + if (I == E) { // No more characters left? H.HandleIncompleteFormatSpecifier(Start, E - Start); return true; } - + FormatSpecifier FS; - + // Look for flags (if any). bool hasMore = true; for ( ; I != E; ++I) { @@ -136,31 +140,31 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, } if (!hasMore) break; - } + } if (I == E) { // No more characters left? H.HandleIncompleteFormatSpecifier(Start, E - Start); return true; } - + // Look for the field width (if any). FS.setFieldWidth(ParseAmount(I, E)); - + if (I == E) { // No more characters left? H.HandleIncompleteFormatSpecifier(Start, E - Start); return true; - } - - // Look for the precision (if any). + } + + // Look for the precision (if any). if (*I == '.') { ++I; if (I == E) { H.HandleIncompleteFormatSpecifier(Start, E - Start); return true; } - + FS.setPrecision(ParseAmount(I, E)); if (I == E) { @@ -177,7 +181,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, break; case 'h': ++I; - lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort; + lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort; break; case 'l': ++I; @@ -190,7 +194,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, case 'q': lm = AsLongLong; ++I; break; } FS.setLengthModifier(lm); - + if (I == E) { // No more characters left? H.HandleIncompleteFormatSpecifier(Start, E - Start); @@ -202,7 +206,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, H.HandleNullChar(I); return true; } - + // Finally, look for the conversion specifier. const char *conversionPosition = I++; ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; @@ -228,7 +232,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, case 's': k = ConversionSpecifier::CStrArg; break; case 'p': k = ConversionSpecifier::VoidPtrArg; break; case 'n': k = ConversionSpecifier::OutIntPtrArg; break; - case '%': k = ConversionSpecifier::PercentArg; break; + case '%': k = ConversionSpecifier::PercentArg; break; // Objective-C. case '@': k = ConversionSpecifier::ObjCObjArg; break; // Glibc specific. @@ -260,24 +264,127 @@ bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H, if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(), I - FSR.getStart())) return true; - } - assert(I == E && "Format string not exhausted"); + } + assert(I == E && "Format string not exhausted"); return false; } FormatStringHandler::~FormatStringHandler() {} //===----------------------------------------------------------------------===// +// Methods on ArgTypeResult. +//===----------------------------------------------------------------------===// + +bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { + assert(isValid()); + + if (K == UnknownTy) + return true; + + if (K == SpecificTy) { + argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + + if (T == argTy) + return true; + + if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + return T == C.UnsignedCharTy; + case BuiltinType::Char_U: + case BuiltinType::UChar: + return T == C.SignedCharTy; + case BuiltinType::Short: + return T == C.UnsignedShortTy; + case BuiltinType::UShort: + return T == C.ShortTy; + case BuiltinType::Int: + return T == C.UnsignedIntTy; + case BuiltinType::UInt: + return T == C.IntTy; + case BuiltinType::Long: + return T == C.UnsignedLongTy; + case BuiltinType::ULong: + return T == C.LongTy; + case BuiltinType::LongLong: + return T == C.UnsignedLongLongTy; + case BuiltinType::ULongLong: + return T == C.LongLongTy; + } + + return false; + } + + if (K == CStrTy) { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + + QualType pointeeTy = PT->getPointeeType(); + + if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Void: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + return true; + default: + break; + } + + return false; + } + + if (K == WCStrTy) { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + + QualType pointeeTy = PT->getPointeeType(); + return pointeeTy == C.WCharTy; + } + + return false; +} + +QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { + assert(isValid()); + if (K == SpecificTy) + return T; + if (K == CStrTy) + return C.getPointerType(C.CharTy); + if (K == WCStrTy) + return C.getPointerType(C.WCharTy); + if (K == ObjCPointerTy) + return C.ObjCBuiltinIdTy; + + return QualType(); +} + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +ArgTypeResult OptionalAmount::getArgType(ASTContext &Ctx) const { + return Ctx.IntTy; +} + +//===----------------------------------------------------------------------===// // Methods on FormatSpecifier. //===----------------------------------------------------------------------===// ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { if (!CS.consumesDataArgument()) return ArgTypeResult::Invalid(); - + if (CS.isIntArg()) switch (LM) { - case AsLongDouble: + case AsLongDouble: return ArgTypeResult::Invalid(); case None: return Ctx.IntTy; case AsChar: return Ctx.SignedCharTy; @@ -293,7 +400,7 @@ ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { if (CS.isUIntArg()) switch (LM) { - case AsLongDouble: + case AsLongDouble: return ArgTypeResult::Invalid(); case None: return Ctx.UnsignedIntTy; case AsChar: return Ctx.UnsignedCharTy; @@ -303,7 +410,7 @@ ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { case AsIntMax: // FIXME: Return unknown for now. return ArgTypeResult(); - case AsSizeT: + case AsSizeT: // FIXME: How to get the corresponding unsigned // version of size_t? return ArgTypeResult(); @@ -312,13 +419,17 @@ ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { // version of ptrdiff_t? return ArgTypeResult(); } - + if (CS.isDoubleArg()) { if (LM == AsLongDouble) return Ctx.LongDoubleTy; return Ctx.DoubleTy; } + if (CS.getKind() == ConversionSpecifier::CStrArg) + return ArgTypeResult(LM == AsWideChar ? ArgTypeResult::WCStrTy + : ArgTypeResult::CStrTy); + // FIXME: Handle other cases. return ArgTypeResult(); } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index f9466ca385..b62cd19a0b 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -754,7 +754,7 @@ bool Sema::SemaBuiltinEHReturnDataRegNo(CallExpr *TheCall) { if (!TheCall->getArg(0)->isIntegerConstantExpr(Result, Context)) return Diag(TheCall->getLocStart(), diag::err_expr_not_ice) << TheCall->getArg(0)->getSourceRange(); - + return false; } @@ -930,7 +930,7 @@ Sema::CheckNonNullArguments(const NonNullAttr *NonNull, for (NonNullAttr::iterator i = NonNull->begin(), e = NonNull->end(); i != e; ++i) { const Expr *ArgExpr = TheCall->getArg(*i); - if (ArgExpr->isNullPointerConstant(Context, + if (ArgExpr->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull)) Diag(TheCall->getCallee()->getLocStart(), diag::warn_null_arg) << ArgExpr->getSourceRange(); @@ -1049,7 +1049,7 @@ class CheckPrintfHandler : public analyze_printf::FormatStringHandler { const bool HasVAListArg; const CallExpr *TheCall; unsigned FormatIdx; -public: +public: CheckPrintfHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, unsigned numDataArgs, bool isObjCLiteral, @@ -1060,19 +1060,19 @@ public: IsObjCLiteral(isObjCLiteral), Beg(beg), HasVAListArg(hasVAListArg), TheCall(theCall), FormatIdx(formatIdx) {} - + void DoneProcessing(); - + void HandleIncompleteFormatSpecifier(const char *startSpecifier, unsigned specifierLen); - + void HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, const char *startSpecifier, unsigned specifierLen); - + void HandleNullChar(const char *nullCharacter); - + bool HandleFormatSpecifier(const analyze_printf::FormatSpecifier &FS, const char *startSpecifier, unsigned specifierLen); @@ -1081,16 +1081,14 @@ private: SourceRange getFormatSpecifierRange(const char *startSpecifier, unsigned specifierLen); SourceLocation getLocationOfByte(const char *x); - + bool HandleAmount(const analyze_printf::OptionalAmount &Amt, unsigned MissingArgDiag, unsigned BadTypeDiag, const char *startSpecifier, unsigned specifierLen); void HandleFlags(const analyze_printf::FormatSpecifier &FS, llvm::StringRef flag, llvm::StringRef cspec, const char *startSpecifier, unsigned specifierLen); - - bool MatchType(QualType A, QualType B, bool ignoreSign); - + const Expr *getDataArg(unsigned i) const; }; } @@ -1106,12 +1104,12 @@ getFormatSpecifierRange(const char *startSpecifier, unsigned specifierLen) { } SourceLocation CheckPrintfHandler::getLocationOfByte(const char *x) { - return S.getLocationOfStringLiteralByte(FExpr, x - Beg); + return S.getLocationOfStringLiteralByte(FExpr, x - Beg); } void CheckPrintfHandler:: HandleIncompleteFormatSpecifier(const char *startSpecifier, - unsigned specifierLen) { + unsigned specifierLen) { SourceLocation Loc = getLocationOfByte(startSpecifier); S.Diag(Loc, diag::warn_printf_incomplete_specifier) << getFormatSpecifierRange(startSpecifier, specifierLen); @@ -1121,14 +1119,14 @@ void CheckPrintfHandler:: HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, const char *startSpecifier, unsigned specifierLen) { - + ++NumConversions; const analyze_printf::ConversionSpecifier &CS = - FS.getConversionSpecifier(); + FS.getConversionSpecifier(); SourceLocation Loc = getLocationOfByte(CS.getStart()); S.Diag(Loc, diag::warn_printf_invalid_conversion) << llvm::StringRef(CS.getStart(), CS.getLength()) - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getFormatSpecifierRange(startSpecifier, specifierLen); } void CheckPrintfHandler::HandleNullChar(const char *nullCharacter) { @@ -1139,49 +1137,10 @@ void CheckPrintfHandler::HandleNullChar(const char *nullCharacter) { } const Expr *CheckPrintfHandler::getDataArg(unsigned i) const { - return TheCall->getArg(FormatIdx + i); + return TheCall->getArg(FormatIdx + i); } -bool CheckPrintfHandler::MatchType(QualType A, QualType B, bool ignoreSign) { - A = S.Context.getCanonicalType(A).getUnqualifiedType(); - B = S.Context.getCanonicalType(B).getUnqualifiedType(); - - if (A == B) - return true; - - if (ignoreSign) { - if (const BuiltinType *BT = B->getAs<BuiltinType>()) { - switch (BT->getKind()) { - default: - return false; - case BuiltinType::Char_S: - case BuiltinType::SChar: - return A == S.Context.UnsignedCharTy; - case BuiltinType::Char_U: - case BuiltinType::UChar: - return A == S.Context.SignedCharTy; - case BuiltinType::Short: - return A == S.Context.UnsignedShortTy; - case BuiltinType::UShort: - return A == S.Context.ShortTy; - case BuiltinType::Int: - return A == S.Context.UnsignedIntTy; - case BuiltinType::UInt: - return A == S.Context.IntTy; - case BuiltinType::Long: - return A == S.Context.UnsignedLongTy; - case BuiltinType::ULong: - return A == S.Context.LongTy; - case BuiltinType::LongLong: - return A == S.Context.UnsignedLongLongTy; - case BuiltinType::ULongLong: - return A == S.Context.LongLongTy; - } - return A == B; - } - } - return false; -} + void CheckPrintfHandler::HandleFlags(const analyze_printf::FormatSpecifier &FS, llvm::StringRef flag, @@ -1205,21 +1164,25 @@ CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt, if (!HasVAListArg) { if (NumConversions > NumDataArgs) { S.Diag(getLocationOfByte(Amt.getStart()), MissingArgDiag) - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getFormatSpecifierRange(startSpecifier, specifierLen); // Don't do any more checking. We will just emit // spurious errors. return false; } - + // Type check the data argument. It should be an 'int'. // Although not in conformance with C99, we also allow the argument to be // an 'unsigned int' as that is a reasonably safe case. GCC also // doesn't emit a warning for that case. const Expr *Arg = getDataArg(NumConversions); QualType T = Arg->getType(); - if (!MatchType(T, S.Context.IntTy, true)) { + + const analyze_printf::ArgTypeResult &ATR = Amt.getArgType(S.Context); + assert(ATR.isValid()); + + if (!ATR.matchesType(S.Context, T)) { S.Diag(getLocationOfByte(Amt.getStart()), BadTypeDiag) - << S.Context.IntTy << T + << ATR.getRepresentativeType(S.Context) << T << getFormatSpecifierRange(startSpecifier, specifierLen) << Arg->getSourceRange(); // Don't do any more checking. We will just emit @@ -1248,7 +1211,7 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier startSpecifier, specifierLen)) { return false; } - + if (!HandleAmount(FS.getPrecision(), diag::warn_printf_asterisk_precision_missing_arg, diag::warn_printf_asterisk_precision_wrong_type, @@ -1260,7 +1223,7 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier // in a non-ObjC literal. if (!IsObjCLiteral && CS.isObjCArg()) { HandleInvalidConversionSpecifier(FS, startSpecifier, specifierLen); - + // Continue checking the other format specifiers. return true; } @@ -1270,27 +1233,27 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier // makes no sense. Worth issuing a warning at some point. return true; } - - ++NumConversions; - + + ++NumConversions; + // Are we using '%n'? Issue a warning about this being // a possible security issue. if (CS.getKind() == ConversionSpecifier::OutIntPtrArg) { S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_write_back) - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getFormatSpecifierRange(startSpecifier, specifierLen); // Continue checking the other format specifiers. return true; } if (CS.getKind() == ConversionSpecifier::VoidPtrArg) { if (FS.getPrecision().getHowSpecified() != OptionalAmount::NotSpecified) - S.Diag(getLocationOfByte(CS.getStart()), + S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_nonsensical_precision) << CS.getCharacters() << getFormatSpecifierRange(startSpecifier, specifierLen); } - if (CS.getKind() == ConversionSpecifier::VoidPtrArg || - CS.getKind() == ConversionSpecifier::CStrArg) { + if (CS.getKind() == ConversionSpecifier::VoidPtrArg || + CS.getKind() == ConversionSpecifier::CStrArg) { // FIXME: Instead of using "0", "+", etc., eventually get them from // the FormatSpecifier. if (FS.hasLeadingZeros()) @@ -1299,42 +1262,38 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier HandleFlags(FS, "+", CS.getCharacters(), startSpecifier, specifierLen); if (FS.hasSpacePrefix()) HandleFlags(FS, " ", CS.getCharacters(), startSpecifier, specifierLen); - } - + } + // The remaining checks depend on the data arguments. if (HasVAListArg) return true; - + if (NumConversions > NumDataArgs) { S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_insufficient_data_args) - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getFormatSpecifierRange(startSpecifier, specifierLen); // Don't do any more checking. return false; } - + // Now type check the data expression that matches the // format specifier. const Expr *Ex = getDataArg(NumConversions); const analyze_printf::ArgTypeResult &ATR = FS.getArgType(S.Context); - - if (const QualType *T = ATR.getSpecificType()) { - if (!MatchType(*T, Ex->getType(), true)) { - // Check if we didn't match because of an implicit cast from a 'char' - // or 'short' to an 'int'. This is done because printf is a varargs - // function. - if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Ex)) - if (ICE->getType() == S.Context.IntTy) - if (MatchType(*T, ICE->getSubExpr()->getType(), true)) - return true; + if (ATR.isValid() && !ATR.matchesType(S.Context, Ex->getType())) { + // Check if we didn't match because of an implicit cast from a 'char' + // or 'short' to an 'int'. This is done because printf is a varargs + // function. + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Ex)) + if (ICE->getType() == S.Context.IntTy) + if (ATR.matchesType(S.Context, ICE->getSubExpr()->getType())) + return true; - S.Diag(getLocationOfByte(CS.getStart()), - diag::warn_printf_conversion_argument_type_mismatch) - << *T << Ex->getType() + S.Diag(getLocationOfByte(CS.getStart()), + diag::warn_printf_conversion_argument_type_mismatch) + << ATR.getRepresentativeType(S.Context) << Ex->getType() << getFormatSpecifierRange(startSpecifier, specifierLen) << Ex->getSourceRange(); - } - return true; } return true; @@ -1361,19 +1320,19 @@ void Sema::CheckPrintfString(const StringLiteral *FExpr, << OrigFormatExpr->getSourceRange(); return; } - + // Str - The format string. NOTE: this is NOT null-terminated! const char *Str = FExpr->getStrData(); - + // CHECK: empty format string? unsigned StrLen = FExpr->getByteLength(); - + if (StrLen == 0) { Diag(FExpr->getLocStart(), diag::warn_printf_empty_format_string) << OrigFormatExpr->getSourceRange(); return; } - + CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, TheCall->getNumArgs() - firstDataArg, isa<ObjCStringLiteral>(OrigFormatExpr), Str, @@ -1407,11 +1366,11 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, if (C->hasBlockDeclRefExprs()) Diag(C->getLocStart(), diag::err_ret_local_block) << C->getSourceRange(); - + if (AddrLabelExpr *ALE = dyn_cast<AddrLabelExpr>(RetValExp)) Diag(ALE->getLocStart(), diag::warn_ret_addr_label) << ALE->getSourceRange(); - + } else if (lhsType->isReferenceType()) { // Perform checking for stack values returned by reference. // Check for a reference to the stack @@ -1887,7 +1846,7 @@ IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) { if (BO->getLHS()->getType()->isPointerType()) return IntRange::forType(C, E->getType()); // fallthrough - + default: break; } @@ -2328,7 +2287,7 @@ void Sema::CheckUnreachable(AnalysisContext &AC) { CFG *cfg = AC.getCFG(); if (cfg == 0) return; - + llvm::BitVector live(cfg->getNumBlockIDs()); // Mark all live things first. count = MarkLive(&cfg->getEntry(), live); @@ -2527,7 +2486,7 @@ void Sema::CheckFallThroughForFunctionDef(Decl *D, Stmt *Body, // which this code would then warn about. if (getDiagnostics().hasErrorOccurred()) return; - + bool ReturnsVoid = false; bool HasNoReturn = false; if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { diff --git a/test/Sema/block-printf-attribute-1.c b/test/Sema/block-printf-attribute-1.c index 8ea77ece12..c19b3788a3 100644 --- a/test/Sema/block-printf-attribute-1.c +++ b/test/Sema/block-printf-attribute-1.c @@ -6,7 +6,6 @@ int main() { void (^z) (int arg, const char * format, ...) __attribute__ ((__format__ (__printf__, 2, 3))) = ^ __attribute__ ((__format__ (__printf__, 2, 3))) (int arg, const char * format, ...) {}; - // FIXME: argument type poking not yet supportted. - z(1, "%s", 1); /* { dg-warning "format \\'\%s\\' expects type \\'char \\*\\'\, but argument 3 has type \\'int\\'" } */ - z(1, "%s", "HELLO"); // OK + z(1, "%s", 1); // expected-warning{{conversion specifies type 'char *' but the argument has type 'int'}} + z(1, "%s", "HELLO"); // no-warning } diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index 1bbc68cd0d..ab37e3abdd 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -183,6 +183,12 @@ void test11(void *p, char *s) { printf("%0s", p); // expected-warning{{flag '0' results in undefined behavior in 's' conversion specifier}} } +void test12() { + unsigned char buf[4]; + printf ("%.4s\n", buf); // no-warning + printf ("%.4s\n", &buf); // expected-result{{conversion specifies type 'char *' but the argument has type 'unsigned char (*)[4]'}} +} + 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))); |