diff options
author | dim <dim@FreeBSD.org> | 2011-05-02 19:39:53 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2011-05-02 19:39:53 +0000 |
commit | 110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab (patch) | |
tree | 64a10f4c4154739d4a8191d7e1b52ce497f4ebd6 /lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | |
parent | a0fb00f9837bd0d2e5948f16f6a6b82a7a628f51 (diff) | |
download | FreeBSD-src-110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab.zip FreeBSD-src-110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab.tar.gz |
Vendor import of clang trunk r130700:
http://llvm.org/svn/llvm-project/cfe/trunk@130700
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 204 |
1 files changed, 183 insertions, 21 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 7aff201..235b400 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -13,10 +13,9 @@ // //===----------------------------------------------------------------------===// -#include "BasicObjCFoundationChecks.h" - #include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/CheckerV2.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" @@ -24,7 +23,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" @@ -44,20 +42,21 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) { +static const char* GetReceiverNameType(const ObjCMessage &msg) { if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) - return ID->getTypeForDecl()->getAs<ObjCInterfaceType>(); - return NULL; + return ID->getIdentifier()->getNameStart(); + return 0; } -static const char* GetReceiverNameType(const ObjCMessage &msg) { - if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg)) - return ReceiverType->getDecl()->getIdentifier()->getNameStart(); - return NULL; -} +static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID, + llvm::StringRef ClassName) { + if (ID->getIdentifier()->getName() == ClassName) + return true; -static bool isNSString(llvm::StringRef ClassName) { - return ClassName == "NSString" || ClassName == "NSMutableString"; + if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) + return isReceiverClassOrSuperclass(Super, ClassName); + + return false; } static inline bool isNil(SVal X) { @@ -69,7 +68,7 @@ static inline bool isNil(SVal X) { //===----------------------------------------------------------------------===// namespace { - class NilArgChecker : public CheckerV2<check::PreObjCMessage> { + class NilArgChecker : public Checker<check::PreObjCMessage> { mutable llvm::OwningPtr<APIMisuse> BT; void WarnNilArg(CheckerContext &C, @@ -101,11 +100,11 @@ void NilArgChecker::WarnNilArg(CheckerContext &C, void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const { - const ObjCInterfaceType *ReceiverType = GetReceiverType(msg); - if (!ReceiverType) + const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); + if (!ID) return; - if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { + if (isReceiverClassOrSuperclass(ID, "NSString")) { Selector S = msg.getSelector(); if (S.isUnarySelector()) @@ -140,7 +139,7 @@ void NilArgChecker::checkPreObjCMessage(ObjCMessage msg, //===----------------------------------------------------------------------===// namespace { -class CFNumberCreateChecker : public CheckerV2< check::PreStmt<CallExpr> > { +class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { mutable llvm::OwningPtr<APIMisuse> BT; mutable IdentifierInfo* II; public: @@ -347,7 +346,7 @@ void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, //===----------------------------------------------------------------------===// namespace { -class CFRetainReleaseChecker : public CheckerV2< check::PreStmt<CallExpr> > { +class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { mutable llvm::OwningPtr<APIMisuse> BT; mutable IdentifierInfo *Retain, *Release; public: @@ -429,7 +428,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr* CE, //===----------------------------------------------------------------------===// namespace { -class ClassReleaseChecker : public CheckerV2<check::PreObjCMessage> { +class ClassReleaseChecker : public Checker<check::PreObjCMessage> { mutable Selector releaseS; mutable Selector retainS; mutable Selector autoreleaseS; @@ -479,6 +478,165 @@ void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg, } //===----------------------------------------------------------------------===// +// Check for passing non-Objective-C types to variadic methods that expect +// only Objective-C types. +//===----------------------------------------------------------------------===// + +namespace { +class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { + mutable Selector arrayWithObjectsS; + mutable Selector dictionaryWithObjectsAndKeysS; + mutable Selector setWithObjectsS; + mutable Selector initWithObjectsS; + mutable Selector initWithObjectsAndKeysS; + mutable llvm::OwningPtr<BugType> BT; + + bool isVariadicMessage(const ObjCMessage &msg) const; + +public: + void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; +}; +} + +/// isVariadicMessage - Returns whether the given message is a variadic message, +/// where all arguments must be Objective-C types. +bool +VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const { + const ObjCMethodDecl *MD = msg.getMethodDecl(); + + if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) + return false; + + Selector S = msg.getSelector(); + + if (msg.isInstanceMessage()) { + // FIXME: Ideally we'd look at the receiver interface here, but that's not + // useful for init, because alloc returns 'id'. In theory, this could lead + // to false positives, for example if there existed a class that had an + // initWithObjects: implementation that does accept non-Objective-C pointer + // types, but the chance of that happening is pretty small compared to the + // gains that this analysis gives. + const ObjCInterfaceDecl *Class = MD->getClassInterface(); + + // -[NSArray initWithObjects:] + if (isReceiverClassOrSuperclass(Class, "NSArray") && + S == initWithObjectsS) + return true; + + // -[NSDictionary initWithObjectsAndKeys:] + if (isReceiverClassOrSuperclass(Class, "NSDictionary") && + S == initWithObjectsAndKeysS) + return true; + + // -[NSSet initWithObjects:] + if (isReceiverClassOrSuperclass(Class, "NSSet") && + S == initWithObjectsS) + return true; + } else { + const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); + + // -[NSArray arrayWithObjects:] + if (isReceiverClassOrSuperclass(Class, "NSArray") && + S == arrayWithObjectsS) + return true; + + // -[NSDictionary dictionaryWithObjectsAndKeys:] + if (isReceiverClassOrSuperclass(Class, "NSDictionary") && + S == dictionaryWithObjectsAndKeysS) + return true; + + // -[NSSet setWithObjects:] + if (isReceiverClassOrSuperclass(Class, "NSSet") && + S == setWithObjectsS) + return true; + } + + return false; +} + +void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, + CheckerContext &C) const { + if (!BT) { + BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " + "Objective-C pointer types")); + + ASTContext &Ctx = C.getASTContext(); + arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); + dictionaryWithObjectsAndKeysS = + GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); + setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); + + initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); + initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); + } + + if (!isVariadicMessage(msg)) + return; + + // We are not interested in the selector arguments since they have + // well-defined types, so the compiler will issue a warning for them. + unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); + + // We're not interested in the last argument since it has to be nil or the + // compiler would have issued a warning for it elsewhere. + unsigned variadicArgsEnd = msg.getNumArgs() - 1; + + if (variadicArgsEnd <= variadicArgsBegin) + return; + + // Verify that all arguments have Objective-C types. + llvm::Optional<ExplodedNode*> errorNode; + const GRState *state = C.getState(); + + for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { + QualType ArgTy = msg.getArgType(I); + if (ArgTy->isObjCObjectPointerType()) + continue; + + // Block pointers are treaded as Objective-C pointers. + if (ArgTy->isBlockPointerType()) + continue; + + // Ignore pointer constants. + if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state))) + continue; + + // Ignore pointer types annotated with 'NSObject' attribute. + if (C.getASTContext().isObjCNSObjectType(ArgTy)) + continue; + + // Ignore CF references, which can be toll-free bridged. + if (cocoa::isCFObjectRef(ArgTy)) + continue; + + // Generate only one error node to use for all bug reports. + if (!errorNode.hasValue()) { + errorNode = C.generateNode(); + } + + if (!errorNode.getValue()) + continue; + + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + + if (const char *TypeName = GetReceiverNameType(msg)) + os << "Argument to '" << TypeName << "' method '"; + else + os << "Argument to method '"; + + os << msg.getSelector().getAsString() + << "' should be an Objective-C pointer type, not '" + << ArgTy.getAsString() << "'"; + + RangedBugReport *R = new RangedBugReport(*BT, os.str(), + errorNode.getValue()); + R->addRange(msg.getArgSourceRange(I)); + C.EmitReport(R); + } +} + +//===----------------------------------------------------------------------===// // Check registration. //===----------------------------------------------------------------------===// @@ -497,3 +655,7 @@ void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { void ento::registerClassReleaseChecker(CheckerManager &mgr) { mgr.registerChecker<ClassReleaseChecker>(); } + +void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { + mgr.registerChecker<VariadicMethodTypeChecker>(); +} |