2008-03-16 02:59:48 +03:00
|
|
|
// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--//
|
2008-03-06 03:08:09 +03:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2008-03-06 13:40:09 +03:00
|
|
|
// This file defines the methods for CFRefCount, which implements
|
2008-03-06 03:08:09 +03:00
|
|
|
// a reference count checker for Core Foundation (Mac OS X).
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
#include "GRSimpleVals.h"
|
2008-03-06 03:08:09 +03:00
|
|
|
#include "clang/Analysis/PathSensitive/ValueState.h"
|
2008-03-31 22:26:32 +04:00
|
|
|
#include "clang/Analysis/PathDiagnostic.h"
|
2008-03-06 03:08:09 +03:00
|
|
|
#include "clang/Analysis/LocalCheckers.h"
|
2008-04-09 05:10:13 +04:00
|
|
|
#include "clang/Analysis/PathDiagnostic.h"
|
|
|
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
2008-03-11 09:39:11 +03:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
#include "llvm/ADT/ImmutableMap.h"
|
2008-04-09 05:10:13 +04:00
|
|
|
#include "llvm/Support/Compiler.h"
|
2008-03-11 22:44:10 +03:00
|
|
|
#include <ostream>
|
2008-03-06 03:08:09 +03:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Symbolic Evaluation of Reference Counting Logic
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
namespace {
|
|
|
|
enum ArgEffect { IncRef, DecRef, DoNothing };
|
|
|
|
typedef std::vector<ArgEffect> ArgEffects;
|
|
|
|
}
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
namespace llvm {
|
|
|
|
template <> struct FoldingSetTrait<ArgEffects> {
|
2008-04-11 02:58:08 +04:00
|
|
|
static void Profile(const ArgEffects& X, FoldingSetNodeID& ID) {
|
2008-03-11 09:39:11 +03:00
|
|
|
for (ArgEffects::const_iterator I = X.begin(), E = X.end(); I!= E; ++I)
|
|
|
|
ID.AddInteger((unsigned) *I);
|
2008-04-11 02:58:08 +04:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
} // end llvm namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class RetEffect {
|
|
|
|
public:
|
|
|
|
enum Kind { Alias = 0x0, OwnedSymbol = 0x1, NotOwnedSymbol = 0x2 };
|
|
|
|
|
|
|
|
private:
|
|
|
|
unsigned Data;
|
2008-04-11 02:58:08 +04:00
|
|
|
RetEffect(Kind k, unsigned D) { Data = (D << 2) | (unsigned) k; }
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
Kind getKind() const { return (Kind) (Data & 0x3); }
|
|
|
|
|
|
|
|
unsigned getValue() const {
|
|
|
|
assert(getKind() == Alias);
|
2008-04-11 02:58:08 +04:00
|
|
|
return Data >> 2;
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); }
|
|
|
|
|
|
|
|
static RetEffect MakeOwned() { return RetEffect(OwnedSymbol, 0); }
|
|
|
|
|
|
|
|
static RetEffect MakeNotOwned() { return RetEffect(NotOwnedSymbol, 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(); }
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
ArgEffect getArg(unsigned idx) const {
|
|
|
|
assert (idx < getNumArgs());
|
|
|
|
return (*Args)[idx];
|
|
|
|
}
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
RetEffect getRet() const {
|
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
typedef ArgEffects::const_iterator arg_iterator;
|
|
|
|
|
|
|
|
arg_iterator begin_args() const { return Args->begin(); }
|
|
|
|
arg_iterator end_args() const { return Args->end(); }
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
class CFRefSummaryManager {
|
|
|
|
typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<ArgEffects> > AESetTy;
|
|
|
|
typedef llvm::FoldingSet<CFRefSummary> SummarySetTy;
|
|
|
|
typedef llvm::DenseMap<FunctionDecl*, CFRefSummary*> SummaryMapTy;
|
|
|
|
|
2008-04-11 02:58:08 +04:00
|
|
|
ASTContext& Ctx;
|
|
|
|
SummarySetTy SummarySet;
|
|
|
|
SummaryMapTy SummaryMap;
|
|
|
|
AESetTy AESet;
|
|
|
|
llvm::BumpPtrAllocator BPAlloc;
|
|
|
|
ArgEffects ScratchArgs;
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
|
|
|
|
ArgEffects* getArgEffects();
|
|
|
|
|
|
|
|
CFRefSummary* getCannedCFSummary(FunctionTypeProto* FT, bool isRetain);
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
CFRefSummary* getCFSummary(FunctionDecl* FD, const char* FName);
|
|
|
|
|
|
|
|
CFRefSummary* getCFSummaryCreateRule(FunctionTypeProto* FT);
|
|
|
|
CFRefSummary* getCFSummaryGetRule(FunctionTypeProto* FT);
|
|
|
|
|
|
|
|
CFRefSummary* getPersistentSummary(ArgEffects* AE, RetEffect RE);
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
public:
|
2008-04-11 02:58:08 +04:00
|
|
|
CFRefSummaryManager(ASTContext& ctx) : Ctx(ctx) {}
|
2008-03-11 09:39:11 +03:00
|
|
|
~CFRefSummaryManager();
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
CFRefSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Implementation of checker data structures.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
CFRefSummaryManager::~CFRefSummaryManager() {
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// FIXME: The ArgEffects could eventually be allocated from BPAlloc,
|
|
|
|
// mitigating the need to do explicit cleanup of the
|
|
|
|
// Argument-Effect summaries.
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
for (AESetTy::iterator I = AESet.begin(), E = AESet.end(); I!=E; ++I)
|
|
|
|
I->getValue().~ArgEffects();
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
ArgEffects* CFRefSummaryManager::getArgEffects() {
|
|
|
|
|
|
|
|
assert (!ScratchArgs.empty());
|
|
|
|
|
|
|
|
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;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
{ // 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;
|
|
|
|
}
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
#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
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
const char* FName = FD->getIdentifier()->getName();
|
|
|
|
|
|
|
|
if (FName[0] == 'C' && FName[1] == 'F') {
|
|
|
|
CFRefSummary* S = getCFSummary(FD, FName);
|
|
|
|
SummaryMap[FD] = S;
|
|
|
|
return S;
|
|
|
|
}
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
return NULL;
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
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);
|
|
|
|
|
2008-04-11 02:58:08 +04:00
|
|
|
if (strcmp("CFTypeRef", TDName) != 0)
|
2008-03-12 04:21:45 +03:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!ArgT->isPointerType())
|
|
|
|
return NULL;
|
2008-04-11 02:58:08 +04:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
QualType RetTy = FT->getResultType();
|
|
|
|
|
2008-04-11 02:58:08 +04:00
|
|
|
if (isRetain) {
|
|
|
|
// CFRetain: the return type should also be "CFTypeRef".
|
|
|
|
if (RetTy.getTypePtr() != ArgT)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// CFRelease: the return type should be void.
|
|
|
|
|
|
|
|
if (RetTy != Ctx.VoidTy)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
// The function's interface checks out. Generate a canned summary.
|
|
|
|
|
|
|
|
assert (ScratchArgs.empty());
|
|
|
|
ScratchArgs.push_back(isRetain ? IncRef : DecRef);
|
|
|
|
|
|
|
|
return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
for (unsigned i = 0, n = FT->getNumArgs(); i != n; ++i)
|
|
|
|
ScratchArgs.push_back(DoNothing);
|
|
|
|
|
|
|
|
return getPersistentSummary(getArgEffects(), RetEffect::MakeOwned());
|
|
|
|
}
|
|
|
|
|
|
|
|
CFRefSummary*
|
|
|
|
CFRefSummaryManager::getCFSummaryGetRule(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.
|
|
|
|
|
|
|
|
for (unsigned i = 0, n = FT->getNumArgs(); i != n; ++i)
|
|
|
|
ScratchArgs.push_back(DoNothing);
|
|
|
|
|
|
|
|
return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned());
|
|
|
|
}
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// 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
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Transfer functions.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
class VISIBILITY_HIDDEN RefVal {
|
2008-03-11 20:48:22 +03:00
|
|
|
unsigned Data;
|
|
|
|
|
|
|
|
RefVal(unsigned K, unsigned D) : Data((D << 3) | K) {
|
2008-04-11 02:58:08 +04:00
|
|
|
assert ((K & ~0x7) == 0x0);
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
RefVal(unsigned K) : Data(K) {
|
2008-04-11 02:58:08 +04:00
|
|
|
assert ((K & ~0x7) == 0x0);
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
enum Kind { Owned = 0, AcqOwned = 1, NotOwned = 2, Released = 3,
|
|
|
|
ErrorUseAfterRelease = 4, ErrorReleaseNotOwned = 5 };
|
|
|
|
|
|
|
|
|
|
|
|
Kind getKind() const { return (Kind) (Data & 0x5); }
|
|
|
|
|
|
|
|
unsigned getCount() const {
|
|
|
|
assert (getKind() == Owned || getKind() == AcqOwned);
|
|
|
|
return Data >> 3;
|
|
|
|
}
|
|
|
|
|
2008-03-11 21:14:09 +03:00
|
|
|
static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
static RefVal makeOwned(unsigned Count) { return RefVal(Owned, Count); }
|
|
|
|
static RefVal makeAcqOwned(unsigned Count) { return RefVal(AcqOwned, Count); }
|
|
|
|
static RefVal makeNotOwned() { return RefVal(NotOwned); }
|
|
|
|
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); }
|
2008-03-11 22:44:10 +03:00
|
|
|
|
|
|
|
void print(std::ostream& Out) const;
|
2008-03-11 20:48:22 +03:00
|
|
|
};
|
2008-03-11 22:44:10 +03:00
|
|
|
|
|
|
|
void RefVal::print(std::ostream& Out) const {
|
|
|
|
switch (getKind()) {
|
|
|
|
default: assert(false);
|
|
|
|
case Owned:
|
|
|
|
Out << "Owned(" << getCount() << ")";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AcqOwned:
|
|
|
|
Out << "Acquired-Owned(" << getCount() << ")";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NotOwned:
|
|
|
|
Out << "Not-Owned";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Released:
|
|
|
|
Out << "Released";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorUseAfterRelease:
|
|
|
|
Out << "Use-After-Release [ERROR]";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ErrorReleaseNotOwned:
|
|
|
|
Out << "Release of Not-Owned [ERROR]";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
class VISIBILITY_HIDDEN CFRefCount : public GRSimpleVals {
|
2008-03-11 22:44:10 +03:00
|
|
|
|
|
|
|
// Type definitions.
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
typedef llvm::ImmutableMap<SymbolID, RefVal> RefBindings;
|
2008-03-11 09:39:11 +03:00
|
|
|
typedef RefBindings::Factory RefBFactoryTy;
|
2008-03-11 21:14:09 +03:00
|
|
|
|
|
|
|
typedef llvm::SmallPtrSet<GRExprEngine::NodeTy*,2> UseAfterReleasesTy;
|
|
|
|
typedef llvm::SmallPtrSet<GRExprEngine::NodeTy*,2> ReleasesNotOwnedTy;
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
class BindingsPrinter : public ValueState::CheckerStatePrinter {
|
|
|
|
public:
|
|
|
|
virtual void PrintCheckerState(std::ostream& Out, void* State,
|
|
|
|
const char* nl, const char* sep);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Instance variables.
|
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
CFRefSummaryManager Summaries;
|
2008-03-12 04:21:45 +03:00
|
|
|
RefBFactoryTy RefBFactory;
|
|
|
|
|
2008-03-11 21:14:09 +03:00
|
|
|
UseAfterReleasesTy UseAfterReleases;
|
|
|
|
ReleasesNotOwnedTy ReleasesNotOwned;
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
BindingsPrinter Printer;
|
|
|
|
|
|
|
|
// Private methods.
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
RefBindings Update(RefBindings B, SymbolID sym, RefVal V, ArgEffect E,
|
2008-03-11 21:14:09 +03:00
|
|
|
RefVal::Kind& hasError);
|
2008-03-11 22:44:10 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
public:
|
2008-04-11 02:58:08 +04:00
|
|
|
CFRefCount(ASTContext& Ctx) : Summaries(Ctx) {}
|
2008-03-11 09:39:11 +03:00
|
|
|
virtual ~CFRefCount() {}
|
2008-04-10 03:49:11 +04:00
|
|
|
|
|
|
|
virtual void RegisterChecks(GRExprEngine& Eng);
|
2008-03-11 22:44:10 +03:00
|
|
|
|
|
|
|
virtual ValueState::CheckerStatePrinter* getCheckerStatePrinter() {
|
|
|
|
return &Printer;
|
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
// Calls.
|
|
|
|
|
|
|
|
virtual void EvalCall(ExplodedNodeSet<ValueState>& Dst,
|
2008-03-13 00:06:49 +03:00
|
|
|
GRExprEngine& Eng,
|
2008-03-11 09:39:11 +03:00
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
CallExpr* CE, LVal L,
|
|
|
|
ExplodedNode<ValueState>* Pred);
|
2008-04-09 05:10:13 +04:00
|
|
|
|
|
|
|
// Error iterators.
|
|
|
|
|
|
|
|
typedef UseAfterReleasesTy::iterator use_after_iterator;
|
|
|
|
typedef ReleasesNotOwnedTy::iterator bad_release_iterator;
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
use_after_iterator use_after_begin() { return UseAfterReleases.begin(); }
|
|
|
|
use_after_iterator use_after_end() { return UseAfterReleases.end(); }
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
bad_release_iterator bad_release_begin() { return ReleasesNotOwned.begin(); }
|
|
|
|
bad_release_iterator bad_release_end() { return ReleasesNotOwned.end(); }
|
2008-03-11 09:39:11 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
void CFRefCount::RegisterChecks(GRExprEngine& Eng) {
|
|
|
|
GRSimpleVals::RegisterChecks(Eng);
|
|
|
|
Eng.Register(new UseAfterRelease(*this));
|
|
|
|
Eng.Register(new BadRelease(*this));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-11 22:44:10 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-06 03:08:09 +03:00
|
|
|
void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
|
2008-03-13 00:06:49 +03:00
|
|
|
GRExprEngine& Eng,
|
2008-03-12 04:21:45 +03:00
|
|
|
GRStmtNodeBuilder<ValueState>& Builder,
|
|
|
|
CallExpr* CE, LVal L,
|
|
|
|
ExplodedNode<ValueState>* Pred) {
|
|
|
|
|
2008-03-13 00:06:49 +03:00
|
|
|
ValueStateManager& StateMgr = Eng.getStateManager();
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// FIXME: Support calls to things other than lval::FuncVal. At the very
|
|
|
|
// least we should stop tracking ref-state for ref-counted objects passed
|
|
|
|
// to these functions.
|
|
|
|
|
|
|
|
assert (isa<lval::FuncVal>(L) && "Not yet implemented.");
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// Get the summary.
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
lval::FuncVal FV = cast<lval::FuncVal>(L);
|
|
|
|
FunctionDecl* FD = FV.getDecl();
|
2008-03-13 00:06:49 +03:00
|
|
|
CFRefSummary* Summ = Summaries.getSummary(FD, Eng.getContext());
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
// Get the state.
|
|
|
|
|
|
|
|
ValueState* St = Builder.GetState(Pred);
|
|
|
|
|
|
|
|
// Evaluate the effects of the call.
|
|
|
|
|
|
|
|
ValueState StVals = *St;
|
2008-03-11 21:14:09 +03:00
|
|
|
RefVal::Kind hasError = (RefVal::Kind) 0;
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
if (!Summ) {
|
|
|
|
|
|
|
|
// This function has no summary. Invalidate all reference-count state
|
|
|
|
// for arguments passed to this function, and also nuke the values of
|
|
|
|
// arguments passed-by-reference.
|
|
|
|
|
|
|
|
ValueState StVals = *St;
|
2008-03-06 03:08:09 +03:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
|
|
|
|
I != E; ++I) {
|
|
|
|
|
|
|
|
RVal V = StateMgr.GetRVal(St, *I);
|
|
|
|
|
|
|
|
if (isa<lval::SymbolVal>(V)) {
|
|
|
|
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
|
|
|
|
RefBindings B = GetRefBindings(StVals);
|
|
|
|
SetRefBindings(StVals, Remove(B, Sym));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isa<LVal>(V))
|
|
|
|
StateMgr.Unbind(StVals, cast<LVal>(V));
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
St = StateMgr.getPersistentState(StVals);
|
2008-03-13 00:06:49 +03:00
|
|
|
|
|
|
|
// Make up a symbol for the return value of this function.
|
|
|
|
|
|
|
|
if (CE->getType() != Eng.getContext().VoidTy) {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-03-13 00:45:47 +03:00
|
|
|
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
|
2008-03-13 00:06:49 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-03-22 00:30:14 +03:00
|
|
|
Builder.MakeNode(Dst, CE, Pred, St);
|
2008-03-12 04:21:45 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function has a summary. Evaluate the effect of the arguments.
|
|
|
|
|
|
|
|
unsigned idx = 0;
|
|
|
|
|
|
|
|
for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end();
|
|
|
|
I!=E; ++I, ++idx) {
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
RVal V = StateMgr.GetRVal(St, *I);
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
if (isa<lval::SymbolVal>(V)) {
|
|
|
|
SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
|
|
|
|
RefBindings B = GetRefBindings(StVals);
|
2008-03-11 20:48:22 +03:00
|
|
|
|
2008-03-12 04:21:45 +03:00
|
|
|
if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
|
|
|
|
B = Update(B, Sym, T->getValue().second, Summ->getArg(idx), hasError);
|
|
|
|
SetRefBindings(StVals, B);
|
|
|
|
if (hasError) break;
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
if (hasError) {
|
2008-03-12 04:21:45 +03:00
|
|
|
St = StateMgr.getPersistentState(StVals);
|
2008-03-11 21:14:09 +03:00
|
|
|
GRExprEngine::NodeTy* N = Builder.generateNode(CE, St, Pred);
|
|
|
|
|
|
|
|
if (N) {
|
|
|
|
N->markAsSink();
|
|
|
|
|
|
|
|
switch (hasError) {
|
|
|
|
default: assert(false);
|
|
|
|
case RefVal::ErrorUseAfterRelease:
|
|
|
|
UseAfterReleases.insert(N);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::ErrorReleaseNotOwned:
|
|
|
|
ReleasesNotOwned.insert(N);
|
|
|
|
break;
|
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2008-03-11 20:48:22 +03:00
|
|
|
}
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
// Finally, consult the summary for the return value.
|
|
|
|
|
|
|
|
RetEffect RE = Summ->getRet();
|
|
|
|
St = StateMgr.getPersistentState(StVals);
|
|
|
|
|
|
|
|
|
|
|
|
switch (RE.getKind()) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled RetEffect."); break;
|
|
|
|
|
|
|
|
case RetEffect::Alias: {
|
|
|
|
unsigned idx = RE.getValue();
|
|
|
|
assert (idx < CE->getNumArgs());
|
|
|
|
RVal V = StateMgr.GetRVal(St, CE->getArg(idx));
|
2008-03-13 00:06:49 +03:00
|
|
|
St = StateMgr.SetRVal(St, CE, V, Eng.getCFG().isBlkExpr(CE), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RetEffect::OwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-03-13 00:45:47 +03:00
|
|
|
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
ValueState StImpl = *St;
|
|
|
|
RefBindings B = GetRefBindings(StImpl);
|
|
|
|
SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned(1)));
|
|
|
|
|
|
|
|
St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
|
|
|
|
CE, lval::SymbolVal(Sym),
|
2008-03-13 00:06:49 +03:00
|
|
|
Eng.getCFG().isBlkExpr(CE), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RetEffect::NotOwnedSymbol: {
|
|
|
|
unsigned Count = Builder.getCurrentBlockCount();
|
2008-03-13 00:45:47 +03:00
|
|
|
SymbolID Sym = Eng.getSymbolManager().getConjuredSymbol(CE, Count);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
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),
|
2008-03-13 00:06:49 +03:00
|
|
|
Eng.getCFG().isBlkExpr(CE), false);
|
2008-03-12 04:21:45 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-22 00:30:14 +03:00
|
|
|
Builder.MakeNode(Dst, CE, Pred, St);
|
2008-03-06 03:08:09 +03:00
|
|
|
}
|
2008-03-11 09:39:11 +03:00
|
|
|
|
|
|
|
|
|
|
|
CFRefCount::RefBindings CFRefCount::Update(RefBindings B, SymbolID sym,
|
2008-03-11 20:48:22 +03:00
|
|
|
RefVal V, ArgEffect E,
|
2008-03-11 21:14:09 +03:00
|
|
|
RefVal::Kind& hasError) {
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
// FIXME: This dispatch can potentially be sped up by unifiying it into
|
|
|
|
// a single switch statement. Opt for simplicity for now.
|
2008-03-11 09:39:11 +03:00
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
switch (E) {
|
|
|
|
default:
|
|
|
|
assert (false && "Unhandled CFRef transition.");
|
|
|
|
|
|
|
|
case DoNothing:
|
2008-03-12 04:21:45 +03:00
|
|
|
if (V.getKind() == RefVal::Released) {
|
|
|
|
V = RefVal::makeUseAfterRelease();
|
|
|
|
hasError = V.getKind();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-03-11 20:48:22 +03:00
|
|
|
return B;
|
|
|
|
|
|
|
|
case IncRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
|
|
|
|
case RefVal::Owned:
|
|
|
|
V = RefVal::makeOwned(V.getCount()+1); break;
|
|
|
|
|
|
|
|
case RefVal::AcqOwned:
|
|
|
|
V = RefVal::makeAcqOwned(V.getCount()+1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
V = RefVal::makeAcqOwned(1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
|
|
|
V = RefVal::makeUseAfterRelease();
|
2008-03-11 21:14:09 +03:00
|
|
|
hasError = V.getKind();
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DecRef:
|
|
|
|
switch (V.getKind()) {
|
|
|
|
default:
|
|
|
|
assert (false);
|
|
|
|
|
|
|
|
case RefVal::Owned: {
|
|
|
|
unsigned Count = V.getCount() - 1;
|
|
|
|
V = Count ? RefVal::makeOwned(Count) : RefVal::makeReleased();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RefVal::AcqOwned: {
|
|
|
|
unsigned Count = V.getCount() - 1;
|
|
|
|
V = Count ? RefVal::makeAcqOwned(Count) : RefVal::makeNotOwned();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case RefVal::NotOwned:
|
|
|
|
V = RefVal::makeReleaseNotOwned();
|
2008-03-11 21:14:09 +03:00
|
|
|
hasError = V.getKind();
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case RefVal::Released:
|
|
|
|
V = RefVal::makeUseAfterRelease();
|
2008-03-11 21:14:09 +03:00
|
|
|
hasError = V.getKind();
|
2008-03-11 20:48:22 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return RefBFactory.Add(B, sym, V);
|
2008-03-11 09:39:11 +03:00
|
|
|
}
|
|
|
|
|
2008-04-09 05:10:13 +04:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-10 03:49:11 +04:00
|
|
|
// Error reporting.
|
2008-04-09 05:10:13 +04:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
void UseAfterRelease::EmitWarnings(BugReporter& BR) {
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
for (CFRefCount::use_after_iterator I = TF.use_after_begin(),
|
|
|
|
E = TF.use_after_end(); I != E; ++I) {
|
|
|
|
|
|
|
|
BugReport report(*this);
|
|
|
|
BR.EmitPathWarning(report, *I);
|
2008-04-09 05:10:13 +04:00
|
|
|
}
|
2008-04-10 03:49:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void BadRelease::EmitWarnings(BugReporter& BR) {
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-04-10 03:49:11 +04:00
|
|
|
for (CFRefCount::bad_release_iterator I = TF.bad_release_begin(),
|
|
|
|
E = TF.bad_release_end(); I != E; ++I) {
|
|
|
|
|
|
|
|
BugReport report(*this);
|
|
|
|
BR.EmitPathWarning(report, *I);
|
|
|
|
}
|
|
|
|
}
|
2008-04-09 05:10:13 +04:00
|
|
|
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-11 02:16:52 +04:00
|
|
|
// Transfer function creation for external clients.
|
2008-03-11 09:39:11 +03:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2008-04-11 02:58:08 +04:00
|
|
|
GRTransferFuncs* clang::MakeCFRefCountTF(ASTContext& Ctx) {
|
|
|
|
return new CFRefCount(Ctx);
|
|
|
|
}
|