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"
#include "clang/Basic/LangOptions.h"
Ted Kremenek
committed
#include "clang/Basic/SourceManager.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;
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) {
IdentifierInfo* II = &Ctx.Idents.get(name);
return Ctx.Selectors.getSelector(0, &II);
}
static inline Selector GetUnarySelector(const char* name, ASTContext& Ctx) {
IdentifierInfo* II = &Ctx.Idents.get(name);
return Ctx.Selectors.getSelector(1, &II);
}
//===----------------------------------------------------------------------===//
// Symbolic Evaluation of Reference Counting Logic
//===----------------------------------------------------------------------===//
enum ArgEffect { IncRef, DecRef, DoNothing, StopTracking };
Ted Kremenek
committed
typedef std::vector<std::pair<unsigned,ArgEffect> > ArgEffects;
Ted Kremenek
committed
namespace llvm {
template <> struct FoldingSetTrait<ArgEffects> {
static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) {
Ted Kremenek
committed
for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I) {
ID.AddInteger(I->first);
ID.AddInteger((unsigned) I->second);
}
};
} // end llvm namespace
namespace {
class RetEffect {
public:
Ted Kremenek
committed
enum Kind { NoRet = 0x0, Alias = 0x1, OwnedSymbol = 0x2,
NotOwnedSymbol = 0x3, ReceiverAlias=0x4 };
private:
unsigned Data;
RetEffect(Kind k, unsigned D) { Data = (D << 3) | (unsigned) k; }
Kind getKind() const { return (Kind) (Data & 0x7); }
unsigned getValue() const {
assert(getKind() == Alias);
return Data >> 3;
Ted Kremenek
committed
static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); }
static RetEffect MakeReceiverAlias() { return RetEffect(ReceiverAlias, 0); }
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 RetainSummary : public llvm::FoldingSetNode {
ArgEffects* Args;
RetEffect Ret;
public:
RetainSummary(ArgEffects* A, RetEffect R, ArgEffect ReceiverEff = DoNothing)
: Args(A), Receiver(ReceiverEff), Ret(R) {}
unsigned getNumArgs() const { return Args->size(); }
ArgEffect getArg(unsigned idx) const {
Ted Kremenek
committed
if (!Args)
return DoNothing;
// If Args is present, it is likely to contain only 1 element.
// Just do a linear search. Do it from the back because functions with
// large numbers of arguments will be tail heavy with respect to which
// argument they actually modify with respect to the reference count.
for (ArgEffects::reverse_iterator I=Args->rbegin(), E=Args->rend();
I!=E; ++I) {
if (idx > I->first)
return DoNothing;
if (idx == I->first)
return I->second;
}
return DoNothing;
RetEffect getRetEffect() const {
return Ret;
}
ArgEffect getReceiverEffect() const {
return Receiver;
}
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 RetEff, ArgEffect ReceiverEff) {
ID.AddPointer(A);
ID.Add(RetEff);
ID.AddInteger((unsigned) ReceiverEff);
}
void Profile(llvm::FoldingSetNodeID& ID) const {
Profile(ID, Args, Ret, Receiver);
Ted Kremenek
committed
class RetainSummaryManager {
//==-----------------------------------------------------------------==//
// Typedefs.
//==-----------------------------------------------------------------==//
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> >
ArgEffectsSetTy;
typedef llvm::FoldingSet<RetainSummary>
SummarySetTy;
typedef llvm::DenseMap<FunctionDecl*, RetainSummary*>
FuncSummariesTy;
typedef llvm::DenseMap<Selector, RetainSummary*>
ObjCMethSummariesTy;
//==-----------------------------------------------------------------==//
// Data.
//==-----------------------------------------------------------------==//
// Ctx - The ASTContext object for the analyzed ASTs.
ASTContext& Ctx;
// GCEnabled - Records whether or not the analyzed code runs in GC mode.
const bool GCEnabled;
// SummarySet - A FoldingSet of uniqued summaries.
SummarySetTy SummarySet;
// FuncSummaries - A map from FunctionDecls to summaries.
FuncSummariesTy FuncSummaries;
// ObjCInstMethSummaries - A map from selectors (for instance methods)
// to summaries.
ObjCMethSummariesTy ObjCInstMethSummaries;
// ObjCMethSummaries - A map from selectors to summaries.
ObjCMethSummariesTy ObjCMethSummaries;
// ArgEffectsSet - A FoldingSet of uniqued ArgEffects.
ArgEffectsSetTy ArgEffectsSet;
// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects,
// and all other data used by the checker.
llvm::BumpPtrAllocator BPAlloc;
// ScratchArgs - A holding buffer for construct ArgEffects.
ArgEffects ScratchArgs;
//==-----------------------------------------------------------------==//
// Methods.
//==-----------------------------------------------------------------==//
// getArgEffects - Returns a persistent ArgEffects object based on the
// data in ScratchArgs.
ArgEffects* getArgEffects();
Ted Kremenek
committed
enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable };
RetainSummary* getUnarySummary(FunctionDecl* FD, UnaryFuncKind func);
RetainSummary* getNSSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCFSummary(FunctionDecl* FD, const char* FName);
RetainSummary* getCFSummaryCreateRule(FunctionDecl* FD);
RetainSummary* getCFSummaryGetRule(FunctionDecl* FD);
RetainSummary* getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
ArgEffect ReceiverEff = DoNothing);
RetainSummary* getPersistentSummary(RetEffect RE,
ArgEffect ReceiverEff = DoNothing) {
return getPersistentSummary(getArgEffects(), RE, ReceiverEff);
RetainSummary* getInitMethodSummary(Selector S);
Ted Kremenek
committed
void InitializeInstMethSummaries();
void InitializeMethSummaries();
RetainSummaryManager(ASTContext& ctx, bool gcenabled)
: Ctx(ctx), GCEnabled(gcenabled) {
Ted Kremenek
committed
InitializeInstMethSummaries();
InitializeMethSummaries();
~RetainSummaryManager();
RetainSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
Ted Kremenek
committed
RetainSummary* getMethodSummary(Selector S);
RetainSummary* getInstanceMethodSummary(IdentifierInfo* ClsName, Selector S);
bool isGCEnabled() const { return GCEnabled; }
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Implementation of checker data structures.
//===----------------------------------------------------------------------===//
RetainSummaryManager::~RetainSummaryManager() {
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 (ArgEffectsSetTy::iterator I = ArgEffectsSet.begin(),
E = ArgEffectsSet.end(); I!=E; ++I)
I->getValue().~ArgEffects();
Ted Kremenek
committed
}
ArgEffects* RetainSummaryManager::getArgEffects() {
Ted Kremenek
committed
if (ScratchArgs.empty())
return NULL;
// Compute a profile for a non-empty ScratchArgs.
llvm::FoldingSetNodeID profile;
Ted Kremenek
committed
profile.Add(ScratchArgs);
void* InsertPos;
Ted Kremenek
committed
// Look up the uniqued copy, or create a new one.
llvm::FoldingSetNodeWrapper<ArgEffects>* E =
ArgEffectsSet.FindNodeOrInsertPos(profile, InsertPos);
Ted Kremenek
committed
if (E) {
ScratchArgs.clear();
return &E->getValue();
}
E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
ArgEffectsSet.InsertNode(E, InsertPos);
ScratchArgs.clear();
return &E->getValue();
}
RetainSummary*
RetainSummaryManager::getPersistentSummary(ArgEffects* AE, RetEffect RetEff,
ArgEffect ReceiverEff) {
Ted Kremenek
committed
// Generate a profile for the summary.
llvm::FoldingSetNodeID profile;
RetainSummary::Profile(profile, AE, RetEff, ReceiverEff);
Ted Kremenek
committed
// Look up the uniqued summary, or create one if it doesn't exist.
void* InsertPos;
RetainSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
if (Summ)
return Summ;
Ted Kremenek
committed
// Create the summary and return it.
Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>();
new (Summ) RetainSummary(AE, RetEff, ReceiverEff);
SummarySet.InsertNode(Summ, InsertPos);
return Summ;
}
//===----------------------------------------------------------------------===//
// Summary creation for functions (largely uses of Core Foundation).
//===----------------------------------------------------------------------===//
RetainSummary* RetainSummaryManager::getSummary(FunctionDecl* FD,
SourceLocation Loc = FD->getLocation();
if (!Loc.isFileID())
return NULL;
Ted Kremenek
committed
// Look up a summary in our cache of FunctionDecls -> Summaries.
FuncSummariesTy::iterator I = FuncSummaries.find(FD);
Ted Kremenek
committed
if (I != FuncSummaries.end())
Ted Kremenek
committed
return I->second;
// No summary. Generate one.
const char* FName = FD->getIdentifier()->getName();
RetainSummary *S = 0;
Ted Kremenek
committed
if (FName[0] == 'C' && FName[1] == 'F')
S = getCFSummary(FD, FName);
else if (FName[0] == 'N' && FName[1] == 'S')
S = getNSSummary(FD, FName);
Ted Kremenek
committed
FuncSummaries[FD] = S;
Ted Kremenek
committed
return S;
Ted Kremenek
committed
}
RetainSummary* RetainSummaryManager::getNSSummary(FunctionDecl* FD,
Ted Kremenek
committed
FName += 2;
Ted Kremenek
committed
if (strcmp(FName, "MakeCollectable") == 0)
return getUnarySummary(FD, cfmakecollectable);
return 0;
}
RetainSummary* RetainSummaryManager::getCFSummary(FunctionDecl* FD,
Ted Kremenek
committed
FName += 2;
if (strcmp(FName, "Retain") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfretain);
if (strcmp(FName, "Release") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfrelease);
Ted Kremenek
committed
if (strcmp(FName, "MakeCollectable") == 0)
Ted Kremenek
committed
return getUnarySummary(FD, cfmakecollectable);
if (strstr(FName, "Create") || strstr(FName, "Copy"))
return getCFSummaryCreateRule(FD);
if (strstr(FName, "Get"))
Ted Kremenek
committed
return getCFSummaryGetRule(FD);
Ted Kremenek
committed
return 0;
RetainSummary*
RetainSummaryManager::getUnarySummary(FunctionDecl* FD, UnaryFuncKind func) {
Ted Kremenek
committed
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
Ted Kremenek
committed
if (FT) {
if (FT->getNumArgs() != 1)
return 0;
Ted Kremenek
committed
TypedefType* ArgT = dyn_cast<TypedefType>(FT->getArgType(0).getTypePtr());
Ted Kremenek
committed
if (!ArgT)
return 0;
if (!ArgT->isPointerType())
return NULL;
}
Ted Kremenek
committed
assert (ScratchArgs.empty());
switch (func) {
case cfretain: {
ScratchArgs.push_back(std::make_pair(0, IncRef));
return getPersistentSummary(RetEffect::MakeAlias(0));
}
case cfrelease: {
ScratchArgs.push_back(std::make_pair(0, DecRef));
return getPersistentSummary(RetEffect::MakeNoRet());
}
case cfmakecollectable: {
if (GCEnabled)
ScratchArgs.push_back(std::make_pair(0, DecRef));
return getPersistentSummary(RetEffect::MakeAlias(0));
}
default:
Ted Kremenek
committed
assert (false && "Not a supported unary function.");
Ted Kremenek
committed
}
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
}
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;
}
RetainSummary* RetainSummaryManager::getCFSummaryCreateRule(FunctionDecl* FD) {
Ted Kremenek
committed
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
if (FT && !isCFRefType(FT->getResultType()))
return 0;
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
assert (ScratchArgs.empty());
return getPersistentSummary(RetEffect::MakeOwned());
RetainSummary* RetainSummaryManager::getCFSummaryGetRule(FunctionDecl* FD) {
Ted Kremenek
committed
FunctionTypeProto* FT =
dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
Ted Kremenek
committed
if (FT) {
QualType RetTy = FT->getResultType();
Ted Kremenek
committed
// 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 0;
}
// FIXME: Add special-cases for functions that retain/release. For now
// just handle the default case.
Ted Kremenek
committed
assert (ScratchArgs.empty());
return getPersistentSummary(RetEffect::MakeNotOwned());
//===----------------------------------------------------------------------===//
// Summary creation for Selectors.
//===----------------------------------------------------------------------===//
RetainSummary* RetainSummaryManager::getInitMethodSummary(Selector S) {
assert(ScratchArgs.empty());
RetainSummary* Summ =
getPersistentSummary(RetEffect::MakeReceiverAlias());
ObjCMethSummaries[S] = Summ;
return Summ;
}
RetainSummary* RetainSummaryManager::getMethodSummary(Selector S) {
// Look up a summary in our cache of Selectors -> Summaries.
ObjCMethSummariesTy::iterator I = ObjCMethSummaries.find(S);
if (I != ObjCMethSummaries.end())
// "initXXX": pass-through for receiver.
const char* s = S.getIdentifierInfoForSlot(0)->getName();
return getInitMethodSummary(S);
return 0;
}
Ted Kremenek
committed
void RetainSummaryManager::InitializeInstMethSummaries() {
assert (ScratchArgs.empty());
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet() : RetEffect::MakeOwned();
RetainSummary* Summ = getPersistentSummary(E);
// Create the "alloc" selector.
ObjCInstMethSummaries[ GetNullarySelector("alloc", Ctx) ] = Summ;
// Create the "new" selector.
ObjCInstMethSummaries[ GetNullarySelector("new", Ctx) ] = Summ;
// Create the "allocWithZone:" selector.
ObjCInstMethSummaries[ GetUnarySelector("allocWithZone", Ctx) ] = Summ;
// Create the "copyWithZone:" selector.
ObjCInstMethSummaries[ GetUnarySelector("copyWithZone", Ctx) ] = Summ;
// Create the "mutableCopyWithZone:" selector.
ObjCInstMethSummaries[ GetUnarySelector("mutableCopyWithZone", Ctx) ] = Summ;
}
Ted Kremenek
committed
void RetainSummaryManager::InitializeMethSummaries() {
assert (ScratchArgs.empty());
// Create the "init" selector.
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeReceiverAlias());
ObjCMethSummaries[ GetNullarySelector("init", Ctx) ] = Summ;
// Create the "copy" selector.
RetEffect E = isGCEnabled() ? RetEffect::MakeNoRet() : RetEffect::MakeOwned();
Summ = getPersistentSummary(E);
ObjCMethSummaries[ GetNullarySelector("copy", Ctx) ] = Summ;
// Create the "mutableCopy" selector.
ObjCMethSummaries[ GetNullarySelector("mutableCopy", Ctx) ] = Summ;
// Create the "retain" selector.
E = RetEffect::MakeReceiverAlias();
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : IncRef);
ObjCMethSummaries[ GetNullarySelector("retain", Ctx) ] = Summ;
// Create the "release" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
ObjCMethSummaries[ GetNullarySelector("release", Ctx) ] = Summ;
// Create the "autorelease" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : StopTracking);
ObjCMethSummaries[ GetNullarySelector("autorelease", Ctx) ] = Summ;
Ted Kremenek
committed
}
RetainSummary*
RetainSummaryManager::getInstanceMethodSummary(IdentifierInfo* ClsName,
Selector S) {
// Look up a summary in our cache of Selectors -> Summaries.
ObjCMethSummariesTy::iterator I = ObjCInstMethSummaries.find(S);
if (I != ObjCInstMethSummaries.end())
// Don't track anything if using GC.
if (isGCEnabled())
return 0;
// Heuristic: XXXXwithYYYY, where XXX is the class name with the "NS"
// stripped off is usually an allocation.
const char* cls = ClsName->getName();
const char* s = S.getIdentifierInfoForSlot(0)->getName();
if (cls[0] == 'N' && cls[1] == 'S')
cls += 2;
if (cls[0] == '\0' || s[0] == '\0' || tolower(cls[0]) != s[0])
return 0;
++cls;
++s;
// Now look at the rest of the characters.
unsigned len = strlen(cls);
// Prefix matches?
return 0;
s += len;
// If 's' is the same as clsName, or 's' has "With" after the class name,
// treat it as an allocator.
do {
if (s[0] == '\0')
break;
break;
return 0;
} while(0);
// Generate the summary.
assert (ScratchArgs.empty());
RetainSummary* Summ = getPersistentSummary(RetEffect::MakeOwned());
ObjCInstMethSummaries[S] = Summ;
return Summ;
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
// Reference-counting logic (typestate + counts).
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN RefVal {
public:
enum Kind {
Owned = 0, // Owning reference.
NotOwned, // Reference is not owned by still valid (not freed).
Released, // Object has been released.
ReturnedOwned, // Returned object passes ownership to caller.
ReturnedNotOwned, // Return object does not pass ownership to caller.
ErrorUseAfterRelease, // Object used after released.
ErrorReleaseNotOwned, // Release of an object that was not owned.
ErrorLeak // A memory leak due to excessive reference counts.
};
private:
Kind kind;
unsigned Cnt;
RefVal(Kind k, unsigned cnt) : kind(k), Cnt(cnt) {}
RefVal(Kind k) : kind(k), Cnt(0) {}
public:
Kind getKind() const { return kind; }
unsigned getCount() const { return Cnt; }
// Useful predicates.
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
static bool isLeak(Kind k) { return k == ErrorLeak; }
Ted Kremenek
committed
bool isOwned() const {
return getKind() == Owned;
}
bool isNotOwned() const {
return getKind() == NotOwned;
}
bool isReturnedOwned() const {
return getKind() == ReturnedOwned;
}
bool isReturnedNotOwned() const {
return getKind() == ReturnedNotOwned;
}
bool isNonLeakError() const {
Kind k = getKind();
return isError(k) && !isLeak(k);
}
// State creation: normal state.
static RefVal makeOwned(unsigned Count = 0) {
return RefVal(Owned, Count);
}
static RefVal makeNotOwned(unsigned Count = 0) {
return RefVal(NotOwned, Count);
}
static RefVal makeReturnedOwned(unsigned Count) {
return RefVal(ReturnedOwned, Count);
}
static RefVal makeReturnedNotOwned() {
return RefVal(ReturnedNotOwned);
}
// State creation: errors.
static RefVal makeLeak(unsigned Count) { return RefVal(ErrorLeak, Count); }
static RefVal makeReleased() { return RefVal(Released); }
static RefVal makeUseAfterRelease() { return RefVal(ErrorUseAfterRelease); }
static RefVal makeReleaseNotOwned() { return RefVal(ErrorReleaseNotOwned); }
// Comparison, profiling, and pretty-printing.
bool operator==(const RefVal& X) const {
return kind == X.kind && Cnt == X.Cnt;
}
Ted Kremenek
committed
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddInteger((unsigned) kind);
ID.AddInteger(Cnt);
}
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
Out << "NotOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
case ReturnedOwned: {
Out << "ReturnedOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
Ted Kremenek
committed
break;
Ted Kremenek
committed
case ReturnedNotOwned: {
Out << "ReturnedNotOwned";
unsigned cnt = getCount();
if (cnt) Out << " (+ " << cnt << ")";
break;
}
Ted Kremenek
committed
case Released:
Out << "Released";
break;
case ErrorLeak:
Out << "Leaked";
break;
Ted Kremenek
committed
case ErrorUseAfterRelease:
Out << "Use-After-Release [ERROR]";
break;
case ErrorReleaseNotOwned:
Out << "Release of Not-Owned [ERROR]";
break;
}
}
static inline unsigned GetCount(RefVal V) {
switch (V.getKind()) {
default:
return V.getCount();
case RefVal::Owned:
return V.getCount()+1;
}
}
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
public:
Ted Kremenek
committed
// Type definitions.
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
typedef RefBindings::Factory RefBFactoryTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*,std::pair<Expr*, SymbolID> >
ReleasesNotOwnedTy;
typedef ReleasesNotOwnedTy UseAfterReleasesTy;
typedef llvm::DenseMap<GRExprEngine::NodeTy*, std::vector<SymbolID>*>
LeaksTy;
Ted Kremenek
committed
class BindingsPrinter : public ValueState::CheckerStatePrinter {
public:
virtual void PrintCheckerState(std::ostream& Out, void* State,
const char* nl, const char* sep);
};
private:
Ted Kremenek
committed
// Instance variables.
RetainSummaryManager Summaries;
const bool EmitStandardWarnings;
const LangOptions& LOpts;
RefBFactoryTy RefBFactory;
UseAfterReleasesTy UseAfterReleases;
ReleasesNotOwnedTy ReleasesNotOwned;
LeaksTy Leaks;
Ted Kremenek
committed
BindingsPrinter Printer;
Selector RetainSelector;
Selector ReleaseSelector;
Selector AutoreleaseSelector;
public:
static RefBindings GetRefBindings(ValueState& StImpl) {
return RefBindings((RefBindings::TreeTy*) StImpl.CheckerState);
}
private:
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,
RefVal::Kind& hasErr);
void ProcessNonLeakError(ExplodedNodeSet<ValueState>& Dst,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* NodeExpr, Expr* ErrorExpr,
ExplodedNode<ValueState>* Pred,
ValueState* St,
RefVal::Kind hasErr, SymbolID Sym);
ValueState* HandleSymbolDeath(ValueStateManager& VMgr, ValueState* St,
SymbolID sid, RefVal V, bool& hasLeak);
ValueState* NukeBinding(ValueStateManager& VMgr, ValueState* St,
SymbolID sid);
Ted Kremenek
committed
Ted Kremenek
committed
CFRefCount(ASTContext& Ctx, bool gcenabled, bool StandardWarnings,
const LangOptions& lopts)
: Summaries(Ctx, gcenabled),
Ted Kremenek
committed
EmitStandardWarnings(StandardWarnings),
LOpts(lopts),
RetainSelector(GetNullarySelector("retain", Ctx)),
ReleaseSelector(GetNullarySelector("release", Ctx)),
AutoreleaseSelector(GetNullarySelector("autorelease", Ctx)) {}
virtual ~CFRefCount() {
for (LeaksTy::iterator I = Leaks.begin(), E = Leaks.end(); I!=E; ++I)
delete I->second;
}
virtual void RegisterChecks(GRExprEngine& Eng);
Ted Kremenek
committed
virtual ValueState::CheckerStatePrinter* getCheckerStatePrinter() {
return &Printer;
}
bool isGCEnabled() const { return Summaries.isGCEnabled(); }
const LangOptions& getLangOptions() const { return LOpts; }
void EvalSummary(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* Ex,
Expr* Receiver,
RetainSummary* Summ,
Expr** arg_beg, Expr** arg_end,
ExplodedNode<ValueState>* Pred);
virtual void EvalCall(ExplodedNodeSet<ValueState>& Dst,
Ted Kremenek
committed
GRExprEngine& Eng,
GRStmtNodeBuilder<ValueState>& Builder,
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
// Stores.
virtual void EvalStore(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
Expr* E, ExplodedNode<ValueState>* Pred,
ValueState* St, RVal TargetLV, RVal Val);
Ted Kremenek
committed
// End-of-path.
virtual void EvalEndPath(GRExprEngine& Engine,
GREndPathNodeBuilder<ValueState>& Builder);
Ted Kremenek
committed
virtual void EvalDeadSymbols(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ExplodedNode<ValueState>* Pred,
Stmt* S,
Ted Kremenek
committed
ValueState* St,
const ValueStateManager::DeadSymbolsTy& Dead);
// Return statements.
virtual void EvalReturn(ExplodedNodeSet<ValueState>& Dst,
GRExprEngine& Engine,
GRStmtNodeBuilder<ValueState>& Builder,
ReturnStmt* S,
ExplodedNode<ValueState>* Pred);
// Assumptions.
virtual ValueState* EvalAssume(GRExprEngine& Engine, ValueState* St,
RVal Cond, bool Assumption, bool& isFeasible);
Ted Kremenek
committed
// Error iterators.
typedef UseAfterReleasesTy::iterator use_after_iterator;
typedef ReleasesNotOwnedTy::iterator bad_release_iterator;
typedef LeaksTy::iterator leaks_iterator;
Ted Kremenek
committed
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(); }
leaks_iterator leaks_begin() { return Leaks.begin(); }
leaks_iterator leaks_end() { return Leaks.end(); }
};
} // end anonymous namespace
Ted Kremenek
committed
void CFRefCount::BindingsPrinter::PrintCheckerState(std::ostream& Out,
void* State, const char* nl,
const char* sep) {
RefBindings B((RefBindings::TreeTy*) State);