summaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Core/CFRefCount.cpp
diff options
context:
space:
mode:
authordim <dim@FreeBSD.org>2011-07-17 15:40:56 +0000
committerdim <dim@FreeBSD.org>2011-07-17 15:40:56 +0000
commit611ba3ea3300b71eb95dc4e45f20eee5dddd32e1 (patch)
tree2097d084eb235c0b12c0bff3445f4ec7bbaa8a12 /lib/StaticAnalyzer/Core/CFRefCount.cpp
parentc49018d9cce52d8c9f34b44865ec3ba8e89a1488 (diff)
downloadFreeBSD-src-611ba3ea3300b71eb95dc4e45f20eee5dddd32e1.zip
FreeBSD-src-611ba3ea3300b71eb95dc4e45f20eee5dddd32e1.tar.gz
Vendor import of clang trunk r135360:
http://llvm.org/svn/llvm-project/cfe/trunk@135360
Diffstat (limited to 'lib/StaticAnalyzer/Core/CFRefCount.cpp')
-rw-r--r--lib/StaticAnalyzer/Core/CFRefCount.cpp142
1 files changed, 105 insertions, 37 deletions
diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp
index 0512e2f..bf53029 100644
--- a/lib/StaticAnalyzer/Core/CFRefCount.cpp
+++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp
@@ -126,6 +126,7 @@ public:
/// ArgEffect is used to summarize a function/method call's effect on a
/// particular argument.
enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing,
+ DecRefBridgedTransfered,
DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape,
NewAutoreleasePool, SelfOwn, StopTracking };
@@ -148,7 +149,8 @@ namespace {
class RetEffect {
public:
enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol,
- NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias,
+ NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol,
+ ReceiverAlias,
OwnedWhenTrackedReceiver };
enum ObjKind { CF, ObjC, AnyObj };
@@ -195,7 +197,9 @@ public:
static RetEffect MakeGCNotOwned() {
return RetEffect(GCNotOwnedSymbol, ObjC);
}
-
+ static RetEffect MakeARCNotOwned() {
+ return RetEffect(ARCNotOwnedSymbol, ObjC);
+ }
static RetEffect MakeNoRet() {
return RetEffect(NoRet);
}
@@ -636,6 +640,9 @@ class RetainSummaryManager {
/// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
+ /// Records whether or not the analyzed code runs in ARC mode.
+ const bool ARCEnabled;
+
/// FuncSummaries - A map from FunctionDecls to summaries.
FuncSummariesTy FuncSummaries;
@@ -788,14 +795,20 @@ private:
public:
- RetainSummaryManager(ASTContext& ctx, bool gcenabled)
+ RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC)
: Ctx(ctx),
CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")),
- GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
- ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned()
- : RetEffect::MakeOwned(RetEffect::ObjC, true)),
- ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned()
- : RetEffect::MakeOwnedWhenTrackedReceiver()),
+ GCEnabled(gcenabled),
+ ARCEnabled(usesARC),
+ AF(BPAlloc), ScratchArgs(AF.getEmptyMap()),
+ ObjCAllocRetE(gcenabled
+ ? RetEffect::MakeGCNotOwned()
+ : (usesARC ? RetEffect::MakeARCNotOwned()
+ : RetEffect::MakeOwned(RetEffect::ObjC, true))),
+ ObjCInitRetE(gcenabled
+ ? RetEffect::MakeGCNotOwned()
+ : (usesARC ? RetEffect::MakeARCNotOwned()
+ : RetEffect::MakeOwnedWhenTrackedReceiver())),
DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */,
RetEffect::MakeNoRet() /* return effect */,
MayEscape, /* default argument effect */
@@ -871,6 +884,10 @@ public:
bool isGCEnabled() const { return GCEnabled; }
+ bool isARCEnabled() const { return ARCEnabled; }
+
+ bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; }
+
RetainSummary *copySummary(RetainSummary *OldSumm) {
RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
new (Summ) RetainSummary(*OldSumm);
@@ -1118,8 +1135,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) {
RetainSummary*
RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD,
StringRef FName) {
- if (FName.find("Create") != StringRef::npos ||
- FName.find("Copy") != StringRef::npos)
+ if (coreFoundation::followsCreateRule(FName))
return getCFSummaryCreateRule(FD);
return getCFSummaryGetRule(FD);
@@ -1189,7 +1205,8 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) {
assert(ScratchArgs.isEmpty());
// 'init' methods conceptually return a newly allocated object and claim
// the receiver.
- if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy))
+ if (cocoa::isCocoaObjectRef(RetTy) ||
+ coreFoundation::isCFObjectRef(RetTy))
return getPersistentSummary(ObjCInitRetE, DecRefMsg);
return getDefaultSummary();
@@ -1332,15 +1349,15 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD,
if (cocoa::isCocoaObjectRef(RetTy)) {
// EXPERIMENTAL: assume the Cocoa conventions for all objects returned
// by instance methods.
- RetEffect E = cocoa::followsFundamentalRule(S)
+ RetEffect E = cocoa::followsFundamentalRule(S, MD)
? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC);
return getPersistentSummary(E, ReceiverEff, MayEscape);
}
// Look for methods that return an owned core foundation object.
- if (cocoa::isCFObjectRef(RetTy)) {
- RetEffect E = cocoa::followsFundamentalRule(S)
+ if (coreFoundation::isCFObjectRef(RetTy)) {
+ RetEffect E = cocoa::followsFundamentalRule(S, MD)
? RetEffect::MakeOwned(RetEffect::CF, true)
: RetEffect::MakeNotOwned(RetEffect::CF);
@@ -1411,7 +1428,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S,
assert(ScratchArgs.isEmpty());
// "initXXX": pass-through for receiver.
- if (cocoa::deriveNamingConvention(S) == cocoa::InitRule)
+ if (cocoa::deriveNamingConvention(S, MD) == cocoa::InitRule)
Summ = getInitMethodSummary(RetTy);
else
Summ = getCommonMethodSummary(MD, S, RetTy);
@@ -1654,7 +1671,6 @@ public:
const char* nl, const char* sep);
};
-private:
typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*>
SummaryLogTy;
@@ -1691,7 +1707,7 @@ private:
public:
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
- : Summaries(Ctx, gcenabled),
+ : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount),
LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
deallocGC(0), deallocNotOwned(0),
leakWithinFunction(0), leakAtReturn(0), overAutorelease(0),
@@ -1706,6 +1722,8 @@ public:
}
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
+ bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); }
+
const LangOptions& getLangOptions() const { return LOpts; }
const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const {
@@ -1907,7 +1925,7 @@ namespace {
CFRefBug(tf, "Method should return an owned object") {}
const char *getDescription() const {
- return "Object with +0 retain counts returned to caller where a +1 "
+ return "Object with a +0 retain count returned to caller where a +1 "
"(owning) retain count is expected";
}
};
@@ -2094,7 +2112,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N,
if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) {
assert(CurrV.getObjKind() == RetEffect::CF);
- os << " "
+ os << ". "
"Core Foundation objects are not automatically garbage collected.";
}
}
@@ -2416,14 +2434,14 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
if (RV->getKind() == RefVal::ErrorLeakReturned) {
// FIXME: Per comments in rdar://6320065, "create" only applies to CF
- // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership
+ // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
// to the caller for NS objects.
const Decl *D = &EndN->getCodeDecl();
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
os << " is returned from a method whose name ('"
<< MD->getSelector().getAsString()
<< "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'."
- " This violates the naming convention rules "
+ " This violates the naming convention rules"
" given in the Memory Management Guide for Cocoa";
}
else {
@@ -2431,7 +2449,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC,
os << " is return from a function whose name ('"
<< FD->getNameAsString()
<< "') does not contain 'Copy' or 'Create'. This violates the naming"
- " convention rules given the Memory Management Guide for Core "
+ " convention rules given the Memory Management Guide for Core"
" Foundation";
}
}
@@ -2777,6 +2795,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst,
}
case RetEffect::GCNotOwnedSymbol:
+ case RetEffect::ARCNotOwnedSymbol:
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
SValBuilder &svalBuilder = Eng.getSValBuilder();
@@ -3103,8 +3122,8 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
// In GC mode [... release] and [... retain] do nothing.
switch (E) {
default: break;
- case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break;
- case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break;
+ case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break;
+ case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break;
case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break;
case NewAutoreleasePool: E = isGCEnabled() ? DoNothing :
NewAutoreleasePool; break;
@@ -3118,9 +3137,13 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
}
switch (E) {
- default:
- assert (false && "Unhandled CFRef transition.");
-
+ case DecRefMsg:
+ case IncRefMsg:
+ case MakeCollectable:
+ assert(false &&
+ "DecRefMsg/IncRefMsg/MakeCollectable already transformed");
+ return state;
+
case Dealloc:
// Any use of -dealloc in GC is *bad*.
if (isGCEnabled()) {
@@ -3193,6 +3216,7 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
V = V ^ RefVal::NotOwned;
// Fall-through.
case DecRef:
+ case DecRefBridgedTransfered:
switch (V.getKind()) {
default:
// case 'RefVal::Released' handled above.
@@ -3200,7 +3224,9 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym,
case RefVal::Owned:
assert(V.getCount() > 0);
- if (V.getCount() == 1) V = V ^ RefVal::Released;
+ if (V.getCount() == 1)
+ V = V ^ (E == DecRefBridgedTransfered ?
+ RefVal::NotOwned : RefVal::Released);
V = V - 1;
break;
@@ -3280,15 +3306,10 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state,
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- os << "Object over-autoreleased: object was sent -autorelease";
+ os << "Object over-autoreleased: object was sent -autorelease ";
if (V.getAutoreleaseCount() > 1)
- os << V.getAutoreleaseCount() << " times";
- os << " but the object has ";
- if (V.getCount() == 0)
- os << "zero (locally visible)";
- else
- os << "+" << V.getCount();
- os << " retain counts";
+ os << V.getAutoreleaseCount() << " times ";
+ os << "but the object has a +" << V.getCount() << " retain count";
CFRefReport *report =
new CFRefReport(*static_cast<CFRefBug*>(overAutorelease),
@@ -3468,7 +3489,9 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst,
namespace {
class RetainReleaseChecker
- : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > {
+ : public Checker< check::PostStmt<BlockExpr>,
+ check::PostStmt<CastExpr>,
+ check::RegionChanges > {
public:
bool wantsRegionUpdate;
@@ -3476,6 +3499,9 @@ public:
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
+
+ void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
+
const GRState *checkRegionChanges(const GRState *state,
const StoreManager::InvalidatedSymbols *invalidated,
const MemRegion * const *begin,
@@ -3545,6 +3571,48 @@ void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE,
C.addTransition(state);
}
+void RetainReleaseChecker::checkPostStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
+ if (!BE)
+ return;
+
+ ArgEffect AE = IncRef;
+
+ switch (BE->getBridgeKind()) {
+ case clang::OBC_Bridge:
+ // Do nothing.
+ return;
+ case clang::OBC_BridgeRetained:
+ AE = IncRef;
+ break;
+ case clang::OBC_BridgeTransfer:
+ AE = DecRefBridgedTransfered;
+ break;
+ }
+
+ const GRState *state = C.getState();
+ SymbolRef Sym = state->getSVal(CE).getAsLocSymbol();
+ if (!Sym)
+ return;
+ const RefVal* T = state->get<RefBindings>(Sym);
+ if (!T)
+ return;
+
+ // This is gross. Once the checker and CFRefCount are unified,
+ // this will go away.
+ CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF());
+ RefVal::Kind hasErr = (RefVal::Kind) 0;
+ state = cf.Update(state, Sym, *T, AE, hasErr);
+
+ if (hasErr) {
+
+ return;
+ }
+
+ C.generateNode(state);
+}
+
//===----------------------------------------------------------------------===//
// Transfer function creation for external clients.
//===----------------------------------------------------------------------===//
OpenPOWER on IntegriCloud