aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-06-11 16:40:41 +0000
committerJordan Rose <jordan_rose@apple.com>2012-06-11 16:40:41 +0000
commit1895a0a6936001374f66adbdfcf8abe5edf912ea (patch)
tree8b5d352892a92fdbb74aa23a7241f069b6f5f824 /lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
parent9765ea9f755be50bb571100b44865f488e958d6d (diff)
[analyzer] Add ObjCLoopChecker: objects from NSArray et al are non-nil.
While collections containing nil elements can still be iterated over in an Objective-C for-in loop, the most common Cocoa collections -- NSArray, NSDictionary, and NSSet -- cannot contain nil elements. This checker adds that assumption to the analyzer state. This was the cause of some minor false positives concerning CFRelease calls on objects in an NSArray. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@158319 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp74
1 files changed, 74 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index d4632488e9..c75c86faf4 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -27,6 +27,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/AST/ASTContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
@@ -650,6 +651,75 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
}
//===----------------------------------------------------------------------===//
+// Improves the modeling of loops over Cocoa collections.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ObjCLoopChecker
+ : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
+
+public:
+ void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
+};
+}
+
+static bool isKnownNonNilCollectionType(QualType T) {
+ const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
+ if (!PT)
+ return false;
+
+ const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
+ if (!ID)
+ return false;
+
+ switch (findKnownClass(ID)) {
+ case FC_NSArray:
+ case FC_NSDictionary:
+ case FC_NSEnumerator:
+ case FC_NSOrderedSet:
+ case FC_NSSet:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Check if this is the branch for the end of the loop.
+ SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
+ if (CollectionSentinel.isZeroConstant())
+ return;
+
+ // See if the collection is one where we /know/ the elements are non-nil.
+ const Expr *Collection = FCS->getCollection();
+ if (!isKnownNonNilCollectionType(Collection->getType()))
+ return;
+
+ // FIXME: Copied from ExprEngineObjC.
+ const Stmt *Element = FCS->getElement();
+ SVal ElementVar;
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
+ const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
+ assert(ElemDecl->getInit() == 0);
+ ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
+ } else {
+ ElementVar = State->getSVal(Element, C.getLocationContext());
+ }
+
+ if (!isa<Loc>(ElementVar))
+ return;
+
+ // Go ahead and assume the value is non-nil.
+ SVal Val = State->getSVal(cast<Loc>(ElementVar));
+ State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
+ C.addTransition(State);
+}
+
+
+//===----------------------------------------------------------------------===//
// Check registration.
//===----------------------------------------------------------------------===//
@@ -672,3 +742,7 @@ void ento::registerClassReleaseChecker(CheckerManager &mgr) {
void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<VariadicMethodTypeChecker>();
}
+
+void ento::registerObjCLoopChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCLoopChecker>();
+}