aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp739
1 files changed, 412 insertions, 327 deletions
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index b569e412c41f..5503b23f4ae6 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -23,10 +23,10 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableList.h"
@@ -66,8 +66,9 @@ public:
/// particular argument.
enum ArgEffect { DoNothing, Autorelease, Dealloc, DecRef, DecRefMsg,
DecRefBridgedTransfered,
+ DecRefAndStopTracking, DecRefMsgAndStopTracking,
IncRefMsg, IncRef, MakeCollectable, MayEscape,
- NewAutoreleasePool, SelfOwn, StopTracking };
+ NewAutoreleasePool, StopTracking };
namespace llvm {
template <> struct FoldingSetTrait<ArgEffect> {
@@ -351,6 +352,20 @@ struct ProgramStateTrait<RefBindings>
}
}
+static inline const RefVal *getRefBinding(ProgramStateRef State,
+ SymbolRef Sym) {
+ return State->get<RefBindings>(Sym);
+}
+
+static inline ProgramStateRef setRefBinding(ProgramStateRef State,
+ SymbolRef Sym, RefVal Val) {
+ return State->set<RefBindings>(Sym, Val);
+}
+
+static ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
+ return State->remove<RefBindings>(Sym);
+}
+
//===----------------------------------------------------------------------===//
// Function/Method behavior summaries.
//===----------------------------------------------------------------------===//
@@ -431,6 +446,12 @@ public:
bool isSimple() const {
return Args.isEmpty();
}
+
+private:
+ ArgEffects getArgEffects() const { return Args; }
+ ArgEffect getDefaultArgEffect() const { return DefaultArgEffect; }
+
+ friend class RetainSummaryManager;
};
} // end anonymous namespace
@@ -449,9 +470,6 @@ public:
ObjCSummaryKey(const ObjCInterfaceDecl *d, Selector s)
: II(d ? d->getIdentifier() : 0), S(s) {}
- ObjCSummaryKey(const ObjCInterfaceDecl *d, IdentifierInfo *ii, Selector s)
- : II(d ? d->getIdentifier() : ii), S(s) {}
-
ObjCSummaryKey(Selector s)
: II(0), S(s) {}
@@ -473,17 +491,14 @@ template <> struct DenseMapInfo<ObjCSummaryKey> {
}
static unsigned getHashValue(const ObjCSummaryKey &V) {
- return (DenseMapInfo<IdentifierInfo*>::getHashValue(V.getIdentifier())
- & 0x88888888)
- | (DenseMapInfo<Selector>::getHashValue(V.getSelector())
- & 0x55555555);
+ typedef std::pair<IdentifierInfo*, Selector> PairTy;
+ return DenseMapInfo<PairTy>::getHashValue(PairTy(V.getIdentifier(),
+ V.getSelector()));
}
static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) {
- return DenseMapInfo<IdentifierInfo*>::isEqual(LHS.getIdentifier(),
- RHS.getIdentifier()) &&
- DenseMapInfo<Selector>::isEqual(LHS.getSelector(),
- RHS.getSelector());
+ return LHS.getIdentifier() == RHS.getIdentifier() &&
+ LHS.getSelector() == RHS.getSelector();
}
};
@@ -498,21 +513,16 @@ class ObjCSummaryCache {
public:
ObjCSummaryCache() {}
- const RetainSummary * find(const ObjCInterfaceDecl *D, IdentifierInfo *ClsName,
- Selector S) {
- // Lookup the method using the decl for the class @interface. If we
- // have no decl, lookup using the class name.
- return D ? find(D, S) : find(ClsName, S);
- }
-
const RetainSummary * find(const ObjCInterfaceDecl *D, Selector S) {
// Do a lookup with the (D,S) pair. If we find a match return
// the iterator.
ObjCSummaryKey K(D, S);
MapTy::iterator I = M.find(K);
- if (I != M.end() || !D)
+ if (I != M.end())
return I->second;
+ if (!D)
+ return NULL;
// Walk the super chain. If we find a hit with a parent, we'll end
// up returning that summary. We actually allow that key (null,S), as
@@ -628,9 +638,6 @@ class RetainSummaryManager {
ArgEffects getArgEffects();
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
-
-public:
- RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
const RetainSummary *getUnarySummary(const FunctionType* FT,
UnaryFuncKind func);
@@ -648,6 +655,10 @@ public:
return getPersistentSummary(Summ);
}
+ const RetainSummary *getDoNothingSummary() {
+ return getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
+ }
+
const RetainSummary *getDefaultSummary() {
return getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing, MayEscape);
@@ -739,41 +750,32 @@ public:
InitializeMethodSummaries();
}
- const RetainSummary *getSummary(const FunctionDecl *FD);
+ const RetainSummary *getSummary(const CallEvent &Call,
+ ProgramStateRef State = 0);
- const RetainSummary *getMethodSummary(Selector S, IdentifierInfo *ClsName,
- const ObjCInterfaceDecl *ID,
+ const RetainSummary *getFunctionSummary(const FunctionDecl *FD);
+
+ const RetainSummary *getMethodSummary(Selector S, const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD,
QualType RetTy,
ObjCMethodSummariesTy &CachedSummaries);
- const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
- ProgramStateRef state,
- const LocationContext *LC);
-
- const RetainSummary *getInstanceMethodSummary(const ObjCMessage &msg,
- const ObjCInterfaceDecl *ID) {
- return getMethodSummary(msg.getSelector(), 0, ID, msg.getMethodDecl(),
- msg.getType(Ctx), ObjCMethodSummaries);
- }
+ const RetainSummary *getInstanceMethodSummary(const ObjCMethodCall &M,
+ ProgramStateRef State);
- const RetainSummary *getClassMethodSummary(const ObjCMessage &msg) {
- const ObjCInterfaceDecl *Class = 0;
- if (!msg.isInstanceMessage())
- Class = msg.getReceiverInterface();
+ const RetainSummary *getClassMethodSummary(const ObjCMethodCall &M) {
+ assert(!M.isInstanceMessage());
+ const ObjCInterfaceDecl *Class = M.getReceiverInterface();
- return getMethodSummary(msg.getSelector(), Class->getIdentifier(),
- Class, msg.getMethodDecl(), msg.getType(Ctx),
- ObjCClassMethodSummaries);
+ return getMethodSummary(M.getSelector(), Class, M.getDecl(),
+ M.getResultType(), ObjCClassMethodSummaries);
}
/// getMethodSummary - This version of getMethodSummary is used to query
/// the summary for the current method being analyzed.
const RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) {
- // FIXME: Eventually this should be unneeded.
const ObjCInterfaceDecl *ID = MD->getClassInterface();
Selector S = MD->getSelector();
- IdentifierInfo *ClsName = ID->getIdentifier();
QualType ResultTy = MD->getResultType();
ObjCMethodSummariesTy *CachedSummaries;
@@ -782,11 +784,11 @@ public:
else
CachedSummaries = &ObjCClassMethodSummaries;
- return getMethodSummary(S, ClsName, ID, MD, ResultTy, *CachedSummaries);
+ return getMethodSummary(S, ID, MD, ResultTy, *CachedSummaries);
}
const RetainSummary *getStandardMethodSummary(const ObjCMethodDecl *MD,
- Selector S, QualType RetTy);
+ Selector S, QualType RetTy);
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const ObjCMethodDecl *MD);
@@ -794,11 +796,18 @@ public:
void updateSummaryFromAnnotations(const RetainSummary *&Summ,
const FunctionDecl *FD);
+ void updateSummaryForCall(const RetainSummary *&Summ,
+ const CallEvent &Call);
+
bool isGCEnabled() const { return GCEnabled; }
bool isARCEnabled() const { return ARCEnabled; }
bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
+
+ RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; }
+
+ friend class RetainSummaryTemplate;
};
// Used to avoid allocating long-term (BPAlloc'd) memory for default retain
@@ -811,10 +820,8 @@ class RetainSummaryTemplate {
RetainSummary ScratchSummary;
bool Accessed;
public:
- RetainSummaryTemplate(const RetainSummary *&real, const RetainSummary &base,
- RetainSummaryManager &mgr)
- : Manager(mgr), RealSummary(real), ScratchSummary(real ? *real : base),
- Accessed(false) {}
+ RetainSummaryTemplate(const RetainSummary *&real, RetainSummaryManager &mgr)
+ : Manager(mgr), RealSummary(real), ScratchSummary(*real), Accessed(false) {}
~RetainSummaryTemplate() {
if (Accessed)
@@ -886,7 +893,101 @@ static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
return FName.find("MakeCollectable") != StringRef::npos;
}
-const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
+static ArgEffect getStopTrackingEquivalent(ArgEffect E) {
+ switch (E) {
+ case DoNothing:
+ case Autorelease:
+ case DecRefBridgedTransfered:
+ case IncRef:
+ case IncRefMsg:
+ case MakeCollectable:
+ case MayEscape:
+ case NewAutoreleasePool:
+ case StopTracking:
+ return StopTracking;
+ case DecRef:
+ case DecRefAndStopTracking:
+ return DecRefAndStopTracking;
+ case DecRefMsg:
+ case DecRefMsgAndStopTracking:
+ return DecRefMsgAndStopTracking;
+ case Dealloc:
+ return Dealloc;
+ }
+
+ llvm_unreachable("Unknown ArgEffect kind");
+}
+
+void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S,
+ const CallEvent &Call) {
+ if (Call.hasNonZeroCallbackArg()) {
+ ArgEffect RecEffect = getStopTrackingEquivalent(S->getReceiverEffect());
+ ArgEffect DefEffect = getStopTrackingEquivalent(S->getDefaultArgEffect());
+
+ ArgEffects CustomArgEffects = S->getArgEffects();
+ for (ArgEffects::iterator I = CustomArgEffects.begin(),
+ E = CustomArgEffects.end();
+ I != E; ++I) {
+ ArgEffect Translated = getStopTrackingEquivalent(I->second);
+ if (Translated != DefEffect)
+ ScratchArgs = AF.add(ScratchArgs, I->first, Translated);
+ }
+
+ RetEffect RE = RetEffect::MakeNoRet();
+
+ // Special cases where the callback argument CANNOT free the return value.
+ // This can generally only happen if we know that the callback will only be
+ // called when the return value is already being deallocated.
+ if (const FunctionCall *FC = dyn_cast<FunctionCall>(&Call)) {
+ IdentifierInfo *Name = FC->getDecl()->getIdentifier();
+
+ // This callback frees the associated buffer.
+ if (Name->isStr("CGBitmapContextCreateWithData"))
+ RE = S->getRetEffect();
+ }
+
+ S = getPersistentSummary(RE, RecEffect, DefEffect);
+ }
+}
+
+const RetainSummary *
+RetainSummaryManager::getSummary(const CallEvent &Call,
+ ProgramStateRef State) {
+ const RetainSummary *Summ;
+ switch (Call.getKind()) {
+ case CE_Function:
+ Summ = getFunctionSummary(cast<FunctionCall>(Call).getDecl());
+ break;
+ case CE_CXXMember:
+ case CE_CXXMemberOperator:
+ case CE_Block:
+ case CE_CXXConstructor:
+ case CE_CXXDestructor:
+ case CE_CXXAllocator:
+ // FIXME: These calls are currently unsupported.
+ return getPersistentStopSummary();
+ case CE_ObjCMessage: {
+ const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
+ if (Msg.isInstanceMessage())
+ Summ = getInstanceMethodSummary(Msg, State);
+ else
+ Summ = getClassMethodSummary(Msg);
+ break;
+ }
+ }
+
+ updateSummaryForCall(Summ, Call);
+
+ assert(Summ && "Unknown call type?");
+ return Summ;
+}
+
+const RetainSummary *
+RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
+ // If we don't know what function we're calling, use our default summary.
+ if (!FD)
+ return getDefaultSummary();
+
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
if (I != FuncSummaries.end())
@@ -894,6 +995,7 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// No summary? Generate one.
const RetainSummary *S = 0;
+ bool AllowAnnotations = true;
do {
// We generate "stop" summaries for implicitly defined functions.
@@ -901,13 +1003,6 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
S = getPersistentStopSummary();
break;
}
- // For C++ methods, generate an implicit "stop" summary as well. We
- // can relax this once we have a clear policy for C++ methods and
- // ownership attributes.
- if (isa<CXXMethodDecl>(FD)) {
- S = getPersistentStopSummary();
- break;
- }
// [PR 3337] Use 'getAs<FunctionType>' to strip away any typedefs on the
// function's type.
@@ -929,18 +1024,22 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// filters.
assert(ScratchArgs.isEmpty());
- if (FName == "pthread_create") {
- // Part of: <rdar://problem/7299394>. This will be addressed
- // better with IPA.
+ if (FName == "pthread_create" || FName == "pthread_setspecific") {
+ // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>.
+ // This will be addressed better with IPA.
S = getPersistentStopSummary();
} else if (FName == "NSMakeCollectable") {
// Handle: id NSMakeCollectable(CFTypeRef)
S = (RetTy->isObjCIdType())
? getUnarySummary(FT, cfmakecollectable)
: getPersistentStopSummary();
+ // The headers on OS X 10.8 use cf_consumed/ns_returns_retained,
+ // but we can fully model NSMakeCollectable ourselves.
+ AllowAnnotations = false;
} else if (FName == "IOBSDNameMatching" ||
FName == "IOServiceMatching" ||
FName == "IOServiceNameMatching" ||
+ FName == "IORegistryEntrySearchCFProperty" ||
FName == "IORegistryEntryIDMatching" ||
FName == "IOOpenFirmwarePathMatching") {
// Part of <rdar://problem/6961230>. (IOKit)
@@ -993,6 +1092,8 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
// libdispatch finalizers.
ScratchArgs = AF.add(ScratchArgs, 1, StopTracking);
S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing);
+ } else if (FName.startswith("NSLog")) {
+ S = getDoNothingSummary();
} else if (FName.startswith("NS") &&
(FName.find("Insert") != StringRef::npos)) {
// Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
@@ -1090,8 +1191,13 @@ const RetainSummary * RetainSummaryManager::getSummary(const FunctionDecl *FD) {
}
while (0);
+ // If we got all the way here without any luck, use a default summary.
+ if (!S)
+ S = getDefaultSummary();
+
// Annotations override defaults.
- updateSummaryFromAnnotations(S, FD);
+ if (AllowAnnotations)
+ updateSummaryFromAnnotations(S, FD);
FuncSummaries[FD] = S;
return S;
@@ -1152,7 +1258,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
if (!FD)
return;
- RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this);
+ assert(Summ && "Must have a summary to add annotations to.");
+ RetainSummaryTemplate Template(Summ, *this);
// Effects on the parameters.
unsigned parm_idx = 0;
@@ -1200,7 +1307,8 @@ RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
if (!MD)
return;
- RetainSummaryTemplate Template(Summ, *getDefaultSummary(), *this);
+ assert(Summ && "Must have a valid summary to add annotations to");
+ RetainSummaryTemplate Template(Summ, *this);
bool isTrackedLoc = false;
// Effects on the receiver.
@@ -1341,10 +1449,15 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
// because the reference count is quite possibly handled by a delegate
// method.
if (S.isKeywordSelector()) {
- const std::string &str = S.getAsString();
- assert(!str.empty());
- if (StrInStrNoCase(str, "delegate:") != StringRef::npos)
- ReceiverEff = StopTracking;
+ for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) {
+ StringRef Slot = S.getNameForSlot(i);
+ if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) {
+ if (ResultEff == ObjCInitRetE)
+ ResultEff = RetEffect::MakeNoRet();
+ else
+ ReceiverEff = StopTracking;
+ }
+ }
}
if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing &&
@@ -1355,56 +1468,46 @@ RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
}
const RetainSummary *
-RetainSummaryManager::getInstanceMethodSummary(const ObjCMessage &msg,
- ProgramStateRef state,
- const LocationContext *LC) {
-
- // We need the type-information of the tracked receiver object
- // Retrieve it from the state.
- const Expr *Receiver = msg.getInstanceReceiver();
- const ObjCInterfaceDecl *ID = 0;
-
- // FIXME: Is this really working as expected? There are cases where
- // we just use the 'ID' from the message expression.
- SVal receiverV;
-
- if (Receiver) {
- receiverV = state->getSValAsScalarOrLoc(Receiver, LC);
-
- // FIXME: Eventually replace the use of state->get<RefBindings> with
- // a generic API for reasoning about the Objective-C types of symbolic
- // objects.
- if (SymbolRef Sym = receiverV.getAsLocSymbol())
- if (const RefVal *T = state->get<RefBindings>(Sym))
- if (const ObjCObjectPointerType* PT =
+RetainSummaryManager::getInstanceMethodSummary(const ObjCMethodCall &Msg,
+ ProgramStateRef State) {
+ const ObjCInterfaceDecl *ReceiverClass = 0;
+
+ // We do better tracking of the type of the object than the core ExprEngine.
+ // See if we have its type in our private state.
+ // FIXME: Eventually replace the use of state->get<RefBindings> with
+ // a generic API for reasoning about the Objective-C types of symbolic
+ // objects.
+ SVal ReceiverV = Msg.getReceiverSVal();
+ if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
+ if (const RefVal *T = getRefBinding(State, Sym))
+ if (const ObjCObjectPointerType *PT =
T->getType()->getAs<ObjCObjectPointerType>())
- ID = PT->getInterfaceDecl();
+ ReceiverClass = PT->getInterfaceDecl();
- // FIXME: this is a hack. This may or may not be the actual method
- // that is called.
- if (!ID) {
- if (const ObjCObjectPointerType *PT =
- Receiver->getType()->getAs<ObjCObjectPointerType>())
- ID = PT->getInterfaceDecl();
- }
- } else {
- // FIXME: Hack for 'super'.
- ID = msg.getReceiverInterface();
- }
+ // If we don't know what kind of object this is, fall back to its static type.
+ if (!ReceiverClass)
+ ReceiverClass = Msg.getReceiverInterface();
// FIXME: The receiver could be a reference to a class, meaning that
// we should use the class method.
- return getInstanceMethodSummary(msg, ID);
+ // id x = [NSObject class];
+ // [x performSelector:... withObject:... afterDelay:...];
+ Selector S = Msg.getSelector();
+ const ObjCMethodDecl *Method = Msg.getDecl();
+ if (!Method && ReceiverClass)
+ Method = ReceiverClass->getInstanceMethod(S);
+
+ return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(),
+ ObjCMethodSummaries);
}
const RetainSummary *
-RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName,
- const ObjCInterfaceDecl *ID,
+RetainSummaryManager::getMethodSummary(Selector S, const ObjCInterfaceDecl *ID,
const ObjCMethodDecl *MD, QualType RetTy,
ObjCMethodSummariesTy &CachedSummaries) {
// Look up a summary in our summary cache.
- const RetainSummary *Summ = CachedSummaries.find(ID, ClsName, S);
+ const RetainSummary *Summ = CachedSummaries.find(ID, S);
if (!Summ) {
Summ = getStandardMethodSummary(MD, S, RetTy);
@@ -1413,7 +1516,7 @@ RetainSummaryManager::getMethodSummary(Selector S, IdentifierInfo *ClsName,
updateSummaryFromAnnotations(Summ, MD);
// Memoize the summary.
- CachedSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ;
+ CachedSummaries[ObjCSummaryKey(ID, S)] = Summ;
}
return Summ;
@@ -1430,29 +1533,6 @@ void RetainSummaryManager::InitializeClassMethodSummaries() {
addClassMethSummary("NSAutoreleasePool", "addObject",
getPersistentSummary(RetEffect::MakeNoRet(),
DoNothing, Autorelease));
-
- // Create the summaries for [NSObject performSelector...]. We treat
- // these as 'stop tracking' for the arguments because they are often
- // used for delegates that can release the object. When we have better
- // inter-procedural analysis we can potentially do something better. This
- // workaround is to remove false positives.
- const RetainSummary *Summ =
- getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking);
- IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject");
- addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
- "afterDelay", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject",
- "afterDelay", "inModes", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
- "withObject", "waitUntilDone", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread",
- "withObject", "waitUntilDone", "modes", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
- "withObject", "waitUntilDone", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread",
- "withObject", "waitUntilDone", "modes", NULL);
- addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground",
- "withObject", NULL);
}
void RetainSummaryManager::InitializeMethodSummaries() {
@@ -1558,6 +1638,10 @@ void RetainSummaryManager::InitializeMethodSummaries() {
//===----------------------------------------------------------------------===//
// AutoreleaseBindings - State used to track objects in autorelease pools.
//===----------------------------------------------------------------------===//
+#define AUTORELEASE_POOL_MODELING (0)
+// We do not currently have complete modeling of autorelease pools.
+
+#if AUTORELEASE_POOL_MODELING
typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts;
typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents;
@@ -1605,6 +1689,7 @@ SendAutorelease(ProgramStateRef state,
return state->set<AutoreleasePoolContents>(pool, newCnts);
}
+#endif
//===----------------------------------------------------------------------===//
// Error reporting.
@@ -1690,32 +1775,18 @@ namespace {
};
class Leak : public CFRefBug {
- const bool isReturn;
- protected:
- Leak(StringRef name, bool isRet)
- : CFRefBug(name), isReturn(isRet) {
+ public:
+ Leak(StringRef name)
+ : CFRefBug(name) {
// Leaks should not be reported if they are post-dominated by a sink.
setSuppressOnSink(true);
}
- public:
const char *getDescription() const { return ""; }
bool isLeak() const { return true; }
};
- class LeakAtReturn : public Leak {
- public:
- LeakAtReturn(StringRef name)
- : Leak(name, true) {}
- };
-
- class LeakWithinFunction : public Leak {
- public:
- LeakWithinFunction(StringRef name)
- : Leak(name, false) {}
- };
-
//===---------===//
// Bug Reports. //
//===---------===//
@@ -1854,25 +1925,21 @@ static inline bool contains(const SmallVectorImpl<ArgEffect>& V,
return false;
}
-static bool isPropertyAccess(const Stmt *S, ParentMap &PM) {
- unsigned maxDepth = 4;
- while (S && maxDepth) {
- if (const PseudoObjectExpr *PO = dyn_cast<PseudoObjectExpr>(S)) {
- if (!isa<ObjCMessageExpr>(PO->getSyntacticForm()))
- return true;
- return false;
- }
- S = PM.getParent(S);
- --maxDepth;
- }
- return false;
+static bool isNumericLiteralExpression(const Expr *E) {
+ // FIXME: This set of cases was copied from SemaExprObjC.
+ return isa<IntegerLiteral>(E) ||
+ isa<CharacterLiteral>(E) ||
+ isa<FloatingLiteral>(E) ||
+ isa<ObjCBoolLiteralExpr>(E) ||
+ isa<CXXBoolLiteralExpr>(E);
}
PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC,
BugReport &BR) {
-
+ // FIXME: We will eventually need to handle non-statement-based events
+ // (__attribute__((cleanup))).
if (!isa<StmtPoint>(N->getLocation()))
return NULL;
@@ -1881,11 +1948,11 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
ProgramStateRef CurrSt = N->getState();
const LocationContext *LCtx = N->getLocationContext();
- const RefVal* CurrT = CurrSt->get<RefBindings>(Sym);
+ const RefVal* CurrT = getRefBinding(CurrSt, Sym);
if (!CurrT) return NULL;
const RefVal &CurrV = *CurrT;
- const RefVal *PrevT = PrevSt->get<RefBindings>(Sym);
+ const RefVal *PrevT = getRefBinding(PrevSt, Sym);
// Create a string buffer to constain all the useful things we want
// to tell the user.
@@ -1903,6 +1970,24 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
else if (isa<ObjCDictionaryLiteral>(S)) {
os << "NSDictionary literal is an object with a +0 retain count";
}
+ else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
+ if (isNumericLiteralExpression(BL->getSubExpr()))
+ os << "NSNumber literal is an object with a +0 retain count";
+ else {
+ const ObjCInterfaceDecl *BoxClass = 0;
+ if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
+ BoxClass = Method->getClassInterface();
+
+ // We should always be able to find the boxing class interface,
+ // but consider this future-proofing.
+ if (BoxClass)
+ os << *BoxClass << " b";
+ else
+ os << "B";
+
+ os << "oxed expression produces an object with a +0 retain count";
+ }
+ }
else {
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
// Get the name of the callee (if it is available).
@@ -1913,10 +1998,22 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
os << "function call";
}
else {
- assert(isa<ObjCMessageExpr>(S));
- // The message expression may have between written directly or as
- // a property access. Lazily determine which case we are looking at.
- os << (isPropertyAccess(S, N->getParentMap()) ? "Property" : "Method");
+ assert(isa<ObjCMessageExpr>(S));
+ CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Call
+ = Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+
+ switch (Call->getMessageKind()) {
+ case OCM_Message:
+ os << "Method";
+ break;
+ case OCM_PropertyAccess:
+ os << "Property";
+ break;
+ case OCM_Subscript:
+ os << "Subscript";
+ break;
+ }
}
if (CurrV.getObjKind() == RetEffect::CF) {
@@ -2143,9 +2240,8 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
while (N) {
ProgramStateRef St = N->getState();
- RefBindings B = St->get<RefBindings>();
- if (!B.lookup(Sym))
+ if (!getRefBinding(St, Sym))
break;
StoreManager::FindUniqueBinding FB(Sym);
@@ -2216,7 +2312,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
os << "allocated object";
// Get the retain count.
- const RefVal* RV = EndN->getState()->get<RefBindings>(Sym);
+ const RefVal* RV = getRefBinding(EndN->getState(), Sym);
if (RV->getKind() == RefVal::ErrorLeakReturned) {
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
@@ -2276,8 +2372,15 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
// Get the SourceLocation for the allocation site.
+ // FIXME: This will crash the analyzer if an allocation comes from an
+ // implicit call. (Currently there are no such allocations in Cocoa, though.)
+ const Stmt *AllocStmt;
ProgramPoint P = AllocNode->getLocation();
- const Stmt *AllocStmt = cast<PostStmt>(P).getStmt();
+ if (CallExitEnd *Exit = dyn_cast<CallExitEnd>(&P))
+ AllocStmt = Exit->getCalleeContext()->getCallSite();
+ else
+ AllocStmt = cast<PostStmt>(P).getStmt();
+ assert(AllocStmt && "All allocations must come from explicit calls");
Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
n->getLocationContext());
// Fill in the description of the bug.
@@ -2307,11 +2410,10 @@ class RetainCountChecker
check::EndPath,
check::PostStmt<BlockExpr>,
check::PostStmt<CastExpr>,
- check::PostStmt<CallExpr>,
- check::PostStmt<CXXConstructExpr>,
check::PostStmt<ObjCArrayLiteral>,
check::PostStmt<ObjCDictionaryLiteral>,
- check::PostObjCMessage,
+ check::PostStmt<ObjCBoxedExpr>,
+ check::PostCall,
check::PreStmt<ReturnStmt>,
check::RegionChanges,
eval::Assume,
@@ -2330,7 +2432,9 @@ class RetainCountChecker
mutable OwningPtr<RetainSummaryManager> Summaries;
mutable OwningPtr<RetainSummaryManager> SummariesGC;
+#if AUTORELEASE_POOL_MODELING
mutable ARCounts::Factory ARCountFactory;
+#endif
mutable SummaryLogTy SummaryLog;
mutable bool ShouldResetSummaryLog;
@@ -2382,20 +2486,17 @@ public:
bool GCEnabled) const {
if (GCEnabled) {
if (!leakWithinFunctionGC)
- leakWithinFunctionGC.reset(new LeakWithinFunction("Leak of object when "
- "using garbage "
- "collection"));
+ leakWithinFunctionGC.reset(new Leak("Leak of object when using "
+ "garbage collection"));
return leakWithinFunctionGC.get();
} else {
if (!leakWithinFunction) {
if (LOpts.getGC() == LangOptions::HybridGC) {
- leakWithinFunction.reset(new LeakWithinFunction("Leak of object when "
- "not using garbage "
- "collection (GC) in "
- "dual GC/non-GC "
- "code"));
+ leakWithinFunction.reset(new Leak("Leak of object when not using "
+ "garbage collection (GC) in "
+ "dual GC/non-GC code"));
} else {
- leakWithinFunction.reset(new LeakWithinFunction("Leak"));
+ leakWithinFunction.reset(new Leak("Leak"));
}
}
return leakWithinFunction.get();
@@ -2405,17 +2506,17 @@ public:
CFRefBug *getLeakAtReturnBug(const LangOptions &LOpts, bool GCEnabled) const {
if (GCEnabled) {
if (!leakAtReturnGC)
- leakAtReturnGC.reset(new LeakAtReturn("Leak of returned object when "
- "using garbage collection"));
+ leakAtReturnGC.reset(new Leak("Leak of returned object when using "
+ "garbage collection"));
return leakAtReturnGC.get();
} else {
if (!leakAtReturn) {
if (LOpts.getGC() == LangOptions::HybridGC) {
- leakAtReturn.reset(new LeakAtReturn("Leak of returned object when "
- "not using garbage collection "
- "(GC) in dual GC/non-GC code"));
+ leakAtReturn.reset(new Leak("Leak of returned object when not using "
+ "garbage collection (GC) in dual "
+ "GC/non-GC code"));
} else {
- leakAtReturn.reset(new LeakAtReturn("Leak of returned object"));
+ leakAtReturn.reset(new Leak("Leak of returned object"));
}
}
return leakAtReturn.get();
@@ -2453,13 +2554,13 @@ public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
+ void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
+ void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
CheckerContext &C) const;
bool evalCall(const CallExpr *CE, CheckerContext &C) const;
@@ -2472,7 +2573,7 @@ public:
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const;
+ const CallEvent *Call) const;
bool wantsRegionChangeUpdate(ProgramStateRef state) const {
return true;
@@ -2499,8 +2600,8 @@ public:
const ProgramPointTag *getDeadSymbolTag(SymbolRef sym) const;
ProgramStateRef handleSymbolDeath(ProgramStateRef state,
- SymbolRef sid, RefVal V,
- SmallVectorImpl<SymbolRef> &Leaked) const;
+ SymbolRef sid, RefVal V,
+ SmallVectorImpl<SymbolRef> &Leaked) const;
std::pair<ExplodedNode *, ProgramStateRef >
handleAutoreleaseCounts(ProgramStateRef state,
@@ -2597,7 +2698,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol();
if (!Sym)
return;
- const RefVal* T = state->get<RefBindings>(Sym);
+ const RefVal* T = getRefBinding(state, Sym);
if (!T)
return;
@@ -2613,54 +2714,6 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
C.addTransition(state);
}
-void RetainCountChecker::checkPostStmt(const CallExpr *CE,
- CheckerContext &C) const {
- if (C.wasInlined)
- return;
-
- // Get the callee.
- ProgramStateRef state = C.getState();
- const Expr *Callee = CE->getCallee();
- SVal L = state->getSVal(Callee, C.getLocationContext());
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
- const RetainSummary *Summ = 0;
-
- // FIXME: Better support for blocks. For now we stop tracking anything
- // that is passed to blocks.
- // FIXME: Need to handle variables that are "captured" by the block.
- if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
- Summ = Summaries.getPersistentStopSummary();
- } else if (const FunctionDecl *FD = L.getAsFunctionDecl()) {
- Summ = Summaries.getSummary(FD);
- } else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
- if (const CXXMethodDecl *MD = me->getMethodDecl())
- Summ = Summaries.getSummary(MD);
- }
-
- if (!Summ)
- Summ = Summaries.getDefaultSummary();
-
- checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
-}
-
-void RetainCountChecker::checkPostStmt(const CXXConstructExpr *CE,
- CheckerContext &C) const {
- const CXXConstructorDecl *Ctor = CE->getConstructor();
- if (!Ctor)
- return;
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
- const RetainSummary *Summ = Summaries.getSummary(Ctor);
-
- // If we didn't get a summary, this constructor doesn't affect retain counts.
- if (!Summ)
- return;
-
- ProgramStateRef state = C.getState();
- checkSummary(*Summ, CallOrObjCMessage(CE, state, C.getLocationContext()), C);
-}
-
void RetainCountChecker::processObjCLiterals(CheckerContext &C,
const Expr *Ex) const {
ProgramStateRef state = C.getState();
@@ -2670,7 +2723,7 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C,
const Stmt *child = *it;
SVal V = state->getSVal(child, pred->getLocationContext());
if (SymbolRef sym = V.getAsSymbol())
- if (const RefVal* T = state->get<RefBindings>(sym)) {
+ if (const RefVal* T = getRefBinding(state, sym)) {
RefVal::Kind hasErr = (RefVal::Kind) 0;
state = updateSymbol(state, sym, *T, MayEscape, hasErr, C);
if (hasErr) {
@@ -2685,8 +2738,8 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C,
if (SymbolRef sym =
state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
QualType ResultTy = Ex->getType();
- state = state->set<RefBindings>(sym, RefVal::makeNotOwned(RetEffect::ObjC,
- ResultTy));
+ state = setRefBinding(state, sym,
+ RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
}
C.addTransition(state);
@@ -2704,30 +2757,34 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
processObjCLiterals(C, DL);
}
-void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg,
- CheckerContext &C) const {
- ProgramStateRef state = C.getState();
-
- RetainSummaryManager &Summaries = getSummaryManager(C);
+void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
+ CheckerContext &C) const {
+ const ExplodedNode *Pred = C.getPredecessor();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
- const RetainSummary *Summ;
- if (Msg.isInstanceMessage()) {
- const LocationContext *LC = C.getLocationContext();
- Summ = Summaries.getInstanceMethodSummary(Msg, state, LC);
- } else {
- Summ = Summaries.getClassMethodSummary(Msg);
+ if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) {
+ QualType ResultTy = Ex->getType();
+ State = setRefBinding(State, Sym,
+ RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
}
- // If we didn't get a summary, this message doesn't affect retain counts.
- if (!Summ)
+ C.addTransition(State);
+}
+
+void RetainCountChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (C.wasInlined)
return;
- checkSummary(*Summ, CallOrObjCMessage(Msg, state, C.getLocationContext()), C);
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const RetainSummary *Summ = Summaries.getSummary(Call, C.getState());
+ checkSummary(*Summ, Call, C);
}
/// GetReturnType - Used to get the return type of a message expression or
/// function call with the intention of affixing that type to a tracked symbol.
-/// While the the return type can be queried directly from RetEx, when
+/// While the return type can be queried directly from RetEx, when
/// invoking class methods we augment to the return type to be that of
/// a pointer to the class (as opposed it just being id).
// FIXME: We may be able to do this with related result types instead.
@@ -2754,7 +2811,7 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
}
void RetainCountChecker::checkSummary(const RetainSummary &Summ,
- const CallOrObjCMessage &CallOrMsg,
+ const CallEvent &CallOrMsg,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
@@ -2767,7 +2824,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
SVal V = CallOrMsg.getArgSVal(idx);
if (SymbolRef Sym = V.getAsLocSymbol()) {
- if (RefBindings::data_type *T = state->get<RefBindings>(Sym)) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
state = updateSymbol(state, Sym, *T, Summ.getArg(idx), hasErr, C);
if (hasErr) {
ErrorRange = CallOrMsg.getArgSourceRange(idx);
@@ -2780,17 +2837,18 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
// Evaluate the effect on the message receiver.
bool ReceiverIsTracked = false;
- if (!hasErr && CallOrMsg.isObjCMessage()) {
- const LocationContext *LC = C.getLocationContext();
- SVal Receiver = CallOrMsg.getInstanceMessageReceiver(LC);
- if (SymbolRef Sym = Receiver.getAsLocSymbol()) {
- if (const RefVal *T = state->get<RefBindings>(Sym)) {
- ReceiverIsTracked = true;
- state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
- hasErr, C);
- if (hasErr) {
- ErrorRange = CallOrMsg.getReceiverSourceRange();
- ErrorSym = Sym;
+ if (!hasErr) {
+ const ObjCMethodCall *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg);
+ if (MsgInvocation) {
+ if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
+ ReceiverIsTracked = true;
+ state = updateSymbol(state, Sym, *T, Summ.getReceiverEffect(),
+ hasErr, C);
+ if (hasErr) {
+ ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
+ ErrorSym = Sym;
+ }
}
}
}
@@ -2827,11 +2885,11 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
if (!Sym)
break;
- // Use the result type from callOrMsg as it automatically adjusts
+ // Use the result type from the CallEvent as it automatically adjusts
// for methods/functions that return references.
- QualType ResultTy = CallOrMsg.getResultType(C.getASTContext());
- state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
- ResultTy));
+ QualType ResultTy = CallOrMsg.getResultType();
+ state = setRefBinding(state, Sym, RefVal::makeOwned(RE.getObjKind(),
+ ResultTy));
// FIXME: Add a flag to the checker where allocations are assumed to
// *not* fail. (The code below is out-of-date, though.)
@@ -2856,8 +2914,8 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
// Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *.
QualType ResultTy = GetReturnType(Ex, C.getASTContext());
- state = state->set<RefBindings>(Sym, RefVal::makeNotOwned(RE.getObjKind(),
- ResultTy));
+ state = setRefBinding(state, Sym, RefVal::makeNotOwned(RE.getObjKind(),
+ ResultTy));
break;
}
}
@@ -2895,25 +2953,37 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
switch (E) {
- default: break;
- case IncRefMsg: E = IgnoreRetainMsg ? DoNothing : IncRef; break;
- case DecRefMsg: E = IgnoreRetainMsg ? DoNothing : DecRef; break;
- case MakeCollectable: E = C.isObjCGCEnabled() ? DecRef : DoNothing; break;
- case NewAutoreleasePool: E = C.isObjCGCEnabled() ? DoNothing :
- NewAutoreleasePool; break;
+ default:
+ break;
+ case IncRefMsg:
+ E = IgnoreRetainMsg ? DoNothing : IncRef;
+ break;
+ case DecRefMsg:
+ E = IgnoreRetainMsg ? DoNothing : DecRef;
+ break;
+ case DecRefMsgAndStopTracking:
+ E = IgnoreRetainMsg ? StopTracking : DecRefAndStopTracking;
+ break;
+ case MakeCollectable:
+ E = C.isObjCGCEnabled() ? DecRef : DoNothing;
+ break;
+ case NewAutoreleasePool:
+ E = C.isObjCGCEnabled() ? DoNothing : NewAutoreleasePool;
+ break;
}
// Handle all use-after-releases.
if (!C.isObjCGCEnabled() && V.getKind() == RefVal::Released) {
V = V ^ RefVal::ErrorUseAfterRelease;
hasErr = V.getKind();
- return state->set<RefBindings>(sym, V);
+ return setRefBinding(state, sym, V);
}
switch (E) {
case DecRefMsg:
case IncRefMsg:
case MakeCollectable:
+ case DecRefMsgAndStopTracking:
llvm_unreachable("DecRefMsg/IncRefMsg/MakeCollectable already converted");
case Dealloc:
@@ -2931,7 +3001,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
// The object immediately transitions to the released state.
V = V ^ RefVal::Released;
V.clearCounts();
- return state->set<RefBindings>(sym, V);
+ return setRefBinding(state, sym, V);
case RefVal::NotOwned:
V = V ^ RefVal::ErrorDeallocNotOwned;
hasErr = V.getKind();
@@ -2941,7 +3011,10 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
case NewAutoreleasePool:
assert(!C.isObjCGCEnabled());
- return state->add<AutoreleaseStack>(sym);
+#if AUTORELEASE_POOL_MODELING
+ state = state->add<AutoreleaseStack>(sym);
+#endif
+ return state;
case MayEscape:
if (V.getKind() == RefVal::Owned) {
@@ -2959,12 +3032,16 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
return state;
// Update the autorelease counts.
+ // TODO: AutoreleasePoolContents are not currently used. We will need to
+ // call SendAutorelease after it's wired up.
+#if AUTORELEASE_POOL_MODELING
state = SendAutorelease(state, ARCountFactory, sym);
+#endif
V = V.autorelease();
break;
case StopTracking:
- return state->remove<RefBindings>(sym);
+ return removeRefBinding(state, sym);
case IncRef:
switch (V.getKind()) {
@@ -2982,11 +3059,9 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
}
break;
- case SelfOwn:
- V = V ^ RefVal::NotOwned;
- // Fall-through.
case DecRef:
case DecRefBridgedTransfered:
+ case DecRefAndStopTracking:
switch (V.getKind()) {
default:
// case 'RefVal::Released' handled above.
@@ -2997,13 +3072,18 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
if (V.getCount() == 1)
V = V ^ (E == DecRefBridgedTransfered ?
RefVal::NotOwned : RefVal::Released);
+ else if (E == DecRefAndStopTracking)
+ return removeRefBinding(state, sym);
+
V = V - 1;
break;
case RefVal::NotOwned:
- if (V.getCount() > 0)
+ if (V.getCount() > 0) {
+ if (E == DecRefAndStopTracking)
+ return removeRefBinding(state, sym);
V = V - 1;
- else {
+ } else {
V = V ^ RefVal::ErrorReleaseNotOwned;
hasErr = V.getKind();
}
@@ -3018,7 +3098,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym,
}
break;
}
- return state->set<RefBindings>(sym, V);
+ return setRefBinding(state, sym, V);
}
void RetainCountChecker::processNonLeakError(ProgramStateRef St,
@@ -3117,7 +3197,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// If the receiver is unknown, conjure a return value.
SValBuilder &SVB = C.getSValBuilder();
unsigned Count = C.getCurrentBlockCount();
- SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
+ RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count);
}
state = state->BindExpr(CE, LCtx, RetVal, false);
@@ -3126,9 +3206,9 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
if (const MemRegion *ArgRegion = RetVal.getAsRegion()) {
// Save the refcount status of the argument.
SymbolRef Sym = RetVal.getAsLocSymbol();
- RefBindings::data_type *Binding = 0;
+ const RefVal *Binding = 0;
if (Sym)
- Binding = state->get<RefBindings>(Sym);
+ Binding = getRefBinding(state, Sym);
// Invalidate the argument region.
unsigned Count = C.getCurrentBlockCount();
@@ -3136,7 +3216,7 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// Restore the refcount status of the argument.
if (Binding)
- state = state->set<RefBindings>(Sym, *Binding);
+ state = setRefBinding(state, Sym, *Binding);
}
C.addTransition(state);
@@ -3175,7 +3255,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
return;
// Get the reference count binding (if any).
- const RefVal *T = state->get<RefBindings>(Sym);
+ const RefVal *T = getRefBinding(state, Sym);
if (!T)
return;
@@ -3208,7 +3288,7 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
}
// Update the binding.
- state = state->set<RefBindings>(Sym, X);
+ state = setRefBinding(state, Sym, X);
ExplodedNode *Pred = C.addTransition(state);
// At this point we have updated the state properly.
@@ -3230,29 +3310,27 @@ void RetainCountChecker::checkPreStmt(const ReturnStmt *S,
return;
// Get the updated binding.
- T = state->get<RefBindings>(Sym);
+ T = getRefBinding(state, Sym);
assert(T);
X = *T;
// Consult the summary of the enclosing method.
RetainSummaryManager &Summaries = getSummaryManager(C);
const Decl *CD = &Pred->getCodeDecl();
+ RetEffect RE = RetEffect::MakeNoRet();
+ // FIXME: What is the convention for blocks? Is there one?
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
- // Unlike regular functions, /all/ ObjC methods are assumed to always
- // follow Cocoa retain-count conventions, not just those with special
- // names or attributes.
const RetainSummary *Summ = Summaries.getMethodSummary(MD);
- RetEffect RE = Summ ? Summ->getRetEffect() : RetEffect::MakeNoRet();
- checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
+ RE = Summ->getRetEffect();
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
+ if (!isa<CXXMethodDecl>(FD)) {
+ const RetainSummary *Summ = Summaries.getFunctionSummary(FD);
+ RE = Summ->getRetEffect();
+ }
}
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
- if (!isa<CXXMethodDecl>(FD))
- if (const RetainSummary *Summ = Summaries.getSummary(FD))
- checkReturnWithRetEffect(S, C, Pred, Summ->getRetEffect(), X,
- Sym, state);
- }
+ checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
}
void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
@@ -3283,7 +3361,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (hasError) {
// Generate an error node.
- state = state->set<RefBindings>(Sym, X);
+ state = setRefBinding(state, Sym, X);
static SimpleProgramPointTag
ReturnOwnLeakTag("RetainCountChecker : ReturnsOwnLeak");
@@ -3303,7 +3381,7 @@ void RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (RE.isOwned()) {
// Trying to return a not owned object to a caller expecting an
// owned object.
- state = state->set<RefBindings>(Sym, X ^ RefVal::ErrorReturnedNotOwned);
+ state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
static SimpleProgramPointTag
ReturnNotOwnedTag("RetainCountChecker : ReturnNotOwnedForOwned");
@@ -3346,7 +3424,11 @@ void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
// To test (3), generate a new state with the binding added. If it is
// the same state, then it escapes (since the store cannot represent
// the binding).
- escapes = (state == (state->bindLoc(*regionLoc, val)));
+ // Do this only if we know that the store is not supposed to generate the
+ // same state.
+ SVal StoredVal = state->getSVal(regionLoc->getRegion());
+ if (StoredVal != val)
+ escapes = (state == (state->bindLoc(*regionLoc, val)));
}
if (!escapes) {
// Case 4: We do not currently model what happens when a symbol is
@@ -3406,7 +3488,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state,
const StoreManager::InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions,
- const CallOrObjCMessage *Call) const {
+ const CallEvent *Call) const {
if (!invalidated)
return state;
@@ -3423,7 +3505,7 @@ RetainCountChecker::checkRegionChanges(ProgramStateRef state,
if (WhitelistedSymbols.count(sym))
continue;
// Remove any existing reference-count binding.
- state = state->remove<RefBindings>(sym);
+ state = removeRefBinding(state, sym);
}
return state;
}
@@ -3463,7 +3545,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
V.setCount(Cnt - ACnt);
V.setAutoreleaseCount(0);
}
- state = state->set<RefBindings>(Sym, V);
+ state = setRefBinding(state, Sym, V);
ExplodedNode *N = Bd.MakeNode(state, Pred);
if (N == 0)
state = 0;
@@ -3473,7 +3555,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
// Woah! More autorelease counts then retain counts left.
// Emit hard error.
V = V ^ RefVal::ErrorOverAutorelease;
- state = state->set<RefBindings>(Sym, V);
+ state = setRefBinding(state, Sym, V);
if (ExplodedNode *N = Bd.MakeNode(state, Pred, true)) {
SmallString<128> sbuf;
@@ -3507,10 +3589,10 @@ RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
hasLeak = (V.getCount() > 0);
if (!hasLeak)
- return state->remove<RefBindings>(sid);
+ return removeRefBinding(state, sid);
Leaked.push_back(sid);
- return state->set<RefBindings>(sid, V ^ RefVal::ErrorLeak);
+ return setRefBinding(state, sid, V ^ RefVal::ErrorLeak);
}
ExplodedNode *
@@ -3559,7 +3641,7 @@ void RetainCountChecker::checkEndPath(CheckerContext &Ctx) const {
// If the current LocationContext has a parent, don't check for leaks.
// We will do that later.
- // FIXME: we should instead check for imblances of the retain/releases,
+ // FIXME: we should instead check for imbalances of the retain/releases,
// and suggest annotations.
if (Ctx.getLocationContext()->getParent())
return;
@@ -3641,6 +3723,7 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// Debug printing of refcount bindings and autorelease pools.
//===----------------------------------------------------------------------===//
+#if AUTORELEASE_POOL_MODELING
static void PrintPool(raw_ostream &Out, SymbolRef Sym,
ProgramStateRef State) {
Out << ' ';
@@ -3654,7 +3737,6 @@ static void PrintPool(raw_ostream &Out, SymbolRef Sym,
if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym))
for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I)
Out << '(' << I.getKey() << ',' << I.getData() << ')';
-
Out << '}';
}
@@ -3664,6 +3746,7 @@ static bool UsesAutorelease(ProgramStateRef state) {
return !state->get<AutoreleaseStack>().isEmpty() ||
state->get<AutoreleasePoolContents>(SymbolRef());
}
+#endif
void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
@@ -3679,6 +3762,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
Out << NL;
}
+#if AUTORELEASE_POOL_MODELING
// Print the autorelease stack.
if (UsesAutorelease(State)) {
Out << Sep << NL << "AR pool stack:";
@@ -3690,6 +3774,7 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
Out << NL;
}
+#endif
}
//===----------------------------------------------------------------------===//