Newer
Older
// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
Ted Kremenek
committed
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the methods for CFRefCount, which implements
Ted Kremenek
committed
// a reference count checker for Core Foundation (Mac OS X).
//
//===----------------------------------------------------------------------===//
#include "GRSimpleVals.h"
Ted Kremenek
committed
#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek
committed
#include "clang/Analysis/LocalCheckers.h"
Ted Kremenek
committed
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableMap.h"
Ted Kremenek
committed
#include "llvm/Support/Compiler.h"
Ted Kremenek
committed
#include <ostream>
Ted Kremenek
committed
using namespace clang;
//===----------------------------------------------------------------------===//
// Symbolic Evaluation of Reference Counting Logic
//===----------------------------------------------------------------------===//
namespace {
enum ArgEffect { IncRef, DecRef, DoNothing };
typedef std::vector<ArgEffect> ArgEffects;
}
Ted Kremenek
committed
namespace llvm {
template <> struct FoldingSetTrait<ArgEffects> {
static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) {
for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I)
ID.AddInteger((unsigned) *I);
};
} // end llvm namespace
namespace {
class RetEffect {
public:
Ted Kremenek
committed
enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2,
NotOwnedSymbol = 0x3 };
private:
unsigned Data;
RetEffect(Kind k, unsigned D) { Data = (D << 2) | (unsigned) k; }
public:
Kind getKind() const { return (Kind) (Data & 0x3); }
unsigned getValue() const {
assert(getKind() == Alias);
return Data >> 2;
Ted Kremenek
committed
static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); }
static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); }
static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 0); }
Ted Kremenek
committed
static RetEffect MakeNoRet() { return RetEffect(NoRet, 0); }
operator Kind() const { return getKind(); }
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
};
class CFRefSummary : public llvm::FoldingSetNode {
ArgEffects* Args;
RetEffect Ret;
public:
CFRefSummary(ArgEffects* A, RetEffect R) : Args(A), Ret(R) {}
unsigned getNumArgs() const { return Args->size(); }
ArgEffect getArg(unsigned idx) const {
assert (idx < getNumArgs());
return (*Args)[idx];
}
RetEffect getRet() const {
return Ret;
}
typedef ArgEffects::const_iterator arg_iterator;
arg_iterator begin_args() const { return Args->begin(); }
arg_iterator end_args() const { return Args->end(); }
Ted Kremenek
committed
static void Profile(llvm::FoldingSetNodeID& ID, ArgEffects* A, RetEffect R) {
ID.AddPointer(A);
ID.Add(R);
}
void Profile(llvm::FoldingSetNodeID& ID) const {
Profile(ID, Args, Ret);
}
};
Ted Kremenek
committed
class CFRefSummaryManager {
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> > AESetTy;
typedef llvm::FoldingSet<CFRefSummary> SummarySetTy;
typedef llvm::DenseMap<FunctionDecl*, CFRefSummary*> SummaryMapTy;
ASTContext& Ctx;
SummarySetTy SummarySet;
SummaryMapTy SummaryMap;
AESetTy AESet;
llvm::BumpPtrAllocator BPAlloc;
ArgEffects ScratchArgs;
ArgEffects* getArgEffects();
CFRefSummary* getCannedCFSummary(FunctionTypeProto* FT, bool isRetain);
CFRefSummary* getCFSummary(FunctionDecl* FD, const char* FName);
CFRefSummary* getCFSummaryCreateRule(FunctionTypeProto* FT);
CFRefSummary* getCFSummaryGetRule(FunctionTypeProto* FT);
CFRefSummary* getPersistentSummary(ArgEffects* AE, RetEffect RE);
Ted Kremenek
committed
void FillDoNothing(unsigned Args);
CFRefSummaryManager(ASTContext& ctx) : Ctx(ctx) {}
~CFRefSummaryManager();
Ted Kremenek
committed
CFRefSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Implementation of checker data structures.
//===----------------------------------------------------------------------===//
CFRefSummaryManager::~CFRefSummaryManager() {
Ted Kremenek
committed
// FIXME: The ArgEffects could eventually be allocated from BPAlloc,
// mitigating the need to do explicit cleanup of the
// Argument-Effect summaries.
Ted Kremenek
committed
for (AESetTy::iterator I = AESet.begin(), E = AESet.end(); I!=E; ++I)
I->getValue().~ArgEffects();
Ted Kremenek
committed
}
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
ArgEffects* CFRefSummaryManager::getArgEffects() {
llvm::FoldingSetNodeID profile;
profile.Add(ScratchArgs);
void* InsertPos;
llvm::FoldingSetNodeWrapper<ArgEffects>* E =
AESet.FindNodeOrInsertPos(profile, InsertPos);
if (E) {
ScratchArgs.clear();
return &E->getValue();
}
E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
AESet.InsertNode(E, InsertPos);
ScratchArgs.clear();
return &E->getValue();
}
CFRefSummary* CFRefSummaryManager::getPersistentSummary(ArgEffects* AE,
RetEffect RE) {
llvm::FoldingSetNodeID profile;
CFRefSummary::Profile(profile, AE, RE);
void* InsertPos;
CFRefSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
if (Summ)
return Summ;
Summ = (CFRefSummary*) BPAlloc.Allocate<CFRefSummary>();
new (Summ) CFRefSummary(AE, RE);
SummarySet.InsertNode(Summ, InsertPos);
return Summ;
}
CFRefSummary* CFRefSummaryManager::getSummary(FunctionDecl* FD,
ASTContext& Ctx) {
SourceLocation Loc = FD->getLocation();
if (!Loc.isFileID())
return NULL;
{ // Look into our cache of summaries to see if we have already computed
// a summary for this FunctionDecl.
SummaryMapTy::iterator I = SummaryMap.find(FD);
if (I != SummaryMap.end())
return I->second;
}
#if 0
SourceManager& SrcMgr = Ctx.getSourceManager();
unsigned fid = Loc.getFileID();
const FileEntry* FE = SrcMgr.getFileEntryForID(fid);
if (!FE)
return NULL;
const char* DirName = FE->getDir()->getName();
assert (DirName);
assert (strlen(DirName) > 0);
if (!strstr(DirName, "CoreFoundation")) {
SummaryMap[FD] = NULL;
return NULL;
}
#endif
const char* FName = FD->getIdentifier()->getName();
if (FName[0] == 'C' && FName[1] == 'F') {
CFRefSummary* S = getCFSummary(FD, FName);
SummaryMap[FD] = S;
return S;
}
Ted Kremenek
committed
Ted Kremenek
committed
}
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
CFRefSummary* CFRefSummaryManager::getCFSummary(FunctionDecl* FD,
const char* FName) {
// For now, only generate summaries for functions that have a prototype.
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
if (!FT)
return NULL;
FName += 2;
if (strcmp(FName, "Retain") == 0)
return getCannedCFSummary(FT, true);
if (strcmp(FName, "Release") == 0)
return getCannedCFSummary(FT, false);
assert (ScratchArgs.empty());
bool usesCreateRule = false;
if (strstr(FName, "Create"))
usesCreateRule = true;
if (!usesCreateRule && strstr(FName, "Copy"))
usesCreateRule = true;
if (usesCreateRule)
return getCFSummaryCreateRule(FT);
if (strstr(FName, "Get"))
return getCFSummaryGetRule(FT);
return NULL;
}
CFRefSummary* CFRefSummaryManager::getCannedCFSummary(FunctionTypeProto* FT,
bool isRetain) {
if (FT->getNumArgs() != 1)
return NULL;
TypedefType* ArgT = dyn_cast<TypedefType>(FT->getArgType(0).getTypePtr());
if (!ArgT)
return NULL;
// For CFRetain/CFRelease, the first (and only) argument is of type
// "CFTypeRef".
const char* TDName = ArgT->getDecl()->getIdentifier()->getName();
assert (TDName);
if (strcmp("CFTypeRef", TDName) != 0)
return NULL;
if (!ArgT->isPointerType())
return NULL;
QualType RetTy = FT->getResultType();
if (isRetain) {
// CFRetain: the return type should also be "CFTypeRef".
if (RetTy.getTypePtr() != ArgT)
return NULL;
Ted Kremenek
committed
// The function's interface checks out. Generate a canned summary.
assert (ScratchArgs.empty());
ScratchArgs.push_back(IncRef);
return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0));
}
else {
// CFRelease: the return type should be void.
if (RetTy != Ctx.VoidTy)
return NULL;
Ted Kremenek
committed
assert (ScratchArgs.empty());
ScratchArgs.push_back(DecRef);
return getPersistentSummary(getArgEffects(), RetEffect::MakeNoRet());
}
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
}
static bool isCFRefType(QualType T) {
if (!T->isPointerType())
return false;
// Check the typedef for the name "CF" and the substring "Ref".
TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
if (!TD)
return false;
const char* TDName = TD->getDecl()->getIdentifier()->getName();
assert (TDName);
if (TDName[0] != 'C' || TDName[1] != 'F')
return false;
if (strstr(TDName, "Ref") == 0)
return false;
return true;
}
Ted Kremenek
committed
void CFRefSummaryManager::FillDoNothing(unsigned Args) {
for (unsigned i = 0; i != Args; ++i)
ScratchArgs.push_back(DoNothing);
}
CFRefSummary*
CFRefSummaryManager::getCFSummaryCreateRule(FunctionTypeProto* FT) {
if (!isCFRefType(FT->getResultType()))
return NULL;
assert (ScratchArgs.empty());
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
FillDoNothing(FT->getNumArgs());
return getPersistentSummary(getArgEffects(), RetEffect::MakeOwned());
}
CFRefSummary*
CFRefSummaryManager::getCFSummaryGetRule(FunctionTypeProto* FT) {
QualType RetTy = FT->getResultType();
// FIXME: For now we assume that all pointer types returned are referenced
// counted. Since this is the "Get" rule, we assume non-ownership, which
// works fine for things that are not reference counted. We do this because
// some generic data structures return "void*". We need something better
// in the future.
if (!isCFRefType(RetTy) && !RetTy->isPointerType())
return NULL;
assert (ScratchArgs.empty());
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
FillDoNothing(FT->getNumArgs());
return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned());
}
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
//===----------------------------------------------------------------------===//
// Bug Descriptions.
//===----------------------------------------------------------------------===//
namespace {
class CFRefCount;
class VISIBILITY_HIDDEN CFRefBug : public BugType {
protected:
CFRefCount& TF;
public:
CFRefBug(CFRefCount& tf) : TF(tf) {}
};
class VISIBILITY_HIDDEN UseAfterRelease : public CFRefBug {
public:
UseAfterRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) use-after-release";
}
virtual const char* getDescription() const {
return "(CoreFoundation) Reference-counted object is used"
" after it is released.";
}
virtual void EmitWarnings(BugReporter& BR);
};
class VISIBILITY_HIDDEN BadRelease : public CFRefBug {
public:
BadRelease(CFRefCount& tf) : CFRefBug(tf) {}
virtual const char* getName() const {
return "(CoreFoundation) release of non-owned object";
}
virtual const char* getDescription() const {
return "Incorrect decrement of the reference count of a "
"CoreFoundation object:\n"
"The object is not owned at this point by the caller.";
}
virtual void EmitWarnings(BugReporter& BR);
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN RefVal {
unsigned Data;
RefVal(unsigned K, unsigned D) : Data((D << 3) | K) {
assert ((K & ~0x7) == 0x0);
}
RefVal(unsigned K) : Data(K) {
assert ((K & ~0x7) == 0x0);
}
public:
enum Kind { Owned = 0, NotOwned = 1, Released = 2,
ErrorUseAfterRelease = 3, ErrorReleaseNotOwned = 4 };
Kind getKind() const { return (Kind) (Data & 0x7); }
unsigned getCount() const {
assert (getKind() == Owned || getKind() == NotOwned);
return Data >> 3;
}
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
Ted Kremenek
committed
bool isOwned() const {
return getKind() == Owned;
}
static RefVal makeOwned(unsigned Count = 0) {
return RefVal(Owned, Count);
}
static RefVal makeNotOwned(unsigned Count = 0) {
return RefVal(NotOwned, Count);
}
static RefVal makeReleased() { return RefVal(Released); }
static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
bool operator==(const RefVal& X) const { return Data == X.Data; }
void Profile(llvm::FoldingSetNodeID& ID) const { ID.AddInteger(Data); }
Ted Kremenek
committed
void print(std::ostream& Out) const;
Ted Kremenek
committed
void RefVal::print(std::ostream& Out) const {
switch (getKind()) {
default: assert(false);
case Owned: {
Out << "Owned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
Ted Kremenek
committed
Out << "Not-Owned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
case Released:
Out << "Released";
break;
case ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]";
break;
case ErrorReleaseNotOwned:
Out << "Release of Not-Owned [ERROR]";
break;
}
}
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
Ted Kremenek
committed
// Type definitions.
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
typedef RefBindings::Factory RefBFactoryTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*,Expr*> UseAfterReleasesTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*,Expr*> ReleasesNotOwnedTy;
Ted Kremenek
committed
class BindingsPrinter : public ValueState::CheckerStatePrinter {
public:
virtual void PrintCheckerState(std::ostream& Out, void* State,
const char* nl, const char* sep);
};
// Instance variables.
CFRefSummaryManager Summaries;
RefBFactoryTy RefBFactory;
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
Ted Kremenek
committed
BindingsPrinter Printer;
// Private methods.
static RefBindings GetRefBindings(ValueState& StImpl) {
return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState);
}
static void SetRefBindings(ValueState& StImpl, RefBindings B) {
StImpl.CheckerState = B.getRoot();
}
RefBindings Remove(RefBindings B, SymbolID sym) {
return RefBFactory.Remove(B, sym);
}
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
Ted Kremenek
committed
CFRefCount(ASTContext& Ctx) : Summaries(Ctx) {}
virtual ~CFRefCount() {}
virtual void RegisterChecks(GRExprEngine& Eng);
Ted Kremenek
committed
virtual ValueState::CheckerStatePrinter* getCheckerStatePrinter() {
return &Printer;
}
// Calls.
virtual void EvalCall(ExplodedNodeSet<ValueState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
CallExpr* CE, LVal L,
ExplodedNode<ValueState>* Pred);
Ted Kremenek
committed
Ted Kremenek
committed
virtual void EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred);
bool EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred);
Ted Kremenek
committed
// End-of-path.
virtual void EvalEndPath(GRExprEngine& Engine,
GREndPathNodeBuilder<ValueState>& Builder);
Ted Kremenek
committed
// Error iterators.
typedef UseAfterReleasesTy::iterator use_after_iterator;
typedef ReleasesNotOwnedTy::iterator bad_release_iterator;
use_after_iterator use_after_begin() { return UseAfterReleases.begin(); }
use_after_iterator use_after_end() { return UseAfterReleases.end(); }
Ted Kremenek
committed
bad_release_iterator bad_release_begin() { return ReleasesNotOwned.begin(); }
bad_release_iterator bad_release_end() { return ReleasesNotOwned.end(); }
};
} // end anonymous namespace
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
GRSimpleVals::RegisterChecks(Eng);
Eng.Register(new UseAfterRelease(*this));
Eng.Register(new BadRelease(*this));
}
Ted Kremenek
committed
void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out,
void* State, const char* nl,
const char* sep) {
RefBindings B((RefBindings::TreeTy*) State);
if (State)
Out << sep << nl;
for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) {
Out << (*I).first << " : ";
(*I).second.print(Out);
Out << nl;
}
}
static inline ArgEffect GetArgE(CFRefSummary* Summ, unsigned idx) {
return Summ ? Summ->getArg(idx) : DoNothing;
}
static inline RetEffect GetRetE(CFRefSummary* Summ) {
return Summ ? Summ->getRet() : RetEffect::MakeNoRet();
}
Ted Kremenek
committed
void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
CallExpr* CE, LVal L,
ExplodedNode<ValueState>* Pred) {
Ted Kremenek
committed
ValueStateManager& StateMgr = Eng.getStateManager();
Ted Kremenek
committed
CFRefSummary* Summ = NULL;
Ted Kremenek
committed
// Get the summary.
Ted Kremenek
committed
if (isa<lval::FuncVal>(L)) {
lval::FuncVal FV = cast<lval::FuncVal>(L);
FunctionDecl* FD = FV.getDecl();
Summ = Summaries.getSummary(FD, Eng.getContext());
}
Ted Kremenek
committed
// Get the state.
ValueState* St = Builder.GetState(Pred);
// Evaluate the effects of the call.
ValueState StVals = *St;
RefVal::Kind hasError = (RefVal::Kind) 0;
// This function has a summary. Evaluate the effect of the arguments.
unsigned idx = 0;
Expr* ErrorExpr = NULL;
for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
I != E; ++I, ++idx) {
RVal V = StateMgr.GetRVal(St, *I);
if (isa<lval::SymbolVal>(V)) {
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
RefBindings B = GetRefBindings(StVals);
if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
B = Update(B, Sym, T->getValue().second, GetArgE(Summ, idx), hasError);
SetRefBindings(StVals, B);
if (hasError) {
ErrorExpr = *I;
break;
}
}
else if (isa<LVal>(V)) { // Nuke all arguments passed by reference.
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
// should compose behavior, not copy it.
StateMgr.Unbind(StVals, cast<LVal>(V));
if (hasError) {
St = StateMgr.getPersistentState(StVals);
Ted Kremenek
committed
Builder.BuildSinks = true;
GRExprEngine::NodeTy* N = Builder.MakeNode(Dst, CE, Pred, St);
if (N) {
switch (hasError) {
default: assert(false);
case RefVal::ErrorUseAfterRelease:
UseAfterReleases[N] = ErrorExpr;
break;
case RefVal::ErrorReleaseNotOwned:
ReleasesNotOwned[N] = ErrorExpr;
// Finally, consult the summary for the return value.
RetEffect RE = GetRetE(Summ);
St = StateMgr.getPersistentState(StVals);
switch (RE.getKind()) {
default:
assert (false && "Unhandled RetEffect."); break;
Ted Kremenek
committed
case RetEffect::NoRet:
// Make up a symbol for the return value (not reference counted).
// FIXME: This is basically copy-and-paste from GRSimpleVals. We
// should compose behavior, not copy it.
if (CE->getType() != Eng.getContext().VoidTy) {
unsigned Count = Builder.getCurrentBlockCount();
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
RVal X = CE->getType()->isPointerType()
? cast<RVal>(lval::SymbolVal(Sym))
: cast<RVal>(nonlval::SymbolVal(Sym));
St = StateMgr.SetRVal(St, CE, X, Eng.getCFG().isBlkExpr(CE), false);
}
Ted Kremenek
committed
break;
case RetEffect::Alias: {
unsigned idx = RE.getValue();
assert (idx < CE->getNumArgs());
RVal V = StateMgr.GetRVal(St, CE->getArg(idx));
Ted Kremenek
committed
St = StateMgr.SetRVal(St, CE, V, Eng.getCFG().isBlkExpr(CE), false);
break;
}
case RetEffect::OwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned()));
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
CE, lval::SymbolVal(Sym),
Ted Kremenek
committed
Eng.getCFG().isBlkExpr(CE), false);
break;
}
case RetEffect::NotOwnedSymbol: {
unsigned Count = Builder.getCurrentBlockCount();
Ted Kremenek
committed
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
ValueState StImpl = *St;
RefBindings B = GetRefBindings(StImpl);
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeNotOwned()));
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
CE, lval::SymbolVal(Sym),
Ted Kremenek
committed
Eng.getCFG().isBlkExpr(CE), false);
break;
}
}
Ted Kremenek
committed
}
Ted Kremenek
committed
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
void CFRefCount::EvalObjCMessageExpr(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred) {
if (EvalObjCMessageExprAux(Dst, Eng, Builder, ME, Pred))
GRSimpleVals::EvalObjCMessageExpr(Dst, Eng, Builder, ME, Pred);
}
bool CFRefCount::EvalObjCMessageExprAux(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
ObjCMessageExpr* ME,
ExplodedNode<ValueState>* Pred) {
// Handle "toll-free bridging." Eventually we will want to track the
// underlying object type associated.
Selector S = ME->getSelector();
if (!S.isUnarySelector())
return true;
return true; // FIXME: change to return false when more is implemented.
}
Ted Kremenek
committed
// End-of-path.
void CFRefCount::EvalEndPath(GRExprEngine& Engine,
GREndPathNodeBuilder<ValueState>& Builder) {
RefBindings B = GetRefBindings(*Builder.getState());
// Scan the set of bindings for symbols that are in the Owned state.
for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I)
if ((*I).second.isOwned()) {
// FIXME: Register an error with the diagnostic engine. Since we
// don't have a Stmt* here, we need some extra machinery to get
// a sourcelocation.
}
}
CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
RefVal V, ArgEffect E,
// FIXME: This dispatch can potentially be sped up by unifiying it into
// a single switch statement. Opt for simplicity for now.
switch (E) {
default:
assert (false && "Unhandled CFRef transition.");
case DoNothing:
if (V.getKind() == RefVal::Released) {
V = RefVal::makeUseAfterRelease();
hasError = V.getKind();
break;
}
return B;
case IncRef:
switch (V.getKind()) {
default:
assert(false);
case RefVal::Owned:
Ted Kremenek
committed
V = RefVal::makeOwned(V.getCount()+1);
break;
case RefVal::NotOwned:
V = RefVal::makeNotOwned(V.getCount()+1);
break;
case RefVal::Released:
V = RefVal::makeUseAfterRelease();
break;
}
Ted Kremenek
committed
break;
case DecRef:
switch (V.getKind()) {
default:
assert (false);
case RefVal::Owned: {
signed Count = ((signed) V.getCount()) - 1;
V = Count >= 0 ? RefVal::makeOwned(Count) : RefVal::makeReleased();
break;
}
case RefVal::NotOwned: {
signed Count = ((signed) V.getCount()) - 1;
if (Count >= 0)
V = RefVal::makeNotOwned(Count);
else {
V = RefVal::makeReleaseNotOwned();
hasError = V.getKind();
}
break;
case RefVal::Released:
V = RefVal::makeUseAfterRelease();
break;
}
Ted Kremenek
committed
break;
}
return RefBFactory.Add(B, sym, V);
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Error reporting.
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
void UseAfterRelease::EmitWarnings(BugReporter& BR) {
Ted Kremenek
committed
for (CFRefCount::use_after_iterator I = TF.use_after_begin(),
E = TF.use_after_end(); I != E; ++I) {
Ted Kremenek
committed
RangedBugReport report(*this, I->first);
report.addRange(I->second->getSourceRange());
Ted Kremenek
committed
BR.EmitPathWarning(report);
Ted Kremenek
committed
}
}
void BadRelease::EmitWarnings(BugReporter& BR) {
Ted Kremenek
committed
for (CFRefCount::bad_release_iterator I = TF.bad_release_begin(),
E = TF.bad_release_end(); I != E; ++I) {
Ted Kremenek
committed
RangedBugReport report(*this, I->first);
report.addRange(I->second->getSourceRange());
Ted Kremenek
committed
BR.EmitPathWarning(report);
}
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Transfer function creation for external clients.
//===----------------------------------------------------------------------===//
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx) {
return new CFRefCount(Ctx);
}